diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..1c31d7ba --- /dev/null +++ b/.clang-format @@ -0,0 +1,67 @@ +# Generated from CLion C/C++ Code Style settings +BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveAssignments: true +AlignOperands: Align +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Always +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterUnion: false + BeforeCatch: true + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +ColumnLimit: 0 +CompactNamespaces: false +ContinuationIndentWidth: 2 +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 2 +KeepEmptyLinesAtTheStartOfBlocks: true +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: None +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PointerAlignment: Right +ReflowComments: false +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: Never +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 2 +Cpp11BracedListStyle: false +UseTab: Never diff --git a/.gitignore b/.gitignore index c676cc4c..ad68b535 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ build -cmake-build-* +cmake-build* .DS_Store - +.vscode +.vs *.swp *.kdev4 -.idea +.cache +.idea \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 5938843f..497981c4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,9 @@ [submodule "moonlight-common-c"] - path = moonlight-common-c + path = third-party/moonlight-common-c url = https://github.com/moonlight-stream/moonlight-common-c.git [submodule "Simple-Web-Server"] - path = Simple-Web-Server + path = third-party/Simple-Web-Server url = https://github.com/loki-47-6F-64/Simple-Web-Server.git [submodule "ViGEmClient"] - path = ViGEmClient + path = third-party/ViGEmClient url = https://github.com/ViGEm/ViGEmClient -[submodule "pre-compiled"] - path = pre-compiled - url = https://bitbucket.org/Loki-47-6F-64/pre-compiled.git diff --git a/CMakeLists.txt b/CMakeLists.txt index a57321d6..35e46e8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,101 +1,128 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.0) project(Sunshine) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) -# On MSYS2, building a stand-alone binary that links with ffmpeg is not possible, -# Therefore, ffmpeg, libx264 and libx265 must be build from source +add_subdirectory(third-party/Simple-Web-Server) + if(WIN32) - option(SUNSHINE_STANDALONE "Compile stand-alone binary of Sunshine" OFF) - if(SUNSHINE_STANDALONE) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") - - if(NOT DEFINED SUNSHINE_PREPARED_BINARIES) - set(SUNSHINE_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/pre-compiled/windows") - endif() - list(PREPEND PLATFORM_LIBRARIES - C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/${CMAKE_CXX_COMPILER_VERSION}/libstdc++.a - C:/msys64/mingw64/x86_64-w64-mingw32/lib/libwinpthread.a - ) - - set(FFMPEG_INCLUDE_DIRS - ${SUNSHINE_PREPARED_BINARIES}/include) - set(FFMPEG_LIBRARIES - ${SUNSHINE_PREPARED_BINARIES}/lib/libavcodec.a - ${SUNSHINE_PREPARED_BINARIES}/lib/libavdevice.a - ${SUNSHINE_PREPARED_BINARIES}/lib/libavfilter.a - ${SUNSHINE_PREPARED_BINARIES}/lib/libavformat.a - ${SUNSHINE_PREPARED_BINARIES}/lib/libavutil.a - ${SUNSHINE_PREPARED_BINARIES}/lib/libpostproc.a - ${SUNSHINE_PREPARED_BINARIES}/lib/libswresample.a - ${SUNSHINE_PREPARED_BINARIES}/lib/libswscale.a - ${SUNSHINE_PREPARED_BINARIES}/lib/libx264.a - ${SUNSHINE_PREPARED_BINARIES}/lib/libx265.a - z lzma bcrypt C:/msys64/mingw64/lib/libiconv.a) - endif() -else() - set(SUNSHINE_STANDALONE OFF) + # Ugly hack to compile with #include + add_compile_definitions( + QOS_FLOWID=UINT32 + PQOS_FLOWID=UINT32* + QOS_NON_ADAPTIVE_FLOW=2) endif() - -add_subdirectory(Simple-Web-Server) -add_subdirectory(moonlight-common-c/enet) +add_subdirectory(third-party/moonlight-common-c/enet) find_package(Threads REQUIRED) find_package(OpenSSL REQUIRED) -if(NOT SUNSHINE_STANDALONE) - find_package(FFmpeg REQUIRED) -endif() - list(APPEND SUNSHINE_COMPILE_OPTIONS -fPIC -Wall -Wno-missing-braces -Wno-maybe-uninitialized -Wno-sign-compare) if(WIN32) + file( + DOWNLOAD "https://github.com/TheElixZammuto/sunshine-prebuilt/releases/download/1.0.0/pre-compiled.zip" "${CMAKE_CURRENT_BINARY_DIR}/pre-compiled.zip" + TIMEOUT 60 + EXPECTED_HASH SHA256=5d59986bd7f619eaaf82b2dd56b5127b747c9cbe8db61e3b898ff6b485298ed6) + + file(ARCHIVE_EXTRACT + INPUT "${CMAKE_CURRENT_BINARY_DIR}/pre-compiled.zip" + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/pre-compiled) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") + + if(NOT DEFINED SUNSHINE_PREPARED_BINARIES) + set(SUNSHINE_PREPARED_BINARIES "${CMAKE_CURRENT_BINARY_DIR}/pre-compiled/windows") + endif() + + add_compile_definitions(SUNSHINE_PLATFORM="windows") add_subdirectory(tools) #This is temporary, only tools for Windows are needed, for now list(APPEND SUNSHINE_DEFINITIONS APPS_JSON="apps_windows.json") - include_directories( - ViGEmClient/include) + + include_directories(third-party/ViGEmClient/include) + set(PLATFORM_TARGET_FILES + sunshine/platform/windows/misc.cpp sunshine/platform/windows/input.cpp sunshine/platform/windows/display.h sunshine/platform/windows/display_base.cpp sunshine/platform/windows/display_vram.cpp sunshine/platform/windows/display_ram.cpp sunshine/platform/windows/audio.cpp - ViGEmClient/src/ViGEmClient.cpp - ViGEmClient/include/ViGEm/Client.h - ViGEmClient/include/ViGEm/Common.h - ViGEmClient/include/ViGEm/Util.h - ViGEmClient/include/ViGEm/km/BusShared.h) + third-party/ViGEmClient/src/ViGEmClient.cpp + third-party/ViGEmClient/include/ViGEm/Client.h + third-party/ViGEmClient/include/ViGEm/Common.h + third-party/ViGEmClient/include/ViGEm/Util.h + third-party/ViGEmClient/include/ViGEm/km/BusShared.h) + + set(OPENSSL_LIBRARIES + libssl.a + libcrypto.a) + + set(FFMPEG_INCLUDE_DIRS + ${SUNSHINE_PREPARED_BINARIES}/include) + set(FFMPEG_LIBRARIES + ${SUNSHINE_PREPARED_BINARIES}/lib/libavcodec.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libavdevice.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libavfilter.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libavformat.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libavutil.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libpostproc.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libswresample.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libswscale.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libx264.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libx265.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libhdr10plus.a + z lzma bcrypt libiconv.a) + list(PREPEND PLATFORM_LIBRARIES + libstdc++.a + libwinpthread.a + libssp.a + Qwave winmm ksuser wsock32 ws2_32 iphlpapi - d3d11 dxgi + d3d11 dxgi D3DCompiler setupapi ) - set_source_files_properties(ViGEmClient/src/ViGEmClient.cpp PROPERTIES COMPILE_DEFINITIONS "UNICODE=1;ERROR_INVALID_DEVICE_OBJECT_PARAMETER=650") - set_source_files_properties(ViGEmClient/src/ViGEmClient.cpp PROPERTIES COMPILE_FLAGS "-Wno-unknown-pragmas -Wno-misleading-indentation -Wno-class-memaccess") + set_source_files_properties(third-party/ViGEmClient/src/ViGEmClient.cpp PROPERTIES COMPILE_DEFINITIONS "UNICODE=1;ERROR_INVALID_DEVICE_OBJECT_PARAMETER=650") + set_source_files_properties(third-party/ViGEmClient/src/ViGEmClient.cpp PROPERTIES COMPILE_FLAGS "-Wno-unknown-pragmas -Wno-misleading-indentation -Wno-class-memaccess") else() + add_compile_definitions(SUNSHINE_PLATFORM="linux") list(APPEND SUNSHINE_DEFINITIONS APPS_JSON="apps_linux.json") find_package(X11 REQUIRED) pkg_check_modules(AVAHI REQUIRED avahi-client) + find_package(FFmpeg REQUIRED) + set(PLATFORM_TARGET_FILES + sunshine/platform/linux/vaapi.h + sunshine/platform/linux/vaapi.cpp + sunshine/platform/linux/misc.cpp sunshine/platform/linux/display.cpp - sunshine/platform/linux/input.cpp) - + sunshine/platform/linux/audio.cpp + sunshine/platform/linux/input.cpp + third-party/glad/src/egl.c + third-party/glad/src/gl.c + third-party/glad/include/EGL/eglplatform.h + third-party/glad/include/KHR/khrplatform.h + third-party/glad/include/glad/gl.h + third-party/glad/include/glad/egl.h) + set(PLATFORM_LIBRARIES Xfixes Xtst xcb xcb-shm xcb-xfixes + Xrandr ${X11_LIBRARIES} + dl evdev pulse pulse-simple @@ -105,7 +132,8 @@ else() set(PLATFORM_INCLUDE_DIRS ${X11_INCLUDE_DIR} ${AVAHI_INCLUDE_DIRS} - /usr/include/libevdev-1.0) + /usr/include/libevdev-1.0 + third-party/glad/include) if(NOT DEFINED SUNSHINE_EXECUTABLE_PATH) set(SUNSHINE_EXECUTABLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/sunshine") @@ -114,16 +142,19 @@ else() configure_file(sunshine.service.in sunshine.service @ONLY) endif() +add_subdirectory(third-party/cbs) + set(Boost_USE_STATIC_LIBS ON) find_package(Boost COMPONENTS log filesystem REQUIRED) set(SUNSHINE_TARGET_FILES - moonlight-common-c/reedsolomon/rs.c - moonlight-common-c/reedsolomon/rs.h - moonlight-common-c/src/Input.h - moonlight-common-c/src/Rtsp.h - moonlight-common-c/src/RtspParser.c - moonlight-common-c/src/Video.h + third-party/moonlight-common-c/reedsolomon/rs.c + third-party/moonlight-common-c/reedsolomon/rs.h + third-party/moonlight-common-c/src/Input.h + third-party/moonlight-common-c/src/Rtsp.h + third-party/moonlight-common-c/src/RtspParser.c + third-party/moonlight-common-c/src/Video.h + sunshine/cbs.cpp sunshine/utility.h sunshine/uuid.h sunshine/config.h @@ -134,6 +165,10 @@ set(SUNSHINE_TARGET_FILES sunshine/crypto.h sunshine/nvhttp.cpp sunshine/nvhttp.h + sunshine/httpcommon.cpp + sunshine/httpcommon.h + sunshine/confighttp.cpp + sunshine/confighttp.h sunshine/rtsp.cpp sunshine/rtsp.h sunshine/stream.cpp @@ -161,9 +196,10 @@ set(SUNSHINE_TARGET_FILES include_directories( ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/Simple-Web-Server - ${CMAKE_CURRENT_SOURCE_DIR}/moonlight-common-c/enet/include - ${CMAKE_CURRENT_SOURCE_DIR}/moonlight-common-c/reedsolomon + ${CMAKE_CURRENT_SOURCE_DIR}/third-party + ${CMAKE_CURRENT_SOURCE_DIR}/third-party/cbs/include + ${CMAKE_CURRENT_SOURCE_DIR}/third-party/moonlight-common-c/enet/include + ${CMAKE_CURRENT_SOURCE_DIR}/third-party/moonlight-common-c/reedsolomon ${FFMPEG_INCLUDE_DIRS} ${PLATFORM_INCLUDE_DIRS} ) @@ -183,13 +219,11 @@ if(NOT SUNSHINE_ASSETS_DIR) set(SUNSHINE_ASSETS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/assets") endif() -if(SUNSHINE_STANDALONE) - set(OPENSSL_LIBRARIES - C:/msys64/mingw64/lib/libssl.a - C:/msys64/mingw64/lib/libcrypto.a) -endif() +list(APPEND CBS_EXTERNAL_LIBRARIES + cbs) list(APPEND SUNSHINE_EXTERNAL_LIBRARIES + ${CBS_EXTERNAL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} stdc++fs enet diff --git a/README.md b/README.md index 79053d2c..2311d4cf 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # Introduction Sunshine is a Gamestream host for Moonlight +[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/cgrtw2g3fq9b0b70/branch/master?svg=true)](https://ci.appveyor.com/project/loki-47-6F-64/sunshine/branch/master) +[![Downloads](https://img.shields.io/github/downloads/Loki-47-6F-64/sunshine/total)](https://github.com/Loki-47-6F-64/sunshine/releases) + - [Building](README.md#building) - [Credits](README.md#credits) @@ -12,48 +15,60 @@ Sunshine is a Gamestream host for Moonlight ### Requirements: Ubuntu 20.04: - - sudo apt install cmake libssl-dev libavdevice-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libpulse-dev libopus-dev libxtst-dev libx11-dev libxfixes-dev libevdev-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev libavahi-client-dev +Install the following +``` +sudo apt install cmake libssl-dev libavdevice-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libpulse-dev libopus-dev libxtst-dev libx11-dev libxrandr-dev libxfixes-dev libevdev-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev libavahi-client-dev +``` ### Compilation: - `git clone https://github.com/loki-47-6F-64/sunshine.git --recurse-submodules` - `cd sunshine && mkdir build && cd build` - `cmake ..` -- `make`: It is suggested to use the `-j C#` flags with this command, `C#` being the number of cores your PC has +- `make -j ${nproc}` ### Setup: sunshine needs access to uinput to create mouse and gamepad events: -- Add user to group 'input': "usermod -a -G input username -- Create a file: "/etc/udev/rules.d/85-sunshine-input.rules" -- The contents of the file is as follows: - KERNEL=="uinput", GROUP="input", mode="0660" -- assets/sunshine.conf is an example configuration file. Modify it as you see fit and use it by running: "sunshine path/to/sunshine.conf" -- path/to/build/dir/sunshine.service is used to start sunshine in the background: - - `cp sunshine.service $HOME/.config/systemd/user/` - - Modify $HOME/.config/systemd/user/sunshine.conf to point to the sunshine executable - - `systemctl --user start sunshine` +- Add user to group 'input': + `usermod -a -G input $USER` +- Create udev rules: + - Run the following command: + `nano /etc/udev/rules.d/85-sunshine-input.rules` + - Input the following contents: + `KERNEL=="uinput", GROUP="input", MODE="0660"` + - Save the file and exit + 1. `CTRL+X` to start exit + 2. `Y` to save modifications +- `assets/sunshine.conf` is an example configuration file. Modify it as you see fit, then use it by running: + `sunshine path/to/sunshine.conf` +- Configure autostart service + `path/to/build/dir/sunshine.service` is used to start sunshine in the background. To use it, do the following: + 1. Copy it to the users systemd, `cp sunshine.service ~/.config/systemd/user/` + 2. Starting + - Onetime: + `systemctl --user start sunshine` + - Always on boot: + `systemctl --user enable sunshine` -- assets/apps.json is an [example](README.md#application-list) of a list of applications that are started just before running a stream +- `assets/apps.json` is an [example](README.md#application-list) of a list of applications that are started just before running a stream ### Trouleshooting: - * If you get "Could not create Sunshine Gamepad: Permission Denied", ensure you are part of the group "input": - * groups - * If Sunshine sends audio from the microphone instead of the speaker, try the following steps: - * pacmd list-sources | grep "name:" - * Copy the name to the configuration option "audio_sink" - * restart sunshine - - +- If you get "Could not create Sunshine Gamepad: Permission Denied", ensure you are part of the group "input": + - `groups $USER` + +- If Sunshine sends audio from the microphone instead of the speaker, try the following steps: + 1. `$ pacmd list-sources | grep "name:"` or `$ pactl info | grep Source` if running pipewire. + 2. Copy the name to the configuration option "audio_sink" + 3. restart sunshine ## Windows 10 ### Requirements: - MSYS2 : mingw-w64-x86_64-openssl mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-ffmpeg mingw-w64-x86_64-boost + mingw-w64-x86_64-openssl mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-opus mingw-w64-x86_64-x265 mingw-w64-x86_64-boost git yasm nasm diffutils make ### Compilation: -- `git clone https://github.com/loki-47-6F-64/sunshine.git --recurse-submodules` +- `git clone https://github.com/loki-47-6F-64/sunshine.git --recursive` - `cd sunshine && mkdir build && cd build` - `cmake -G"Unix Makefiles" ..` - `make` @@ -61,28 +76,20 @@ sunshine needs access to uinput to create mouse and gamepad events: ### Setup: - **OPTIONAL** Gamepad support: Download and run 'ViGEmBus_Setup_1.16.116.exe' from [https://github.com/ViGEm/ViGEmBus/releases] -### Static build -#### Requirements: - MSYS2 : mingw-w64-x86_64-openssl mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-ffmpeg mingw-w64-x86_64-boost git-lfs - -#### Compilation: -- `git lfs install` -- `git clone https://github.com/loki-47-6F-64/sunshine.git --recurse-submodules` -- `cd sunshine && mkdir build && cd build` -- `cmake -DSUNSHINE_STANDALONE=ON -G"Unix Makefiles" ..` -- `make` - # Common ## Usage: - run "sunshine path/to/sunshine.conf" +- If running for the first time, make sure to note the username and password Sunshine showed to you, since you **cannot get back later**! - In Moonlight: Add PC manually -- When Moonlight request you insert the correct pin on sunshine, either: - - Type in the URL bar of your browser: `xxx.xxx.xxx.xxx:47989/pin/####` - - `wget xxx.xxx.xxx.xxx:47989/pin/####` - - The x's are the IP of your instance, `####` is the pin +- When Moonlight request you insert the correct pin on sunshine: + - Type in the URL bar of your browser: `https://xxx.xxx.xxx.xxx:47990` where `xxx.xxx.xxx.xxx` is the IP address of your computer + - Ignore any warning given by your browser about "insecure website" + - Type in the username and password shown the first time you run Sunshine + - Go to "PIN" in the Header + - Type in your PIN and press Enter, you should get a Success Message - Click on one of the Applications listed - Have fun :) @@ -101,8 +108,10 @@ sunshine needs access to uinput to create mouse and gamepad events: - [Simple-Web-Server](https://gitlab.com/eidheim/Simple-Web-Server) - [Moonlight](https://github.com/moonlight-stream) - [Looking-Glass](https://github.com/gnif/LookingGlass) (For showing me how to properly capture frames on Windows, saving me a lot of time :) +- [Eretik](http://eretik.omegahg.com/) (For creating PolicyConfig.h, allowing me to change the default audio device on Windows programmatically) ## Application List: +**Note:** You can change the Application List in the "Apps" section of the User Interface `https://xxx.xxx.xxx.xxx:47990/` - You can use Environment variables in place of values - $(HOME) will be replaced by the value of $HOME - $$ will be replaced by $ --> $$(HOME) will be replaced by $(HOME) @@ -116,15 +125,20 @@ sunshine needs access to uinput to create mouse and gamepad events: "cmd":"command to open app", "prep-cmd":[ { - "do":"somecommand", - "undo":"undothatcommand" + "do":"some-command", + "undo":"undo-that-command" } + ], + "detached":[ + "some-command", + "another-command" ] } ``` - name: Self explanatory - output : The file where the output of the command is stored - If it is not specified, the output is ignored + - detached: A list of commands to be run and forgotten about - prep-cmd: A list of commands to be run before/after the application - If any of the prep-commands fail, starting the application is aborted - do: Run before the application diff --git a/appveyor.yml b/appveyor.yml index 2d2c6bb9..ae6ca859 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,5 @@ image: - - Ubuntu + - Ubuntu2004 - Visual Studio 2019 environment: @@ -8,8 +8,8 @@ environment: - BUILD_TYPE: Release install: - - sh: sudo apt update - - sh: sudo apt install -y build-essential cmake libssl-dev libavdevice-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libpulse-dev libopus-dev libxtst-dev libx11-dev libxfixes-dev libevdev-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev libavahi-client-dev + - sh: sudo apt update --ignore-missing + - sh: sudo apt install -y build-essential fakeroot gcc-10 g++-10 cmake libssl-dev libavdevice-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libpulse-dev libopus-dev libxtst-dev libx11-dev libxrandr-dev libxfixes-dev libevdev-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev libavahi-client-dev - cmd: C:\msys64\usr\bin\bash -lc "pacman --needed --noconfirm -S mingw-w64-x86_64-openssl mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-opus mingw-w64-x86_64-x265 mingw-w64-x86_64-boost git yasm nasm diffutils make" before_build: @@ -20,8 +20,8 @@ before_build: build_script: - cmd: set OLDPATH=%PATH% - cmd: set PATH=C:\msys64\mingw64\bin - - sh: cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSUNSHINE_EXECUTABLE_PATH=sunshine -DSUNSHINE_ASSETS_DIR=/etc/sunshine .. - - cmd: cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSUNSHINE_STANDALONE=ON -DSUNSHINE_ASSETS_DIR=assets -G "MinGW Makefiles" .. + - sh: cmake -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSUNSHINE_EXECUTABLE_PATH=sunshine -DSUNSHINE_ASSETS_DIR=/etc/sunshine .. + - cmd: cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSUNSHINE_ASSETS_DIR=assets -G "MinGW Makefiles" .. - sh: make -j$(nproc) - cmd: mingw32-make -j2 - cmd: set PATH=%OLDPATH% diff --git a/assets/apps_linux.json b/assets/apps_linux.json index 13950778..1cfeb3d4 100644 --- a/assets/apps_linux.json +++ b/assets/apps_linux.json @@ -13,7 +13,7 @@ "name":"Steam BigPicture", "output":"steam.txt", - "cmd":"steam -bigpicture" + "detached":["setsid steam steam://open/bigpicture"] } ] } diff --git a/assets/apps_windows.json b/assets/apps_windows.json index 512fd497..f2a2a7ec 100644 --- a/assets/apps_windows.json +++ b/assets/apps_windows.json @@ -7,9 +7,7 @@ "name":"Steam BigPicture", "output":"steam.txt", - "prep-cmd":[ - {"do":"steam \"steam://open/bigpicture\""} - ] + "detached":["steam steam://open/bigpicture"] } ] } diff --git a/assets/shaders/directx/ConvertUVPS.hlsl b/assets/shaders/directx/ConvertUVPS.hlsl new file mode 100644 index 00000000..2b72cddf --- /dev/null +++ b/assets/shaders/directx/ConvertUVPS.hlsl @@ -0,0 +1,33 @@ +Texture2D image : register(t0); + +SamplerState def_sampler : register(s0); + +struct FragTexWide { + float3 uuv : TEXCOORD0; +}; + +cbuffer ColorMatrix : register(b0) { + float4 color_vec_y; + float4 color_vec_u; + float4 color_vec_v; + float2 range_y; + float2 range_uv; +}; + +//-------------------------------------------------------------------------------------- +// Pixel Shader +//-------------------------------------------------------------------------------------- +float2 main_ps(FragTexWide input) : SV_Target +{ + float3 rgb_left = image.Sample(def_sampler, input.uuv.xz).rgb; + float3 rgb_right = image.Sample(def_sampler, input.uuv.yz).rgb; + float3 rgb = (rgb_left + rgb_right) * 0.5; + + float u = dot(color_vec_u.xyz, rgb) + color_vec_u.w; + float v = dot(color_vec_v.xyz, rgb) + color_vec_v.w; + + u = u * range_uv.x + range_uv.y; + v = v * range_uv.x + range_uv.y; + + return float2(u, v * 224.0f/256.0f + 0.0625); +} \ No newline at end of file diff --git a/assets/shaders/directx/ConvertUVVS.hlsl b/assets/shaders/directx/ConvertUVVS.hlsl new file mode 100644 index 00000000..66d97d43 --- /dev/null +++ b/assets/shaders/directx/ConvertUVVS.hlsl @@ -0,0 +1,29 @@ +struct VertTexPosWide { + float3 uuv : TEXCOORD; + float4 pos : SV_POSITION; +}; + +cbuffer info : register(b0) { + float width_i; +}; + +//-------------------------------------------------------------------------------------- +// Vertex Shader +//-------------------------------------------------------------------------------------- +VertTexPosWide main_vs(uint vI : SV_VERTEXID) +{ + float idHigh = float(vI >> 1); + float idLow = float(vI & uint(1)); + + float x = idHigh * 4.0 - 1.0; + float y = idLow * 4.0 - 1.0; + + float u_right = idHigh * 2.0; + float u_left = u_right - width_i; + float v = 1.0 - idLow * 2.0; + + VertTexPosWide vert_out; + vert_out.uuv = float3(u_left, u_right, v); + vert_out.pos = float4(x, y, 0.0, 1.0); + return vert_out; +} \ No newline at end of file diff --git a/assets/shaders/directx/ConvertYPS.hlsl b/assets/shaders/directx/ConvertYPS.hlsl new file mode 100644 index 00000000..386133c8 --- /dev/null +++ b/assets/shaders/directx/ConvertYPS.hlsl @@ -0,0 +1,25 @@ +Texture2D image : register(t0); + +SamplerState def_sampler : register(s0); + +cbuffer ColorMatrix : register(b0) { + float4 color_vec_y; + float4 color_vec_u; + float4 color_vec_v; + float2 range_y; + float2 range_uv; +}; + +struct PS_INPUT +{ + float4 pos : SV_POSITION; + float2 tex : TEXCOORD; +}; + +float main_ps(PS_INPUT frag_in) : SV_Target +{ + float3 rgb = image.Sample(def_sampler, frag_in.tex, 0).rgb; + float y = dot(color_vec_y.xyz, rgb); + + return y * range_y.x + range_y.y; +} \ No newline at end of file diff --git a/assets/shaders/directx/ScenePS.hlsl b/assets/shaders/directx/ScenePS.hlsl new file mode 100644 index 00000000..aa601231 --- /dev/null +++ b/assets/shaders/directx/ScenePS.hlsl @@ -0,0 +1,14 @@ +Texture2D image : register(t0); + +SamplerState def_sampler : register(s0); + +struct PS_INPUT +{ + float4 pos : SV_POSITION; + float2 tex : TEXCOORD; +}; + +float4 main_ps(PS_INPUT frag_in) : SV_Target +{ + return image.Sample(def_sampler, frag_in.tex, 0); +} \ No newline at end of file diff --git a/assets/shaders/directx/SceneVS.hlsl b/assets/shaders/directx/SceneVS.hlsl new file mode 100644 index 00000000..51319ddb --- /dev/null +++ b/assets/shaders/directx/SceneVS.hlsl @@ -0,0 +1,22 @@ +struct PS_INPUT +{ + float4 pos : SV_POSITION; + float2 tex : TEXCOORD; +}; + +PS_INPUT main_vs(uint vI : SV_VERTEXID) +{ + float idHigh = float(vI >> 1); + float idLow = float(vI & uint(1)); + + float x = idHigh * 4.0 - 1.0; + float y = idLow * 4.0 - 1.0; + + float u = idHigh * 2.0; + float v = 1.0 - idLow * 2.0; + + PS_INPUT vert_out; + vert_out.pos = float4(x, y, 0.0, 1.0); + vert_out.tex = float2(u, v); + return vert_out; +} \ No newline at end of file diff --git a/assets/shaders/opengl/ConvertUV.frag b/assets/shaders/opengl/ConvertUV.frag new file mode 100644 index 00000000..416e1e8b --- /dev/null +++ b/assets/shaders/opengl/ConvertUV.frag @@ -0,0 +1,35 @@ +#version 300 es + +#ifdef GL_ES +precision lowp float; +#endif + +uniform sampler2D image; + +layout(shared) uniform ColorMatrix { + vec4 color_vec_y; + vec4 color_vec_u; + vec4 color_vec_v; + vec2 range_y; + vec2 range_uv; +}; + +in vec3 uuv; +layout(location = 0) out vec2 color; + +//-------------------------------------------------------------------------------------- +// Pixel Shader +//-------------------------------------------------------------------------------------- +void main() { + vec3 rgb_left = texture(image, uuv.xz).rgb; + vec3 rgb_right = texture(image, uuv.yz).rgb; + vec3 rgb = (rgb_left + rgb_right) * 0.5; + + float u = dot(color_vec_u.xyz, rgb) + color_vec_u.w; + float v = dot(color_vec_v.xyz, rgb) + color_vec_v.w; + + u = u * range_uv.x + range_uv.y; + v = v * range_uv.x + range_uv.y; + + color = vec2(u, v * 224.0f / 256.0f + 0.0625); +} \ No newline at end of file diff --git a/assets/shaders/opengl/ConvertUV.vert b/assets/shaders/opengl/ConvertUV.vert new file mode 100644 index 00000000..a71fe58d --- /dev/null +++ b/assets/shaders/opengl/ConvertUV.vert @@ -0,0 +1,27 @@ +#version 300 es + +#ifdef GL_ES +precision mediump float; +#endif + +uniform float width_i; + +out vec3 uuv; +//-------------------------------------------------------------------------------------- +// Vertex Shader +//-------------------------------------------------------------------------------------- +void main() +{ + float idHigh = float(gl_VertexID >> 1); + float idLow = float(gl_VertexID & int(1)); + + float x = idHigh * 4.0 - 1.0; + float y = idLow * 4.0 - 1.0; + + float u_right = idHigh * 2.0; + float u_left = u_right - width_i; + float v = idLow * 2.0; + + uuv = vec3(u_left, u_right, v); + gl_Position = vec4(x, y, 0.0, 1.0); +} \ No newline at end of file diff --git a/assets/shaders/opengl/ConvertY.frag b/assets/shaders/opengl/ConvertY.frag new file mode 100644 index 00000000..564fa25a --- /dev/null +++ b/assets/shaders/opengl/ConvertY.frag @@ -0,0 +1,26 @@ +#version 300 es + +#ifdef GL_ES +precision lowp float; +#endif + +uniform sampler2D image; + +layout(shared) uniform ColorMatrix { + vec4 color_vec_y; + vec4 color_vec_u; + vec4 color_vec_v; + vec2 range_y; + vec2 range_uv; +}; + +in vec2 tex; +layout(location = 0) out float color; + +void main() +{ + vec3 rgb = texture(image, tex).rgb; + float y = dot(color_vec_y.xyz, rgb); + + color = y * range_y.x + range_y.y; +} \ No newline at end of file diff --git a/assets/shaders/opengl/Scene.frag b/assets/shaders/opengl/Scene.frag new file mode 100644 index 00000000..6375f1fd --- /dev/null +++ b/assets/shaders/opengl/Scene.frag @@ -0,0 +1,14 @@ +#version 300 es + +#ifdef GL_ES +precision lowp float; +#endif + +uniform sampler2D image; + +in vec2 tex; +layout(location = 0) out vec4 color; +void main() +{ + color = texture(image, tex); +} \ No newline at end of file diff --git a/assets/shaders/opengl/Scene.vert b/assets/shaders/opengl/Scene.vert new file mode 100644 index 00000000..258878f4 --- /dev/null +++ b/assets/shaders/opengl/Scene.vert @@ -0,0 +1,22 @@ +#version 300 es + +#ifdef GL_ES +precision mediump float; +#endif + +out vec2 tex; + +void main() +{ + float idHigh = float(gl_VertexID >> 1); + float idLow = float(gl_VertexID & int(1)); + + float x = idHigh * 4.0 - 1.0; + float y = idLow * 4.0 - 1.0; + + float u = idHigh * 2.0; + float v = idLow * 2.0; + + gl_Position = vec4(x, y, 0.0, 1.0); + tex = vec2(u, v); +} \ No newline at end of file diff --git a/assets/sunshine.conf b/assets/sunshine.conf index 3fabb634..14e3dc58 100644 --- a/assets/sunshine.conf +++ b/assets/sunshine.conf @@ -37,6 +37,30 @@ # The file where current state of Sunshine is stored # file_state = sunshine_state.json +# The file where user credentials for the UI are stored +# By default, credentials are stored in `file_state` +# credentials_file = sunshine_state.json + +# The display modes advertised by Sunshine +# +# Some versions of Moonlight, such as Moonlight-nx (Switch), +# rely on this list to ensure that the requested resolutions and fps +# are supported. +# +# fps = [10, 30, 60, 90, 120] +# resolutions = [ +# 352x240, +# 480x360, +# 858x480, +# 1280x720, +# 1920x1080, +# 2560x1080, +# 3440x1440, +# 1920x1200, +# 3860x2160, +# 3840x1600, +# ] + # How long to wait in milliseconds for data from moonlight before shutting down the stream # ping_timeout = 2000 @@ -79,12 +103,17 @@ # # You can find the name of the audio sink using the following command: # !! Linux only !! -# pacmd list-sources | grep "name:" -# audio_sink = alsa_output.pci-0000_09_00.3.analog-stereo.monitor +# pacmd list-sinks | grep "name:" if running vanilla pulseaudio +# pactl info | grep Source` if running pipewire +# audio_sink = alsa_output.pci-0000_09_00.3.analog-stereo # # !! Windows only !! # tools\audio-info.exe -# audio_sink = {0.0.0.00000000}.{FD47D9CC-4218-4135-9CE2-0C195C87405B} +# audio_sink = {0.0.0.00000000}.{FD47D9CC-4218-4135-9CE2-0C195C87405B} +# +# The virtual sink, is the audio device that's virtual (Like Steam Streaming Speakers), it allows Sunshine +# to stream audio, while muting the speakers. +# virtual_sink = {0.0.0.00000000}.{8edba70c-1125-467c-b89c-15da389bc1d4} # !! Windows only !! # You can select the video card you want to stream: @@ -93,6 +122,13 @@ # adapter_name = Radeon RX 580 Series # output_name = \\.\DISPLAY1 +# !! Linux only !! +# Set the display number to stream. +# You can find them by the following command: +# xrandr --listmonitors +# Example output: "0: +HDMI-1 1920/518x1200/324+0+0 HDMI-1" +# ^ <-- You need this. +# output_name = 0 ############################################### # FFmpeg software encoding parameters @@ -121,11 +157,12 @@ # If set to 1, Sunshine will not advertise support for HEVC # If set to 2, Sunshine will advertise support for HEVC Main profile # If set to 3, Sunshine will advertise support for HEVC Main and Main10 (HDR) profiles -# hevc_mode = 0 +# hevc_mode = 2 # Force a specific encoder, otherwise Sunshine will use the first encoder that is available # supported encoders: # nvenc +# amdvce # NOTE: alpha stage. The cursor is not yet displayed # software # # encoder = nvenc @@ -170,6 +207,42 @@ ########################## # nv_coder = auto +##################################### AMD ##################################### +###### presets ########### +# default +# speed +# balanced +########################## +# amd_preset = balanced +# +####### rate control ##### +# auto -- let ffmpeg decide rate control +# constqp -- constant QP mode +# vbr_latency -- Latency Constrained Variable Bitrate +# vbr_peak -- Peak Contrained Variable Bitrate +# cbr -- constant bitrate +########################## +# amd_rc = auto + +###### h264 entropy ###### +# auto -- let ffmpeg nvenc decide the entropy encoding +# cabac +# cavlc +########################## +# amd_coder = auto + +#################################### VAAPI ################################### +####### adapter ########## +# Unlike with `amdvce` and `nvenc`, it doesn't matter if video encoding is done +# on a different GPU. +# Run the following commands: +# 1. ls /dev/dri/renderD* +# to find all devices capable of VAAPI +# 2. vainfo --display drm --device /dev/dri/renderD129 | grep -E "((VAProfileH264High|VAProfileHEVCMain|VAProfileHEVCMain10).*VAEntrypointEncSlice)|Driver version" +# Lists the name and capabilities of the device. +# To be supported by Sunshine, it needs to have at the very minimum: +# VAProfileH264High : VAEntrypointEncSlice +# adapter_name = /dev/dri/renderD128 ############################################## # Some configurable parameters, are merely toggles for specific features @@ -177,6 +250,6 @@ # Here, you change the default state of any flag # # To set the initial state of flags -0 and -1 to on, set the following flags: -# flags = 01 +# flags = 012 # # See: sunshine --help for all options under the header: flags diff --git a/assets/web/apps.html b/assets/web/apps.html new file mode 100644 index 00000000..6a5056d8 --- /dev/null +++ b/assets/web/apps.html @@ -0,0 +1,162 @@ +
+
+

Applications

+
Applications are refreshed only when Client is restarted
+
+
+ + + + + + + + + + + + + +
NameActions
{{app.name}} + +
+
+
+
+ +
+ + +
Application Name, as shown on Moonlight
+
+ +
+ + +
The file where the output of the command is stored, if it is not + specified, the output is ignored
+
+ +
+ +
A list of commands to be run before/after the application.
If any of the + prep-commands fail, starting the application is aborted
+ + + + + + + + + + + + + +
DoUndo
+ +
+ +
+ +
+
{{c}}
+ +
+
+ + +
+
A list of commands to be run and forgotten about
+
+ +
+ + +
The main application, if it is not specified, a processs is started that + sleeps indefinitely
+
+ +
+ + +
+
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/assets/web/clients.html b/assets/web/clients.html new file mode 100644 index 00000000..c2d03025 --- /dev/null +++ b/assets/web/clients.html @@ -0,0 +1,3 @@ +
+

Clients

+
\ No newline at end of file diff --git a/assets/web/config.html b/assets/web/config.html new file mode 100644 index 00000000..d9e8628f --- /dev/null +++ b/assets/web/config.html @@ -0,0 +1,560 @@ +
+

Configuration

+
+ + + +
+ +
+ + +
The name displayed by Moonlight. If not specified, the PC's hostname is used +
+
+ +
+ + +
The minimum log level printed to standard out
+
+ +
+ + +
The origin of the remote endpoint address that is not denied for HTTP method /pin +
+
+ +
+ + +
If no external IP address is given, the local IP address is used
+
+ +
+ + +
How long to wait in milliseconds for data from moonlight before shutting down the + stream
+
+ +
+ +
+ +
+
+ {{r}} + × +
+
+ + +
+
+
+
+ +
+
+ {{f}} + × +
+
+ + +
+
+
+
+ The display modes advertised by Sunshine
+ Some versions of Moonlight, such as Moonlight-nx (Switch), + rely on this list to ensure that the requested resolutions and fps + are supported. +
+
+
+ +
+ +
+ + +
The private key must be 2048 bits
+
+ +
+ + +
The certificate must be signed with a 2048 bit key
+
+ + +
+ + +
The file where current state of Sunshine is stored
+
+ +
+ + +
The file where current apps of Sunshine are stored
+
+
+
+ +
+ + +
+ The back/select button on the controller.
+ On the Shield, the home and powerbutton are not passed to Moonlight.
+ If, after the timeout, the back button is still pressed down, Home/Guide button press is + emulated.
+ If back_button_timeout < 0, then the Home/Guide button will not be emulated
+
+
+ +
+ + +
+ Control how fast keys will repeat themselves
+ The initial delay in milliseconds before repeating keys +
+
+ +
+ + +
+ How often keys repeat every second
+ This configurable option supports decimals +
+
+
+ +
+ +
+ + +
+ The name of the audio sink used for Audio Loopback
+ You can find the name of the audio sink using the following command:
+
tools\audio-info.exe
+
+
+
+ + +
+ The name of the audio sink used for Audio Loopback
+ If you do not specify this variable, pulseaudio will select the default monitor device.
+
+ You can find the name of the audio sink using either command:
+
pacmd list-sinks | grep "name:"
+
pactl info | grep Source

+
+
+ +
+ + +
+ The virtual sink, is the audio device that's virtual (Like Steam Streaming Speakers), it allows + Sunshine + to stream audio, while muting the speakers. +
+
+ +
+ + +
+ You can select the video card you want to stream:
+ The appropriate values can be found using the following command:
+
tools\dxgi-info.exe
+
+
+ +
+ + +
+ You can select the video card you want to stream:
+ The appropriate values can be found using the following command:
+ tools\dxgi-info.exe

+
+
+
+ + +
+ xrandr --listmonitors
+ Example output: +
   0: +HDMI-1 1920/518x1200/324+0+0 HDMI-1
+
+
+
+
+ +
+ + +
+ Constant Rate Factor. Between 1 and 52. It allows QP to go up during motion and down with still + image, + resulting in constant perceived quality
+ Higher value means more compression, but less quality
+ If crf == 0, then use QP directly instead +
+
+ +
+ + +
+ Quantitization Parameter
+ Higher value means more compression, but less quality
+ If crf != 0, then this parameter is ignored +
+
+ +
+ + +
+ Minimum number of threads used by ffmpeg to encode the video.
+ Increasing the value slightly reduces encoding efficiency, but the tradeoff is usually
+ worth it to gain the use of more CPU cores for encoding. The ideal value is the lowest
+ value that can reliably encode at your desired streaming settings on your hardware. +
+
+ +
+ + +
+ Allows the client to request HEVC Main or HEVC Main10 video streams.
+ HEVC is more CPU-intensive to encode, so enabling this may reduce performance when using + software + encoding. +
+
+ +
+ + +
+ Force a specific encoder, otherwise Sunshine will use the first encoder that is available +
+
+ +
+ + +
+ How much error correcting packets must be send for every video.
+ This is just some random number, don't know the optimal value.
+ The higher fec_percentage, the lower space for the actual data to send per frame there is +
+
+ +
+ + +
+ When multicasting, it could be useful to have different configurations for each connected + Client. + For example: +
    +
  • Clients connected through WAN and LAN have different bitrate contstraints.
  • +
  • Decoders may require different settings for color
  • +
+ Unlike simply broadcasting to multiple Client, this will generate distinct video streams.
+ Note, CPU usage increases for each distinct video stream generated +
+
+ +
+ + +
+ Store Username/Password seperately from Sunshine's state file. +
+
+
+ +
+
+ + +
+
+ + +
+
+ +
+ +
+ + +
+
+ + +
+
+ + +
+
+ +
+ +
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+
Success! Restart Sunshine to apply changes
+
+ +
+
+ + + + diff --git a/assets/web/header.html b/assets/web/header.html new file mode 100644 index 00000000..1c9702e6 --- /dev/null +++ b/assets/web/header.html @@ -0,0 +1,45 @@ + + + + + + + + Sunshine + + + + + + + \ No newline at end of file diff --git a/assets/web/index.html b/assets/web/index.html new file mode 100644 index 00000000..4b02f845 --- /dev/null +++ b/assets/web/index.html @@ -0,0 +1,9 @@ +
+
+
+

Hello, Sunshine!

+

Sunshine is a Gamestream host for Moonlight

+ Official GitHub Repository +
+
+
\ No newline at end of file diff --git a/assets/web/password.html b/assets/web/password.html new file mode 100644 index 00000000..3e3f2140 --- /dev/null +++ b/assets/web/password.html @@ -0,0 +1,97 @@ +
+

Password Change

+
+
+
+

Current Credentials

+
+ + +
 
+
+
+ + +
+
+
+

New Credentials

+
+ + +
If not specified, the username will not change +
+
+
+ + +
+
+ + +
+
+
+
Error: {{error}}
+
Success! This page will reload soon, your browser will ask you for the new credentials
+
+ +
+
+
+ + + + \ No newline at end of file diff --git a/assets/web/pin.html b/assets/web/pin.html new file mode 100644 index 00000000..89e5b704 --- /dev/null +++ b/assets/web/pin.html @@ -0,0 +1,31 @@ + +
+

PIN Pairing

+
+
+ + +
+
+ Warning! Make sure you have access to the client you are pairing with.
+ This software can give total control to your computer, so be careful! +
+
+
+
+ + \ No newline at end of file diff --git a/gen-deb.in b/gen-deb.in index cc7c6a0a..7f05e136 100755 --- a/gen-deb.in +++ b/gen-deb.in @@ -35,8 +35,8 @@ Package: sunshine Architecture: amd64 Maintainer: @loki Priority: optional -Version: 0.2.1 -Depends: libssl1.1, libavdevice58, libboost-thread1.71.0, libboost-filesystem1.71.0, libboost-log1.71.0, libpulse0, libopus0, libxcb-shm0, libxcb-xfixes0 +Version: 0.7.7 +Depends: libssl1.1, libavdevice58, libboost-thread1.67.0 | libboost-thread1.71.0, libboost-filesystem1.67.0 | libboost-filesystem1.71.0, libboost-log1.67.0 | libboost-log1.71.0, libpulse0, libopus0, libxcb-shm0, libxcb-xfixes0 Description: Gamestream host for Moonlight EOF @@ -54,19 +54,36 @@ if [ -f /etc/group ]; then else echo "Warning: /etc/group not found" fi + +# Update permissions on config files for Web Manager +if [ -f /etc/sunshine/apps_linux.json ]; then + echo "chmod 666 /etc/sunshine/apps_linux.json" + chmod 666 /etc/sunshine/apps_linux.json +fi + +if [ -f /etc/sunshine/sunshine.conf ]; then + echo "chmod 666 /etc/sunshine/sunshine.conf" + chmod 666 /etc/sunshine/sunshine.conf +fi EOF cat << 'EOF' > $RULES/85-sunshine-rules.rules KERNEL=="uinput", GROUP="input", MODE="0660" EOF +mkdir -p $ASSETS/shaders + cp sunshine $BIN/sunshine cp @CMAKE_CURRENT_SOURCE_DIR@/assets/apps_linux.json $ASSETS/apps_linux.json cp @CMAKE_CURRENT_SOURCE_DIR@/assets/sunshine.conf $ASSETS/sunshine.conf +cp -r @CMAKE_CURRENT_SOURCE_DIR@/assets/web $ASSETS/web +cp -r @CMAKE_CURRENT_SOURCE_DIR@/assets/shaders/opengl $ASSETS/shaders/opengl chmod 755 $DEBIAN/postinst chmod 755 $BIN/sunshine chmod 644 $RULES/85-sunshine-rules.rules +chmod 666 $ASSETS/apps_linux.json +chmod 666 $ASSETS/sunshine.conf cd package-deb if fakeroot dpkg-deb --build sunshine; then diff --git a/moonlight-common-c b/moonlight-common-c deleted file mode 160000 index cfeb0ffd..00000000 --- a/moonlight-common-c +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cfeb0ffd90992ee0fa4b6b4a179652e3e2786873 diff --git a/pre-compiled b/pre-compiled deleted file mode 160000 index afd9a9bb..00000000 --- a/pre-compiled +++ /dev/null @@ -1 +0,0 @@ -Subproject commit afd9a9bbfc6ee1a064b0c1f9210bc20b2170c416 diff --git a/sunshine.service.in b/sunshine.service.in index 802442df..c0c38289 100644 --- a/sunshine.service.in +++ b/sunshine.service.in @@ -2,12 +2,7 @@ Description=Sunshine Gamestream Server for Moonlight [Service] -WorkingDirectory=/home/%u -Environment="DISPLAY=:0" -Type=simple -# wait for Xorg -ExecStartPre=/bin/sh -c 'while ! pgrep Xorg; do sleep 2; done' ExecStart=@SUNSHINE_EXECUTABLE_PATH@ [Install] -WantedBy=default.target +WantedBy=graphical-session.target diff --git a/sunshine/audio.cpp b/sunshine/audio.cpp index 4e13cc8f..437703ba 100644 --- a/sunshine/audio.cpp +++ b/sunshine/audio.cpp @@ -4,55 +4,80 @@ #include "platform/common.h" -#include "utility.h" -#include "thread_safe.h" #include "audio.h" +#include "config.h" #include "main.h" +#include "thread_safe.h" +#include "utility.h" namespace audio { using namespace std::literals; -using opus_t = util::safe_ptr; +using opus_t = util::safe_ptr; using sample_queue_t = std::shared_ptr>>; -struct opus_stream_config_t { - std::int32_t sampleRate; - int channelCount; - int streams; - int coupledStreams; - const std::uint8_t *mapping; +struct audio_ctx_t { + // We want to change the sink for the first stream only + std::unique_ptr sink_flag; + + std::unique_ptr control; + + bool restore_sink; + platf::sink_t sink; }; -constexpr std::uint8_t map_stereo[] { 0, 1 }; -constexpr std::uint8_t map_surround51[] {0, 4, 1, 5, 2, 3}; -constexpr std::uint8_t map_high_surround51[] {0, 1, 2, 3, 4, 5}; +static int start_audio_control(audio_ctx_t &ctx); +static void stop_audio_control(audio_ctx_t &); + +int map_stream(int channels, bool quality); + constexpr auto SAMPLE_RATE = 48000; -static opus_stream_config_t stereo = { + +opus_stream_config_t stream_configs[MAX_STREAM_CONFIG] { + { SAMPLE_RATE, 2, 1, 1, - map_stereo -}; - -static opus_stream_config_t Surround51 = { + platf::speaker::map_stereo, + }, + { SAMPLE_RATE, 6, 4, 2, - map_surround51 -}; - -static opus_stream_config_t HighSurround51 = { + platf::speaker::map_surround51, + }, + { SAMPLE_RATE, 6, 6, 0, - map_high_surround51 + platf::speaker::map_surround51, + }, + { + SAMPLE_RATE, + 8, + 5, + 3, + platf::speaker::map_surround71, + }, + { + SAMPLE_RATE, + 8, + 8, + 0, + platf::speaker::map_surround71, + }, }; -void encodeThread(packet_queue_t packets, sample_queue_t samples, config_t config, void *channel_data) { +auto control_shared = safe::make_shared(start_audio_control, stop_audio_control); + +void encodeThread(sample_queue_t samples, config_t config, void *channel_data) { + auto packets = mail::man->queue(mail::audio_packets); + //FIXME: Pick correct opus_stream_config_t based on config.channels - auto stream = &stereo; + auto stream = &stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])]; + opus_t opus { opus_multistream_encoder_create( stream->sampleRate, stream->channelCount, @@ -60,16 +85,17 @@ void encodeThread(packet_queue_t packets, sample_queue_t samples, config_t confi stream->coupledStreams, stream->mapping, OPUS_APPLICATION_AUDIO, - nullptr) - }; + nullptr) }; + + opus_multistream_encoder_ctl(opus.get(), OPUS_SET_VBR(0)); auto frame_size = config.packetDuration * stream->sampleRate / 1000; while(auto sample = samples->pop()) { - packet_t packet { 16*1024 }; // 16KB + buffer_t packet { 1024 }; // 1KB int bytes = opus_multistream_encode(opus.get(), sample->data(), frame_size, std::begin(packet), packet.size()); if(bytes < 0) { - BOOST_LOG(error) << opus_strerror(bytes); + BOOST_LOG(error) << "Couldn't encode audio: "sv << opus_strerror(bytes); packets->stop(); return; @@ -80,9 +106,53 @@ void encodeThread(packet_queue_t packets, sample_queue_t samples, config_t confi } } -void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t config, void *channel_data) { +void capture(safe::mail_t mail, config_t config, void *channel_data) { + auto shutdown_event = mail->event(mail::shutdown); + + //FIXME: Pick correct opus_stream_config_t based on config.channels + auto stream = &stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])]; + + auto ref = control_shared.ref(); + if(!ref) { + return; + } + + auto &control = ref->control; + if(!control) { + BOOST_LOG(error) << "Couldn't create audio control"sv; + + return; + } + + std::string *sink = + config::audio.sink.empty() ? &ref->sink.host : &config::audio.sink; + if(ref->sink.null) { + auto &null = *ref->sink.null; + switch(stream->channelCount) { + case 2: + sink = &null.stereo; + break; + case 6: + sink = &null.surround51; + break; + case 8: + sink = &null.surround71; + break; + } + } + + // Only the first to start a session may change the default sink + if(!ref->sink_flag->exchange(true, std::memory_order_acquire)) { + ref->restore_sink = !config.flags[config_t::HOST_AUDIO]; + + // If the client requests audio on the host, don't change the default sink + if(!config.flags[config_t::HOST_AUDIO] && control->set_sink(*sink)) { + return; + } + } + auto samples = std::make_shared(30); - std::thread thread { encodeThread, packets, samples, config, channel_data }; + std::thread thread { encodeThread, samples, config, channel_data }; auto fg = util::fail_guard([&]() { samples->stop(); @@ -91,43 +161,85 @@ void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t co shutdown_event->view(); }); - //FIXME: Pick correct opus_stream_config_t based on config.channels - auto stream = &stereo; + auto frame_size = config.packetDuration * stream->sampleRate / 1000; + int samples_per_frame = frame_size * stream->channelCount; - auto mic = platf::microphone(stream->sampleRate); + auto mic = control->microphone(stream->mapping, stream->channelCount, stream->sampleRate, frame_size); if(!mic) { - BOOST_LOG(error) << "Couldn't create audio input"sv ; + BOOST_LOG(error) << "Couldn't create audio input"sv; return; } - auto frame_size = config.packetDuration * stream->sampleRate / 1000; - int samples_per_frame = frame_size * stream->channelCount; - while(!shutdown_event->peek()) { std::vector sample_buffer; sample_buffer.resize(samples_per_frame); auto status = mic->sample(sample_buffer); switch(status) { - case platf::capture_e::ok: - break; - case platf::capture_e::timeout: - continue; - case platf::capture_e::reinit: - mic.reset(); - mic = platf::microphone(stream->sampleRate); - if(!mic) { - BOOST_LOG(error) << "Couldn't re-initialize audio input"sv ; + case platf::capture_e::ok: + break; + case platf::capture_e::timeout: + continue; + case platf::capture_e::reinit: + mic.reset(); + mic = control->microphone(stream->mapping, stream->channelCount, stream->sampleRate, frame_size); + if(!mic) { + BOOST_LOG(error) << "Couldn't re-initialize audio input"sv; - return; - } - return; - default: return; + } + return; + default: + return; } samples->raise(std::move(sample_buffer)); } } + +int map_stream(int channels, bool quality) { + int shift = quality ? 1 : 0; + switch(channels) { + case 2: + return STEREO; + case 6: + return SURROUND51 + shift; + case 8: + return SURROUND71 + shift; + } + return STEREO; } + +int start_audio_control(audio_ctx_t &ctx) { + ctx.sink_flag = std::make_unique(false); + + if(!(ctx.control = platf::audio_control())) { + return -1; + } + + auto sink = ctx.control->sink_info(); + if(!sink) { + return -1; + } + + // The default sink has not been replaced yet. + ctx.restore_sink = false; + + ctx.sink = std::move(*sink); + return 0; +} + +void stop_audio_control(audio_ctx_t &ctx) { + // restore audio-sink if applicable + if(!ctx.restore_sink) { + return; + } + + const std::string &sink = config::audio.sink.empty() ? ctx.sink.host : config::audio.sink; + if(!sink.empty()) { + // Best effort, it's allowed to fail + ctx.control->set_sink(sink); + } +} +} // namespace audio diff --git a/sunshine/audio.h b/sunshine/audio.h index 4e27c8c0..67f3af13 100644 --- a/sunshine/audio.h +++ b/sunshine/audio.h @@ -1,18 +1,45 @@ #ifndef SUNSHINE_AUDIO_H #define SUNSHINE_AUDIO_H -#include "utility.h" #include "thread_safe.h" +#include "utility.h" namespace audio { +enum stream_config_e : int { + STEREO, + SURROUND51, + HIGH_SURROUND51, + SURROUND71, + HIGH_SURROUND71, + MAX_STREAM_CONFIG +}; + +struct opus_stream_config_t { + std::int32_t sampleRate; + int channelCount; + int streams; + int coupledStreams; + const std::uint8_t *mapping; +}; + +extern opus_stream_config_t stream_configs[MAX_STREAM_CONFIG]; + struct config_t { + enum flags_e : int { + HIGH_QUALITY, + HOST_AUDIO, + MAX_FLAGS + }; + int packetDuration; int channels; int mask; + + std::bitset flags; }; -using packet_t = util::buffer_t; -using packet_queue_t = std::shared_ptr>>; -void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t config, void *channel_data); -} +using buffer_t = util::buffer_t; +using packet_t = std::pair; +void capture(safe::mail_t mail, config_t config, void *channel_data); +} // namespace audio #endif diff --git a/sunshine/cbs.cpp b/sunshine/cbs.cpp new file mode 100644 index 00000000..e4e24571 --- /dev/null +++ b/sunshine/cbs.cpp @@ -0,0 +1,300 @@ +extern "C" { +#include +#include +#include +#include +#include +} + +#include "cbs.h" +#include "main.h" +#include "utility.h" + +using namespace std::literals; +namespace cbs { +void close(CodedBitstreamContext *c) { + ff_cbs_close(&c); +} + +using ctx_t = util::safe_ptr; + +class frag_t : public CodedBitstreamFragment { +public: + frag_t(frag_t &&o) { + std::copy((std::uint8_t *)&o, (std::uint8_t *)(&o + 1), (std::uint8_t *)this); + + o.data = nullptr; + o.units = nullptr; + }; + + frag_t() { + std::fill_n((std::uint8_t *)this, sizeof(*this), 0); + } + + frag_t &operator=(frag_t &&o) { + std::copy((std::uint8_t *)&o, (std::uint8_t *)(&o + 1), (std::uint8_t *)this); + + o.data = nullptr; + o.units = nullptr; + + return *this; + }; + + + ~frag_t() { + if(data || units) { + ff_cbs_fragment_free(this); + } + } +}; + +util::buffer_t write(const cbs::ctx_t &cbs_ctx, std::uint8_t nal, void *uh, AVCodecID codec_id) { + cbs::frag_t frag; + auto err = ff_cbs_insert_unit_content(&frag, -1, nal, uh, nullptr); + if(err < 0) { + char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; + BOOST_LOG(error) << "Could not insert NAL unit SPS: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); + + return {}; + } + + err = ff_cbs_write_fragment_data(cbs_ctx.get(), &frag); + if(err < 0) { + char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; + BOOST_LOG(error) << "Could not write fragment data: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); + + return {}; + } + + // frag.data_size * 8 - frag.data_bit_padding == bits in fragment + util::buffer_t data { frag.data_size }; + std::copy_n(frag.data, frag.data_size, std::begin(data)); + + return data; +} + +util::buffer_t write(std::uint8_t nal, void *uh, AVCodecID codec_id) { + cbs::ctx_t cbs_ctx; + ff_cbs_init(&cbs_ctx, codec_id, nullptr); + + return write(cbs_ctx, nal, uh, codec_id); +} + +util::buffer_t make_sps_h264(const AVCodecContext *ctx) { + H264RawSPS sps {}; + + /* b_per_p == ctx->max_b_frames for h264 */ + /* desired_b_depth == avoption("b_depth") == 1 */ + /* max_b_depth == std::min(av_log2(ctx->b_per_p) + 1, desired_b_depth) ==> 1 */ + auto max_b_depth = 1; + auto dpb_frame = ctx->gop_size == 1 ? 0 : 1 + max_b_depth; + auto mb_width = (FFALIGN(ctx->width, 16) / 16) * 16; + auto mb_height = (FFALIGN(ctx->height, 16) / 16) * 16; + + + sps.nal_unit_header.nal_ref_idc = 3; + sps.nal_unit_header.nal_unit_type = H264_NAL_SPS; + + sps.profile_idc = FF_PROFILE_H264_HIGH & 0xFF; + + sps.constraint_set1_flag = 1; + + if(ctx->level != FF_LEVEL_UNKNOWN) { + sps.level_idc = ctx->level; + } + else { + auto framerate = ctx->framerate; + + auto level = ff_h264_guess_level( + sps.profile_idc, + ctx->bit_rate, + framerate.num / framerate.den, + mb_width, + mb_height, + dpb_frame); + + if(!level) { + BOOST_LOG(error) << "Could not guess h264 level"sv; + + return {}; + } + sps.level_idc = level->level_idc; + } + + sps.seq_parameter_set_id = 0; + sps.chroma_format_idc = 1; + + sps.log2_max_frame_num_minus4 = 3; //4; + sps.pic_order_cnt_type = 0; + sps.log2_max_pic_order_cnt_lsb_minus4 = 0; //4; + + sps.max_num_ref_frames = dpb_frame; + + sps.pic_width_in_mbs_minus1 = mb_width / 16 - 1; + sps.pic_height_in_map_units_minus1 = mb_height / 16 - 1; + + sps.frame_mbs_only_flag = 1; + sps.direct_8x8_inference_flag = 1; + + if(ctx->width != mb_width || ctx->height != mb_height) { + sps.frame_cropping_flag = 1; + sps.frame_crop_left_offset = 0; + sps.frame_crop_top_offset = 0; + sps.frame_crop_right_offset = (mb_width - ctx->width) / 2; + sps.frame_crop_bottom_offset = (mb_height - ctx->height) / 2; + } + + sps.vui_parameters_present_flag = 1; + + auto &vui = sps.vui; + + vui.video_format = 5; + vui.colour_description_present_flag = 1; + vui.video_signal_type_present_flag = 1; + vui.video_full_range_flag = ctx->color_range == AVCOL_RANGE_JPEG; + vui.colour_primaries = ctx->color_primaries; + vui.transfer_characteristics = ctx->color_trc; + vui.matrix_coefficients = ctx->colorspace; + + vui.low_delay_hrd_flag = 1 - vui.fixed_frame_rate_flag; + + vui.bitstream_restriction_flag = 1; + vui.motion_vectors_over_pic_boundaries_flag = 1; + vui.log2_max_mv_length_horizontal = 15; + vui.log2_max_mv_length_vertical = 15; + vui.max_num_reorder_frames = max_b_depth; + vui.max_dec_frame_buffering = max_b_depth + 1; + + return write(sps.nal_unit_header.nal_unit_type, (void *)&sps.nal_unit_header, AV_CODEC_ID_H264); +} + +hevc_t make_sps_hevc(const AVCodecContext *avctx, const AVPacket *packet) { + cbs::ctx_t ctx; + if(ff_cbs_init(&ctx, AV_CODEC_ID_H265, nullptr)) { + return {}; + } + + cbs::frag_t frag; + + int err = ff_cbs_read_packet(ctx.get(), &frag, packet); + if(err < 0) { + char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; + BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); + + return {}; + } + + + auto vps_p = ((CodedBitstreamH265Context *)ctx->priv_data)->active_vps; + auto sps_p = ((CodedBitstreamH265Context *)ctx->priv_data)->active_sps; + + H265RawSPS sps { *sps_p }; + H265RawVPS vps { *vps_p }; + + vps.profile_tier_level.general_profile_compatibility_flag[4] = 1; + sps.profile_tier_level.general_profile_compatibility_flag[4] = 1; + + auto &vui = sps.vui; + std::memset(&vui, 0, sizeof(vui)); + + sps.vui_parameters_present_flag = 1; + + // skip sample aspect ratio + + vui.video_format = 5; + vui.colour_description_present_flag = 1; + vui.video_signal_type_present_flag = 1; + vui.video_full_range_flag = avctx->color_range == AVCOL_RANGE_JPEG; + vui.colour_primaries = avctx->color_primaries; + vui.transfer_characteristics = avctx->color_trc; + vui.matrix_coefficients = avctx->colorspace; + + + vui.vui_timing_info_present_flag = vps.vps_timing_info_present_flag; + vui.vui_num_units_in_tick = vps.vps_num_units_in_tick; + vui.vui_time_scale = vps.vps_time_scale; + vui.vui_poc_proportional_to_timing_flag = vps.vps_poc_proportional_to_timing_flag; + vui.vui_num_ticks_poc_diff_one_minus1 = vps.vps_num_ticks_poc_diff_one_minus1; + vui.vui_hrd_parameters_present_flag = 0; + + vui.bitstream_restriction_flag = 1; + vui.motion_vectors_over_pic_boundaries_flag = 1; + vui.restricted_ref_pic_lists_flag = 1; + vui.max_bytes_per_pic_denom = 0; + vui.max_bits_per_min_cu_denom = 0; + vui.log2_max_mv_length_horizontal = 15; + vui.log2_max_mv_length_vertical = 15; + + cbs::ctx_t write_ctx; + ff_cbs_init(&write_ctx, AV_CODEC_ID_H265, nullptr); + + + return hevc_t { + nal_t { + write(write_ctx, vps.nal_unit_header.nal_unit_type, (void *)&vps.nal_unit_header, AV_CODEC_ID_H265), + write(ctx, vps_p->nal_unit_header.nal_unit_type, (void *)&vps_p->nal_unit_header, AV_CODEC_ID_H265), + }, + + nal_t { + write(write_ctx, sps.nal_unit_header.nal_unit_type, (void *)&sps.nal_unit_header, AV_CODEC_ID_H265), + write(ctx, sps_p->nal_unit_header.nal_unit_type, (void *)&sps_p->nal_unit_header, AV_CODEC_ID_H265), + }, + }; +} + +util::buffer_t read_sps_h264(const AVPacket *packet) { + cbs::ctx_t ctx; + if(ff_cbs_init(&ctx, AV_CODEC_ID_H264, nullptr)) { + return {}; + } + + cbs::frag_t frag; + + int err = ff_cbs_read_packet(ctx.get(), &frag, &*packet); + if(err < 0) { + char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; + BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); + + return {}; + } + + auto h264 = (H264RawNALUnitHeader *)((CodedBitstreamH264Context *)ctx->priv_data)->active_sps; + return write(h264->nal_unit_type, (void *)h264, AV_CODEC_ID_H264); +} + +h264_t make_sps_h264(const AVCodecContext *ctx, const AVPacket *packet) { + return h264_t { + make_sps_h264(ctx), + read_sps_h264(packet), + }; +} + +bool validate_sps(const AVPacket *packet, int codec_id) { + cbs::ctx_t ctx; + if(ff_cbs_init(&ctx, (AVCodecID)codec_id, nullptr)) { + return false; + } + + cbs::frag_t frag; + + int err = ff_cbs_read_packet(ctx.get(), &frag, packet); + if(err < 0) { + char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; + BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); + + return false; + } + + if(codec_id == AV_CODEC_ID_H264) { + auto h264 = (CodedBitstreamH264Context *)ctx->priv_data; + + if(!h264->active_sps->vui_parameters_present_flag) { + return false; + } + + return true; + } + + return ((CodedBitstreamH265Context *)ctx->priv_data)->active_sps->vui_parameters_present_flag; +} +} // namespace cbs \ No newline at end of file diff --git a/sunshine/cbs.h b/sunshine/cbs.h new file mode 100644 index 00000000..dc772dd9 --- /dev/null +++ b/sunshine/cbs.h @@ -0,0 +1,34 @@ +#ifndef SUNSHINE_CBS_H +#define SUNSHINE_CBS_H + +#include "utility.h" + +struct AVPacket; +struct AVCodecContext; + +namespace cbs { + +struct nal_t { + util::buffer_t _new; + util::buffer_t old; +}; + +struct hevc_t { + nal_t vps; + nal_t sps; +}; + +struct h264_t { + nal_t sps; +}; + +hevc_t make_sps_hevc(const AVCodecContext *ctx, const AVPacket *packet); +h264_t make_sps_h264(const AVCodecContext *ctx, const AVPacket *packet); + +/** + * Check if SPS->VUI is present + */ +bool validate_sps(const AVPacket *packet, int codec_id); +} // namespace cbs + +#endif \ No newline at end of file diff --git a/sunshine/config.cpp b/sunshine/config.cpp index 83c7db9b..ad86ef09 100644 --- a/sunshine/config.cpp +++ b/sunshine/config.cpp @@ -1,12 +1,20 @@ +#include +#include #include -#include #include +#include #include #include -#include "utility.h" #include "config.h" +#include "main.h" +#include "utility.h" + +#include "platform/common.h" + +namespace fs = std::filesystem; +using namespace std::literals; #define CA_DIR "credentials" #define PRIVATE_KEY_FILE CA_DIR "/cakey.pem" @@ -14,7 +22,6 @@ #define APPS_JSON_PATH SUNSHINE_ASSETS_DIR "/" APPS_JSON namespace config { -using namespace std::literals; namespace nv { enum preset_e : int { @@ -33,12 +40,12 @@ enum preset_e : int { }; enum rc_e : int { - constqp = 0x0, /**< Constant QP mode */ - vbr = 0x1, /**< Variable bitrate mode */ - cbr = 0x2, /**< Constant bitrate mode */ - cbr_ld_hq = 0x8, /**< low-delay CBR, high quality */ - cbr_hq = 0x10, /**< CBR, high quality (slower) */ - vbr_hq = 0x20 /**< VBR, high quality (slower) */ + constqp = 0x0, /**< Constant QP mode */ + vbr = 0x1, /**< Variable bitrate mode */ + cbr = 0x2, /**< Constant bitrate mode */ + cbr_ld_hq = 0x8, /**< low-delay CBR, high quality */ + cbr_hq = 0x10, /**< CBR, high quality (slower) */ + vbr_hq = 0x20 /**< VBR, high quality (slower) */ }; enum coder_e : int { @@ -48,7 +55,8 @@ enum coder_e : int { }; std::optional preset_from_view(const std::string_view &preset) { -#define _CONVERT_(x) if(preset == #x##sv) return x +#define _CONVERT_(x) \ + if(preset == #x##sv) return x _CONVERT_(slow); _CONVERT_(medium); _CONVERT_(fast); @@ -65,7 +73,8 @@ std::optional preset_from_view(const std::string_view &preset) { } std::optional rc_from_view(const std::string_view &rc) { -#define _CONVERT_(x) if(rc == #x##sv) return x +#define _CONVERT_(x) \ + if(rc == #x##sv) return x _CONVERT_(constqp); _CONVERT_(vbr); _CONVERT_(cbr); @@ -78,34 +87,90 @@ std::optional rc_from_view(const std::string_view &rc) { int coder_from_view(const std::string_view &coder) { if(coder == "auto"sv) return _auto; - if(coder == "cabac"sv || coder == "ac"sv) return cabac; - if(coder == "cavlc"sv || coder == "vlc"sv) return cavlc; + if(coder == "cabac"sv || coder == "ac"sv) return cabac; + if(coder == "cavlc"sv || coder == "vlc"sv) return cavlc; return -1; } +} // namespace nv + +namespace amd { +enum quality_e : int { + _default = 0, + speed, + balanced, + //quality2, +}; + +enum rc_e : int { + constqp, /**< Constant QP mode */ + vbr_latency, /**< Latency Constrained Variable Bitrate */ + vbr_peak, /**< Peak Contrained Variable Bitrate */ + cbr, /**< Constant bitrate mode */ +}; + +enum coder_e : int { + _auto = 0, + cabac, + cavlc +}; + +std::optional quality_from_view(const std::string_view &quality) { +#define _CONVERT_(x) \ + if(quality == #x##sv) return x + _CONVERT_(speed); + _CONVERT_(balanced); + //_CONVERT_(quality2); + if(quality == "default"sv) return _default; +#undef _CONVERT_ + return std::nullopt; } +std::optional rc_from_view(const std::string_view &rc) { +#define _CONVERT_(x) \ + if(rc == #x##sv) return x + _CONVERT_(constqp); + _CONVERT_(vbr_latency); + _CONVERT_(vbr_peak); + _CONVERT_(cbr); +#undef _CONVERT_ + return std::nullopt; +} + +int coder_from_view(const std::string_view &coder) { + if(coder == "auto"sv) return _auto; + if(coder == "cabac"sv || coder == "ac"sv) return cabac; + if(coder == "cavlc"sv || coder == "vlc"sv) return cavlc; + + return -1; +} +} // namespace amd + video_t video { - 0, // crf + 0, // crf 28, // qp 0, // hevc_mode 1, // min_threads { - "superfast"s, // preset + "superfast"s, // preset "zerolatency"s, // tune - }, // software + }, // software { nv::llhq, std::nullopt, - -1 - }, // nv + -1 }, // nv + + { + amd::balanced, + std::nullopt, + -1 }, // amd {}, // encoder {}, // adapter_name - {} // output_name + {}, // output_name }; audio_t audio {}; @@ -116,7 +181,7 @@ stream_t stream { APPS_JSON_PATH, 10, // fecPercentage - 1 // channels + 1 // channels }; nvhttp_t nvhttp { @@ -125,55 +190,136 @@ nvhttp_t nvhttp { CERTIFICATE_FILE, boost::asio::ip::host_name(), // sunshine_name, - "sunshine_state.json"s // file_state + "sunshine_state.json"s, // file_state + {}, // external_ip + { + "352x240"s, + "480x360"s, + "858x480"s, + "1280x720"s, + "1920x1080"s, + "2560x1080"s, + "3440x1440"s + "1920x1200"s, + "3860x2160"s, + "3840x1600"s, + }, // supported resolutions + + { 10, 30, 60, 90, 120 }, // supported fps }; input_t input { - 2s, // back_button_timeout - 500ms, // key_repeat_delay - std::chrono::duration { 1 / 24.9 } // key_repeat_period + 2s, // back_button_timeout + 500ms, // key_repeat_delay + std::chrono::duration { 1 / 24.9 } // key_repeat_period }; sunshine_t sunshine { - 2, // min_log_level - 0 // flags + 2, // min_log_level + 0, // flags + {}, // User file + {}, // Username + {}, // Password + {}, // Password Salt + SUNSHINE_ASSETS_DIR "/sunshine.conf", // config file + {} // cmd args }; -bool whitespace(char ch) { +bool endline(char ch) { + return ch == '\r' || ch == '\n'; +} + +bool space_tab(char ch) { return ch == ' ' || ch == '\t'; } -std::string to_string(const char *begin, const char *end) { - return { begin, (std::size_t)(end - begin) }; +bool whitespace(char ch) { + return space_tab(ch) || endline(ch); } -std::optional> parse_line(std::string_view::const_iterator begin, std::string_view::const_iterator end) { - begin = std::find_if(begin, end, std::not_fn(whitespace)); - end = std::find(begin, end, '#'); - end = std::find_if(std::make_reverse_iterator(end), std::make_reverse_iterator(begin), std::not_fn(whitespace)).base(); +std::string to_string(const char *begin, const char *end) { + std::string result; - auto eq = std::find(begin, end, '='); - if(eq == end || eq == begin) { - return std::nullopt; + KITTY_WHILE_LOOP(auto pos = begin, pos != end, { + auto comment = std::find(pos, end, '#'); + auto endl = std::find_if(comment, end, endline); + + result.append(pos, comment); + + pos = endl; + }) + + return result; +} + +template +It skip_list(It skipper, It end) { + int stack = 1; + while(skipper != end && stack) { + if(*skipper == '[') { + ++stack; + } + if(*skipper == ']') { + --stack; + } + + ++skipper; } - auto end_name = std::find_if(std::make_reverse_iterator(eq), std::make_reverse_iterator(begin), std::not_fn(whitespace)).base(); - auto begin_val = std::find_if(eq + 1, end, std::not_fn(whitespace)); - - return std::pair { to_string(begin, end_name), to_string(begin_val, end) }; + return skipper; } -std::unordered_map parse_config(std::string_view file_content) { +std::pair< + std::string_view::const_iterator, + std::optional>> +parse_option(std::string_view::const_iterator begin, std::string_view::const_iterator end) { + begin = std::find_if_not(begin, end, whitespace); + auto endl = std::find_if(begin, end, endline); + auto endc = std::find(begin, endl, '#'); + endc = std::find_if(std::make_reverse_iterator(endc), std::make_reverse_iterator(begin), std::not_fn(whitespace)).base(); + + auto eq = std::find(begin, endc, '='); + if(eq == endc || eq == begin) { + return std::make_pair(endl, std::nullopt); + } + + auto end_name = std::find_if_not(std::make_reverse_iterator(eq), std::make_reverse_iterator(begin), space_tab).base(); + auto begin_val = std::find_if_not(eq + 1, endc, space_tab); + + if(begin_val == endl) { + return std::make_pair(endl, std::nullopt); + } + + // Lists might contain newlines + if(*begin_val == '[') { + endl = skip_list(begin_val + 1, end); + if(endl == end) { + std::cout << "Warning: Config option ["sv << to_string(begin, end_name) << "] Missing ']'"sv; + + return std::make_pair(endl, std::nullopt); + } + } + + return std::make_pair( + endl, + std::make_pair(to_string(begin, end_name), to_string(begin_val, endl))); +} + +std::unordered_map parse_config(const std::string_view &file_content) { std::unordered_map vars; auto pos = std::begin(file_content); auto end = std::end(file_content); while(pos < end) { - auto newline = std::find_if(pos, end, [](auto ch) { return ch == '\n' || ch == '\r'; }); - auto var = parse_line(pos, newline); + // auto newline = std::find_if(pos, end, [](auto ch) { return ch == '\n' || ch == '\r'; }); + TUPLE_2D(endl, var, parse_option(pos, end)); + + pos = endl; + if(pos != end) { + pos += (*pos == '\r') ? 2 : 1; + } - pos = (*newline == '\r') ? newline + 2 : newline + 1; if(!var) { continue; } @@ -207,6 +353,38 @@ void string_restricted_f(std::unordered_map &vars, con } } +void path_f(std::unordered_map &vars, const std::string &name, fs::path &input) { + // appdata needs to be retrieved once only + static auto appdata = platf::appdata(); + + std::string temp; + string_f(vars, name, temp); + + if(!temp.empty()) { + input = temp; + } + + if(input.is_relative()) { + input = appdata / input; + } + + auto dir = input; + dir.remove_filename(); + + // Ensure the directories exists + if(!fs::exists(dir)) { + fs::create_directories(dir); + } +} + +void path_f(std::unordered_map &vars, const std::string &name, std::string &input) { + fs::path temp = input; + + path_f(vars, name, temp); + + input = temp.string(); +} + void int_f(std::unordered_map &vars, const std::string &name, int &input) { auto it = vars.find(name); @@ -215,7 +393,7 @@ void int_f(std::unordered_map &vars, const std::string } auto &val = it->second; - input = util::from_chars(&val[0], &val[0] + val.size()); + input = util::from_chars(&val[0], &val[0] + val.size()); vars.erase(it); } @@ -228,7 +406,7 @@ void int_f(std::unordered_map &vars, const std::string } auto &val = it->second; - input = util::from_chars(&val[0], &val[0] + val.size()); + input = util::from_chars(&val[0], &val[0] + val.size()); vars.erase(it); } @@ -263,15 +441,14 @@ void int_between_f(std::unordered_map &vars, const std } bool to_bool(std::string &boolean) { - std::for_each(std::begin(boolean), std::end(boolean), [](char ch) { return (char)std::tolower(ch); }); + std::for_each(std::begin(boolean), std::end(boolean), [](char ch) { return (char)std::tolower(ch); }); - return - boolean == "true"sv || - boolean == "yes"sv || - boolean == "enable"sv || - (std::find(std::begin(boolean), std::end(boolean), '1') != std::end(boolean)); + return boolean == "true"sv || + boolean == "yes"sv || + boolean == "enable"sv || + (std::find(std::begin(boolean), std::end(boolean), '1') != std::end(boolean)); } -void bool_f(std::unordered_map &vars, const std::string &name, int &input) { +void bool_f(std::unordered_map &vars, const std::string &name, int &input) { std::string tmp; string_f(vars, name, tmp); @@ -282,7 +459,7 @@ void bool_f(std::unordered_map &vars, const std::strin input = to_bool(tmp) ? 1 : 0; } -void double_f(std::unordered_map &vars, const std::string &name, double &input) { +void double_f(std::unordered_map &vars, const std::string &name, double &input) { std::string tmp; string_f(vars, name, tmp); @@ -311,30 +488,76 @@ void double_between_f(std::unordered_map &vars, const } } -void print_help(const char *name) { - std::cout << - "Usage: "sv << name << " [options] [/path/to/configuration_file]"sv << std::endl << - " Any configurable option can be overwritten with: \"name=value\""sv << std::endl << std::endl << - " --help | print help"sv << std::endl << std::endl << - " flags"sv << std::endl << - " -0 | Read PIN from stdin"sv << std::endl << - " -1 | Do not load previously saved state and do retain any state after shutdown"sv << std::endl << - " | Effectively starting as if for the first time without overwriting any pairings with your devices"sv; +void list_string_f(std::unordered_map &vars, const std::string &name, std::vector &input) { + std::string string; + string_f(vars, name, string); + + if(string.empty()) { + return; + } + + input.clear(); + + auto begin = std::cbegin(string); + if(*begin == '[') { + ++begin; + } + + begin = std::find_if_not(begin, std::cend(string), whitespace); + if(begin == std::cend(string)) { + return; + } + + auto pos = begin; + while(pos < std::cend(string)) { + if(*pos == '[') { + pos = skip_list(pos + 1, std::cend(string)) + 1; + } + else if(*pos == ']') { + break; + } + else if(*pos == ',') { + input.emplace_back(begin, pos); + pos = begin = std::find_if_not(pos + 1, std::cend(string), whitespace); + } + else { + ++pos; + } + } + + if(pos != begin) { + input.emplace_back(begin, pos); + } +} + +void list_int_f(std::unordered_map &vars, const std::string &name, std::vector &input) { + std::vector list; + list_string_f(vars, name, list); + + for(auto &el : list) { + input.emplace_back(util::from_view(el)); + } } int apply_flags(const char *line) { int ret = 0; while(*line != '\0') { switch(*line) { - case '0': - config::sunshine.flags[config::flag::PIN_STDIN].flip(); - break; - case '1': - config::sunshine.flags[config::flag::FRESH_STATE].flip(); - break; - default: - std::cout << "Warning: Unrecognized flag: ["sv << *line << ']' << std::endl; - ret = -1; + case '0': + config::sunshine.flags[config::flag::PIN_STDIN].flip(); + break; + case '1': + config::sunshine.flags[config::flag::FRESH_STATE].flip(); + break; + case '2': + config::sunshine.flags[config::flag::FORCE_VIDEO_HEADER_REPLACE].flip(); + break; + case 'p': + config::sunshine.flags[config::flag::CONST_PIN].flip(); + break; + default: + std::cout << "Warning: Unrecognized flag: ["sv << *line << ']' << std::endl; + ret = -1; } ++line; @@ -351,46 +574,50 @@ void apply_config(std::unordered_map &&vars) { int_f(vars, "crf", video.crf); int_f(vars, "qp", video.qp); int_f(vars, "min_threads", video.min_threads); - int_between_f(vars, "hevc_mode", video.hevc_mode, { - 0, 3 - }); + int_between_f(vars, "hevc_mode", video.hevc_mode, { 0, 3 }); string_f(vars, "sw_preset", video.sw.preset); string_f(vars, "sw_tune", video.sw.tune); int_f(vars, "nv_preset", video.nv.preset, nv::preset_from_view); - int_f(vars, "nv_rc", video.nv.preset, nv::rc_from_view); + int_f(vars, "nv_rc", video.nv.rc, nv::rc_from_view); int_f(vars, "nv_coder", video.nv.coder, nv::coder_from_view); + + int_f(vars, "amd_quality", video.amd.quality, amd::quality_from_view); + int_f(vars, "amd_rc", video.amd.rc, amd::rc_from_view); + int_f(vars, "amd_coder", video.amd.coder, amd::coder_from_view); + string_f(vars, "encoder", video.encoder); string_f(vars, "adapter_name", video.adapter_name); string_f(vars, "output_name", video.output_name); - string_f(vars, "pkey", nvhttp.pkey); - string_f(vars, "cert", nvhttp.cert); + path_f(vars, "pkey", nvhttp.pkey); + path_f(vars, "cert", nvhttp.cert); string_f(vars, "sunshine_name", nvhttp.sunshine_name); - string_f(vars, "file_state", nvhttp.file_state); + + path_f(vars, "file_state", nvhttp.file_state); + + // Must be run after "file_state" + config::sunshine.credentials_file = config::nvhttp.file_state; + path_f(vars, "credentials_file", config::sunshine.credentials_file); + string_f(vars, "external_ip", nvhttp.external_ip); + list_string_f(vars, "resolutions"s, nvhttp.resolutions); + list_int_f(vars, "fps"s, nvhttp.fps); string_f(vars, "audio_sink", audio.sink); + string_f(vars, "virtual_sink", audio.virtual_sink); - string_restricted_f(vars, "origin_pin_allowed", nvhttp.origin_pin_allowed, { - "pc"sv, "lan"sv, "wan"sv - }); + string_restricted_f(vars, "origin_pin_allowed", nvhttp.origin_pin_allowed, { "pc"sv, "lan"sv, "wan"sv }); int to = -1; - int_between_f(vars, "ping_timeout", to, { - -1, std::numeric_limits::max() - }); + int_between_f(vars, "ping_timeout", to, { -1, std::numeric_limits::max() }); if(to != -1) { stream.ping_timeout = std::chrono::milliseconds(to); } - int_between_f(vars, "channels", stream.channels, { - 1, std::numeric_limits::max() - }); + int_between_f(vars, "channels", stream.channels, { 1, std::numeric_limits::max() }); - string_f(vars, "file_apps", stream.file_apps); - int_between_f(vars, "fec_percentage", stream.fec_percentage, { - 1, 100 - }); + path_f(vars, "file_apps", stream.file_apps); + int_between_f(vars, "fec_percentage", stream.fec_percentage, { 1, 100 }); to = std::numeric_limits::min(); int_f(vars, "back_button_timeout", to); @@ -400,12 +627,10 @@ void apply_config(std::unordered_map &&vars) { } double repeat_frequency { 0 }; - double_between_f(vars, "key_repeat_frequency", repeat_frequency, { - 0, std::numeric_limits::max() - }); + double_between_f(vars, "key_repeat_frequency", repeat_frequency, { 0, std::numeric_limits::max() }); if(repeat_frequency > 0) { - config::input.key_repeat_period = std::chrono::duration {1 / repeat_frequency }; + config::input.key_repeat_period = std::chrono::duration { 1 / repeat_frequency }; } to = -1; @@ -415,9 +640,7 @@ void apply_config(std::unordered_map &&vars) { } std::string log_level_string; - string_restricted_f(vars, "min_log_level", log_level_string, { - "verbose"sv, "debug"sv, "info"sv, "warning"sv, "error"sv, "fatal"sv, "none"sv - }); + string_restricted_f(vars, "min_log_level", log_level_string, { "verbose"sv, "debug"sv, "info"sv, "warning"sv, "error"sv, "fatal"sv, "none"sv }); if(!log_level_string.empty()) { if(log_level_string == "verbose"sv) { @@ -441,6 +664,13 @@ void apply_config(std::unordered_map &&vars) { else if(log_level_string == "none"sv) { sunshine.min_log_level = 6; } + else { + // accept digit directly + auto val = log_level_string[0]; + if(val >= '0' && val < '7') { + sunshine.min_log_level = val - '0'; + } + } } auto it = vars.find("flags"s); @@ -451,18 +681,16 @@ void apply_config(std::unordered_map &&vars) { } if(sunshine.min_log_level <= 3) { - for(auto &[var,_] : vars) { + for(auto &[var, _] : vars) { std::cout << "Warning: Unrecognized configurable option ["sv << var << ']' << std::endl; } } } int parse(int argc, char *argv[]) { - const char *config_file = SUNSHINE_ASSETS_DIR "/sunshine.conf"; - std::unordered_map cmd_vars; - for(auto x = argc -1; x > 0; --x) { + for(auto x = 1; x < argc; ++x) { auto line = argv[x]; if(line == "--help"sv) { @@ -470,6 +698,13 @@ int parse(int argc, char *argv[]) { return 1; } else if(*line == '-') { + if(*(line + 1) == '-') { + sunshine.cmd.name = line + 2; + sunshine.cmd.argc = argc - x - 1; + sunshine.cmd.argv = argv + x + 1; + + break; + } if(apply_flags(line + 1)) { print_help(*argv); return -1; @@ -480,35 +715,30 @@ int parse(int argc, char *argv[]) { auto pos = std::find(line, line_end, '='); if(pos == line_end) { - config_file = line; + sunshine.config_file = line; } else { - auto var = parse_line(line, line_end); + TUPLE_EL(var, 1, parse_option(line, line_end)); if(!var) { print_help(*argv); return -1; } + TUPLE_EL_REF(name, 0, *var); + + auto it = cmd_vars.find(name); + if(it != std::end(cmd_vars)) { + cmd_vars.erase(it); + } + cmd_vars.emplace(std::move(*var)); } } } - std::ifstream in { config_file }; + auto vars = parse_config(read_file(sunshine.config_file.c_str())); - if(!in.is_open()) { - std::cout << "Error: Couldn't open "sv << config_file << std::endl; - - return -1; - } - - auto vars = parse_config(std::string { - // Quick and dirty - std::istreambuf_iterator(in), - std::istreambuf_iterator() - }); - - for(auto &[name,value] : cmd_vars) { + for(auto &[name, value] : cmd_vars) { vars.insert_or_assign(std::move(name), std::move(value)); } @@ -516,4 +746,4 @@ int parse(int argc, char *argv[]) { return 0; } -} +} // namespace config diff --git a/sunshine/config.h b/sunshine/config.h index 9c14b1fe..221df6f9 100644 --- a/sunshine/config.h +++ b/sunshine/config.h @@ -1,16 +1,18 @@ #ifndef SUNSHINE_CONFIG_H #define SUNSHINE_CONFIG_H -#include -#include #include +#include #include +#include +#include +#include namespace config { struct video_t { // ffmpeg params int crf; // higher == more compression and less quality - int qp; // higher == more compression and less quality, ignored if crf != 0 + int qp; // higher == more compression and less quality, ignored if crf != 0 int hevc_mode; @@ -26,6 +28,12 @@ struct video_t { int coder; } nv; + struct { + std::optional quality; + std::optional rc; + int coder; + } amd; + std::string encoder; std::string adapter_name; std::string output_name; @@ -33,6 +41,7 @@ struct video_t { struct audio_t { std::string sink; + std::string virtual_sink; }; struct stream_t { @@ -59,6 +68,8 @@ struct nvhttp_t { std::string file_state; std::string external_ip; + std::vector resolutions; + std::vector fps; }; struct input_t { @@ -69,16 +80,30 @@ struct input_t { namespace flag { enum flag_e : std::size_t { - PIN_STDIN = 0, // Read PIN from stdin instead of http - FRESH_STATE, // Do not load or save state + PIN_STDIN = 0, // Read PIN from stdin instead of http + FRESH_STATE, // Do not load or save state + FORCE_VIDEO_HEADER_REPLACE, // force replacing headers inside video data + CONST_PIN, // Use "universal" pin FLAG_SIZE }; } struct sunshine_t { int min_log_level; - std::bitset flags; + std::string credentials_file; + + std::string username; + std::string password; + std::string salt; + + std::string config_file; + + struct cmd_t { + std::string name; + int argc; + char **argv; + } cmd; }; extern video_t video; @@ -89,6 +114,6 @@ extern input_t input; extern sunshine_t sunshine; int parse(int argc, char *argv[]); -} - +std::unordered_map parse_config(const std::string_view &file_content); +} // namespace config #endif diff --git a/sunshine/confighttp.cpp b/sunshine/confighttp.cpp new file mode 100644 index 00000000..ed0a3b88 --- /dev/null +++ b/sunshine/confighttp.cpp @@ -0,0 +1,514 @@ +// +// Created by TheElixZammuto on 2021-05-09. +// TODO: Authentication, better handling of routes common to nvhttp, cleanup + +#define BOOST_BIND_GLOBAL_PLACEHOLDERS + +#include "process.h" + +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include "config.h" +#include "confighttp.h" +#include "crypto.h" +#include "httpcommon.h" +#include "main.h" +#include "network.h" +#include "nvhttp.h" +#include "platform/common.h" +#include "rtsp.h" +#include "utility.h" +#include "uuid.h" + +namespace confighttp { +using namespace std::literals; +constexpr auto PORT_HTTPS = 47990; + +namespace fs = std::filesystem; +namespace pt = boost::property_tree; + +using https_server_t = SimpleWeb::Server; + +using args_t = SimpleWeb::CaseInsensitiveMultimap; +using resp_https_t = std::shared_ptr::Response>; +using req_https_t = std::shared_ptr::Request>; + +enum class op_e { + ADD, + REMOVE +}; + +void print_req(const req_https_t &request) { + BOOST_LOG(debug) << "METHOD :: "sv << request->method; + BOOST_LOG(debug) << "DESTINATION :: "sv << request->path; + + for(auto &[name, val] : request->header) { + BOOST_LOG(debug) << name << " -- " << val; + } + + BOOST_LOG(debug) << " [--] "sv; + + for(auto &[name, val] : request->parse_query_string()) { + BOOST_LOG(debug) << name << " -- " << val; + } + + BOOST_LOG(debug) << " [--] "sv; +} + +void send_unauthorized(resp_https_t response, req_https_t request) { + auto address = request->remote_endpoint_address(); + BOOST_LOG(info) << '[' << address << "] -- denied"sv; + const SimpleWeb::CaseInsensitiveMultimap headers { + { "WWW-Authenticate", R"(Basic realm="Sunshine Gamestream Host", charset="UTF-8")" } + }; + response->write(SimpleWeb::StatusCode::client_error_unauthorized, headers); +} + +bool authenticate(resp_https_t response, req_https_t request) { + auto address = request->remote_endpoint_address(); + auto ip_type = net::from_address(address); + + if(ip_type > http::origin_pin_allowed) { + BOOST_LOG(info) << '[' << address << "] -- denied"sv; + response->write(SimpleWeb::StatusCode::client_error_forbidden); + return false; + } + + auto fg = util::fail_guard([&]() { + send_unauthorized(response, request); + }); + + auto auth = request->header.find("authorization"); + if(auth == request->header.end()) { + return false; + } + + auto &rawAuth = auth->second; + auto authData = SimpleWeb::Crypto::Base64::decode(rawAuth.substr("Basic "sv.length())); + + int index = authData.find(':'); + if(index >= authData.size() - 1) { + return false; + } + + auto username = authData.substr(0, index); + auto password = authData.substr(index + 1); + auto hash = util::hex(crypto::hash(password + config::sunshine.salt)).to_string(); + + if(username != config::sunshine.username || hash != config::sunshine.password) { + return false; + } + + fg.disable(); + return true; +} + +void not_found(resp_https_t response, req_https_t request) { + pt::ptree tree; + tree.put("root..status_code", 404); + + std::ostringstream data; + + pt::write_xml(data, tree); + response->write(data.str()); + + *response << "HTTP/1.1 404 NOT FOUND\r\n" + << data.str(); +} + +void getIndexPage(resp_https_t response, req_https_t request) { + if(!authenticate(response, request)) return; + + print_req(request); + + std::string header = read_file(WEB_DIR "header.html"); + std::string content = read_file(WEB_DIR "index.html"); + response->write(header + content); +} + +void getPinPage(resp_https_t response, req_https_t request) { + if(!authenticate(response, request)) return; + + print_req(request); + + std::string header = read_file(WEB_DIR "header.html"); + std::string content = read_file(WEB_DIR "pin.html"); + response->write(header + content); +} + +void getAppsPage(resp_https_t response, req_https_t request) { + if(!authenticate(response, request)) return; + + print_req(request); + + std::string header = read_file(WEB_DIR "header.html"); + std::string content = read_file(WEB_DIR "apps.html"); + response->write(header + content); +} + +void getClientsPage(resp_https_t response, req_https_t request) { + if(!authenticate(response, request)) return; + + print_req(request); + + std::string header = read_file(WEB_DIR "header.html"); + std::string content = read_file(WEB_DIR "clients.html"); + response->write(header + content); +} + +void getConfigPage(resp_https_t response, req_https_t request) { + if(!authenticate(response, request)) return; + + print_req(request); + + std::string header = read_file(WEB_DIR "header.html"); + std::string content = read_file(WEB_DIR "config.html"); + response->write(header + content); +} + +void getPasswordPage(resp_https_t response, req_https_t request) { + if(!authenticate(response, request)) return; + + print_req(request); + + std::string header = read_file(WEB_DIR "header.html"); + std::string content = read_file(WEB_DIR "password.html"); + response->write(header + content); +} + +void getApps(resp_https_t response, req_https_t request) { + if(!authenticate(response, request)) return; + + print_req(request); + + std::string content = read_file(config::stream.file_apps.c_str()); + response->write(content); +} + +void saveApp(resp_https_t response, req_https_t request) { + if(!authenticate(response, request)) return; + + print_req(request); + + std::stringstream ss; + ss << request->content.rdbuf(); + + pt::ptree outputTree; + auto g = util::fail_guard([&]() { + std::ostringstream data; + + pt::write_json(data, outputTree); + response->write(data.str()); + }); + + pt::ptree inputTree, fileTree; + + BOOST_LOG(fatal) << config::stream.file_apps; + try { + //TODO: Input Validation + pt::read_json(ss, inputTree); + pt::read_json(config::stream.file_apps, fileTree); + + if(inputTree.get_child("prep-cmd").empty()) { + inputTree.erase("prep-cmd"); + } + + if(inputTree.get_child("detached").empty()) { + inputTree.erase("detached"); + } + + auto &apps_node = fileTree.get_child("apps"s); + int index = inputTree.get("index"); + + inputTree.erase("index"); + + if(index == -1) { + apps_node.push_back(std::make_pair("", inputTree)); + } + else { + //Unfortuantely Boost PT does not allow to directly edit the array, copy should do the trick + pt::ptree newApps; + int i = 0; + for(const auto &kv : apps_node) { + if(i == index) { + newApps.push_back(std::make_pair("", inputTree)); + } + else { + newApps.push_back(std::make_pair("", kv.second)); + } + i++; + } + fileTree.erase("apps"); + fileTree.push_back(std::make_pair("apps", newApps)); + } + pt::write_json(config::stream.file_apps, fileTree); + } + catch(std::exception &e) { + BOOST_LOG(warning) << "SaveApp: "sv << e.what(); + + outputTree.put("status", "false"); + outputTree.put("error", "Invalid Input JSON"); + return; + } + + outputTree.put("status", "true"); + proc::refresh(config::stream.file_apps); +} + +void deleteApp(resp_https_t response, req_https_t request) { + if(!authenticate(response, request)) return; + + print_req(request); + + pt::ptree outputTree; + auto g = util::fail_guard([&]() { + std::ostringstream data; + + pt::write_json(data, outputTree); + response->write(data.str()); + }); + pt::ptree fileTree; + try { + pt::read_json(config::stream.file_apps, fileTree); + auto &apps_node = fileTree.get_child("apps"s); + int index = stoi(request->path_match[1]); + + if(index < 0) { + outputTree.put("status", "false"); + outputTree.put("error", "Invalid Index"); + return; + } + else { + //Unfortuantely Boost PT does not allow to directly edit the array, copy should do the trick + pt::ptree newApps; + int i = 0; + for(const auto &kv : apps_node) { + if(i++ != index) { + newApps.push_back(std::make_pair("", kv.second)); + } + } + fileTree.erase("apps"); + fileTree.push_back(std::make_pair("apps", newApps)); + } + pt::write_json(config::stream.file_apps, fileTree); + } + catch(std::exception &e) { + BOOST_LOG(warning) << "DeleteApp: "sv << e.what(); + outputTree.put("status", "false"); + outputTree.put("error", "Invalid File JSON"); + return; + } + + outputTree.put("status", "true"); + proc::refresh(config::stream.file_apps); +} + +void getConfig(resp_https_t response, req_https_t request) { + if(!authenticate(response, request)) return; + + print_req(request); + + pt::ptree outputTree; + auto g = util::fail_guard([&]() { + std::ostringstream data; + + pt::write_json(data, outputTree); + response->write(data.str()); + }); + + outputTree.put("status", "true"); + outputTree.put("platform", SUNSHINE_PLATFORM); + + auto vars = config::parse_config(read_file(config::sunshine.config_file.c_str())); + + for(auto &[name, value] : vars) { + outputTree.put(std::move(name), std::move(value)); + } +} + +void saveConfig(resp_https_t response, req_https_t request) { + if(!authenticate(response, request)) return; + + print_req(request); + + std::stringstream ss; + std::stringstream configStream; + ss << request->content.rdbuf(); + pt::ptree outputTree; + auto g = util::fail_guard([&]() { + std::ostringstream data; + + pt::write_json(data, outputTree); + response->write(data.str()); + }); + pt::ptree inputTree; + try { + //TODO: Input Validation + pt::read_json(ss, inputTree); + for(const auto &kv : inputTree) { + std::string value = inputTree.get(kv.first); + if(value.length() == 0 || value.compare("null") == 0) continue; + + configStream << kv.first << " = " << value << std::endl; + } + write_file(config::sunshine.config_file.c_str(), configStream.str()); + } + catch(std::exception &e) { + BOOST_LOG(warning) << "SaveConfig: "sv << e.what(); + outputTree.put("status", "false"); + outputTree.put("error", e.what()); + return; + } +} + +void savePassword(resp_https_t response, req_https_t request) { + if(!authenticate(response, request)) return; + + print_req(request); + + std::stringstream ss; + std::stringstream configStream; + ss << request->content.rdbuf(); + + pt::ptree inputTree, outputTree; + + auto g = util::fail_guard([&]() { + std::ostringstream data; + pt::write_json(data, outputTree); + response->write(data.str()); + }); + + try { + //TODO: Input Validation + pt::read_json(ss, inputTree); + auto username = inputTree.get("currentUsername"); + auto newUsername = inputTree.get("newUsername"); + auto password = inputTree.get("currentPassword"); + auto newPassword = inputTree.get("newPassword"); + auto confirmPassword = inputTree.get("confirmNewPassword"); + if(newUsername.length() == 0) newUsername = username; + + auto hash = util::hex(crypto::hash(password + config::sunshine.salt)).to_string(); + if(username == config::sunshine.username && hash == config::sunshine.password) { + if(newPassword != confirmPassword) { + outputTree.put("status", false); + outputTree.put("error", "Password Mismatch"); + } + + http::save_user_creds(config::sunshine.credentials_file, newUsername, newPassword); + http::reload_user_creds(config::sunshine.credentials_file); + outputTree.put("status", true); + } + else { + outputTree.put("status", false); + outputTree.put("error", "Invalid Current Credentials"); + } + } + catch(std::exception &e) { + BOOST_LOG(warning) << "SavePassword: "sv << e.what(); + outputTree.put("status", false); + outputTree.put("error", e.what()); + return; + } +} + +void savePin(resp_https_t response, req_https_t request) { + if(!authenticate(response, request)) return; + + print_req(request); + + std::stringstream ss; + ss << request->content.rdbuf(); + + pt::ptree inputTree, outputTree; + + auto g = util::fail_guard([&]() { + std::ostringstream data; + pt::write_json(data, outputTree); + response->write(data.str()); + }); + + try { + //TODO: Input Validation + pt::read_json(ss, inputTree); + std::string pin = inputTree.get("pin"); + outputTree.put("status", nvhttp::pin(pin)); + } + catch(std::exception &e) { + BOOST_LOG(warning) << "SavePin: "sv << e.what(); + outputTree.put("status", false); + outputTree.put("error", e.what()); + return; + } +} + +void start() { + auto shutdown_event = mail::man->event(mail::shutdown); + + auto ctx = std::make_shared(boost::asio::ssl::context::tls); + ctx->use_certificate_chain_file(config::nvhttp.cert); + ctx->use_private_key_file(config::nvhttp.pkey, boost::asio::ssl::context::pem); + https_server_t server { ctx, 0 }; + server.default_resource = not_found; + server.resource["^/$"]["GET"] = getIndexPage; + server.resource["^/pin$"]["GET"] = getPinPage; + server.resource["^/apps$"]["GET"] = getAppsPage; + server.resource["^/clients$"]["GET"] = getClientsPage; + server.resource["^/config$"]["GET"] = getConfigPage; + server.resource["^/password$"]["GET"] = getPasswordPage; + server.resource["^/api/pin"]["POST"] = savePin; + server.resource["^/api/apps$"]["GET"] = getApps; + server.resource["^/api/apps$"]["POST"] = saveApp; + server.resource["^/api/config$"]["GET"] = getConfig; + server.resource["^/api/config$"]["POST"] = saveConfig; + server.resource["^/api/password$"]["POST"] = savePassword; + server.resource["^/api/apps/([0-9]+)$"]["DELETE"] = deleteApp; + server.config.reuse_address = true; + server.config.address = "0.0.0.0"s; + server.config.port = PORT_HTTPS; + + try { + server.bind(); + BOOST_LOG(info) << "Configuration UI available at [https://localhost:"sv << PORT_HTTPS << "]"; + } + catch(boost::system::system_error &err) { + BOOST_LOG(fatal) << "Couldn't bind http server to ports ["sv << PORT_HTTPS << "]: "sv << err.what(); + + shutdown_event->raise(true); + return; + } + auto accept_and_run = [&](auto *server) { + try { + server->accept_and_run(); + } + catch(boost::system::system_error &err) { + // It's possible the exception gets thrown after calling server->stop() from a different thread + if(shutdown_event->peek()) { + return; + } + + BOOST_LOG(fatal) << "Couldn't start Configuration HTTP server to ports ["sv << PORT_HTTPS << ", "sv << PORT_HTTPS << "]: "sv << err.what(); + shutdown_event->raise(true); + return; + } + }; + std::thread tcp { accept_and_run, &server }; + + // Wait for any event + shutdown_event->view(); + + server.stop(); + + tcp.join(); +} +} // namespace confighttp \ No newline at end of file diff --git a/sunshine/confighttp.h b/sunshine/confighttp.h new file mode 100644 index 00000000..25507503 --- /dev/null +++ b/sunshine/confighttp.h @@ -0,0 +1,20 @@ +// +// Created by loki on 6/3/19. +// + +#ifndef SUNSHINE_CONFIGHTTP_H +#define SUNSHINE_CONFIGHTTP_H + +#include +#include + +#include "thread_safe.h" + +#define WEB_DIR SUNSHINE_ASSETS_DIR "/web/" + + +namespace confighttp { +void start(); +} + +#endif //SUNSHINE_CONFIGHTTP_H diff --git a/sunshine/crypto.cpp b/sunshine/crypto.cpp index 70a98289..d109cc51 100644 --- a/sunshine/crypto.cpp +++ b/sunshine/crypto.cpp @@ -2,14 +2,14 @@ // Created by loki on 5/31/19. // -#include #include "crypto.h" +#include namespace crypto { using big_num_t = util::safe_ptr; //using rsa_t = util::safe_ptr; using asn1_string_t = util::safe_ptr; -cert_chain_t::cert_chain_t() : _certs {}, _cert_ctx {X509_STORE_CTX_new() } {} +cert_chain_t::cert_chain_t() : _certs {}, _cert_ctx { X509_STORE_CTX_new() } {} void cert_chain_t::add(x509_t &&cert) { x509_store_t x509_store { X509_STORE_new() }; @@ -26,7 +26,7 @@ void cert_chain_t::add(x509_t &&cert) { */ const char *cert_chain_t::verify(x509_t::element_type *cert) { int err_code = 0; - for(auto &[_,x509_store] : _certs) { + for(auto &[_, x509_store] : _certs) { auto fg = util::fail_guard([this]() { X509_STORE_CTX_cleanup(_cert_ctx.get()); }); @@ -36,7 +36,7 @@ const char *cert_chain_t::verify(x509_t::element_type *cert) { auto err = X509_verify_cert(_cert_ctx.get()); - if (err == 1) { + if(err == 1) { return nullptr; } @@ -46,7 +46,7 @@ const char *cert_chain_t::verify(x509_t::element_type *cert) { if(err_code == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) { return nullptr; } - if (err_code != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && err_code != X509_V_ERR_INVALID_CA) { + if(err_code != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && err_code != X509_V_ERR_INVALID_CA) { return X509_verify_cert_error_string(err_code); } } @@ -63,7 +63,7 @@ int cipher_t::decrypt(const std::string_view &cipher, std::vector }); // Gen 7 servers use 128-bit AES ECB - if (EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_ecb(), nullptr, key.data(), nullptr) != 1) { + if(EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_ecb(), nullptr, key.data(), nullptr) != 1) { return -1; } @@ -72,11 +72,11 @@ int cipher_t::decrypt(const std::string_view &cipher, std::vector plaintext.resize((cipher.size() + 15) / 16 * 16); auto size = (int)plaintext.size(); // Encrypt into the caller's buffer, leaving room for the auth tag to be prepended - if (EVP_DecryptUpdate(ctx.get(), plaintext.data(), &size, (const std::uint8_t*)cipher.data(), cipher.size()) != 1) { + if(EVP_DecryptUpdate(ctx.get(), plaintext.data(), &size, (const std::uint8_t *)cipher.data(), cipher.size()) != 1) { return -1; } - if (EVP_DecryptFinal_ex(ctx.get(), plaintext.data(), &len) != 1) { + if(EVP_DecryptFinal_ex(ctx.get(), plaintext.data(), &len) != 1) { return -1; } @@ -85,7 +85,7 @@ int cipher_t::decrypt(const std::string_view &cipher, std::vector } int cipher_t::decrypt_gcm(aes_t &iv, const std::string_view &tagged_cipher, - std::vector &plaintext) { + std::vector &plaintext) { auto cipher = tagged_cipher.substr(16); auto tag = tagged_cipher.substr(0, 16); @@ -93,15 +93,15 @@ int cipher_t::decrypt_gcm(aes_t &iv, const std::string_view &tagged_cipher, EVP_CIPHER_CTX_reset(ctx.get()); }); - if (EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_gcm(), nullptr, nullptr, nullptr) != 1) { + if(EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_gcm(), nullptr, nullptr, nullptr) != 1) { return -1; } - if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, iv.size(), nullptr) != 1) { + if(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, iv.size(), nullptr) != 1) { return -1; } - if (EVP_DecryptInit_ex(ctx.get(), nullptr, nullptr, key.data(), iv.data()) != 1) { + if(EVP_DecryptInit_ex(ctx.get(), nullptr, nullptr, key.data(), iv.data()) != 1) { return -1; } @@ -109,16 +109,16 @@ int cipher_t::decrypt_gcm(aes_t &iv, const std::string_view &tagged_cipher, plaintext.resize((cipher.size() + 15) / 16 * 16); int size; - if (EVP_DecryptUpdate(ctx.get(), plaintext.data(), &size, (const std::uint8_t*)cipher.data(), cipher.size()) != 1) { + if(EVP_DecryptUpdate(ctx.get(), plaintext.data(), &size, (const std::uint8_t *)cipher.data(), cipher.size()) != 1) { return -1; } - if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, tag.size(), const_cast(tag.data())) != 1) { + if(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, tag.size(), const_cast(tag.data())) != 1) { return -1; } int len = size; - if (EVP_DecryptFinal_ex(ctx.get(), plaintext.data() + size, &len) != 1) { + if(EVP_DecryptFinal_ex(ctx.get(), plaintext.data() + size, &len) != 1) { return -1; } @@ -134,7 +134,7 @@ int cipher_t::encrypt(const std::string_view &plaintext, std::vectordata, (std::size_t)asn1->length }; + return { (const char *)asn1->data, (std::size_t)asn1->length }; } std::string rand(std::size_t bytes) { std::string r; r.resize(bytes); - RAND_bytes((uint8_t*)r.data(), r.size()); + RAND_bytes((uint8_t *)r.data(), r.size()); return r; } @@ -297,8 +297,8 @@ creds_t gen_creds(const std::string_view &cn, std::uint32_t key_bits) { X509_set_pubkey(x509.get(), pkey.get()); auto name = X509_get_subject_name(x509.get()); - X509_NAME_add_entry_by_txt(name,"CN", MBSTRING_ASC, - (const std::uint8_t*)cn.data(), cn.size(), + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, + (const std::uint8_t *)cn.data(), cn.size(), -1, 0); X509_set_issuer_name(x509.get(), name); @@ -324,7 +324,7 @@ bool verify(const x509_t &x509, const std::string_view &data, const std::string_ return false; } - if(EVP_DigestVerifyFinal(ctx.get(), (const uint8_t*)signature.data(), signature.size()) != 1) { + if(EVP_DigestVerifyFinal(ctx.get(), (const uint8_t *)signature.data(), signature.size()) != 1) { return false; } @@ -338,4 +338,14 @@ bool verify256(const x509_t &x509, const std::string_view &data, const std::stri void md_ctx_destroy(EVP_MD_CTX *ctx) { EVP_MD_CTX_destroy(ctx); } + +std::string rand_alphabet(std::size_t bytes, const std::string_view &alphabet) { + auto value = rand(bytes); + + for(std::size_t i = 0; i != value.size(); ++i) { + value[i] = alphabet[value[i] % alphabet.length()]; + } + return value; } + +} // namespace crypto \ No newline at end of file diff --git a/sunshine/crypto.h b/sunshine/crypto.h index faf1fc1f..a8599507 100644 --- a/sunshine/crypto.h +++ b/sunshine/crypto.h @@ -5,12 +5,11 @@ #ifndef SUNSHINE_CRYPTO_H #define SUNSHINE_CRYPTO_H -#include #include #include +#include #include #include -#include #include "utility.h" @@ -25,16 +24,17 @@ void md_ctx_destroy(EVP_MD_CTX *); using sha256_t = std::array; -using aes_t = std::array; -using x509_t = util::safe_ptr; -using x509_store_t = util::safe_ptr; +using aes_t = std::array; +using x509_t = util::safe_ptr; +using x509_store_t = util::safe_ptr; using x509_store_ctx_t = util::safe_ptr; -using cipher_ctx_t = util::safe_ptr; -using md_ctx_t = util::safe_ptr; -using bio_t = util::safe_ptr; -using pkey_t = util::safe_ptr; +using cipher_ctx_t = util::safe_ptr; +using md_ctx_t = util::safe_ptr; +using bio_t = util::safe_ptr; +using pkey_t = util::safe_ptr; sha256_t hash(const std::string_view &plaintext); + aes_t gen_aes_key(const std::array &salt, const std::string_view &pin); x509_t x509(const std::string_view &x); @@ -50,6 +50,8 @@ creds_t gen_creds(const std::string_view &cn, std::uint32_t key_bits); std::string_view signature(const x509_t &x); std::string rand(std::size_t bytes); +std::string rand_alphabet(std::size_t bytes, + const std::string_view &alphabet = std::string_view { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!%&()=-" }); class cert_chain_t { public: @@ -58,6 +60,7 @@ public: void add(x509_t &&cert); const char *verify(x509_t::element_type *cert); + private: std::vector> _certs; x509_store_ctx_t _cert_ctx; @@ -66,13 +69,14 @@ private: class cipher_t { public: cipher_t(const aes_t &key); - cipher_t(cipher_t&&) noexcept = default; - cipher_t &operator=(cipher_t&&) noexcept = default; + cipher_t(cipher_t &&) noexcept = default; + cipher_t &operator=(cipher_t &&) noexcept = default; int encrypt(const std::string_view &plaintext, std::vector &cipher); int decrypt_gcm(aes_t &iv, const std::string_view &cipher, std::vector &plaintext); int decrypt(const std::string_view &cipher, std::vector &plaintext); + private: cipher_ctx_t ctx; aes_t key; @@ -80,6 +84,6 @@ private: public: bool padding; }; -} +} // namespace crypto #endif //SUNSHINE_CRYPTO_H diff --git a/sunshine/httpcommon.cpp b/sunshine/httpcommon.cpp new file mode 100644 index 00000000..5f796595 --- /dev/null +++ b/sunshine/httpcommon.cpp @@ -0,0 +1,189 @@ +#define BOOST_BIND_GLOBAL_PLACEHOLDERS + +#include "process.h" + +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include "config.h" +#include "crypto.h" +#include "httpcommon.h" +#include "main.h" +#include "network.h" +#include "nvhttp.h" +#include "platform/common.h" +#include "rtsp.h" +#include "utility.h" +#include "uuid.h" + +namespace http { +using namespace std::literals; +namespace fs = std::filesystem; +namespace pt = boost::property_tree; + +int reload_user_creds(const std::string &file); +bool user_creds_exist(const std::string &file); + +std::string unique_id; +net::net_e origin_pin_allowed; + +int init() { + bool clean_slate = config::sunshine.flags[config::flag::FRESH_STATE]; + origin_pin_allowed = net::from_enum_string(config::nvhttp.origin_pin_allowed); + if(clean_slate) { + unique_id = util::uuid_t::generate().string(); + auto dir = std::filesystem::temp_directory_path() / "Sushine"sv; + config::nvhttp.cert = (dir / ("cert-"s + unique_id)).string(); + config::nvhttp.pkey = (dir / ("pkey-"s + unique_id)).string(); + } + + if(!fs::exists(config::nvhttp.pkey) || !fs::exists(config::nvhttp.cert)) { + if(create_creds(config::nvhttp.pkey, config::nvhttp.cert)) { + return -1; + } + } + if(!user_creds_exist(config::sunshine.credentials_file)) { + if(save_user_creds(config::sunshine.credentials_file, "sunshine"s, crypto::rand_alphabet(16), true)) { + return -1; + } + } + if(reload_user_creds(config::sunshine.credentials_file)) { + return -1; + } + + return 0; +} + +int save_user_creds(const std::string &file, const std::string &username, const std::string &password, bool run_our_mouth) { + pt::ptree outputTree; + + if(fs::exists(file)) { + try { + pt::read_json(file, outputTree); + } + catch(std::exception &e) { + BOOST_LOG(error) << "Couldn't read user credentials: "sv << e.what(); + return -1; + } + } + + auto salt = crypto::rand_alphabet(16); + outputTree.put("username", username); + outputTree.put("salt", salt); + outputTree.put("password", util::hex(crypto::hash(password + salt)).to_string()); + try { + pt::write_json(file, outputTree); + } + catch(std::exception &e) { + BOOST_LOG(error) << "generating user credentials: "sv << e.what(); + return -1; + } + + BOOST_LOG(info) << "New credentials have been created"sv; + + if(run_our_mouth) { + BOOST_LOG(info) << "Username: "sv << username; + BOOST_LOG(info) << "Password: "sv << password; + } + + return 0; +} + +bool user_creds_exist(const std::string &file) { + if(!fs::exists(file)) { + return false; + } + + pt::ptree inputTree; + try { + pt::read_json(file, inputTree); + return inputTree.find("username") != inputTree.not_found() && + inputTree.find("password") != inputTree.not_found() && + inputTree.find("salt") != inputTree.not_found(); + } + catch(std::exception &e) { + BOOST_LOG(error) << "validating user credentials: "sv << e.what(); + } + + return false; +} + +int reload_user_creds(const std::string &file) { + pt::ptree inputTree; + try { + pt::read_json(file, inputTree); + config::sunshine.username = inputTree.get("username"); + config::sunshine.password = inputTree.get("password"); + config::sunshine.salt = inputTree.get("salt"); + } + catch(std::exception &e) { + BOOST_LOG(error) << "loading user credentials: "sv << e.what(); + return -1; + } + return 0; +} + +int create_creds(const std::string &pkey, const std::string &cert) { + fs::path pkey_path = pkey; + fs::path cert_path = cert; + + auto creds = crypto::gen_creds("Sunshine Gamestream Host"sv, 2048); + + auto pkey_dir = pkey_path; + auto cert_dir = cert_path; + pkey_dir.remove_filename(); + cert_dir.remove_filename(); + + std::error_code err_code {}; + fs::create_directories(pkey_dir, err_code); + if(err_code) { + BOOST_LOG(error) << "Couldn't create directory ["sv << pkey_dir << "] :"sv << err_code.message(); + return -1; + } + + fs::create_directories(cert_dir, err_code); + if(err_code) { + BOOST_LOG(error) << "Couldn't create directory ["sv << cert_dir << "] :"sv << err_code.message(); + return -1; + } + + if(write_file(pkey.c_str(), creds.pkey)) { + BOOST_LOG(error) << "Couldn't open ["sv << config::nvhttp.pkey << ']'; + return -1; + } + + if(write_file(cert.c_str(), creds.x509)) { + BOOST_LOG(error) << "Couldn't open ["sv << config::nvhttp.cert << ']'; + return -1; + } + + fs::permissions(pkey_path, + fs::perms::owner_read | fs::perms::owner_write, + fs::perm_options::replace, err_code); + + if(err_code) { + BOOST_LOG(error) << "Couldn't change permissions of ["sv << config::nvhttp.pkey << "] :"sv << err_code.message(); + return -1; + } + + fs::permissions(cert_path, + fs::perms::owner_read | fs::perms::group_read | fs::perms::others_read | fs::perms::owner_write, + fs::perm_options::replace, err_code); + + if(err_code) { + BOOST_LOG(error) << "Couldn't change permissions of ["sv << config::nvhttp.cert << "] :"sv << err_code.message(); + return -1; + } + + return 0; +} +} // namespace http \ No newline at end of file diff --git a/sunshine/httpcommon.h b/sunshine/httpcommon.h new file mode 100644 index 00000000..fbd43253 --- /dev/null +++ b/sunshine/httpcommon.h @@ -0,0 +1,18 @@ +#include "network.h" +#include "thread_safe.h" + +namespace http { + +int init(); +int create_creds(const std::string &pkey, const std::string &cert); +int save_user_creds( + const std::string &file, + const std::string &username, + const std::string &password, + bool run_our_mouth = false); + +int reload_user_creds(const std::string &file); +extern std::string unique_id; +extern net::net_e origin_pin_allowed; + +} // namespace http \ No newline at end of file diff --git a/sunshine/input.cpp b/sunshine/input.cpp index 7d9b7733..f8ba308d 100644 --- a/sunshine/input.cpp +++ b/sunshine/input.cpp @@ -2,21 +2,27 @@ // Created by loki on 6/20/19. // +// define uint32_t for +#include extern "C" { #include } #include -#include "main.h" #include "config.h" -#include "utility.h" +#include "input.h" +#include "main.h" #include "platform/common.h" #include "thread_pool.h" +#include "utility.h" namespace input { -constexpr auto MAX_GAMEPADS = std::min((std::size_t)platf::MAX_GAMEPADS, sizeof(std::int16_t)*8); +constexpr auto MAX_GAMEPADS = std::min((std::size_t)platf::MAX_GAMEPADS, sizeof(std::int16_t) * 8); +#define DISABLE_LEFT_BUTTON_DELAY ((util::ThreadPool::task_id_t)0x01) +#define ENABLE_LEFT_BUTTON_DELAY nullptr + enum class button_state_e { NONE, DOWN, @@ -48,7 +54,7 @@ static platf::input_t platf_input; static std::bitset gamepadMask {}; void free_gamepad(platf::input_t &platf_input, int id) { - platf::gamepad(platf_input, id, platf::gamepad_state_t{}); + platf::gamepad(platf_input, id, platf::gamepad_state_t {}); platf::free_gamepad(platf_input, id); free_id(gamepadMask, id); @@ -57,7 +63,7 @@ struct gamepad_t { gamepad_t() : gamepad_state {}, back_timeout_id {}, id { -1 }, back_button_state { button_state_e::NONE } {} ~gamepad_t() { if(id >= 0) { - task_pool.push([id=this->id]() { + task_pool.push([id = this->id]() { free_gamepad(platf_input, id); }); } @@ -78,20 +84,41 @@ struct gamepad_t { }; struct input_t { - input_t() : active_gamepad_state {}, gamepads (MAX_GAMEPADS) { } + input_t(safe::mail_raw_t::event_t touch_port_event) + : active_gamepad_state {}, + gamepads(MAX_GAMEPADS), + touch_port_event { std::move(touch_port_event) }, + mouse_left_button_timeout {}, + touch_port { 0, 0, 0, 0, 0, 0, 1.0f } {} std::uint16_t active_gamepad_state; std::vector gamepads; + + safe::mail_raw_t::event_t touch_port_event; + + util::ThreadPool::task_id_t mouse_left_button_timeout; + + input::touch_port_t touch_port; }; using namespace std::literals; -void print(PNV_MOUSE_MOVE_PACKET packet) { +void print(PNV_REL_MOUSE_MOVE_PACKET packet) { BOOST_LOG(debug) - << "--begin mouse move packet--"sv << std::endl + << "--begin relative mouse move packet--"sv << std::endl << "deltaX ["sv << util::endian::big(packet->deltaX) << ']' << std::endl << "deltaY ["sv << util::endian::big(packet->deltaY) << ']' << std::endl - << "--end mouse move packet--"sv; + << "--end relative mouse move packet--"sv; +} + +void print(PNV_ABS_MOUSE_MOVE_PACKET packet) { + BOOST_LOG(debug) + << "--begin absolute mouse move packet--"sv << std::endl + << "x ["sv << util::endian::big(packet->x) << ']' << std::endl + << "y ["sv << util::endian::big(packet->y) << ']' << std::endl + << "width ["sv << util::endian::big(packet->width) << ']' << std::endl + << "height ["sv << util::endian::big(packet->height) << ']' << std::endl + << "--end absolute mouse move packet--"sv; } void print(PNV_MOUSE_BUTTON_PACKET packet) { @@ -135,50 +162,147 @@ void print(PNV_MULTI_CONTROLLER_PACKET packet) { constexpr int PACKET_TYPE_SCROLL_OR_KEYBOARD = PACKET_TYPE_SCROLL; void print(void *input) { - int input_type = util::endian::big(*(int*)input); + int input_type = util::endian::big(*(int *)input); switch(input_type) { - case PACKET_TYPE_MOUSE_MOVE: - print((PNV_MOUSE_MOVE_PACKET)input); - break; - case PACKET_TYPE_MOUSE_BUTTON: - print((PNV_MOUSE_BUTTON_PACKET)input); - break; - case PACKET_TYPE_SCROLL_OR_KEYBOARD: - { - char *tmp_input = (char*)input + 4; - if(tmp_input[0] == 0x0A) { - print((PNV_SCROLL_PACKET)input); - } - else { - print((PNV_KEYBOARD_PACKET)input); - } - - break; + case PACKET_TYPE_REL_MOUSE_MOVE: + print((PNV_REL_MOUSE_MOVE_PACKET)input); + break; + case PACKET_TYPE_ABS_MOUSE_MOVE: + print((PNV_ABS_MOUSE_MOVE_PACKET)input); + break; + case PACKET_TYPE_MOUSE_BUTTON: + print((PNV_MOUSE_BUTTON_PACKET)input); + break; + case PACKET_TYPE_SCROLL_OR_KEYBOARD: { + char *tmp_input = (char *)input + 4; + if(tmp_input[0] == 0x0A) { + print((PNV_SCROLL_PACKET)input); } - case PACKET_TYPE_MULTI_CONTROLLER: - print((PNV_MULTI_CONTROLLER_PACKET)input); - break; + else { + print((PNV_KEYBOARD_PACKET)input); + } + + break; + } + case PACKET_TYPE_MULTI_CONTROLLER: + print((PNV_MULTI_CONTROLLER_PACKET)input); + break; } } -void passthrough(platf::input_t &input, PNV_MOUSE_MOVE_PACKET packet) { +void passthrough(std::shared_ptr &input, PNV_REL_MOUSE_MOVE_PACKET packet) { display_cursor = true; - platf::move_mouse(input, util::endian::big(packet->deltaX), util::endian::big(packet->deltaY)); + input->mouse_left_button_timeout = DISABLE_LEFT_BUTTON_DELAY; + platf::move_mouse(platf_input, util::endian::big(packet->deltaX), util::endian::big(packet->deltaY)); +} + +void passthrough(std::shared_ptr &input, PNV_ABS_MOUSE_MOVE_PACKET packet) { + display_cursor = true; + + if(input->mouse_left_button_timeout == DISABLE_LEFT_BUTTON_DELAY) { + input->mouse_left_button_timeout = ENABLE_LEFT_BUTTON_DELAY; + } + + auto &touch_port_event = input->touch_port_event; + auto &touch_port = input->touch_port; + if(touch_port_event->peek()) { + touch_port = *touch_port_event->pop(); + } + + float x = util::endian::big(packet->x); + float y = util::endian::big(packet->y); + + // Prevent divide by zero + // Don't expect it to happen, but just in case + if(!packet->width || !packet->height) { + BOOST_LOG(warning) << "Moonlight passed invalid dimensions"sv; + + return; + } + + int width = util::endian::big(packet->width); + int height = util::endian::big(packet->height); + + auto offsetX = (width - (float)touch_port.width) * 0.5f; + auto offsetY = (height - (float)touch_port.height) * 0.5f; + + std::clamp(x, offsetX, width - offsetX); + std::clamp(y, offsetX, height - offsetY); + + platf::touch_port_t abs_port { + touch_port.offset_x, touch_port.offset_y, + touch_port.env_width, touch_port.env_height + }; + + platf::abs_mouse(platf_input, abs_port, (x - offsetX) * touch_port.scalar_inv, (y - offsetY) * touch_port.scalar_inv); } void passthrough(std::shared_ptr &input, PNV_MOUSE_BUTTON_PACKET packet) { auto constexpr BUTTON_RELEASED = 0x09; + auto constexpr BUTTON_LEFT = 0x01; + auto constexpr BUTTON_RIGHT = 0x03; + display_cursor = true; + auto release = packet->action == BUTTON_RELEASED; + auto button = util::endian::big(packet->button); if(button > 0 && button < mouse_press.size()) { - mouse_press[button] = packet->action != BUTTON_RELEASED; - } + if(mouse_press[button] != release) { + // button state is already what we want + return; + } - platf::button_mouse(platf_input, button, packet->action == BUTTON_RELEASED); + mouse_press[button] = !release; + } + /////////////////////////////////// + /*/ + * When Moonlight sends mouse input through absolute coordinates, + * it's possible that BUTTON_RIGHT is pressed down immediately after releasing BUTTON_LEFT. + * As a result, Sunshine will left click on hyperlinks in the browser before right clicking + * + * This can be solved by delaying BUTTON_LEFT, however, any delay on input is undesirable during gaming + * As a compromise, Sunshine will only put delays on BUTTON_LEFT when + * absolute mouse coordinates have been send. + * + * Try to make sure BUTTON_RIGHT gets called before BUTTON_LEFT is released. + * + * input->mouse_left_button_timeout can only be nullptr + * when the last mouse coordinates were absolute + /*/ + if(button == BUTTON_LEFT && release && !input->mouse_left_button_timeout) { + auto f = [=]() { + auto left_released = mouse_press[BUTTON_LEFT]; + if(left_released) { + // Already released left button + return; + } + platf::button_mouse(platf_input, BUTTON_LEFT, release); + + mouse_press[BUTTON_LEFT] = false; + input->mouse_left_button_timeout = nullptr; + }; + + input->mouse_left_button_timeout = task_pool.pushDelayed(std::move(f), 10ms).task_id; + + return; + } + if( + button == BUTTON_RIGHT && !release && + input->mouse_left_button_timeout > DISABLE_LEFT_BUTTON_DELAY) { + platf::button_mouse(platf_input, BUTTON_RIGHT, false); + platf::button_mouse(platf_input, BUTTON_RIGHT, true); + + mouse_press[BUTTON_RIGHT] = false; + + return; + } + /////////////////////////////////// + + platf::button_mouse(platf_input, button, release); } void repeat_key(short key_code) { @@ -193,6 +317,20 @@ void repeat_key(short key_code) { task_id = task_pool.pushDelayed(repeat_key, config::input.key_repeat_period, key_code).task_id; } +short map_keycode(short keycode) { + keycode &= 0x00FF; + + switch(keycode) { + case 0x10: + return 0xA0; + case 0x11: + return 0xA2; + case 0x12: + return 0xA4; + } + + return keycode; +} void passthrough(std::shared_ptr &input, PNV_KEYBOARD_PACKET packet) { auto constexpr BUTTON_RELEASED = 0x04; @@ -220,18 +358,19 @@ void passthrough(std::shared_ptr &input, PNV_KEYBOARD_PACKET packet) { } pressed = !release; - platf::keyboard(platf_input, packet->keyCode & 0x00FF, release); + + platf::keyboard(platf_input, map_keycode(packet->keyCode), release); } -void passthrough(platf::input_t &input, PNV_SCROLL_PACKET packet) { +void passthrough(PNV_SCROLL_PACKET packet) { display_cursor = true; - platf::scroll(input, util::endian::big(packet->scrollAmt1)); + platf::scroll(platf_input, util::endian::big(packet->scrollAmt1)); } int updateGamepads(std::vector &gamepads, std::int16_t old_state, std::int16_t new_state) { auto xorGamepadMask = old_state ^ new_state; - if (!xorGamepadMask) { + if(!xorGamepadMask) { return 0; } @@ -240,7 +379,7 @@ int updateGamepads(std::vector &gamepads, std::int16_t old_state, std auto &gamepad = gamepads[x]; if((old_state >> x) & 1) { - if (gamepad.id < 0) { + if(gamepad.id < 0) { return -1; } @@ -300,7 +439,7 @@ void passthrough(std::shared_ptr &input, PNV_MULTI_CONTROLLER_PACKET pa display_cursor = false; std::uint16_t bf = packet->buttonFlags; - platf::gamepad_state_t gamepad_state{ + platf::gamepad_state_t gamepad_state { bf, packet->leftTrigger, packet->rightTrigger, @@ -312,30 +451,30 @@ void passthrough(std::shared_ptr &input, PNV_MULTI_CONTROLLER_PACKET pa auto bf_new = gamepad_state.buttonFlags; switch(gamepad.back_button_state) { - case button_state_e::UP: - if(!(platf::BACK & bf_new)) { - gamepad.back_button_state = button_state_e::NONE; - } - gamepad_state.buttonFlags &= ~platf::BACK; - break; - case button_state_e::DOWN: - if(platf::BACK & bf_new) { - gamepad.back_button_state = button_state_e::NONE; - } - gamepad_state.buttonFlags |= platf::BACK; - break; - case button_state_e::NONE: - break; + case button_state_e::UP: + if(!(platf::BACK & bf_new)) { + gamepad.back_button_state = button_state_e::NONE; + } + gamepad_state.buttonFlags &= ~platf::BACK; + break; + case button_state_e::DOWN: + if(platf::BACK & bf_new) { + gamepad.back_button_state = button_state_e::NONE; + } + gamepad_state.buttonFlags |= platf::BACK; + break; + case button_state_e::NONE: + break; } - bf = gamepad_state.buttonFlags ^ gamepad.gamepad_state.buttonFlags; + bf = gamepad_state.buttonFlags ^ gamepad.gamepad_state.buttonFlags; bf_new = gamepad_state.buttonFlags; - if (platf::BACK & bf) { - if (platf::BACK & bf_new) { + if(platf::BACK & bf) { + if(platf::BACK & bf_new) { // Don't emulate home button if timeout < 0 if(config::input.back_button_timeout >= 0ms) { - gamepad.back_timeout_id = task_pool.pushDelayed([input, controller=packet->controllerNumber]() { + auto f = [input, controller = packet->controllerNumber]() { auto &gamepad = input->gamepads[controller]; auto &state = gamepad.gamepad_state; @@ -354,10 +493,12 @@ void passthrough(std::shared_ptr &input, PNV_MULTI_CONTROLLER_PACKET pa platf::gamepad(platf_input, gamepad.id, state); gamepad.back_timeout_id = nullptr; - }, config::input.back_button_timeout).task_id; + }; + + gamepad.back_timeout_id = task_pool.pushDelayed(std::move(f), config::input.back_button_timeout).task_id; } } - else if (gamepad.back_timeout_id) { + else if(gamepad.back_timeout_id) { task_pool.cancel(gamepad.back_timeout_id); gamepad.back_timeout_id = nullptr; } @@ -371,30 +512,32 @@ void passthrough(std::shared_ptr &input, PNV_MULTI_CONTROLLER_PACKET pa void passthrough_helper(std::shared_ptr input, std::vector &&input_data) { void *payload = input_data.data(); - int input_type = util::endian::big(*(int*)payload); + int input_type = util::endian::big(*(int *)payload); switch(input_type) { - case PACKET_TYPE_MOUSE_MOVE: - passthrough(platf_input, (PNV_MOUSE_MOVE_PACKET)payload); - break; - case PACKET_TYPE_MOUSE_BUTTON: - passthrough(input, (PNV_MOUSE_BUTTON_PACKET)payload); - break; - case PACKET_TYPE_SCROLL_OR_KEYBOARD: - { - char *tmp_input = (char*)payload + 4; - if(tmp_input[0] == 0x0A) { - passthrough(platf_input, (PNV_SCROLL_PACKET)payload); - } - else { - passthrough(input, (PNV_KEYBOARD_PACKET)payload); - } - - break; + case PACKET_TYPE_REL_MOUSE_MOVE: + passthrough(input, (PNV_REL_MOUSE_MOVE_PACKET)payload); + break; + case PACKET_TYPE_ABS_MOUSE_MOVE: + passthrough(input, (PNV_ABS_MOUSE_MOVE_PACKET)payload); + break; + case PACKET_TYPE_MOUSE_BUTTON: + passthrough(input, (PNV_MOUSE_BUTTON_PACKET)payload); + break; + case PACKET_TYPE_SCROLL_OR_KEYBOARD: { + char *tmp_input = (char *)payload + 4; + if(tmp_input[0] == 0x0A) { + passthrough((PNV_SCROLL_PACKET)payload); } - case PACKET_TYPE_MULTI_CONTROLLER: - passthrough(input, (PNV_MULTI_CONTROLLER_PACKET)payload); - break; + else { + passthrough(input, (PNV_KEYBOARD_PACKET)payload); + } + + break; + } + case PACKET_TYPE_MULTI_CONTROLLER: + passthrough(input, (PNV_MULTI_CONTROLLER_PACKET)payload); + break; } } @@ -402,19 +545,40 @@ void passthrough(std::shared_ptr &input, std::vector &&in task_pool.push(passthrough_helper, input, util::cmove(input_data)); } +void reset(std::shared_ptr &input) { + task_pool.cancel(task_id); + task_pool.cancel(input->mouse_left_button_timeout); + + // Ensure input is synchronous, by using the task_pool + task_pool.push([]() { + for(int x = 0; x < mouse_press.size(); ++x) { + if(mouse_press[x]) { + platf::button_mouse(platf_input, x, true); + mouse_press[x] = false; + } + } + + for(auto &kp : key_press) { + platf::keyboard(platf_input, kp.first & 0x00FF, true); + key_press[kp.first] = false; + } + }); +} + void init() { platf_input = platf::input(); } -std::shared_ptr alloc() { - auto input = std::make_shared(); +std::shared_ptr alloc(safe::mail_t mail) { + auto input = std::make_shared(mail->event(mail::touch_port)); // Workaround to ensure new frames will be captured when a client connects task_pool.pushDelayed([]() { platf::move_mouse(platf_input, 1, 1); platf::move_mouse(platf_input, -1, -1); - }, 100ms); + }, + 100ms); return input; } -} +} // namespace input diff --git a/sunshine/input.h b/sunshine/input.h index ce5b9161..285828d9 100644 --- a/sunshine/input.h +++ b/sunshine/input.h @@ -5,17 +5,28 @@ #ifndef SUNSHINE_INPUT_H #define SUNSHINE_INPUT_H +#include "platform/common.h" +#include "thread_safe.h" namespace input { struct input_t; void print(void *input); +void reset(std::shared_ptr &input); void passthrough(std::shared_ptr &input, std::vector &&input_data); + void init(); -std::shared_ptr alloc(); -} +std::shared_ptr alloc(safe::mail_t mail); + +struct touch_port_t : public platf::touch_port_t { + int env_width, env_height; + + // inverse of scalar used for aspect ratio + float scalar_inv; +}; +} // namespace input #endif //SUNSHINE_INPUT_H diff --git a/sunshine/main.cpp b/sunshine/main.cpp index 0620cde3..3dc625ef 100644 --- a/sunshine/main.cpp +++ b/sunshine/main.cpp @@ -4,30 +4,36 @@ #include "process.h" -#include -#include #include +#include +#include +#include +#include -#include -#include -#include -#include #include +#include +#include +#include +#include -#include "video.h" -#include "input.h" -#include "nvhttp.h" -#include "rtsp.h" #include "config.h" -#include "thread_pool.h" +#include "confighttp.h" +#include "httpcommon.h" +#include "main.h" +#include "nvhttp.h" #include "publish.h" +#include "rtsp.h" +#include "thread_pool.h" +#include "video.h" #include "platform/common.h" extern "C" { -#include #include +#include } +safe::mail_t mail::man; + using namespace std::literals; namespace bl = boost::log; @@ -50,6 +56,28 @@ struct NoDelete { BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", int) +void print_help(const char *name) { + std::cout + << "Usage: "sv << name << " [options] [/path/to/configuration_file] [--cmd]"sv << std::endl + << " Any configurable option can be overwritten with: \"name=value\""sv << std::endl + << std::endl + << " --help | print help"sv << std::endl + << " --creds username password | set user credentials for the Web manager" << std::endl + << std::endl + << " flags"sv << std::endl + << " -0 | Read PIN from stdin"sv << std::endl + << " -1 | Do not load previously saved state and do retain any state after shutdown"sv << std::endl + << " | Effectively starting as if for the first time without overwriting any pairings with your devices"sv << std::endl + << " -2 | Force replacement of headers in video stream" << std::endl; +} + +namespace help { +int entry(const char *name, int argc, char *argv[]) { + print_help(name); + return 0; +} +} // namespace help + void log_flush() { sink->flush(); } @@ -66,14 +94,37 @@ void on_signal(int sig, FN &&fn) { std::signal(sig, on_signal_forwarder); } +namespace gen_creds { +int entry(const char *name, int argc, char *argv[]) { + if(argc < 2 || argv[0] == "help"sv || argv[1] == "help"sv) { + print_help(name); + return 0; + } + + http::save_user_creds(config::sunshine.credentials_file, argv[0], argv[1]); + + return 0; +} +} // namespace gen_creds + +std::map> cmd_to_func { + { "creds"sv, gen_creds::entry }, + { "help"sv, help::entry } +}; + int main(int argc, char *argv[]) { + mail::man = std::make_shared(); + if(config::parse(argc, argv)) { return 0; } - if(config::sunshine.min_log_level >= 2) { + if(config::sunshine.min_log_level >= 1) { av_log_set_level(AV_LOG_QUIET); } + else { + av_log_set_level(AV_LOG_DEBUG); + } sink = boost::make_shared(); @@ -81,31 +132,31 @@ int main(int argc, char *argv[]) { sink->locked_backend()->add_stream(stream); sink->set_filter(severity >= config::sunshine.min_log_level); - sink->set_formatter([message="Message"s, severity="Severity"s](const bl::record_view &view, bl::formatting_ostream &os) { - constexpr int DATE_BUFFER_SIZE = 21 +2 +1; // Full string plus ": \0" + sink->set_formatter([message = "Message"s, severity = "Severity"s](const bl::record_view &view, bl::formatting_ostream &os) { + constexpr int DATE_BUFFER_SIZE = 21 + 2 + 1; // Full string plus ": \0" auto log_level = view.attribute_values()[severity].extract().get(); std::string_view log_type; switch(log_level) { - case 0: - log_type = "Verbose: "sv; - break; - case 1: - log_type = "Debug: "sv; - break; - case 2: - log_type = "Info: "sv; - break; - case 3: - log_type = "Warning: "sv; - break; - case 4: - log_type = "Error: "sv; - break; - case 5: - log_type = "Fatal: "sv; - break; + case 0: + log_type = "Verbose: "sv; + break; + case 1: + log_type = "Debug: "sv; + break; + case 2: + log_type = "Info: "sv; + break; + case 3: + log_type = "Warning: "sv; + break; + case 4: + log_type = "Error: "sv; + break; + case 5: + log_type = "Fatal: "sv; + break; }; char _date[DATE_BUFFER_SIZE]; @@ -118,40 +169,94 @@ int main(int argc, char *argv[]) { bl::core::get()->add_sink(sink); auto fg = util::fail_guard(log_flush); + if(!config::sunshine.cmd.name.empty()) { + auto fn = cmd_to_func.find(config::sunshine.cmd.name); + if(fn == std::end(cmd_to_func)) { + BOOST_LOG(fatal) << "Unknown command: "sv << config::sunshine.cmd.name; + + BOOST_LOG(info) << "Possible commands:"sv; + for(auto &[key, _] : cmd_to_func) { + BOOST_LOG(info) << '\t' << key; + } + + return 7; + } + + return fn->second(argv[0], config::sunshine.cmd.argc, config::sunshine.cmd.argv); + } + // Create signal handler after logging has been initialized - auto shutdown_event = std::make_shared>(); + auto shutdown_event = mail::man->event(mail::shutdown); on_signal(SIGINT, [shutdown_event]() { BOOST_LOG(info) << "Interrupt handler called"sv; + shutdown_event->raise(true); }); - auto proc_opt = proc::parse(config::stream.file_apps); - if(!proc_opt) { - return 7; - } - - { - proc::ctx_t ctx; - ctx.name = "Desktop"s; - proc_opt->get_apps().emplace(std::begin(proc_opt->get_apps()), std::move(ctx)); - } - - proc::proc = std::move(*proc_opt); + proc::refresh(config::stream.file_apps); auto deinit_guard = platf::init(); - input::init(); + if(!deinit_guard) { + return 4; + } + reed_solomon_init(); + input::init(); if(video::init()) { return 2; } + if(http::init()) { + return 3; + } + + //FIXME: Temporary workaround: Simple-Web_server needs to be updated or replaced + if(shutdown_event->peek()) { + return 0; + } task_pool.start(1); - std::thread httpThread { nvhttp::start, shutdown_event }; - std::thread publishThread { publish::start, shutdown_event }; - stream::rtpThread(shutdown_event); + std::thread publishThread { publish::start }; + std::thread httpThread { nvhttp::start }; + std::thread configThread { confighttp::start }; + stream::rtpThread(); + publishThread.join(); httpThread.join(); + configThread.join(); + + task_pool.stop(); + task_pool.join(); return 0; } + +std::string read_file(const char *path) { + if(!std::filesystem::exists(path)) { + return {}; + } + + std::ifstream in(path); + + std::string input; + std::string base64_cert; + + while(!in.eof()) { + std::getline(in, input); + base64_cert += input + '\n'; + } + + return base64_cert; +} + +int write_file(const char *path, const std::string_view &contents) { + std::ofstream out(path); + + if(!out.is_open()) { + return -1; + } + + out << contents; + + return 0; +} \ No newline at end of file diff --git a/sunshine/main.h b/sunshine/main.h index b8a85e5b..7de488ab 100644 --- a/sunshine/main.h +++ b/sunshine/main.h @@ -5,8 +5,12 @@ #ifndef SUNSHINE_MAIN_H #define SUNSHINE_MAIN_H -#include +#include + #include "thread_pool.h" +#include "thread_safe.h" + +#include extern util::ThreadPool task_pool; extern bool display_cursor; @@ -19,4 +23,30 @@ extern boost::log::sources::severity_logger error; extern boost::log::sources::severity_logger fatal; void log_flush(); + +void print_help(const char *name); + +std::string read_file(const char *path); +int write_file(const char *path, const std::string_view &contents); + +namespace mail { +#define MAIL(x) \ + constexpr auto x = std::string_view { #x } + +extern safe::mail_t man; + +// Global mail +MAIL(shutdown); +MAIL(broadcast_shutdown); + +MAIL(video_packets); +MAIL(audio_packets); + +// Local mail +MAIL(touch_port); +MAIL(idr); +#undef MAIL +} // namespace mail + + #endif //SUNSHINE_MAIN_H diff --git a/sunshine/move_by_copy.h b/sunshine/move_by_copy.h index 61e205f5..804a72ba 100644 --- a/sunshine/move_by_copy.h +++ b/sunshine/move_by_copy.h @@ -11,23 +11,24 @@ template class MoveByCopy { public: typedef T move_type; + private: move_type _to_move; -public: - explicit MoveByCopy(move_type &&to_move) : _to_move(std::move(to_move)) { } +public: + explicit MoveByCopy(move_type &&to_move) : _to_move(std::move(to_move)) {} MoveByCopy(MoveByCopy &&other) = default; - + MoveByCopy(const MoveByCopy &other) { *this = other; } - MoveByCopy& operator=(MoveByCopy &&other) = default; - - MoveByCopy& operator=(const MoveByCopy &other) { - this->_to_move = std::move(const_cast(other)._to_move); - + MoveByCopy &operator=(MoveByCopy &&other) = default; + + MoveByCopy &operator=(const MoveByCopy &other) { + this->_to_move = std::move(const_cast(other)._to_move); + return *this; } @@ -44,7 +45,7 @@ MoveByCopy cmove(T &movable) { // Do NOT use this unless you are absolutely certain the object to be moved is no longer used by the caller template MoveByCopy const_cmove(const T &movable) { - return MoveByCopy(std::move(const_cast(movable))); -} + return MoveByCopy(std::move(const_cast(movable))); } +} // namespace util #endif diff --git a/sunshine/network.cpp b/sunshine/network.cpp index 1efb5364..28e031ef 100644 --- a/sunshine/network.cpp +++ b/sunshine/network.cpp @@ -2,9 +2,9 @@ // Created by loki on 12/27/19. // -#include #include "network.h" #include "utility.h" +#include namespace net { using namespace std::literals; @@ -21,17 +21,17 @@ std::vector> lan_ips { }; std::uint32_t ip(const std::string_view &ip_str) { - auto begin = std::begin(ip_str); - auto end = std::end(ip_str); + auto begin = std::begin(ip_str); + auto end = std::end(ip_str); auto temp_end = std::find(begin, end, '.'); std::uint32_t ip = 0; - auto shift = 24; + auto shift = 24; while(temp_end != end) { ip += (util::from_chars(begin, temp_end) << shift); shift -= 8; - begin = temp_end + 1; + begin = temp_end + 1; temp_end = std::find(begin, end, '.'); } @@ -43,7 +43,7 @@ std::uint32_t ip(const std::string_view &ip_str) { // In the format "xxx.xxx.xxx.xxx/x" std::pair ip_block(const std::string_view &ip_str) { auto begin = std::begin(ip_str); - auto end = std::find(begin, std::end(ip_str), '/'); + auto end = std::find(begin, std::end(ip_str), '/'); auto addr = ip({ begin, (std::size_t)(end - begin) }); @@ -82,12 +82,12 @@ net_e from_address(const std::string_view &view) { std::string_view to_enum_string(net_e net) { switch(net) { - case PC: - return "pc"sv; - case LAN: - return "lan"sv; - case WAN: - return "wan"sv; + case PC: + return "pc"sv; + case LAN: + return "lan"sv; + case WAN: + return "wan"sv; } // avoid warning @@ -112,4 +112,4 @@ void free_host(ENetHost *host) { enet_host_destroy(host); } -} \ No newline at end of file +} // namespace net \ No newline at end of file diff --git a/sunshine/network.h b/sunshine/network.h index b3e65a97..88f18a40 100644 --- a/sunshine/network.h +++ b/sunshine/network.h @@ -14,8 +14,8 @@ namespace net { void free_host(ENetHost *host); -using host_t = util::safe_ptr; -using peer_t = ENetPeer*; +using host_t = util::safe_ptr; +using peer_t = ENetPeer *; using packet_t = util::safe_ptr; enum net_e : int { @@ -30,6 +30,6 @@ std::string_view to_enum_string(net_e net); net_e from_address(const std::string_view &view); host_t host_create(ENetAddress &addr, std::size_t peers, std::uint16_t port); -} +} // namespace net #endif //SUNSHINE_NETWORK_H diff --git a/sunshine/nvhttp.cpp b/sunshine/nvhttp.cpp index eb9003d1..c2beaa4f 100644 --- a/sunshine/nvhttp.cpp +++ b/sunshine/nvhttp.cpp @@ -2,13 +2,15 @@ // Created by loki on 6/3/19. // +#define BOOST_BIND_GLOBAL_PLACEHOLDERS + #include "process.h" #include +#include #include #include -#include #include @@ -16,16 +18,17 @@ #include #include + #include "config.h" -#include "utility.h" -#include "rtsp.h" #include "crypto.h" +#include "httpcommon.h" +#include "main.h" +#include "network.h" #include "nvhttp.h" #include "platform/common.h" -#include "network.h" +#include "rtsp.h" +#include "utility.h" #include "uuid.h" -#include "main.h" - namespace nvhttp { using namespace std::literals; @@ -36,9 +39,6 @@ constexpr auto GFE_VERSION = "3.12.0.1"; namespace fs = std::filesystem; namespace pt = boost::property_tree; -std::string read_file(const char *path); -int write_file(const char *path, const std::string_view &contents); - using https_server_t = SimpleWeb::Server; using http_server_t = SimpleWeb::Server; @@ -67,8 +67,8 @@ struct pair_session_t { struct { util::Either< std::shared_ptr::Response>, - std::shared_ptr::Response> - > response; + std::shared_ptr::Response>> + response; std::string salt; } async_insert_pin; }; @@ -76,14 +76,12 @@ struct pair_session_t { // uniqueID, session std::unordered_map map_id_sess; std::unordered_map map_id_client; -std::string unique_id; -net::net_e origin_pin_allowed; -using args_t = SimpleWeb::CaseInsensitiveMultimap; +using args_t = SimpleWeb::CaseInsensitiveMultimap; using resp_https_t = std::shared_ptr::Response>; -using req_https_t = std::shared_ptr::Request>; -using resp_http_t = std::shared_ptr::Response>; -using req_http_t = std::shared_ptr::Request>; +using req_https_t = std::shared_ptr::Request>; +using resp_http_t = std::shared_ptr::Response>; +using req_http_t = std::shared_ptr::Request>; enum class op_e { ADD, @@ -93,9 +91,21 @@ enum class op_e { void save_state() { pt::ptree root; - root.put("root.uniqueid", unique_id); + if(fs::exists(config::nvhttp.file_state)) { + try { + pt::read_json(config::nvhttp.file_state, root); + } + catch(std::exception &e) { + BOOST_LOG(error) << "Couldn't read "sv << config::nvhttp.file_state << ": "sv << e.what(); + return; + } + } + + root.erase("root"s); + + root.put("root.uniqueid", http::unique_id); auto &nodes = root.add_child("root.devices", pt::ptree {}); - for(auto &[_,client] : map_id_client) { + for(auto &[_, client] : map_id_client) { pt::ptree node; node.put("uniqueid"s, client.uniqueID); @@ -111,31 +121,44 @@ void save_state() { nodes.push_back(std::make_pair(""s, node)); } - pt::write_json(config::nvhttp.file_state, root); + try { + pt::write_json(config::nvhttp.file_state, root); + } + catch(std::exception &e) { + BOOST_LOG(error) << "Couldn't write "sv << config::nvhttp.file_state << ": "sv << e.what(); + return; + } } void load_state() { - auto file_state = fs::current_path() / config::nvhttp.file_state; - - if(!fs::exists(file_state)) { - unique_id = util::uuid_t::generate().string(); + if(!fs::exists(config::nvhttp.file_state)) { + BOOST_LOG(info) << "DOENST EXIST"sv; + http::unique_id = util::uuid_t::generate().string(); return; } pt::ptree root; try { pt::read_json(config::nvhttp.file_state, root); - } catch (std::exception &e) { - BOOST_LOG(warning) << e.what(); + } + catch(std::exception &e) { + BOOST_LOG(error) << "Couldn't read "sv << config::nvhttp.file_state << ": "sv << e.what(); return; } - unique_id = root.get("root.uniqueid"); + auto unique_id_p = root.get_optional("root.uniqueid"); + if(!unique_id_p) { + // This file doesn't contain moonlight credentials + http::unique_id = util::uuid_t::generate().string(); + return; + } + http::unique_id = std::move(*unique_id_p); + auto device_nodes = root.get_child("root.devices"); - for(auto &[_,device_node] : device_nodes) { - auto uniqID = device_node.get("uniqueid"); + for(auto &[_, device_node] : device_nodes) { + auto uniqID = device_node.get("uniqueid"); auto &client = map_id_client.emplace(uniqID, client_t {}).first->second; client.uniqueID = uniqID; @@ -148,16 +171,14 @@ void load_state() { void update_id_client(const std::string &uniqueID, std::string &&cert, op_e op) { switch(op) { - case op_e::ADD: - { - auto &client = map_id_client[uniqueID]; - client.certs.emplace_back(std::move(cert)); - client.uniqueID = uniqueID; - } - break; - case op_e::REMOVE: - map_id_client.erase(uniqueID); - break; + case op_e::ADD: { + auto &client = map_id_client[uniqueID]; + client.certs.emplace_back(std::move(cert)); + client.uniqueID = uniqueID; + } break; + case op_e::REMOVE: + map_id_client.erase(uniqueID); + break; } if(!config::sunshine.flags[config::flag::FRESH_STATE]) { @@ -165,6 +186,20 @@ void update_id_client(const std::string &uniqueID, std::string &&cert, op_e op) } } +stream::launch_session_t make_launch_session(bool host_audio, const args_t &args) { + stream::launch_session_t launch_session; + + launch_session.host_audio = host_audio; + launch_session.gcm_key = *util::from_hex(args.at("rikey"s), true); + uint32_t prepend_iv = util::endian::big(util::from_view(args.at("rikeyid"s))); + auto prepend_iv_p = (uint8_t *)&prepend_iv; + + auto next = std::copy(prepend_iv_p, prepend_iv_p + sizeof(prepend_iv), std::begin(launch_session.iv)); + std::fill(next, std::end(launch_session.iv), 0); + + return launch_session; +} + void getservercert(pair_session_t &sess, pt::ptree &tree, const std::string &pin) { if(sess.async_insert_pin.salt.size() < 32) { tree.put("root.paired", 0); @@ -173,10 +208,10 @@ void getservercert(pair_session_t &sess, pt::ptree &tree, const std::string &pin } std::string_view salt_view { sess.async_insert_pin.salt.data(), 32 }; - + auto salt = util::from_hex>(salt_view, true); - auto key = crypto::gen_aes_key(*salt, pin); + auto key = crypto::gen_aes_key(*salt, pin); sess.cipher_key = std::make_unique(key); tree.put("root.paired", 1); @@ -195,7 +230,7 @@ void serverchallengeresp(pair_session_t &sess, pt::ptree &tree, const args_t &ar sess.clienthash = std::move(decrypted); auto serversecret = sess.serversecret; - auto sign = crypto::sign256(crypto::pkey(conf_intern.pkey), serversecret); + auto sign = crypto::sign256(crypto::pkey(conf_intern.pkey), serversecret); serversecret.insert(std::end(serversecret), std::begin(sign), std::end(sign)); @@ -213,14 +248,14 @@ void clientchallenge(pair_session_t &sess, pt::ptree &tree, const args_t &args) std::vector decrypted; cipher.decrypt(challenge, decrypted); - auto x509 = crypto::x509(conf_intern.servercert); - auto sign = crypto::signature(x509); + auto x509 = crypto::x509(conf_intern.servercert); + auto sign = crypto::signature(x509); auto serversecret = crypto::rand(16); decrypted.insert(std::end(decrypted), std::begin(sign), std::end(sign)); decrypted.insert(std::end(decrypted), std::begin(serversecret), std::end(serversecret)); - auto hash = crypto::hash({ (char*)decrypted.data(), decrypted.size() }); + auto hash = crypto::hash({ (char *)decrypted.data(), decrypted.size() }); auto serverchallenge = crypto::rand(16); std::string plaintext; @@ -250,7 +285,7 @@ void clientpairingsecret(std::shared_ptr> &add_cer assert((secret.size() + sign.size()) == pairingsecret.size()); - auto x509 = crypto::x509(client.cert); + auto x509 = crypto::x509(client.cert); auto x509_sign = crypto::signature(x509); std::string data; @@ -304,8 +339,8 @@ template void print_req(std::shared_ptr::Request> request) { BOOST_LOG(debug) << "TUNNEL :: "sv << tunnel::to_string; - BOOST_LOG(debug) << "METHOD :: "sv << request->method; - BOOST_LOG(debug) << "DESTINATION :: "sv << request->path; + BOOST_LOG(debug) << "METHOD :: "sv << request->method; + BOOST_LOG(debug) << "DESTINATION :: "sv << request->path; for(auto &[name, val] : request->header) { BOOST_LOG(debug) << name << " -- " << val; @@ -332,33 +367,44 @@ void not_found(std::shared_ptr::Response> resp pt::write_xml(data, tree); response->write(data.str()); - *response << "HTTP/1.1 404 NOT FOUND\r\n" << data.str(); + *response << "HTTP/1.1 404 NOT FOUND\r\n" + << data.str(); } template void pair(std::shared_ptr> &add_cert, std::shared_ptr::Response> response, std::shared_ptr::Request> request) { print_req(request); + pt::ptree tree; + auto args = request->parse_query_string(); + if(args.find("uniqueid"s) == std::end(args)) { + tree.put("root..status_code", 400); + + return; + } + auto uniqID { std::move(args.at("uniqueid"s)) }; auto sess_it = map_id_sess.find(uniqID); - pt::ptree tree; - args_t::const_iterator it; if(it = args.find("phrase"); it != std::end(args)) { if(it->second == "getservercert"sv) { pair_session_t sess; - + sess.client.uniqueID = std::move(uniqID); - sess.client.cert = util::from_hex_vec(args.at("clientcert"s), true); + sess.client.cert = util::from_hex_vec(args.at("clientcert"s), true); BOOST_LOG(debug) << sess.client.cert; auto ptr = map_id_sess.emplace(sess.client.uniqueID, std::move(sess)).first; ptr->second.async_insert_pin.salt = std::move(args.at("salt"s)); - if(config::sunshine.flags[config::flag::PIN_STDIN]) { + if(config::sunshine.flags[config::flag::CONST_PIN]) { + std::string pin("6174"); + getservercert(ptr->second, tree, pin); + } + else if(config::sunshine.flags[config::flag::PIN_STDIN]) { std::string pin; std::cout << "Please insert pin: "sv; @@ -396,30 +442,14 @@ void pair(std::shared_ptr> &add_cert, std::shared_ response->write(data.str()); } -template -void pin(std::shared_ptr::Response> response, std::shared_ptr::Request> request) { - print_req(request); - - auto address = request->remote_endpoint_address(); - auto ip_type = net::from_address(address); - if(ip_type > origin_pin_allowed) { - BOOST_LOG(info) << '[' << address << "] -- denied"sv; - - response->write(SimpleWeb::StatusCode::client_error_forbidden); - - return; - } - +bool pin(std::string pin) { pt::ptree tree; - if(map_id_sess.empty()) { - response->write(SimpleWeb::StatusCode::client_error_im_a_teapot); - - return; + return false; } auto &sess = std::begin(map_id_sess)->second; - getservercert(sess, tree, request->path_match[1]); + getservercert(sess, tree, pin); // response to the request for pin std::ostringstream data; @@ -429,19 +459,40 @@ void pin(std::shared_ptr::Response> response, if(async_response.has_left() && async_response.left()) { async_response.left()->write(data.str()); } - else if(async_response.has_right() && async_response.right()){ + else if(async_response.has_right() && async_response.right()) { async_response.right()->write(data.str()); } else { - response->write(SimpleWeb::StatusCode::client_error_im_a_teapot); - - return; + return false; } // reset async_response async_response = std::decay_t(); // response to the current request - response->write(SimpleWeb::StatusCode::success_ok); + return true; +} + +template +void pin(std::shared_ptr::Response> response, std::shared_ptr::Request> request) { + print_req(request); + + auto address = request->remote_endpoint_address(); + auto ip_type = net::from_address(address); + if(ip_type > http::origin_pin_allowed) { + BOOST_LOG(info) << '[' << address << "] -- denied"sv; + + response->write(SimpleWeb::StatusCode::client_error_forbidden); + + return; + } + + bool pinResponse = pin(request->path_match[1]); + if(pinResponse) { + response->write(SimpleWeb::StatusCode::success_ok); + } + else { + response->write(SimpleWeb::StatusCode::client_error_im_a_teapot); + } } template @@ -449,13 +500,13 @@ void serverinfo(std::shared_ptr::Response> res print_req(request); int pair_status = 0; - if constexpr (std::is_same_v) { - auto args = request->parse_query_string(); + if constexpr(std::is_same_v) { + auto args = request->parse_query_string(); auto clientID = args.find("uniqueid"s); if(clientID != std::end(args)) { - if (auto it = map_id_client.find(clientID->second); it != std::end(map_id_client)) { + if(auto it = map_id_client.find(clientID->second); it != std::end(map_id_client)) { pair_status = 1; } } @@ -468,7 +519,7 @@ void serverinfo(std::shared_ptr::Response> res tree.put("root.appversion", VERSION); tree.put("root.GfeVersion", GFE_VERSION); - tree.put("root.uniqueid", unique_id); + tree.put("root.uniqueid", http::unique_id); tree.put("root.mac", platf::get_mac_address(request->local_endpoint_address())); tree.put("root.MaxLumaPixelsHEVC", config::video.hevc_mode > 1 ? "1869449984" : "0"); tree.put("root.LocalIP", request->local_endpoint_address()); @@ -487,10 +538,35 @@ void serverinfo(std::shared_ptr::Response> res tree.put("root.ExternalIP", config::nvhttp.external_ip); } + pt::ptree display_nodes; + for(auto &resolution : config::nvhttp.resolutions) { + auto pred = [](auto ch) { return ch == ' ' || ch == '\t' || ch == 'x'; }; + + auto middle = std::find_if(std::begin(resolution), std::end(resolution), pred); + if(middle == std::end(resolution)) { + BOOST_LOG(warning) << resolution << " is not in the proper format for a resolution: WIDTHxHEIGHT"sv; + continue; + } + + auto width = util::from_chars(&*std::begin(resolution), &*middle); + auto height = util::from_chars(&*(middle + 1), &*std::end(resolution)); + for(auto fps : config::nvhttp.fps) { + pt::ptree display_node; + display_node.put("Width", width); + display_node.put("Height", height); + display_node.put("RefreshRate", fps); + + display_nodes.add_child("DisplayMode", display_node); + } + } + + if(!config::nvhttp.resolutions.empty()) { + tree.add_child("root.SupportedDisplayMode", display_nodes); + } auto current_appid = proc::proc.running(); tree.put("root.PairStatus", pair_status); tree.put("root.currentgame", current_appid >= 0 ? current_appid + 1 : 0); - tree.put("root.state", current_appid >= 0 ? "_SERVER_BUSY" : "_SERVER_FREE"); + tree.put("root.state", current_appid >= 0 ? "SUNSHINE_SERVER_BUSY" : "SUNSHINE_SERVER_FREE"); std::ostringstream data; @@ -501,9 +577,6 @@ void serverinfo(std::shared_ptr::Response> res void applist(resp_https_t response, req_https_t request) { print_req(request); - auto args = request->parse_query_string(); - auto clientID = args.at("uniqueid"s); - pt::ptree tree; auto g = util::fail_guard([&]() { @@ -513,6 +586,15 @@ void applist(resp_https_t response, req_https_t request) { response->write(data.str()); }); + auto args = request->parse_query_string(); + if(args.find("uniqueid"s) == std::end(args)) { + tree.put("root..status_code", 400); + + return; + } + + auto clientID = args.at("uniqueid"s); + auto client = map_id_client.find(clientID); if(client == std::end(map_id_client)) { tree.put("root..status_code", 501); @@ -536,7 +618,7 @@ void applist(resp_https_t response, req_https_t request) { } } -void launch(resp_https_t response, req_https_t request) { +void launch(bool &host_audio, resp_https_t response, req_https_t request) { print_req(request); pt::ptree tree; @@ -555,7 +637,19 @@ void launch(resp_https_t response, req_https_t request) { } auto args = request->parse_query_string(); - auto appid = util::from_view(args.at("appid")) -1; + if( + args.find("rikey"s) == std::end(args) || + args.find("rikeyid"s) == std::end(args) || + args.find("localAudioPlayMode"s) == std::end(args) || + args.find("appid"s) == std::end(args)) { + + tree.put("root.resume", 0); + tree.put("root..status_code", 400); + + return; + } + + auto appid = util::from_view(args.at("appid")) - 1; auto current_appid = proc::proc.running(); if(current_appid != -1) { @@ -575,23 +669,14 @@ void launch(resp_https_t response, req_https_t request) { } } - stream::launch_session_t launch_session; - - auto clientID = args.at("uniqueid"s); - launch_session.gcm_key = *util::from_hex(args.at("rikey"s), true); - uint32_t prepend_iv = util::endian::big(util::from_view(args.at("rikeyid"s))); - auto prepend_iv_p = (uint8_t*)&prepend_iv; - - auto next = std::copy(prepend_iv_p, prepend_iv_p + sizeof(prepend_iv), std::begin(launch_session.iv)); - std::fill(next, std::end(launch_session.iv), 0); - - stream::launch_session_raise(launch_session); + host_audio = util::from_view(args.at("localAudioPlayMode")); + stream::launch_session_raise(make_launch_session(host_audio, args)); tree.put("root..status_code", 200); tree.put("root.gamesession", 1); } -void resume(resp_https_t response, req_https_t request) { +void resume(bool &host_audio, resp_https_t response, req_https_t request) { print_req(request); pt::ptree tree; @@ -619,18 +704,18 @@ void resume(resp_https_t response, req_https_t request) { return; } - stream::launch_session_t launch_session; - auto args = request->parse_query_string(); - auto clientID = args.at("uniqueid"s); - launch_session.gcm_key = *util::from_hex(args.at("rikey"s), true); - uint32_t prepend_iv = util::endian::big(util::from_view(args.at("rikeyid"s))); - auto prepend_iv_p = (uint8_t*)&prepend_iv; + if( + args.find("rikey"s) == std::end(args) || + args.find("rikeyid"s) == std::end(args)) { - auto next = std::copy(prepend_iv_p, prepend_iv_p + sizeof(prepend_iv), std::begin(launch_session.iv)); - std::fill(next, std::end(launch_session.iv), 0); + tree.put("root.resume", 0); + tree.put("root..status_code", 400); - stream::launch_session_raise(launch_session); + return; + } + + stream::launch_session_raise(make_launch_session(host_audio, args)); tree.put("root..status_code", 200); tree.put("root.resume", 1); @@ -671,87 +756,16 @@ void appasset(resp_https_t response, req_https_t request) { response->write(SimpleWeb::StatusCode::success_ok, in); } -int create_creds(const std::string &pkey, const std::string &cert) { - fs::path pkey_path = pkey; - fs::path cert_path = cert; +void start() { + auto shutdown_event = mail::man->event(mail::shutdown); - auto creds = crypto::gen_creds("Sunshine Gamestream Host"sv, 2048); - - auto pkey_dir = pkey_path; - auto cert_dir = cert_path; - pkey_dir.remove_filename(); - cert_dir.remove_filename(); - - std::error_code err_code{}; - fs::create_directories(pkey_dir, err_code); - if (err_code) { - BOOST_LOG(fatal) << "Couldn't create directory ["sv << pkey_dir << "] :"sv << err_code.message(); - return -1; - } - - fs::create_directories(cert_dir, err_code); - if (err_code) { - BOOST_LOG(fatal) << "Couldn't create directory ["sv << cert_dir << "] :"sv << err_code.message(); - return -1; - } - - if (write_file(pkey.c_str(), creds.pkey)) { - BOOST_LOG(fatal) << "Couldn't open ["sv << config::nvhttp.pkey << ']'; - return -1; - } - - if (write_file(cert.c_str(), creds.x509)) { - BOOST_LOG(fatal) << "Couldn't open ["sv << config::nvhttp.cert << ']'; - return -1; - } - - fs::permissions(pkey_path, - fs::perms::owner_read | fs::perms::owner_write, - fs::perm_options::replace, err_code); - - if (err_code) { - BOOST_LOG(fatal) << "Couldn't change permissions of ["sv << config::nvhttp.pkey << "] :"sv << err_code.message(); - return -1; - } - - fs::permissions(cert_path, - fs::perms::owner_read | fs::perms::group_read | fs::perms::others_read | fs::perms::owner_write, - fs::perm_options::replace, err_code); - - if (err_code) { - BOOST_LOG(fatal) << "Couldn't change permissions of ["sv << config::nvhttp.cert << "] :"sv << err_code.message(); - return -1; - } - - return 0; -} - -void start(std::shared_ptr shutdown_event) { bool clean_slate = config::sunshine.flags[config::flag::FRESH_STATE]; - if(clean_slate) { - unique_id = util::uuid_t::generate().string(); - - auto dir = std::filesystem::temp_directory_path() / "Sushine"sv; - - config::nvhttp.cert = (dir / ("cert-"s + unique_id)).string(); - config::nvhttp.pkey = (dir / ("pkey-"s + unique_id)).string(); - } - - - if(!fs::exists(config::nvhttp.pkey) || !fs::exists(config::nvhttp.cert)) { - if(create_creds(config::nvhttp.pkey, config::nvhttp.cert)) { - shutdown_event->raise(true); - return; - } - } - - origin_pin_allowed = net::from_enum_string(config::nvhttp.origin_pin_allowed); if(!clean_slate) { load_state(); } - conf_intern.pkey = read_file(config::nvhttp.pkey.c_str()); + conf_intern.pkey = read_file(config::nvhttp.pkey.c_str()); conf_intern.servercert = read_file(config::nvhttp.cert.c_str()); auto ctx = std::make_shared(boost::asio::ssl::context::tls); @@ -759,7 +773,7 @@ void start(std::shared_ptr shutdown_event) { ctx->use_private_key_file(config::nvhttp.pkey, boost::asio::ssl::context::pem); crypto::cert_chain_t cert_chain; - for(auto &[_,client] : map_id_client) { + for(auto &[_, client] : map_id_client) { for(auto &cert : client.certs) { cert_chain.add(crypto::x509(cert)); } @@ -803,45 +817,64 @@ void start(std::shared_ptr shutdown_event) { return 1; }); + // /resume doesn't get the parameter "localAudioPlayMode" + // /launch will store it in host_audio + bool host_audio {}; https_server_t https_server { ctx, boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert | boost::asio::ssl::verify_client_once }; http_server_t http_server; - https_server.default_resource = not_found; - https_server.resource["^/serverinfo$"]["GET"] = serverinfo; - https_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair(add_cert, resp, req); }; - https_server.resource["^/applist$"]["GET"] = applist; - https_server.resource["^/appasset$"]["GET"] = appasset; - https_server.resource["^/launch$"]["GET"] = launch; + https_server.default_resource = not_found; + https_server.resource["^/serverinfo$"]["GET"] = serverinfo; + https_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair(add_cert, resp, req); }; + https_server.resource["^/applist$"]["GET"] = applist; + https_server.resource["^/appasset$"]["GET"] = appasset; + https_server.resource["^/launch$"]["GET"] = [&host_audio](auto resp, auto req) { launch(host_audio, resp, req); }; https_server.resource["^/pin/([0-9]+)$"]["GET"] = pin; - https_server.resource["^/resume$"]["GET"] = resume; - https_server.resource["^/cancel$"]["GET"] = cancel; + https_server.resource["^/resume$"]["GET"] = [&host_audio](auto resp, auto req) { resume(host_audio, resp, req); }; + https_server.resource["^/cancel$"]["GET"] = cancel; https_server.config.reuse_address = true; - https_server.config.address = "0.0.0.0"s; - https_server.config.port = PORT_HTTPS; + https_server.config.address = "0.0.0.0"s; + https_server.config.port = PORT_HTTPS; - http_server.default_resource = not_found; - http_server.resource["^/serverinfo$"]["GET"] = serverinfo; - http_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair(add_cert, resp, req); }; + http_server.default_resource = not_found; + http_server.resource["^/serverinfo$"]["GET"] = serverinfo; + http_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair(add_cert, resp, req); }; http_server.resource["^/pin/([0-9]+)$"]["GET"] = pin; http_server.config.reuse_address = true; - http_server.config.address = "0.0.0.0"s; - http_server.config.port = PORT_HTTP; + http_server.config.address = "0.0.0.0"s; + http_server.config.port = PORT_HTTP; try { https_server.bind(); http_server.bind(); - } catch(boost::system::system_error &err) { + } + catch(boost::system::system_error &err) { BOOST_LOG(fatal) << "Couldn't bind http server to ports ["sv << PORT_HTTPS << ", "sv << PORT_HTTP << "]: "sv << err.what(); shutdown_event->raise(true); return; } - std::thread ssl { &https_server_t::accept_and_run, &https_server }; - std::thread tcp { &http_server_t::accept_and_run, &http_server }; + auto accept_and_run = [&](auto *http_server) { + try { + http_server->accept_and_run(); + } + catch(boost::system::system_error &err) { + // It's possible the exception gets thrown after calling http_server->stop() from a different thread + if(shutdown_event->peek()) { + return; + } + + BOOST_LOG(fatal) << "Couldn't start http server to ports ["sv << PORT_HTTPS << ", "sv << PORT_HTTP << "]: "sv << err.what(); + shutdown_event->raise(true); + return; + } + }; + std::thread ssl { accept_and_run, &https_server }; + std::thread tcp { accept_and_run, &http_server }; // Wait for any event shutdown_event->view(); @@ -852,31 +885,4 @@ void start(std::shared_ptr shutdown_event) { ssl.join(); tcp.join(); } - -int write_file(const char *path, const std::string_view &contents) { - std::ofstream out(path); - - if(!out.is_open()) { - return -1; - } - - out << contents; - - return 0; -} - -std::string read_file(const char *path) { - std::ifstream in(path); - - std::string input; - std::string base64_cert; - - //FIXME: Being unable to read file could result in infinite loop - while(!in.eof()) { - std::getline(in, input); - base64_cert += input + '\n'; - } - - return base64_cert; -} -} +} // namespace nvhttp diff --git a/sunshine/nvhttp.h b/sunshine/nvhttp.h index 0bba77f9..dcd39461 100644 --- a/sunshine/nvhttp.h +++ b/sunshine/nvhttp.h @@ -5,19 +5,18 @@ #ifndef SUNSHINE_NVHTTP_H #define SUNSHINE_NVHTTP_H +#include "thread_safe.h" +#include +#include #include #include -#include "thread_safe.h" - -#define CA_DIR SUNSHINE_ASSETS_DIR "/demoCA" -#define PRIVATE_KEY_FILE CA_DIR "/cakey.pem" -#define CERTIFICATE_FILE CA_DIR "/cacert.pem" - namespace nvhttp { constexpr auto PORT_HTTP = 47989; constexpr auto PORT_HTTPS = 47984; -void start(std::shared_ptr shutdown_event); -} + +void start(); +bool pin(std::string pin); +} // namespace nvhttp #endif //SUNSHINE_NVHTTP_H diff --git a/sunshine/platform/common.h b/sunshine/platform/common.h index 140b54b1..2686c72c 100644 --- a/sunshine/platform/common.h +++ b/sunshine/platform/common.h @@ -5,11 +5,16 @@ #ifndef SUNSHINE_COMMON_H #define SUNSHINE_COMMON_H -#include +#include +#include #include +#include + #include "sunshine/utility.h" struct sockaddr; +struct AVFrame; + namespace platf { constexpr auto MAX_GAMEPADS = 32; @@ -29,8 +34,46 @@ constexpr std::uint16_t B = 0x2000; constexpr std::uint16_t X = 0x4000; constexpr std::uint16_t Y = 0x8000; -enum class dev_type_e { - none, +namespace speaker { +enum speaker_e { + FRONT_LEFT, + FRONT_RIGHT, + FRONT_CENTER, + LOW_FREQUENCY, + BACK_LEFT, + BACK_RIGHT, + SIDE_LEFT, + SIDE_RIGHT, + MAX_SPEAKERS, +}; + +constexpr std::uint8_t map_stereo[] { + FRONT_LEFT, FRONT_RIGHT +}; +constexpr std::uint8_t map_surround51[] { + FRONT_LEFT, + FRONT_RIGHT, + FRONT_CENTER, + LOW_FREQUENCY, + BACK_LEFT, + BACK_RIGHT, +}; +constexpr std::uint8_t map_surround71[] { + FRONT_LEFT, + FRONT_RIGHT, + FRONT_CENTER, + LOW_FREQUENCY, + LOW_FREQUENCY, + BACK_LEFT, + BACK_RIGHT, + SIDE_LEFT, + SIDE_RIGHT, +}; +} // namespace speaker + +enum class mem_type_e { + system, + vaapi, dxgi, unknown }; @@ -43,6 +86,29 @@ enum class pix_fmt_e { unknown }; +inline std::string_view from_pix_fmt(pix_fmt_e pix_fmt) { + using namespace std::literals; +#define _CONVERT(x) \ + case pix_fmt_e::x: \ + return #x##sv + switch(pix_fmt) { + _CONVERT(yuv420p); + _CONVERT(yuv420p10); + _CONVERT(nv12); + _CONVERT(p010); + _CONVERT(unknown); + } +#undef _CONVERT + + return "unknown"sv; +} + +// Dimensions for touchscreen input +struct touch_port_t { + int offset_x, offset_y; + int width, height; +}; + struct gamepad_state_t { std::uint16_t buttonFlags; std::uint8_t lt; @@ -60,27 +126,49 @@ public: struct img_t { public: - std::uint8_t *data {}; - std::int32_t width {}; + std::uint8_t *data {}; + std::int32_t width {}; std::int32_t height {}; std::int32_t pixel_pitch {}; std::int32_t row_pitch {}; - img_t() = default; - img_t(const img_t&) = delete; - img_t(img_t&&) = delete; + img_t() = default; + img_t(const img_t &) = delete; + img_t(img_t &&) = delete; virtual ~img_t() = default; }; +struct sink_t { + // Play on host PC + std::string host; + + // On Windows, it is not possible to create a virtual sink + // Therefore, it is optional + struct null_t { + std::string stereo; + std::string surround51; + std::string surround71; + }; + std::optional null; +}; + struct hwdevice_t { void *data {}; - platf::img_t *img {}; + AVFrame *frame {}; virtual int convert(platf::img_t &img) { return -1; } + /** + * implementations must take ownership of 'frame' + */ + virtual int set_frame(AVFrame *frame) { + std::abort(); // ^ This function must never be called + return -1; + }; + virtual void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) {}; virtual ~hwdevice_t() = default; @@ -95,17 +183,22 @@ enum class capture_e : int { class display_t { public: + display_t() noexcept : offset_x { 0 }, offset_y { 0 } {} virtual capture_e snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor) = 0; - virtual std::shared_ptr alloc_img() = 0; + virtual std::shared_ptr alloc_img() = 0; virtual int dummy_img(img_t *img) = 0; - virtual std::shared_ptr make_hwdevice(int width, int height, pix_fmt_e pix_fmt) { + virtual std::shared_ptr make_hwdevice(pix_fmt_e pix_fmt) { return std::make_shared(); } virtual ~display_t() = default; + // Offsets for when streaming a specific monitor. By default, they are 0. + int offset_x, offset_y; + int env_width, env_height; + int width, height; }; @@ -116,21 +209,34 @@ public: virtual ~mic_t() = default; }; +class audio_control_t { +public: + virtual int set_sink(const std::string &sink) = 0; -void freeInput(void*); + virtual std::unique_ptr microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) = 0; + + virtual std::optional sink_info() = 0; + + virtual ~audio_control_t() = default; +}; + +void freeInput(void *); using input_t = util::safe_ptr; +std::filesystem::path appdata(); + std::string get_mac_address(const std::string_view &address); std::string from_sockaddr(const sockaddr *const); std::pair from_sockaddr_ex(const sockaddr *const); -std::unique_ptr microphone(std::uint32_t sample_rate); -std::shared_ptr display(dev_type_e hwdevice_type); +std::unique_ptr audio_control(); +std::shared_ptr display(mem_type_e hwdevice_type); input_t input(); void move_mouse(input_t &input, int deltaX, int deltaY); +void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y); void button_mouse(input_t &input, int button, bool release); void scroll(input_t &input, int distance); void keyboard(input_t &input, uint16_t modcode, bool release); @@ -140,6 +246,6 @@ int alloc_gamepad(input_t &input, int nr); void free_gamepad(input_t &input, int nr); [[nodiscard]] std::unique_ptr init(); -} +} // namespace platf #endif //SUNSHINE_COMMON_H diff --git a/sunshine/platform/linux/audio.cpp b/sunshine/platform/linux/audio.cpp new file mode 100644 index 00000000..99fc02a8 --- /dev/null +++ b/sunshine/platform/linux/audio.cpp @@ -0,0 +1,437 @@ +// +// Created by loki on 5/16/21. +// +#include +#include + +#include + +#include +#include +#include + +#include "sunshine/platform/common.h" + +#include "sunshine/config.h" +#include "sunshine/main.h" +#include "sunshine/thread_safe.h" + +namespace platf { +using namespace std::literals; + +constexpr pa_channel_position_t position_mapping[] { + PA_CHANNEL_POSITION_FRONT_LEFT, + PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_FRONT_CENTER, + PA_CHANNEL_POSITION_LFE, + PA_CHANNEL_POSITION_REAR_LEFT, + PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_SIDE_LEFT, + PA_CHANNEL_POSITION_SIDE_RIGHT, +}; + +std::string to_string(const char *name, const std::uint8_t *mapping, int channels) { + std::stringstream ss; + + ss << "rate=48000 sink_name="sv << name << " format=s16le channels="sv << channels << " channel_map="sv; + std::for_each_n(mapping, channels - 1, [&ss](std::uint8_t pos) { + ss << pa_channel_position_to_string(position_mapping[pos]) << ','; + }); + + ss << pa_channel_position_to_string(position_mapping[mapping[channels - 1]]); + + ss << " sink_properties=device.description="sv << name; + auto result = ss.str(); + + BOOST_LOG(debug) << "null-sink args: "sv << result; + return result; +} + +struct mic_attr_t : public mic_t { + util::safe_ptr mic; + + capture_e sample(std::vector &sample_buf) override { + auto sample_size = sample_buf.size(); + + auto buf = sample_buf.data(); + int status; + if(pa_simple_read(mic.get(), buf, sample_size * 2, &status)) { + BOOST_LOG(error) << "pa_simple_read() failed: "sv << pa_strerror(status); + + return capture_e::error; + } + + return capture_e::ok; + } +}; + +std::unique_ptr microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate) { + auto mic = std::make_unique(); + + pa_sample_spec ss { PA_SAMPLE_S16LE, sample_rate, (std::uint8_t)channels }; + pa_channel_map pa_map; + + pa_map.channels = channels; + std::for_each_n(pa_map.map, pa_map.channels, [mapping](auto &channel) mutable { + channel = position_mapping[*mapping++]; + }); + + int status; + + const char *audio_sink = "@DEFAULT_MONITOR@"; + if(!config::audio.sink.empty()) { + audio_sink = config::audio.sink.c_str(); + } + + mic->mic.reset( + pa_simple_new(nullptr, "sunshine", + pa_stream_direction_t::PA_STREAM_RECORD, audio_sink, + "sunshine-record", &ss, &pa_map, nullptr, &status)); + + if(!mic->mic) { + auto err_str = pa_strerror(status); + BOOST_LOG(error) << "pa_simple_new() failed: "sv << err_str; + + log_flush(); + std::abort(); + } + + return mic; +} + +namespace pa { +template +struct add_const_helper; + +template +struct add_const_helper { + using type = const std::remove_pointer_t *; +}; + +template +struct add_const_helper { + using type = const T *; +}; + +template +using add_const_t = typename add_const_helper, T>::type; + +template +void pa_free(T *p) { + pa_xfree(p); +} +using ctx_t = util::safe_ptr; +using loop_t = util::safe_ptr; +using op_t = util::safe_ptr; +using string_t = util::safe_ptr>; + +template +using cb_t = std::function i, int eol)>; + +template +void cb(ctx_t::pointer ctx, add_const_t i, int eol, void *userdata) { + auto &f = *(cb_t *)userdata; + + // For some reason, pulseaudio calls this callback after disconnecting + if(i && eol) { + return; + } + + f(ctx, i, eol); +} + +void cb_i(ctx_t::pointer ctx, std::uint32_t i, void *userdata) { + auto alarm = (safe::alarm_raw_t *)userdata; + + alarm->ring(i); +} + +void ctx_state_cb(ctx_t::pointer ctx, void *userdata) { + auto &f = *(std::function *)userdata; + + f(ctx); +} + +void success_cb(ctx_t::pointer ctx, int status, void *userdata) { + assert(userdata != nullptr); + + auto alarm = (safe::alarm_raw_t *)userdata; + alarm->ring(status ? 0 : 1); +} + +class server_t : public audio_control_t { + enum ctx_event_e : int { + ready, + terminated, + failed + }; + +public: + loop_t loop; + ctx_t ctx; + + struct { + std::uint32_t stereo = PA_INVALID_INDEX; + std::uint32_t surround51 = PA_INVALID_INDEX; + std::uint32_t surround71 = PA_INVALID_INDEX; + } index; + + std::unique_ptr> events; + std::unique_ptr> events_cb; + + std::thread worker; + int init() { + events = std::make_unique>(); + loop.reset(pa_mainloop_new()); + ctx.reset(pa_context_new(pa_mainloop_get_api(loop.get()), "sunshine")); + + events_cb = std::make_unique>([this](ctx_t::pointer ctx) { + switch(pa_context_get_state(ctx)) { + case PA_CONTEXT_READY: + events->raise(ready); + break; + case PA_CONTEXT_TERMINATED: + BOOST_LOG(debug) << "Pulseadio context terminated"sv; + events->raise(terminated); + break; + case PA_CONTEXT_FAILED: + BOOST_LOG(debug) << "Pulseadio context failed"sv; + events->raise(failed); + break; + case PA_CONTEXT_CONNECTING: + BOOST_LOG(debug) << "Connecting to pulseaudio"sv; + case PA_CONTEXT_UNCONNECTED: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + break; + } + }); + + pa_context_set_state_callback(ctx.get(), ctx_state_cb, events_cb.get()); + + auto status = pa_context_connect(ctx.get(), nullptr, PA_CONTEXT_NOFLAGS, nullptr); + if(status) { + BOOST_LOG(error) << "Couldn't connect to pulseaudio: "sv << pa_strerror(status); + return -1; + } + + worker = std::thread { + [](loop_t::pointer loop) { + int retval; + auto status = pa_mainloop_run(loop, &retval); + + if(status < 0) { + BOOST_LOG(fatal) << "Couldn't run pulseaudio main loop"sv; + + log_flush(); + std::abort(); + } + }, + loop.get() + }; + + auto event = events->pop(); + if(event == failed) { + return -1; + } + + return 0; + } + + int load_null(const char *name, const std::uint8_t *channel_mapping, int channels) { + auto alarm = safe::make_alarm(); + + op_t op { + pa_context_load_module( + ctx.get(), + "module-null-sink", + to_string(name, channel_mapping, channels).c_str(), + cb_i, + alarm.get()), + }; + + alarm->wait(); + return *alarm->status(); + } + + int unload_null(std::uint32_t i) { + if(i == PA_INVALID_INDEX) { + return 0; + } + + auto alarm = safe::make_alarm(); + + op_t op { + pa_context_unload_module(ctx.get(), i, success_cb, alarm.get()) + }; + + alarm->wait(); + + if(*alarm->status()) { + BOOST_LOG(error) << "Couldn't unload null-sink with index ["sv << i << "]: "sv << pa_strerror(pa_context_errno(ctx.get())); + return -1; + } + + return 0; + } + + std::optional sink_info() override { + constexpr auto stereo = "sink-sunshine-stereo"; + constexpr auto surround51 = "sink-sunshine-surround51"; + constexpr auto surround71 = "sink-sunshine-surround71"; + + auto alarm = safe::make_alarm(); + + sink_t sink; + + // If hardware sink with more channels found, set that as host + int channels = 0; + // Count of all virtual sinks that are created by us + int nullcount = 0; + + cb_t f = [&](ctx_t::pointer ctx, const pa_sink_info *sink_info, int eol) { + if(!sink_info) { + if(!eol) { + BOOST_LOG(error) << "Couldn't get pulseaudio sink info: "sv << pa_strerror(pa_context_errno(ctx)); + + alarm->ring(-1); + } + + alarm->ring(0); + return; + } + + if(sink_info->active_port != nullptr) { + sink.host = sink_info->name; + channels = sink_info->channel_map.channels; + } + + // Ensure Sunshine won't create a sink that already exists. + if(!std::strcmp(sink_info->name, stereo)) { + index.stereo = sink_info->owner_module; + + ++nullcount; + } + else if(!std::strcmp(sink_info->name, surround51)) { + index.surround51 = sink_info->owner_module; + + ++nullcount; + } + else if(!std::strcmp(sink_info->name, surround71)) { + index.surround71 = sink_info->owner_module; + + ++nullcount; + } + }; + + op_t op { pa_context_get_sink_info_list(ctx.get(), cb, &f) }; + + if(!op) { + BOOST_LOG(error) << "Couldn't create card info operation: "sv << pa_strerror(pa_context_errno(ctx.get())); + + return std::nullopt; + } + + alarm->wait(); + + if(*alarm->status()) { + return std::nullopt; + } + + if(!channels) { + BOOST_LOG(warning) << "Couldn't find an active sink"sv; + } + + if(index.stereo == PA_INVALID_INDEX) { + index.stereo = load_null(stereo, speaker::map_stereo, sizeof(speaker::map_stereo)); + if(index.stereo == PA_INVALID_INDEX) { + BOOST_LOG(warning) << "Couldn't create virtual sink for stereo: "sv << pa_strerror(pa_context_errno(ctx.get())); + } + else { + ++nullcount; + } + } + + if(index.surround51 == PA_INVALID_INDEX) { + index.surround51 = load_null(surround51, speaker::map_surround51, sizeof(speaker::map_surround51)); + if(index.surround51 == PA_INVALID_INDEX) { + BOOST_LOG(warning) << "Couldn't create virtual sink for surround-51: "sv << pa_strerror(pa_context_errno(ctx.get())); + } + else { + ++nullcount; + } + } + + if(index.surround71 == PA_INVALID_INDEX) { + index.surround71 = load_null(surround71, speaker::map_surround71, sizeof(speaker::map_surround71)); + if(index.surround71 == PA_INVALID_INDEX) { + BOOST_LOG(warning) << "Couldn't create virtual sink for surround-71: "sv << pa_strerror(pa_context_errno(ctx.get())); + } + else { + ++nullcount; + } + } + + if(nullcount == 3) { + sink.null = std::make_optional(sink_t::null_t { stereo, surround51, surround71 }); + } + + return std::make_optional(std::move(sink)); + } + + std::unique_ptr microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) override { + return ::platf::microphone(mapping, channels, sample_rate); + } + + int set_sink(const std::string &sink) override { + auto alarm = safe::make_alarm(); + + op_t op { + pa_context_set_default_sink( + ctx.get(), sink.c_str(), success_cb, alarm.get()), + }; + + if(!op) { + BOOST_LOG(error) << "Couldn't create set default-sink operation: "sv << pa_strerror(pa_context_errno(ctx.get())); + return -1; + } + + alarm->wait(); + if(*alarm->status()) { + BOOST_LOG(error) << "Couldn't set default-sink ["sv << sink << "]: "sv << pa_strerror(pa_context_errno(ctx.get())); + + return -1; + } + + return 0; + } + + ~server_t() override { + unload_null(index.stereo); + unload_null(index.surround51); + unload_null(index.surround71); + + if(worker.joinable()) { + pa_context_disconnect(ctx.get()); + + KITTY_WHILE_LOOP(auto event = events->pop(), event != terminated && event != failed, { + event = events->pop(); + }) + + pa_mainloop_quit(loop.get(), 0); + worker.join(); + } + } +}; +} // namespace pa + +std::unique_ptr audio_control() { + auto audio = std::make_unique(); + + if(audio->init()) { + return nullptr; + } + + return audio; +} +} // namespace platf \ No newline at end of file diff --git a/sunshine/platform/linux/display.cpp b/sunshine/platform/linux/display.cpp index 1452920e..867220d3 100644 --- a/sunshine/platform/linux/display.cpp +++ b/sunshine/platform/linux/display.cpp @@ -5,46 +5,45 @@ #include "sunshine/platform/common.h" #include -#include - -#include -#include #include #include #include #include -#include -#include +#include #include #include +#include +#include -#include -#include - -#include "sunshine/task_pool.h" #include "sunshine/config.h" #include "sunshine/main.h" +#include "sunshine/task_pool.h" + +#include "vaapi.h" + +using namespace std::literals; namespace platf { -using namespace std::literals; void freeImage(XImage *); void freeX(XFixesCursorImage *); -using ifaddr_t = util::safe_ptr; using xcb_connect_t = util::safe_ptr; -using xcb_img_t = util::c_ptr; -using xcb_cursor_img = util::c_ptr; +using xcb_img_t = util::c_ptr; using xdisplay_t = util::safe_ptr_v2; -using ximg_t = util::safe_ptr; -using xcursor_t = util::safe_ptr; +using ximg_t = util::safe_ptr; +using xcursor_t = util::safe_ptr; + +using crtc_info_t = util::safe_ptr<_XRRCrtcInfo, XRRFreeCrtcInfo>; +using output_info_t = util::safe_ptr<_XRROutputInfo, XRRFreeOutputInfo>; +using screen_res_t = util::safe_ptr<_XRRScreenResources, XRRFreeScreenResources>; class shm_id_t { public: shm_id_t() : id { -1 } {} - shm_id_t(int id) : id {id } {} + shm_id_t(int id) : id { id } {} shm_id_t(shm_id_t &&other) noexcept : id(other.id) { other.id = -1; } @@ -60,17 +59,16 @@ public: class shm_data_t { public: - shm_data_t() : data {(void*)-1 } {} - shm_data_t(void *data) : data {data } {} + shm_data_t() : data { (void *)-1 } {} + shm_data_t(void *data) : data { data } {} shm_data_t(shm_data_t &&other) noexcept : data(other.data) { - other.data = (void*)-1; + other.data = (void *)-1; } ~shm_data_t() { if((std::uintptr_t)data != -1) { shmdt(data); - data = (void*)-1; } } @@ -88,7 +86,7 @@ struct shm_img_t : public img_t { } }; -void blend_cursor(Display *display, img_t &img) { +void blend_cursor(Display *display, img_t &img, int offsetX, int offsetY) { xcursor_t overlay { XFixesGetCursorImage(display) }; if(!overlay) { @@ -99,86 +97,146 @@ void blend_cursor(Display *display, img_t &img) { overlay->x -= overlay->xhot; overlay->y -= overlay->yhot; + overlay->x -= offsetX; + overlay->y -= offsetY; + overlay->x = std::max((short)0, overlay->x); overlay->y = std::max((short)0, overlay->y); - auto pixels = (int*)img.data; + auto pixels = (int *)img.data; auto screen_height = img.height; auto screen_width = img.width; auto delta_height = std::min(overlay->height, std::max(0, screen_height - overlay->y)); - auto delta_width = std::min(overlay->width, std::max(0, screen_width - overlay->x)); + auto delta_width = std::min(overlay->width, std::max(0, screen_width - overlay->x)); for(auto y = 0; y < delta_height; ++y) { - auto overlay_begin = &overlay->pixels[y * overlay->width]; auto overlay_end = &overlay->pixels[y * overlay->width + delta_width]; auto pixels_begin = &pixels[(y + overlay->y) * (img.row_pitch / img.pixel_pitch) + overlay->x]; + std::for_each(overlay_begin, overlay_end, [&](long pixel) { - int *pixel_p = (int*)&pixel; + int *pixel_p = (int *)&pixel; - auto colors_in = (uint8_t*)pixels_begin; + auto colors_in = (uint8_t *)pixels_begin; - auto alpha = (*(uint*)pixel_p) >> 24u; + auto alpha = (*(uint *)pixel_p) >> 24u; if(alpha == 255) { *pixels_begin = *pixel_p; } else { - auto colors_out = (uint8_t*)pixel_p; - colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) + 255/2) / 255; - colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255/2) / 255; - colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255/2) / 255; + auto colors_out = (uint8_t *)pixel_p; + colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) + 255 / 2) / 255; + colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255 / 2) / 255; + colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255 / 2) / 255; } ++pixels_begin; }); } } + struct x11_attr_t : public display_t { - x11_attr_t() : xdisplay {XOpenDisplay(nullptr) }, xwindow { }, xattr {} { + xdisplay_t xdisplay; + Window xwindow; + XWindowAttributes xattr; + + mem_type_e mem_type; + + /* + * Last X (NOT the streamed monitor!) size. + * This way we can trigger reinitialization if the dimensions changed while streaming + */ + // int env_width, env_height; + + x11_attr_t(mem_type_e mem_type) : xdisplay { XOpenDisplay(nullptr) }, xwindow {}, xattr {}, mem_type { mem_type } { + XInitThreads(); + } + + int init() { if(!xdisplay) { - BOOST_LOG(fatal) << "Could not open x11 display"sv; - log_flush(); - std::abort(); + BOOST_LOG(error) << "Could not open X11 display"sv; + return -1; } xwindow = DefaultRootWindow(xdisplay.get()); refresh(); - width = xattr.width; - height = xattr.height; + int streamedMonitor = -1; + if(!config::video.output_name.empty()) { + streamedMonitor = (int)util::from_view(config::video.output_name); + } + + if(streamedMonitor != -1) { + BOOST_LOG(info) << "Configuring selected monitor ("sv << streamedMonitor << ") to stream"sv; + screen_res_t screenr { XRRGetScreenResources(xdisplay.get(), xwindow) }; + int output = screenr->noutput; + + output_info_t result; + int monitor = 0; + for(int x = 0; x < output; ++x) { + output_info_t out_info { XRRGetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[x]) }; + if(out_info && out_info->connection == RR_Connected) { + if(monitor++ == streamedMonitor) { + result = std::move(out_info); + break; + } + } + } + + if(!result) { + BOOST_LOG(error) << "Could not stream display number ["sv << streamedMonitor << "], there are only ["sv << monitor << "] displays."sv; + return -1; + } + + crtc_info_t crt_info { XRRGetCrtcInfo(xdisplay.get(), screenr.get(), result->crtc) }; + BOOST_LOG(info) + << "Streaming display: "sv << result->name << " with res "sv << crt_info->width << 'x' << crt_info->height << " offset by "sv << crt_info->x << 'x' << crt_info->y; + + width = crt_info->width; + height = crt_info->height; + offset_x = crt_info->x; + offset_y = crt_info->y; + } + else { + width = xattr.width; + height = xattr.height; + } + + env_width = xattr.width; + env_height = xattr.height; + + return 0; } + /** + * Called when the display attributes should change. + */ void refresh() { - XGetWindowAttributes(xdisplay.get(), xwindow, &xattr); + XGetWindowAttributes(xdisplay.get(), xwindow, &xattr); //Update xattr's } capture_e snapshot(img_t *img_out_base, std::chrono::milliseconds timeout, bool cursor) override { refresh(); - if(width != xattr.width || height != xattr.height) { + //The whole X server changed, so we gotta reinit everything + if(xattr.width != env_width || xattr.height != env_height) { + BOOST_LOG(warning) << "X dimensions changed in non-SHM mode, request reinit"sv; return capture_e::reinit; } + XImage *img { XGetImage(xdisplay.get(), xwindow, offset_x, offset_y, width, height, AllPlanes, ZPixmap) }; - XImage *img { XGetImage( - xdisplay.get(), - xwindow, - 0, 0, - xattr.width, xattr.height, - AllPlanes, ZPixmap) - }; - - auto img_out = (x11_img_t*)img_out_base; - img_out->width = img->width; - img_out->height = img->height; - img_out->data = (uint8_t*)img->data; - img_out->row_pitch = img->bytes_per_line; + auto img_out = (x11_img_t *)img_out_base; + img_out->width = img->width; + img_out->height = img->height; + img_out->data = (uint8_t *)img->data; + img_out->row_pitch = img->bytes_per_line; img_out->pixel_pitch = img->bits_per_pixel / 8; img_out->img.reset(img); if(cursor) { - blend_cursor(xdisplay.get(), *img_out_base); + blend_cursor(xdisplay.get(), *img_out_base, offset_x, offset_y); } return capture_e::ok; @@ -188,14 +246,18 @@ struct x11_attr_t : public display_t { return std::make_shared(); } + std::shared_ptr make_hwdevice(pix_fmt_e pix_fmt) override { + if(mem_type == mem_type_e::vaapi) { + return egl::make_hwdevice(width, height); + } + + return std::make_shared(); + } + int dummy_img(img_t *img) override { snapshot(img, 0s, true); return 0; } - - xdisplay_t xdisplay; - Window xwindow; - XWindowAttributes xattr; }; struct shm_attr_t : public x11_attr_t { @@ -209,58 +271,54 @@ struct shm_attr_t : public x11_attr_t { shm_data_t data; util::TaskPool::task_id_t refresh_task_id; + void delayed_refresh() { refresh(); refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh, 2s, this).task_id; } - shm_attr_t() : x11_attr_t(), shm_xdisplay {XOpenDisplay(nullptr) } { + shm_attr_t(mem_type_e mem_type) : x11_attr_t(mem_type), shm_xdisplay { XOpenDisplay(nullptr) } { refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh, 2s, this).task_id; } ~shm_attr_t() override { - while(!task_pool.cancel(refresh_task_id)); + while(!task_pool.cancel(refresh_task_id)) + ; } capture_e snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor) override { - if(width != xattr.width || height != xattr.height) { + //The whole X server changed, so we gotta reinit everything + if(xattr.width != env_width || xattr.height != env_height) { + BOOST_LOG(warning) << "X dimensions changed in SHM mode, request reinit"sv; return capture_e::reinit; } + else { + auto img_cookie = xcb_shm_get_image_unchecked(xcb.get(), display->root, offset_x, offset_y, width, height, ~0, XCB_IMAGE_FORMAT_Z_PIXMAP, seg, 0); - auto img_cookie = xcb_shm_get_image_unchecked( - xcb.get(), - display->root, - 0, 0, - width, height, - ~0, - XCB_IMAGE_FORMAT_Z_PIXMAP, - seg, - 0 - ); + xcb_img_t img_reply { xcb_shm_get_image_reply(xcb.get(), img_cookie, nullptr) }; + if(!img_reply) { + BOOST_LOG(error) << "Could not get image reply"sv; + return capture_e::reinit; + } - xcb_img_t img_reply { xcb_shm_get_image_reply(xcb.get(), img_cookie, nullptr) }; - if(!img_reply) { - BOOST_LOG(error) << "Could not get image reply"sv; - return capture_e::reinit; + std::copy_n((std::uint8_t *)data.data, frame_size(), img->data); + + if(cursor) { + blend_cursor(shm_xdisplay.get(), *img, offset_x, offset_y); + } + + return capture_e::ok; } - - std::copy_n((std::uint8_t*)data.data, frame_size(), img->data); - - if(cursor) { - blend_cursor(shm_xdisplay.get(), *img); - } - - return capture_e::ok; } std::shared_ptr alloc_img() override { - auto img = std::make_shared(); - img->width = width; - img->height = height; + auto img = std::make_shared(); + img->width = width; + img->height = height; img->pixel_pitch = 4; - img->row_pitch = img->pixel_pitch * width; - img->data = new std::uint8_t[height * img->row_pitch]; + img->row_pitch = img->pixel_pitch * width; + img->data = new std::uint8_t[height * img->row_pitch]; return img; } @@ -270,6 +328,10 @@ struct shm_attr_t : public x11_attr_t { } int init() { + if(x11_attr_t::init()) { + return 1; + } + shm_xdisplay.reset(XOpenDisplay(nullptr)); xcb.reset(xcb_connect(nullptr, nullptr)); if(xcb_connection_has_error(xcb.get())) { @@ -283,8 +345,8 @@ struct shm_attr_t : public x11_attr_t { } auto iter = xcb_setup_roots_iterator(xcb_get_setup(xcb.get())); - display = iter.data; - seg = xcb_generate_id(xcb.get()); + display = iter.data; + seg = xcb_generate_id(xcb.get()); shm_id.id = shmget(IPC_PRIVATE, frame_size(), IPC_CREAT | 0777); if(shm_id.id == -1) { @@ -295,15 +357,12 @@ struct shm_attr_t : public x11_attr_t { xcb_shm_attach(xcb.get(), seg, shm_id.id, false); data.data = shmat(shm_id.id, nullptr, 0); - if ((uintptr_t)data.data == -1) { + if((uintptr_t)data.data == -1) { BOOST_LOG(error) << "shmat failed"sv; return -1; } - width = display->width_in_pixels; - height = display->height_in_pixels; - return 0; } @@ -312,130 +371,32 @@ struct shm_attr_t : public x11_attr_t { } }; -struct mic_attr_t : public mic_t { - pa_sample_spec ss; - util::safe_ptr mic; - - explicit mic_attr_t(pa_sample_format format, std::uint32_t sample_rate, std::uint8_t channels) : ss { format, sample_rate, channels }, mic {} {} - capture_e sample(std::vector &sample_buf) override { - auto sample_size = sample_buf.size(); - - auto buf = sample_buf.data(); - int status; - if(pa_simple_read(mic.get(), buf, sample_size * 2, &status)) { - BOOST_LOG(error) << "pa_simple_read() failed: "sv << pa_strerror(status); - - return capture_e::error; - } - - return capture_e::ok; - } -}; - -std::shared_ptr shm_display() { - auto shm = std::make_shared(); - - if(shm->init()) { +std::shared_ptr display(platf::mem_type_e hwdevice_type) { + if(hwdevice_type != platf::mem_type_e::system && hwdevice_type != platf::mem_type_e::vaapi) { + BOOST_LOG(error) << "Could not initialize display with the given hw device type."sv; return nullptr; } - return shm; -} + // Attempt to use shared memory X11 to avoid copying the frame + auto shm_disp = std::make_shared(hwdevice_type); -std::shared_ptr display(platf::dev_type_e hwdevice_type) { - if(hwdevice_type != platf::dev_type_e::none) { + auto status = shm_disp->init(); + if(status > 0) { + // x11_attr_t::init() failed, don't bother trying again. return nullptr; } - auto shm_disp = shm_display(); - - if(!shm_disp) { - return std::make_shared(); + if(status == 0) { + return shm_disp; } - return shm_disp; -} - -std::unique_ptr microphone(std::uint32_t sample_rate) { - auto mic = std::make_unique(PA_SAMPLE_S16LE, sample_rate, 2); - - int status; - - const char *audio_sink = "@DEFAULT_MONITOR@"; - if(!config::audio.sink.empty()) { - audio_sink = config::audio.sink.c_str(); + // Fallback + auto x11_disp = std::make_shared(hwdevice_type); + if(x11_disp->init()) { + return nullptr; } - mic->mic.reset( - pa_simple_new(nullptr, "sunshine", pa_stream_direction_t::PA_STREAM_RECORD, audio_sink, "sunshine-record", &mic->ss, nullptr, nullptr, &status) - ); - - if(!mic->mic) { - auto err_str = pa_strerror(status); - BOOST_LOG(error) << "pa_simple_new() failed: "sv << err_str; - - log_flush(); - std::abort(); - } - - return mic; -} - -ifaddr_t get_ifaddrs() { - ifaddrs *p { nullptr }; - - getifaddrs(&p); - - return ifaddr_t { p }; -} - -std::string from_sockaddr(const sockaddr *const ip_addr) { - char data[INET6_ADDRSTRLEN]; - - auto family = ip_addr->sa_family; - if(family == AF_INET6) { - inet_ntop(AF_INET6, &((sockaddr_in6*)ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN); - } - - if(family == AF_INET) { - inet_ntop(AF_INET, &((sockaddr_in*)ip_addr)->sin_addr, data, INET_ADDRSTRLEN); - } - - return std::string { data }; -} - -std::pair from_sockaddr_ex(const sockaddr *const ip_addr) { - char data[INET6_ADDRSTRLEN]; - - auto family = ip_addr->sa_family; - std::uint16_t port; - if(family == AF_INET6) { - inet_ntop(AF_INET6, &((sockaddr_in6*)ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN); - port = ((sockaddr_in6*)ip_addr)->sin6_port; - } - - if(family == AF_INET) { - inet_ntop(AF_INET, &((sockaddr_in*)ip_addr)->sin_addr, data, INET_ADDRSTRLEN); - port = ((sockaddr_in*)ip_addr)->sin_port; - } - - return { port, std::string { data } }; -} - -std::string get_mac_address(const std::string_view &address) { - auto ifaddrs = get_ifaddrs(); - for(auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) { - if(pos->ifa_addr && address == from_sockaddr(pos->ifa_addr)) { - std::ifstream mac_file("/sys/class/net/"s + pos->ifa_name + "/address"); - if(mac_file.good()) { - std::string mac_address; - std::getline(mac_file, mac_address); - return mac_address; - } - } - } - BOOST_LOG(warning) << "Unable to find MAC address for "sv << address; - return "00:00:00:00:00:00"s; + return x11_disp; } void freeImage(XImage *p) { @@ -444,4 +405,4 @@ void freeImage(XImage *p) { void freeX(XFixesCursorImage *p) { XFree(p); } -} +} // namespace platf diff --git a/sunshine/platform/linux/input.cpp b/sunshine/platform/linux/input.cpp index f5696522..ad3397d5 100644 --- a/sunshine/platform/linux/input.cpp +++ b/sunshine/platform/linux/input.cpp @@ -1,16 +1,17 @@ -#include #include +#include #include #include #include #include +#include #include #include -#include "sunshine/platform/common.h" #include "sunshine/main.h" +#include "sunshine/platform/common.h" #include "sunshine/utility.h" // Support older versions @@ -24,13 +25,27 @@ namespace platf { using namespace std::literals; -using evdev_t = util::safe_ptr; +using evdev_t = util::safe_ptr; using uinput_t = util::safe_ptr; using keyboard_t = util::safe_ptr_v2; +constexpr touch_port_t target_touch_port { + 0, 0, + 19200, 12000 +}; + struct input_raw_t { public: + void clear_touchscreen() { + std::filesystem::path touch_path { "sunshine_touchscreen"sv }; + + if(std::filesystem::is_symlink(touch_path)) { + std::filesystem::remove(touch_path); + } + + touch_input.reset(); + } void clear_mouse() { std::filesystem::path mouse_path { "sunshine_mouse"sv }; @@ -51,13 +66,11 @@ public: std::filesystem::remove(gamepad_path); } - gamepads[nr] = std::make_pair(uinput_t{}, gamepad_state_t {}); + gamepads[nr] = std::make_pair(uinput_t {}, gamepad_state_t {}); } int create_mouse() { - libevdev_uinput *buf {}; - int err = libevdev_uinput_create_from_device(mouse_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &buf); - mouse_input.reset(buf); + int err = libevdev_uinput_create_from_device(mouse_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &mouse_input); if(err) { BOOST_LOG(error) << "Could not create Sunshine Mouse: "sv << strerror(-err); @@ -69,13 +82,24 @@ public: return 0; } + int create_touchscreen() { + int err = libevdev_uinput_create_from_device(touch_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &touch_input); + + if(err) { + BOOST_LOG(error) << "Could not create Sunshine Touchscreen: "sv << strerror(-err); + return -1; + } + + std::filesystem::create_symlink(libevdev_uinput_get_devnode(touch_input.get()), "sunshine_touchscreen"sv); + + return 0; + } + int alloc_gamepad(int nr) { TUPLE_2D_REF(input, gamepad_state, gamepads[nr]); - libevdev_uinput *buf; - int err = libevdev_uinput_create_from_device(gamepad_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &buf); + int err = libevdev_uinput_create_from_device(gamepad_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &input); - input.reset(buf); gamepad_state = gamepad_state_t {}; if(err) { @@ -96,6 +120,7 @@ public: } void clear() { + clear_touchscreen(); clear_mouse(); for(int x = 0; x < gamepads.size(); ++x) { clear_gamepad(x); @@ -106,18 +131,33 @@ public: clear(); } - evdev_t gamepad_dev; - std::vector> gamepads; - - evdev_t mouse_dev; uinput_t mouse_input; + uinput_t touch_input; + + evdev_t gamepad_dev; + evdev_t touch_dev; + evdev_t mouse_dev; keyboard_t keyboard; }; +void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) { + auto touchscreen = ((input_raw_t *)input.get())->touch_input.get(); + + auto scaled_x = (int)std::lround((x + touch_port.offset_x) * ((float)target_touch_port.width / (float)touch_port.width)); + auto scaled_y = (int)std::lround((y + touch_port.offset_y) * ((float)target_touch_port.height / (float)touch_port.height)); + + libevdev_uinput_write_event(touchscreen, EV_ABS, ABS_X, scaled_x); + libevdev_uinput_write_event(touchscreen, EV_ABS, ABS_Y, scaled_y); + libevdev_uinput_write_event(touchscreen, EV_KEY, BTN_TOOL_FINGER, 1); + libevdev_uinput_write_event(touchscreen, EV_KEY, BTN_TOOL_FINGER, 0); + + libevdev_uinput_write_event(touchscreen, EV_SYN, SYN_REPORT, 0); +} + void move_mouse(input_t &input, int deltaX, int deltaY) { - auto mouse = ((input_raw_t*)input.get())->mouse_input.get(); + auto mouse = ((input_raw_t *)input.get())->mouse_input.get(); if(deltaX) { libevdev_uinput_write_event(mouse, EV_REL, REL_X, deltaX); @@ -136,26 +176,26 @@ void button_mouse(input_t &input, int button, bool release) { if(button == 1) { btn_type = BTN_LEFT; - scan = 90001; + scan = 90001; } else if(button == 2) { btn_type = BTN_MIDDLE; - scan = 90003; + scan = 90003; } else if(button == 3) { btn_type = BTN_RIGHT; - scan = 90002; + scan = 90002; } else if(button == 4) { btn_type = BTN_SIDE; - scan = 90004; + scan = 90004; } else { btn_type = BTN_EXTRA; - scan = 90005; + scan = 90005; } - auto mouse = ((input_raw_t*)input.get())->mouse_input.get(); + auto mouse = ((input_raw_t *)input.get())->mouse_input.get(); libevdev_uinput_write_event(mouse, EV_MSC, MSC_SCAN, scan); libevdev_uinput_write_event(mouse, EV_KEY, btn_type, release ? 0 : 1); libevdev_uinput_write_event(mouse, EV_SYN, SYN_REPORT, 0); @@ -164,7 +204,7 @@ void button_mouse(input_t &input, int button, bool release) { void scroll(input_t &input, int high_res_distance) { int distance = high_res_distance / 120; - auto mouse = ((input_raw_t*)input.get())->mouse_input.get(); + auto mouse = ((input_raw_t *)input.get())->mouse_input.get(); libevdev_uinput_write_event(mouse, EV_REL, REL_WHEEL, distance); libevdev_uinput_write_event(mouse, EV_REL, REL_WHEEL_HI_RES, high_res_distance); libevdev_uinput_write_event(mouse, EV_SYN, SYN_REPORT, 0); @@ -172,7 +212,7 @@ void scroll(input_t &input, int high_res_distance) { uint16_t keysym(uint16_t modcode) { constexpr auto VK_NUMPAD = 0x60; - constexpr auto VK_F1 = 0x70; + constexpr auto VK_F1 = 0x70; if(modcode >= VK_NUMPAD && modcode < VK_NUMPAD + 10) { return XK_KP_0 + (modcode - VK_NUMPAD); @@ -184,108 +224,108 @@ uint16_t keysym(uint16_t modcode) { switch(modcode) { - case 0x08: - return XK_BackSpace; - case 0x09: - return XK_Tab; - case 0x0D: - return XK_Return; - case 0x13: - return XK_Pause; - case 0x14: - return XK_Caps_Lock; - case 0x1B: - return XK_Escape; - case 0x21: - return XK_Page_Up; - case 0x22: - return XK_Page_Down; - case 0x23: - return XK_End; - case 0x24: - return XK_Home; - case 0x25: - return XK_Left; - case 0x26: - return XK_Up; - case 0x27: - return XK_Right; - case 0x28: - return XK_Down; - case 0x29: - return XK_Select; - case 0x2B: - return XK_Execute; - case 0x2C: - return XK_Print; - case 0x2D: - return XK_Insert; - case 0x2E: - return XK_Delete; - case 0x2F: - return XK_Help; - case 0x6A: - return XK_KP_Multiply; - case 0x6B: - return XK_KP_Add; - case 0x6C: - return XK_KP_Separator; - case 0x6D: - return XK_KP_Subtract; - case 0x6E: - return XK_KP_Decimal; - case 0x6F: - return XK_KP_Divide; - case 0x90: - return XK_Num_Lock; - case 0x91: - return XK_Scroll_Lock; - case 0xA0: - return XK_Shift_L; - case 0xA1: - return XK_Shift_R; - case 0xA2: - return XK_Control_L; - case 0xA3: - return XK_Control_R; - case 0xA4: - return XK_Alt_L; - case 0xA5: /* return XK_Alt_R; */ - return XK_Super_L; - case 0x5B: - return XK_Super_L; - case 0x5C: - return XK_Super_R; - case 0xBA: - return XK_semicolon; - case 0xBB: - return XK_equal; - case 0xBC: - return XK_comma; - case 0xBD: - return XK_minus; - case 0xBE: - return XK_period; - case 0xBF: - return XK_slash; - case 0xC0: - return XK_grave; - case 0xDB: - return XK_bracketleft; - case 0xDC: - return XK_backslash; - case 0xDD: - return XK_bracketright; - case 0xDE: - return XK_apostrophe; + case 0x08: + return XK_BackSpace; + case 0x09: + return XK_Tab; + case 0x0D: + return XK_Return; + case 0x13: + return XK_Pause; + case 0x14: + return XK_Caps_Lock; + case 0x1B: + return XK_Escape; + case 0x21: + return XK_Page_Up; + case 0x22: + return XK_Page_Down; + case 0x23: + return XK_End; + case 0x24: + return XK_Home; + case 0x25: + return XK_Left; + case 0x26: + return XK_Up; + case 0x27: + return XK_Right; + case 0x28: + return XK_Down; + case 0x29: + return XK_Select; + case 0x2B: + return XK_Execute; + case 0x2C: + return XK_Print; + case 0x2D: + return XK_Insert; + case 0x2E: + return XK_Delete; + case 0x2F: + return XK_Help; + case 0x6A: + return XK_KP_Multiply; + case 0x6B: + return XK_KP_Add; + case 0x6C: + return XK_KP_Separator; + case 0x6D: + return XK_KP_Subtract; + case 0x6E: + return XK_KP_Decimal; + case 0x6F: + return XK_KP_Divide; + case 0x90: + return XK_Num_Lock; + case 0x91: + return XK_Scroll_Lock; + case 0xA0: + return XK_Shift_L; + case 0xA1: + return XK_Shift_R; + case 0xA2: + return XK_Control_L; + case 0xA3: + return XK_Control_R; + case 0xA4: + return XK_Alt_L; + case 0xA5: /* return XK_Alt_R; */ + return XK_Super_L; + case 0x5B: + return XK_Super_L; + case 0x5C: + return XK_Super_R; + case 0xBA: + return XK_semicolon; + case 0xBB: + return XK_equal; + case 0xBC: + return XK_comma; + case 0xBD: + return XK_minus; + case 0xBE: + return XK_period; + case 0xBF: + return XK_slash; + case 0xC0: + return XK_grave; + case 0xDB: + return XK_bracketleft; + case 0xDC: + return XK_backslash; + case 0xDD: + return XK_bracketright; + case 0xDE: + return XK_apostrophe; } return modcode; } void keyboard(input_t &input, uint16_t modcode, bool release) { - auto &keyboard = ((input_raw_t*)input.get())->keyboard; - KeyCode kc = XKeysymToKeycode(keyboard.get(), keysym(modcode)); + auto &keyboard = ((input_raw_t *)input.get())->keyboard; + KeyCode kc = XKeysymToKeycode(keyboard.get(), keysym(modcode)); if(!kc) { return; @@ -298,18 +338,18 @@ void keyboard(input_t &input, uint16_t modcode, bool release) { } int alloc_gamepad(input_t &input, int nr) { - return ((input_raw_t*)input.get())->alloc_gamepad(nr); + return ((input_raw_t *)input.get())->alloc_gamepad(nr); } void free_gamepad(input_t &input, int nr) { - ((input_raw_t*)input.get())->clear_gamepad(nr); + ((input_raw_t *)input.get())->clear_gamepad(nr); } void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) { - TUPLE_2D_REF(uinput, gamepad_state_old, ((input_raw_t*)input.get())->gamepads[nr]); + TUPLE_2D_REF(uinput, gamepad_state_old, ((input_raw_t *)input.get())->gamepads[nr]); - auto bf = gamepad_state.buttonFlags ^ gamepad_state_old.buttonFlags; + auto bf = gamepad_state.buttonFlags ^ gamepad_state_old.buttonFlags; auto bf_new = gamepad_state.buttonFlags; if(bf) { @@ -326,17 +366,17 @@ void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) { libevdev_uinput_write_event(uinput.get(), EV_ABS, ABS_HAT0X, button_state); } - if(START & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_START, bf_new & START ? 1 : 0); - if(BACK & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_SELECT, bf_new & BACK ? 1 : 0); - if(LEFT_STICK & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_THUMBL, bf_new & LEFT_STICK ? 1 : 0); - if(RIGHT_STICK & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_THUMBR, bf_new & RIGHT_STICK ? 1 : 0); - if(LEFT_BUTTON & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_TL, bf_new & LEFT_BUTTON ? 1 : 0); - if(RIGHT_BUTTON & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_TR, bf_new & RIGHT_BUTTON ? 1 : 0); - if(HOME & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_MODE, bf_new & HOME ? 1 : 0); - if(A & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_SOUTH, bf_new & A ? 1 : 0); - if(B & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_EAST, bf_new & B ? 1 : 0); - if(X & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_NORTH, bf_new & X ? 1 : 0); - if(Y & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_WEST, bf_new & Y ? 1 : 0); + if(START & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_START, bf_new & START ? 1 : 0); + if(BACK & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_SELECT, bf_new & BACK ? 1 : 0); + if(LEFT_STICK & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_THUMBL, bf_new & LEFT_STICK ? 1 : 0); + if(RIGHT_STICK & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_THUMBR, bf_new & RIGHT_STICK ? 1 : 0); + if(LEFT_BUTTON & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_TL, bf_new & LEFT_BUTTON ? 1 : 0); + if(RIGHT_BUTTON & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_TR, bf_new & RIGHT_BUTTON ? 1 : 0); + if(HOME & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_MODE, bf_new & HOME ? 1 : 0); + if(A & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_SOUTH, bf_new & A ? 1 : 0); + if(B & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_EAST, bf_new & B ? 1 : 0); + if(X & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_NORTH, bf_new & X ? 1 : 0); + if(Y & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_WEST, bf_new & Y ? 1 : 0); } if(gamepad_state_old.lt != gamepad_state.lt) { @@ -368,7 +408,7 @@ void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) { } evdev_t mouse() { - evdev_t dev { libevdev_new() }; + evdev_t dev { libevdev_new() }; libevdev_set_uniq(dev.get(), "Sunshine Mouse"); libevdev_set_id_product(dev.get(), 0x4038); @@ -409,6 +449,48 @@ evdev_t mouse() { return dev; } +evdev_t touchscreen() { + evdev_t dev { libevdev_new() }; + + libevdev_set_uniq(dev.get(), "Sunshine Touch"); + libevdev_set_id_product(dev.get(), 0xDEAD); + libevdev_set_id_vendor(dev.get(), 0xBEEF); + libevdev_set_id_bustype(dev.get(), 0x3); + libevdev_set_id_version(dev.get(), 0x111); + libevdev_set_name(dev.get(), "Touchscreen passthrough"); + + libevdev_enable_property(dev.get(), INPUT_PROP_DIRECT); + + + libevdev_enable_event_type(dev.get(), EV_KEY); + libevdev_enable_event_code(dev.get(), EV_KEY, BTN_TOUCH, nullptr); + libevdev_enable_event_code(dev.get(), EV_KEY, BTN_TOOL_PEN, nullptr); // Needed to be enabled for BTN_TOOL_FINGER to work. + libevdev_enable_event_code(dev.get(), EV_KEY, BTN_TOOL_FINGER, nullptr); + + input_absinfo absx { + 0, + 0, + target_touch_port.width, + 1, + 0, + 28 + }; + + input_absinfo absy { + 0, + 0, + target_touch_port.height, + 1, + 0, + 28 + }; + libevdev_enable_event_type(dev.get(), EV_ABS); + libevdev_enable_event_code(dev.get(), EV_ABS, ABS_X, &absx); + libevdev_enable_event_code(dev.get(), EV_ABS, ABS_Y, &absy); + + return dev; +} + evdev_t x360() { evdev_t dev { libevdev_new() }; @@ -471,7 +553,7 @@ evdev_t x360() { input_t input() { input_t result { new input_raw_t() }; - auto &gp = *(input_raw_t*)result.get(); + auto &gp = *(input_raw_t *)result.get(); gp.keyboard.reset(XOpenDisplay(nullptr)); @@ -486,10 +568,11 @@ input_t input() { // Ensure starting from clean slate gp.clear(); - gp.mouse_dev = mouse(); + gp.touch_dev = touchscreen(); + gp.mouse_dev = mouse(); gp.gamepad_dev = x360(); - if(gp.create_mouse()) { + if(gp.create_mouse() || gp.create_touchscreen()) { log_flush(); std::abort(); } @@ -498,9 +581,7 @@ input_t input() { } void freeInput(void *p) { - auto *input = (input_raw_t*)p; + auto *input = (input_raw_t *)p; delete input; } - -std::unique_ptr init() { return nullptr; } -} +} // namespace platf diff --git a/sunshine/platform/linux/misc.cpp b/sunshine/platform/linux/misc.cpp new file mode 100644 index 00000000..d4cdbfe7 --- /dev/null +++ b/sunshine/platform/linux/misc.cpp @@ -0,0 +1,88 @@ +#include "sunshine/platform/common.h" + +#include + +#include +#include +#include +#include + +#include "sunshine/main.h" + +using namespace std::literals; +namespace fs = std::filesystem; + +namespace platf { +using ifaddr_t = util::safe_ptr; + +ifaddr_t get_ifaddrs() { + ifaddrs *p { nullptr }; + + getifaddrs(&p); + + return ifaddr_t { p }; +} + +fs::path appdata() { + const char *homedir; + if((homedir = getenv("HOME")) == nullptr) { + homedir = getpwuid(geteuid())->pw_dir; + } + + return fs::path { homedir } / ".config/sunshine"sv; +} + +std::string from_sockaddr(const sockaddr *const ip_addr) { + char data[INET6_ADDRSTRLEN]; + + auto family = ip_addr->sa_family; + if(family == AF_INET6) { + inet_ntop(AF_INET6, &((sockaddr_in6 *)ip_addr)->sin6_addr, data, + INET6_ADDRSTRLEN); + } + + if(family == AF_INET) { + inet_ntop(AF_INET, &((sockaddr_in *)ip_addr)->sin_addr, data, + INET_ADDRSTRLEN); + } + + return std::string { data }; +} + +std::pair from_sockaddr_ex(const sockaddr *const ip_addr) { + char data[INET6_ADDRSTRLEN]; + + auto family = ip_addr->sa_family; + std::uint16_t port; + if(family == AF_INET6) { + inet_ntop(AF_INET6, &((sockaddr_in6 *)ip_addr)->sin6_addr, data, + INET6_ADDRSTRLEN); + port = ((sockaddr_in6 *)ip_addr)->sin6_port; + } + + if(family == AF_INET) { + inet_ntop(AF_INET, &((sockaddr_in *)ip_addr)->sin_addr, data, + INET_ADDRSTRLEN); + port = ((sockaddr_in *)ip_addr)->sin_port; + } + + return { port, std::string { data } }; +} + +std::string get_mac_address(const std::string_view &address) { + auto ifaddrs = get_ifaddrs(); + for(auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) { + if(pos->ifa_addr && address == from_sockaddr(pos->ifa_addr)) { + std::ifstream mac_file("/sys/class/net/"s + pos->ifa_name + "/address"); + if(mac_file.good()) { + std::string mac_address; + std::getline(mac_file, mac_address); + return mac_address; + } + } + } + + BOOST_LOG(warning) << "Unable to find MAC address for "sv << address; + return "00:00:00:00:00:00"s; +} +} // namespace platf \ No newline at end of file diff --git a/sunshine/platform/linux/vaapi.cpp b/sunshine/platform/linux/vaapi.cpp new file mode 100644 index 00000000..f939bca7 --- /dev/null +++ b/sunshine/platform/linux/vaapi.cpp @@ -0,0 +1,1137 @@ +#include +#include + +#include +#include + +#include +#include + +extern "C" { +#include +} + +#include "sunshine/config.h" +#include "sunshine/main.h" +#include "sunshine/platform/common.h" +#include "sunshine/utility.h" +#include "sunshine/video.h" + +// I want to have as little build dependencies as possible +// There aren't that many DRM_FORMAT I need to use, so define them here +// +// They aren't likely to change any time soon. +#define fourcc_code(a, b, c, d) ((std::uint32_t)(a) | ((std::uint32_t)(b) << 8) | \ + ((std::uint32_t)(c) << 16) | ((std::uint32_t)(d) << 24)) +#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */ +#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */ + + +#define SUNSHINE_SHADERS_DIR SUNSHINE_ASSETS_DIR "/shaders/opengl" + +#define STRINGIFY(x) #x +#define gl_drain_errors_helper(x) gl::drain_errors("line " STRINGIFY(x)) +#define gl_drain_errors gl_drain_errors_helper(__LINE__) + +using namespace std::literals; + +static void free_frame(AVFrame *frame) { + av_frame_free(&frame); +} + +using frame_t = util::safe_ptr; + +namespace dyn { +void *handle(const std::vector &libs) { + void *handle; + + for(auto lib : libs) { + handle = dlopen(lib, RTLD_LAZY | RTLD_LOCAL); + if(handle) { + return handle; + } + } + + std::stringstream ss; + ss << "Couldn't find any of the following libraries: ["sv << libs.front(); + std::for_each(std::begin(libs) + 1, std::end(libs), [&](auto lib) { + ss << ", "sv << lib; + }); + + ss << ']'; + + BOOST_LOG(error) << ss.str(); + + return nullptr; +} + +int load(void *handle, std::vector> &funcs, bool strict = true) { + for(auto &func : funcs) { + TUPLE_2D_REF(fn, name, func); + + *fn = GLAD_GNUC_EXTENSION(GLADapiproc) dlsym(handle, name); + + if(!*fn && strict) { + BOOST_LOG(error) << "Couldn't find function: "sv << name; + + return -1; + } + } + + return 0; +} +} // namespace dyn + + +namespace va { +constexpr auto SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2 = 0x40000000; +constexpr auto EXPORT_SURFACE_WRITE_ONLY = 0x0002; +constexpr auto EXPORT_SURFACE_COMPOSED_LAYERS = 0x0008; + +using VADisplay = void *; +using VAStatus = int; +using VAGenericID = unsigned int; +using VASurfaceID = VAGenericID; + +struct DRMPRIMESurfaceDescriptor { + // VA Pixel format fourcc of the whole surface (VA_FOURCC_*). + uint32_t fourcc; + + uint32_t width; + uint32_t height; + + // Number of distinct DRM objects making up the surface. + uint32_t num_objects; + + struct { + // DRM PRIME file descriptor for this object. + // Needs to be closed manually + int fd; + + /* + * Total size of this object (may include regions which are + * not part of the surface). + */ + uint32_t size; + // Format modifier applied to this object, not sure what that means + uint64_t drm_format_modifier; + } objects[4]; + + // Number of layers making up the surface. + uint32_t num_layers; + struct { + // DRM format fourcc of this layer (DRM_FOURCC_*). + uint32_t drm_format; + + // Number of planes in this layer. + uint32_t num_planes; + + // references objects --> DRMPRIMESurfaceDescriptor.objects[object_index[0]] + uint32_t object_index[4]; + + // Offset within the object of each plane. + uint32_t offset[4]; + + // Pitch of each plane. + uint32_t pitch[4]; + } layers[4]; +}; + +typedef VADisplay (*getDisplayDRM_fn)(int fd); +typedef VAStatus (*terminate_fn)(VADisplay dpy); +typedef VAStatus (*initialize_fn)(VADisplay dpy, int *major_version, int *minor_version); +typedef const char *(*errorStr_fn)(VAStatus error_status); +typedef void (*VAMessageCallback)(void *user_context, const char *message); +typedef VAMessageCallback (*setErrorCallback_fn)(VADisplay dpy, VAMessageCallback callback, void *user_context); +typedef VAMessageCallback (*setInfoCallback_fn)(VADisplay dpy, VAMessageCallback callback, void *user_context); +typedef const char *(*queryVendorString_fn)(VADisplay dpy); +typedef VAStatus (*exportSurfaceHandle_fn)( + VADisplay dpy, VASurfaceID surface_id, + uint32_t mem_type, uint32_t flags, + void *descriptor); + +getDisplayDRM_fn getDisplayDRM; +terminate_fn terminate; +initialize_fn initialize; +errorStr_fn errorStr; +setErrorCallback_fn setErrorCallback; +setInfoCallback_fn setInfoCallback; +queryVendorString_fn queryVendorString; +exportSurfaceHandle_fn exportSurfaceHandle; + +using display_t = util::dyn_safe_ptr_v2; + +int init() { + static void *handle { nullptr }; + static bool funcs_loaded = false; + + if(funcs_loaded) return 0; + + if(!handle) { + handle = dyn::handle({ "libva.so.2", "libva.so" }); + if(!handle) { + return -1; + } + } + + std::vector> funcs { + { (GLADapiproc *)&terminate, "vaTerminate" }, + { (GLADapiproc *)&initialize, "vaInitialize" }, + { (GLADapiproc *)&errorStr, "vaErrorStr" }, + { (GLADapiproc *)&setErrorCallback, "vaSetErrorCallback" }, + { (GLADapiproc *)&setInfoCallback, "vaSetInfoCallback" }, + { (GLADapiproc *)&queryVendorString, "vaQueryVendorString" }, + { (GLADapiproc *)&exportSurfaceHandle, "vaExportSurfaceHandle" }, + }; + + if(dyn::load(handle, funcs)) { + return -1; + } + + funcs_loaded = true; + return 0; +} + +int init_drm() { + if(init()) { + return -1; + } + + static void *handle { nullptr }; + static bool funcs_loaded = false; + + if(funcs_loaded) return 0; + + if(!handle) { + handle = dyn::handle({ "libva-drm.so.2", "libva-drm.so" }); + if(!handle) { + return -1; + } + } + + std::vector> funcs { + { (GLADapiproc *)&getDisplayDRM, "vaGetDisplayDRM" }, + }; + + if(dyn::load(handle, funcs)) { + return -1; + } + + funcs_loaded = true; + return 0; +} +} // namespace va + +namespace gbm { +struct device; +typedef void (*device_destroy_fn)(device *gbm); +typedef device *(*create_device_fn)(int fd); + +device_destroy_fn device_destroy; +create_device_fn create_device; + +int init() { + static void *handle { nullptr }; + static bool funcs_loaded = false; + + if(funcs_loaded) return 0; + + if(!handle) { + handle = dyn::handle({ "libgbm.so.1", "libgbm.so" }); + if(!handle) { + return -1; + } + } + + std::vector> funcs { + { (GLADapiproc *)&device_destroy, "gbm_device_destroy" }, + { (GLADapiproc *)&create_device, "gbm_create_device" }, + }; + + if(dyn::load(handle, funcs)) { + return -1; + } + + funcs_loaded = true; + return 0; +} + +} // namespace gbm + +namespace gl { +static GladGLContext ctx; + +void drain_errors(const std::string_view &prefix) { + GLenum err; + while((err = ctx.GetError()) != GL_NO_ERROR) { + BOOST_LOG(error) << "GL: "sv << prefix << ": ["sv << util::hex(err).to_string_view() << ']'; + } +} + +class tex_t : public util::buffer_t { + using util::buffer_t::buffer_t; + +public: + tex_t(tex_t &&) = default; + tex_t &operator=(tex_t &&) = default; + + ~tex_t() { + if(!size() == 0) { + ctx.DeleteTextures(size(), begin()); + } + } + + static tex_t make(std::size_t count) { + tex_t textures { count }; + + ctx.GenTextures(textures.size(), textures.begin()); + + float color[] = { 0.0f, 0.0f, 0.0f, 1.0f }; + + for(auto tex : textures) { + gl::ctx.BindTexture(GL_TEXTURE_2D, tex); + gl::ctx.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // x + gl::ctx.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // y + gl::ctx.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl::ctx.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl::ctx.TexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color); + } + + return textures; + } +}; + +class frame_buf_t : public util::buffer_t { + using util::buffer_t::buffer_t; + +public: + frame_buf_t(frame_buf_t &&) = default; + frame_buf_t &operator=(frame_buf_t &&) = default; + + ~frame_buf_t() { + if(begin()) { + ctx.DeleteFramebuffers(size(), begin()); + } + } + + static frame_buf_t make(std::size_t count) { + frame_buf_t frame_buf { count }; + + ctx.GenFramebuffers(frame_buf.size(), frame_buf.begin()); + + return frame_buf; + } + + template + void bind(It it_begin, It it_end) { + if(std::distance(it_begin, it_end) > size()) { + BOOST_LOG(warning) << "To many elements to bind"sv; + return; + } + + int x = 0; + std::for_each(it_begin, it_end, [&](auto tex) { + ctx.BindFramebuffer(GL_FRAMEBUFFER, (*this)[x]); + ctx.BindTexture(GL_TEXTURE_2D, tex); + + ctx.FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + x, tex, 0); + + ++x; + }); + } +}; + +class shader_t { + KITTY_USING_MOVE_T(shader_internal_t, GLuint, std::numeric_limits::max(), { + if(el != std::numeric_limits::max()) { + ctx.DeleteShader(el); + } + }); + +public: + std::string err_str() { + int length; + ctx.GetShaderiv(handle(), GL_INFO_LOG_LENGTH, &length); + + std::string string; + string.resize(length); + + ctx.GetShaderInfoLog(handle(), length, &length, string.data()); + + string.resize(length - 1); + + return string; + } + + static util::Either compile(const std::string_view &source, GLenum type) { + shader_t shader; + + auto data = source.data(); + GLint length = source.length(); + + shader._shader.el = ctx.CreateShader(type); + ctx.ShaderSource(shader.handle(), 1, &data, &length); + ctx.CompileShader(shader.handle()); + + int status = 0; + ctx.GetShaderiv(shader.handle(), GL_COMPILE_STATUS, &status); + + if(!status) { + return shader.err_str(); + } + + return shader; + } + + GLuint handle() const { + return _shader.el; + } + +private: + shader_internal_t _shader; +}; + +class buffer_t { + KITTY_USING_MOVE_T(buffer_internal_t, GLuint, std::numeric_limits::max(), { + if(el != std::numeric_limits::max()) { + ctx.DeleteBuffers(1, &el); + } + }); + +public: + static buffer_t make(util::buffer_t &&offsets, const char *block, const std::string_view &data) { + buffer_t buffer; + buffer._block = block; + buffer._size = data.size(); + buffer._offsets = std::move(offsets); + + ctx.GenBuffers(1, &buffer._buffer.el); + ctx.BindBuffer(GL_UNIFORM_BUFFER, buffer.handle()); + ctx.BufferData(GL_UNIFORM_BUFFER, data.size(), (const std::uint8_t *)data.data(), GL_DYNAMIC_DRAW); + + return buffer; + } + + GLuint handle() const { + return _buffer.el; + } + + const char *block() const { + return _block; + } + + void update(const std::string_view &view, std::size_t offset = 0) { + ctx.BindBuffer(GL_UNIFORM_BUFFER, handle()); + ctx.BufferSubData(GL_UNIFORM_BUFFER, offset, view.size(), (const void *)view.data()); + } + + void update(std::string_view *members, std::size_t count, std::size_t offset = 0) { + util::buffer_t buffer { _size }; + + for(int x = 0; x < count; ++x) { + auto val = members[x]; + + std::copy_n((const std::uint8_t *)val.data(), val.size(), &buffer[_offsets[x]]); + } + + update(util::view(buffer.begin(), buffer.end()), offset); + } + +private: + const char *_block; + + std::size_t _size; + + util::buffer_t _offsets; + + buffer_internal_t _buffer; +}; + +class program_t { + KITTY_USING_MOVE_T(program_internal_t, GLuint, std::numeric_limits::max(), { + if(el != std::numeric_limits::max()) { + ctx.DeleteProgram(el); + } + }); + +public: + std::string err_str() { + int length; + ctx.GetProgramiv(handle(), GL_INFO_LOG_LENGTH, &length); + + std::string string; + string.resize(length); + + ctx.GetShaderInfoLog(handle(), length, &length, string.data()); + + string.resize(length - 1); + + return string; + } + + static util::Either link(const shader_t &vert, const shader_t &frag) { + program_t program; + + program._program.el = ctx.CreateProgram(); + + ctx.AttachShader(program.handle(), vert.handle()); + ctx.AttachShader(program.handle(), frag.handle()); + + // p_handle stores a copy of the program handle, since program will be moved before + // the fail guard funcion is called. + auto fg = util::fail_guard([p_handle = program.handle(), &vert, &frag]() { + ctx.DetachShader(p_handle, vert.handle()); + ctx.DetachShader(p_handle, frag.handle()); + }); + + ctx.LinkProgram(program.handle()); + + int status = 0; + ctx.GetProgramiv(program.handle(), GL_LINK_STATUS, &status); + + if(!status) { + return program.err_str(); + } + + return program; + } + + void bind(const buffer_t &buffer) { + ctx.UseProgram(handle()); + auto i = ctx.GetUniformBlockIndex(handle(), buffer.block()); + + ctx.BindBufferBase(GL_UNIFORM_BUFFER, i, buffer.handle()); + } + + std::optional uniform(const char *block, std::pair *members, std::size_t count) { + auto i = ctx.GetUniformBlockIndex(handle(), block); + if(i == GL_INVALID_INDEX) { + BOOST_LOG(error) << "Couldn't find index of ["sv << block << ']'; + return std::nullopt; + } + + int size; + ctx.GetActiveUniformBlockiv(handle(), i, GL_UNIFORM_BLOCK_DATA_SIZE, &size); + + bool error_flag = false; + + util::buffer_t offsets { count }; + auto indices = (std::uint32_t *)alloca(count * sizeof(std::uint32_t)); + auto names = (const char **)alloca(count * sizeof(const char *)); + auto names_p = names; + + std::for_each_n(members, count, [names_p](auto &member) mutable { + *names_p++ = std::get<0>(member); + }); + + std::fill_n(indices, count, GL_INVALID_INDEX); + ctx.GetUniformIndices(handle(), count, names, indices); + + for(int x = 0; x < count; ++x) { + if(indices[x] == GL_INVALID_INDEX) { + error_flag = true; + + BOOST_LOG(error) << "Couldn't find ["sv << block << '.' << members[x].first << ']'; + } + } + + if(error_flag) { + return std::nullopt; + } + + ctx.GetActiveUniformsiv(handle(), count, indices, GL_UNIFORM_OFFSET, offsets.begin()); + util::buffer_t buffer { (std::size_t)size }; + + for(int x = 0; x < count; ++x) { + auto val = std::get<1>(members[x]); + + std::copy_n((const std::uint8_t *)val.data(), val.size(), &buffer[offsets[x]]); + } + + return buffer_t::make(std::move(offsets), block, std::string_view { (char *)buffer.begin(), buffer.size() }); + } + + GLuint handle() const { + return _program.el; + } + +private: + program_internal_t _program; +}; +} // namespace gl + +namespace platf { +namespace egl { + +constexpr auto EGL_LINUX_DMA_BUF_EXT = 0x3270; +constexpr auto EGL_LINUX_DRM_FOURCC_EXT = 0x3271; +constexpr auto EGL_DMA_BUF_PLANE0_FD_EXT = 0x3272; +constexpr auto EGL_DMA_BUF_PLANE0_OFFSET_EXT = 0x3273; +constexpr auto EGL_DMA_BUF_PLANE0_PITCH_EXT = 0x3274; + +using display_t = util::dyn_safe_ptr_v2; +using gbm_t = util::dyn_safe_ptr; + +int vaapi_make_hwdevice_ctx(platf::hwdevice_t *base, AVBufferRef **hw_device_buf); + +KITTY_USING_MOVE_T(file_t, int, -1, { + if(el >= 0) { + close(el); + } +}); + +struct nv12_img_t { + display_t::pointer display; + EGLImage r8; + EGLImage bg88; + + gl::tex_t tex; + gl::frame_buf_t buf; + + static constexpr std::size_t num_fds = + sizeof(va::DRMPRIMESurfaceDescriptor::objects) / sizeof(va::DRMPRIMESurfaceDescriptor::objects[0]); + + std::array fds; +}; + +KITTY_USING_MOVE_T(nv12_t, nv12_img_t, , { + if(el.r8) { + eglDestroyImageKHR(el.display, el.r8); + } + + if(el.bg88) { + eglDestroyImageKHR(el.display, el.bg88); + } +}); + +KITTY_USING_MOVE_T(ctx_t, (std::tuple), , { + TUPLE_2D_REF(disp, ctx, el); + if(ctx) { + if(ctx == eglGetCurrentContext()) { + eglMakeCurrent(disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } + eglDestroyContext(disp, ctx); + } +}); + +bool fail() { + return eglGetError() != EGL_SUCCESS; +} + +class egl_t : public platf::hwdevice_t { +public: + std::optional import(va::VASurfaceID surface) { + // No deallocation necessary + va::DRMPRIMESurfaceDescriptor prime; + + auto status = va::exportSurfaceHandle( + va_display, + surface, + va::SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2, + va::EXPORT_SURFACE_WRITE_ONLY | va::EXPORT_SURFACE_COMPOSED_LAYERS, + &prime); + if(status) { + + BOOST_LOG(error) << "Couldn't export va surface handle: ["sv << (int)surface << "]: "sv << va::errorStr(status); + + return std::nullopt; + } + + // Keep track of file descriptors + std::array fds; + for(int x = 0; x < prime.num_objects; ++x) { + fds[x] = prime.objects[x].fd; + } + + int img_attr_planes[2][13] { + { EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_R8, + EGL_WIDTH, (int)prime.width, + EGL_HEIGHT, (int)prime.height, + EGL_DMA_BUF_PLANE0_FD_EXT, prime.objects[prime.layers[0].object_index[0]].fd, + EGL_DMA_BUF_PLANE0_OFFSET_EXT, (int)prime.layers[0].offset[0], + EGL_DMA_BUF_PLANE0_PITCH_EXT, (int)prime.layers[0].pitch[0], + EGL_NONE }, + + { EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_GR88, + EGL_WIDTH, (int)prime.width / 2, + EGL_HEIGHT, (int)prime.height / 2, + EGL_DMA_BUF_PLANE0_FD_EXT, prime.objects[prime.layers[0].object_index[1]].fd, + EGL_DMA_BUF_PLANE0_OFFSET_EXT, (int)prime.layers[0].offset[1], + EGL_DMA_BUF_PLANE0_PITCH_EXT, (int)prime.layers[0].pitch[1], + EGL_NONE }, + }; + + nv12_t nv12 { + display.get(), + eglCreateImageKHR(display.get(), EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, img_attr_planes[0]), + eglCreateImageKHR(display.get(), EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, img_attr_planes[1]), + gl::tex_t::make(2), + gl::frame_buf_t::make(2), + std::move(fds) + }; + + if(!nv12->r8 || !nv12->bg88) { + BOOST_LOG(error) << "Couldn't create KHR Image"sv; + + return std::nullopt; + } + + gl::ctx.BindTexture(GL_TEXTURE_2D, nv12->tex[0]); + gl::ctx.EGLImageTargetTexture2DOES(GL_TEXTURE_2D, nv12->r8); + + gl::ctx.BindTexture(GL_TEXTURE_2D, nv12->tex[1]); + gl::ctx.EGLImageTargetTexture2DOES(GL_TEXTURE_2D, nv12->bg88); + + nv12->buf.bind(std::begin(nv12->tex), std::end(nv12->tex)); + + gl_drain_errors; + + return nv12; + } + + void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) override { + video::color_t *color_p; + switch(colorspace) { + case 5: // SWS_CS_SMPTE170M + color_p = &video::colors[0]; + break; + case 1: // SWS_CS_ITU709 + color_p = &video::colors[2]; + break; + case 9: // SWS_CS_BT2020 + default: + BOOST_LOG(warning) << "Colorspace: ["sv << colorspace << "] not yet supported: switching to default"sv; + color_p = &video::colors[0]; + }; + + if(color_range > 1) { + // Full range + ++color_p; + } + + std::string_view members[] { + util::view(color_p->color_vec_y), + util::view(color_p->color_vec_u), + util::view(color_p->color_vec_v), + util::view(color_p->range_y), + util::view(color_p->range_uv), + }; + + color_matrix.update(members, sizeof(members) / sizeof(decltype(members[0]))); + } + + int init(int in_width, int in_height, const char *render_device) { + if(!va::initialize || !gbm::create_device) { + if(!va::initialize) BOOST_LOG(warning) << "libva not initialized"sv; + if(!gbm::create_device) BOOST_LOG(warning) << "libgbm not initialized"sv; + return -1; + } + + file.el = open(render_device, O_RDWR); + + if(file.el < 0) { + char error_buf[1024]; + BOOST_LOG(error) << "Couldn't open ["sv << render_device << "]: "sv << strerror_r(errno, error_buf, sizeof(error_buf)); + return -1; + } + + gbm.reset(gbm::create_device(file.el)); + if(!gbm) { + BOOST_LOG(error) << "Couldn't create GBM device: ["sv << util::hex(eglGetError()).to_string_view() << ']'; + return -1; + } + + constexpr auto EGL_PLATFORM_GBM_MESA = 0x31D7; + + display.reset(eglGetPlatformDisplay(EGL_PLATFORM_GBM_MESA, gbm.get(), nullptr)); + if(fail()) { + BOOST_LOG(error) << "Couldn't open EGL display: ["sv << util::hex(eglGetError()).to_string_view() << ']'; + return -1; + } + + int major, minor; + if(!eglInitialize(display.get(), &major, &minor)) { + BOOST_LOG(error) << "Couldn't initialize EGL display: ["sv << util::hex(eglGetError()).to_string_view() << ']'; + return -1; + } + + const char *extension_st = eglQueryString(display.get(), EGL_EXTENSIONS); + const char *version = eglQueryString(display.get(), EGL_VERSION); + const char *vendor = eglQueryString(display.get(), EGL_VENDOR); + const char *apis = eglQueryString(display.get(), EGL_CLIENT_APIS); + + BOOST_LOG(debug) << "EGL: ["sv << vendor << "]: version ["sv << version << ']'; + BOOST_LOG(debug) << "API's supported: ["sv << apis << ']'; + + const char *extensions[] { + "EGL_KHR_create_context", + "EGL_KHR_surfaceless_context", + "EGL_EXT_image_dma_buf_import", + "EGL_KHR_image_pixmap" + }; + + for(auto ext : extensions) { + if(!std::strstr(extension_st, ext)) { + BOOST_LOG(error) << "Missing extension: ["sv << ext << ']'; + return -1; + } + } + + constexpr int conf_attr[] { + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE + }; + + int count; + EGLConfig conf; + if(!eglChooseConfig(display.get(), conf_attr, &conf, 1, &count)) { + BOOST_LOG(error) << "Couldn't set config attributes: ["sv << util::hex(eglGetError()).to_string_view() << ']'; + return -1; + } + + if(!eglBindAPI(EGL_OPENGL_API)) { + BOOST_LOG(error) << "Couldn't bind API: ["sv << util::hex(eglGetError()).to_string_view() << ']'; + return -1; + } + + constexpr int attr[] { + EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE + }; + + ctx.el = { display.get(), eglCreateContext(display.get(), conf, EGL_NO_CONTEXT, attr) }; + if(fail()) { + BOOST_LOG(error) << "Couldn't create EGL context: ["sv << util::hex(eglGetError()).to_string_view() << ']'; + return -1; + } + + TUPLE_EL_REF(ctx_p, 1, ctx.el); + if(!eglMakeCurrent(display.get(), EGL_NO_SURFACE, EGL_NO_SURFACE, ctx_p)) { + BOOST_LOG(error) << "Couldn't make current display"sv; + return -1; + } + + if(!gladLoadGLContext(&gl::ctx, eglGetProcAddress)) { + BOOST_LOG(error) << "Couldn't load OpenGL library"sv; + return -1; + } + + BOOST_LOG(debug) << "GL: vendor: "sv << gl::ctx.GetString(GL_VENDOR); + BOOST_LOG(debug) << "GL: renderer: "sv << gl::ctx.GetString(GL_RENDERER); + BOOST_LOG(debug) << "GL: version: "sv << gl::ctx.GetString(GL_VERSION); + BOOST_LOG(debug) << "GL: shader: "sv << gl::ctx.GetString(GL_SHADING_LANGUAGE_VERSION); + + gl::ctx.PixelStorei(GL_UNPACK_ALIGNMENT, 1); + + { + const char *sources[] { + SUNSHINE_SHADERS_DIR "/ConvertUV.frag", + SUNSHINE_SHADERS_DIR "/ConvertUV.vert", + SUNSHINE_SHADERS_DIR "/ConvertY.frag", + SUNSHINE_SHADERS_DIR "/Scene.vert", + SUNSHINE_SHADERS_DIR "/Scene.frag", + }; + + GLenum shader_type[2] { + GL_FRAGMENT_SHADER, + GL_VERTEX_SHADER, + }; + + constexpr auto count = sizeof(sources) / sizeof(const char *); + + util::Either compiled_sources[count]; + + bool error_flag = false; + for(int x = 0; x < count; ++x) { + auto &compiled_source = compiled_sources[x]; + + compiled_source = gl::shader_t::compile(read_file(sources[x]), shader_type[x % 2]); + gl_drain_errors; + + if(compiled_source.has_right()) { + BOOST_LOG(error) << sources[x] << ": "sv << compiled_source.right(); + error_flag = true; + } + } + + if(error_flag) { + return -1; + } + + auto program = gl::program_t::link(compiled_sources[1].left(), compiled_sources[0].left()); + if(program.has_right()) { + BOOST_LOG(error) << "GL linker: "sv << program.right(); + return -1; + } + + // UV - shader + this->program[1] = std::move(program.left()); + + program = gl::program_t::link(compiled_sources[3].left(), compiled_sources[2].left()); + if(program.has_right()) { + BOOST_LOG(error) << "GL linker: "sv << program.right(); + return -1; + } + + // Y - shader + this->program[0] = std::move(program.left()); + } + + auto color_p = &video::colors[0]; + std::pair members[] { + std::make_pair("color_vec_y", util::view(color_p->color_vec_y)), + std::make_pair("color_vec_u", util::view(color_p->color_vec_u)), + std::make_pair("color_vec_v", util::view(color_p->color_vec_v)), + std::make_pair("range_y", util::view(color_p->range_y)), + std::make_pair("range_uv", util::view(color_p->range_uv)), + }; + + auto color_matrix = program[0].uniform("ColorMatrix", members, sizeof(members) / sizeof(decltype(members[0]))); + if(!color_matrix) { + return -1; + } + + this->color_matrix = std::move(*color_matrix); + + tex_in = gl::tex_t::make(1); + + this->in_width = in_width; + this->in_height = in_height; + + data = (void *)vaapi_make_hwdevice_ctx; + gl_drain_errors; + return 0; + } + + int convert(platf::img_t &img) override { + auto tex = tex_in[0]; + + gl::ctx.BindTexture(GL_TEXTURE_2D, tex); + gl::ctx.TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, in_width, in_height, GL_BGRA, GL_UNSIGNED_BYTE, img.data); + + GLenum attachments[] { + GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1 + }; + + for(int x = 0; x < sizeof(attachments) / sizeof(decltype(attachments[0])); ++x) { + gl::ctx.BindFramebuffer(GL_FRAMEBUFFER, nv12->buf[x]); + gl::ctx.DrawBuffers(1, &attachments[x]); + + auto status = gl::ctx.CheckFramebufferStatus(GL_FRAMEBUFFER); + if(status != GL_FRAMEBUFFER_COMPLETE) { + BOOST_LOG(error) << "Pass "sv << x << ": CheckFramebufferStatus() --> [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + gl::ctx.BindTexture(GL_TEXTURE_2D, tex); + + gl::ctx.UseProgram(program[x].handle()); + program[x].bind(color_matrix); + + gl::ctx.Viewport(offsetX / (x + 1), offsetY / (x + 1), out_width / (x + 1), out_height / (x + 1)); + gl::ctx.DrawArrays(GL_TRIANGLES, 0, 3); + } + + return 0; + } + + int set_frame(AVFrame *frame) override { + this->hwframe.reset(frame); + this->frame = frame; + + if(av_hwframe_get_buffer(frame->hw_frames_ctx, frame, 0)) { + BOOST_LOG(error) << "Couldn't get hwframe for VAAPI"sv; + + return -1; + } + + va::VASurfaceID surface = (std::uintptr_t)frame->data[3]; + + auto nv12_opt = import(surface); + if(!nv12_opt) { + return -1; + } + + nv12 = std::move(*nv12_opt); + + // // Ensure aspect ratio is maintained + auto scalar = std::fminf(frame->width / (float)in_width, frame->height / (float)in_height); + auto out_width_f = in_width * scalar; + auto out_height_f = in_height * scalar; + + // result is always positive + auto offsetX_f = (frame->width - out_width_f) / 2; + auto offsetY_f = (frame->height - out_height_f) / 2; + + out_width = out_width_f; + out_height = out_height_f; + + offsetX = offsetX_f; + offsetY = offsetY_f; + + auto tex = tex_in[0]; + + gl::ctx.BindTexture(GL_TEXTURE_2D, tex); + gl::ctx.TexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, in_width, in_height); + + auto loc_width_i = gl::ctx.GetUniformLocation(program[1].handle(), "width_i"); + if(loc_width_i < 0) { + BOOST_LOG(error) << "Couldn't find uniform [width_i]"sv; + return -1; + } + + auto width_i = 1.0f / out_width; + gl::ctx.UseProgram(program[1].handle()); + gl::ctx.Uniform1fv(loc_width_i, 1, &width_i); + + gl_drain_errors; + return 0; + } + + ~egl_t() override { + if(gl::ctx.GetError) { + gl_drain_errors; + } + } + + int in_width, in_height; + int out_width, out_height; + int offsetX, offsetY; + + frame_t hwframe; + + va::display_t::pointer va_display; + + file_t file; + gbm_t gbm; + display_t display; + ctx_t ctx; + + gl::tex_t tex_in; + nv12_t nv12; + gl::program_t program[2]; + gl::buffer_t color_matrix; +}; + +/** + * This is a private structure of FFmpeg, I need this to manually create + * a VAAPI hardware context + * + * xdisplay will not be used internally by FFmpeg + */ +typedef struct VAAPIDevicePriv { + union { + void *xdisplay; + int fd; + } drm; + int drm_fd; +} VAAPIDevicePriv; + +/** + * VAAPI connection details. + * + * Allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVVAAPIDeviceContext { + /** + * The VADisplay handle, to be filled by the user. + */ + va::VADisplay display; + /** + * Driver quirks to apply - this is filled by av_hwdevice_ctx_init(), + * with reference to a table of known drivers, unless the + * AV_VAAPI_DRIVER_QUIRK_USER_SET bit is already present. The user + * may need to refer to this field when performing any later + * operations using VAAPI with the same VADisplay. + */ + unsigned int driver_quirks; +} AVVAAPIDeviceContext; + +static void __log(void *level, const char *msg) { + BOOST_LOG(*(boost::log::sources::severity_logger *)level) << msg; +} + +int vaapi_make_hwdevice_ctx(platf::hwdevice_t *base, AVBufferRef **hw_device_buf) { + if(!va::initialize) { + BOOST_LOG(warning) << "libva not loaded"sv; + return -1; + } + + if(!va::getDisplayDRM) { + BOOST_LOG(warning) << "libva-drm not loaded"sv; + return -1; + } + + auto egl = (platf::egl::egl_t *)base; + auto fd = dup(egl->file.el); + + auto *priv = (VAAPIDevicePriv *)av_mallocz(sizeof(VAAPIDevicePriv)); + priv->drm_fd = fd; + priv->drm.fd = fd; + + auto fg = util::fail_guard([fd, priv]() { + close(fd); + av_free(priv); + }); + + va::display_t display { va::getDisplayDRM(fd) }; + if(!display) { + auto render_device = config::video.adapter_name.empty() ? "/dev/dri/renderD128" : config::video.adapter_name.c_str(); + + BOOST_LOG(error) << "Couldn't open a va display from DRM with device: "sv << render_device; + return -1; + } + + egl->va_display = display.get(); + + va::setErrorCallback(display.get(), __log, &error); + va::setErrorCallback(display.get(), __log, &info); + + int major, minor; + auto status = va::initialize(display.get(), &major, &minor); + if(status) { + BOOST_LOG(error) << "Couldn't initialize va display: "sv << va::errorStr(status); + return -1; + } + + BOOST_LOG(debug) << "vaapi vendor: "sv << va::queryVendorString(display.get()); + + *hw_device_buf = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI); + auto ctx = (AVVAAPIDeviceContext *)((AVHWDeviceContext *)(*hw_device_buf)->data)->hwctx; + ctx->display = display.release(); + + fg.disable(); + + auto err = av_hwdevice_ctx_init(*hw_device_buf); + if(err) { + char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; + BOOST_LOG(error) << "Failed to create FFMpeg hardware device context: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); + + return err; + } + + return 0; +} + +std::shared_ptr make_hwdevice(int width, int height) { + auto egl = std::make_shared(); + + auto render_device = config::video.adapter_name.empty() ? "/dev/dri/renderD128" : config::video.adapter_name.c_str(); + if(egl->init(width, height, render_device)) { + return nullptr; + } + + return egl; +} +} // namespace egl + +std::unique_ptr init() { + gbm::init(); + va::init_drm(); + + if(!gladLoaderLoadEGL(EGL_NO_DISPLAY) || !eglGetPlatformDisplay) { + BOOST_LOG(warning) << "Couldn't load EGL library"sv; + } + + return std::make_unique(); +} +} // namespace platf \ No newline at end of file diff --git a/sunshine/platform/linux/vaapi.h b/sunshine/platform/linux/vaapi.h new file mode 100644 index 00000000..10b1b3f1 --- /dev/null +++ b/sunshine/platform/linux/vaapi.h @@ -0,0 +1,8 @@ +#ifndef SUNSHINE_DISPLAY_H +#define SUNSHINE_DISPLAY_H + +#include "sunshine/platform/common.h" +namespace platf::egl { +std::shared_ptr make_hwdevice(int width, int height); +} // namespace platf::egl +#endif \ No newline at end of file diff --git a/sunshine/platform/windows/PolicyConfig.h b/sunshine/platform/windows/PolicyConfig.h new file mode 100644 index 00000000..60f36a68 --- /dev/null +++ b/sunshine/platform/windows/PolicyConfig.h @@ -0,0 +1,164 @@ +// ---------------------------------------------------------------------------- +// PolicyConfig.h +// Undocumented COM-interface IPolicyConfig. +// Use for set default audio render endpoint +// @author EreTIk +// http://eretik.omegahg.com/ +// ---------------------------------------------------------------------------- + + +#pragma once + +#ifdef __MINGW32__ +#undef DEFINE_GUID +#ifdef __cplusplus +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) EXTERN_C const GUID DECLSPEC_SELECTANY name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } +#else +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) const GUID DECLSPEC_SELECTANY name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } +#endif + +DEFINE_GUID(IID_IPolicyConfig, 0xf8679f50, 0x850a, 0x41cf, 0x9c, 0x72, 0x43, 0x0f, 0x29, 0x02, 0x90, 0xc8); +DEFINE_GUID(CLSID_CPolicyConfigClient, 0x870af99c, 0x171d, 0x4f9e, 0xaf, 0x0d, 0xe6, 0x3d, 0xf4, 0x0c, 0x2b, 0xc9); + +#endif + +interface DECLSPEC_UUID("f8679f50-850a-41cf-9c72-430f290290c8") IPolicyConfig; +class DECLSPEC_UUID("870af99c-171d-4f9e-af0d-e63df40c2bc9") CPolicyConfigClient; +// ---------------------------------------------------------------------------- +// class CPolicyConfigClient +// {870af99c-171d-4f9e-af0d-e63df40c2bc9} +// +// interface IPolicyConfig +// {f8679f50-850a-41cf-9c72-430f290290c8} +// +// Query interface: +// CComPtr PolicyConfig; +// PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigClient)); +// +// @compatible: Windows 7 and Later +// ---------------------------------------------------------------------------- +interface IPolicyConfig : public IUnknown { +public: + virtual HRESULT GetMixFormat( + PCWSTR, + WAVEFORMATEX **); + + virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat( + PCWSTR, + INT, + WAVEFORMATEX **); + + virtual HRESULT STDMETHODCALLTYPE ResetDeviceFormat( + PCWSTR); + + virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat( + PCWSTR, + WAVEFORMATEX *, + WAVEFORMATEX *); + + virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod( + PCWSTR, + INT, + PINT64, + PINT64); + + virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod( + PCWSTR, + PINT64); + + virtual HRESULT STDMETHODCALLTYPE GetShareMode( + PCWSTR, + struct DeviceShareMode *); + + virtual HRESULT STDMETHODCALLTYPE SetShareMode( + PCWSTR, + struct DeviceShareMode *); + + virtual HRESULT STDMETHODCALLTYPE GetPropertyValue( + PCWSTR, + const PROPERTYKEY &, + PROPVARIANT *); + + virtual HRESULT STDMETHODCALLTYPE SetPropertyValue( + PCWSTR, + const PROPERTYKEY &, + PROPVARIANT *); + + virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint( + PCWSTR wszDeviceId, + ERole eRole); + + virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility( + PCWSTR, + INT); +}; + +interface DECLSPEC_UUID("568b9108-44bf-40b4-9006-86afe5b5a620") IPolicyConfigVista; +class DECLSPEC_UUID("294935CE-F637-4E7C-A41B-AB255460B862") CPolicyConfigVistaClient; +// ---------------------------------------------------------------------------- +// class CPolicyConfigVistaClient +// {294935CE-F637-4E7C-A41B-AB255460B862} +// +// interface IPolicyConfigVista +// {568b9108-44bf-40b4-9006-86afe5b5a620} +// +// Query interface: +// CComPtr PolicyConfig; +// PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigVistaClient)); +// +// @compatible: Windows Vista and Later +// ---------------------------------------------------------------------------- +interface IPolicyConfigVista : public IUnknown { +public: + virtual HRESULT GetMixFormat( + PCWSTR, + WAVEFORMATEX **); // not available on Windows 7, use method from IPolicyConfig + + virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat( + PCWSTR, + INT, + WAVEFORMATEX **); + + virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat( + PCWSTR, + WAVEFORMATEX *, + WAVEFORMATEX *); + + virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod( + PCWSTR, + INT, + PINT64, + PINT64); // not available on Windows 7, use method from IPolicyConfig + + virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod( + PCWSTR, + PINT64); // not available on Windows 7, use method from IPolicyConfig + + virtual HRESULT STDMETHODCALLTYPE GetShareMode( + PCWSTR, + struct DeviceShareMode *); // not available on Windows 7, use method from IPolicyConfig + + virtual HRESULT STDMETHODCALLTYPE SetShareMode( + PCWSTR, + struct DeviceShareMode *); // not available on Windows 7, use method from IPolicyConfig + + virtual HRESULT STDMETHODCALLTYPE GetPropertyValue( + PCWSTR, + const PROPERTYKEY &, + PROPVARIANT *); + + virtual HRESULT STDMETHODCALLTYPE SetPropertyValue( + PCWSTR, + const PROPERTYKEY &, + PROPVARIANT *); + + virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint( + PCWSTR wszDeviceId, + ERole eRole); + + virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility( + PCWSTR, + INT); // not available on Windows 7, use method from IPolicyConfig +}; + +// ---------------------------------------------------------------------------- diff --git a/sunshine/platform/windows/audio.cpp b/sunshine/platform/windows/audio.cpp index a71e4a77..61df413a 100644 --- a/sunshine/platform/windows/audio.cpp +++ b/sunshine/platform/windows/audio.cpp @@ -1,335 +1,988 @@ -// -// Created by loki on 1/12/20. -// - -#include -#include -#include - -#include - -#include - -#include "sunshine/config.h" -#include "sunshine/main.h" -#include "sunshine/platform/common.h" - -const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); -const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); -const IID IID_IAudioClient = __uuidof(IAudioClient); -const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); - -using namespace std::literals; -namespace platf::audio { -template -void Release(T *p) { - p->Release(); -} - -template -void co_task_free(T *p) { - CoTaskMemFree((LPVOID)p); -} - -using device_enum_t = util::safe_ptr>; -using device_t = util::safe_ptr>; -using audio_client_t = util::safe_ptr>; -using audio_capture_t = util::safe_ptr>; -using wave_format_t = util::safe_ptr>; -using handle_t = util::safe_ptr_v2; - -class co_init_t : public deinit_t { -public: - co_init_t() { - CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY); - } - - ~co_init_t() override { - CoUninitialize(); - } -}; - -class mic_wasapi_t : public mic_t { -public: - capture_e sample(std::vector &sample_in) override { - while(sample_buf_pos - std::begin(sample_buf) < sample_in.size()) { - //FIXME: Use IAudioClient3 instead of IAudioClient, that would allows for adjusting the latency of the audio samples - auto capture_result = _fill_buffer(); - - if(capture_result != capture_e::ok) { - return capture_result; - } - } - - std::copy_n(std::begin(sample_buf), sample_in.size(), std::begin(sample_in)); - - // The excess samples should be in front of the queue - std::move(&sample_buf[sample_in.size()], sample_buf_pos, std::begin(sample_buf)); - sample_buf_pos -= sample_in.size(); - - return capture_e::ok; - } - - - int init(std::uint32_t sample_rate) { - audio_event.reset(CreateEventA(nullptr, FALSE, FALSE, nullptr)); - if(!audio_event) { - BOOST_LOG(error) << "Couldn't create Event handle"sv; - - return -1; - } - - HRESULT status; - - device_enum_t::pointer device_enum_p{}; - status = CoCreateInstance( - CLSID_MMDeviceEnumerator, - nullptr, - CLSCTX_ALL, - IID_IMMDeviceEnumerator, - (void **) &device_enum_p); - device_enum.reset(device_enum_p); - - if (FAILED(status)) { - BOOST_LOG(error) << "Couldn't create Device Enumerator [0x"sv << util::hex(status).to_string_view() << ']'; - - return -1; - } - - device_t::pointer device_p{}; - - if(config::audio.sink.empty()) { - status = device_enum->GetDefaultAudioEndpoint( - eRender, - eConsole, - &device_p); - } - else { - std::wstring_convert, wchar_t> converter; - auto wstring_device_id = converter.from_bytes(config::audio.sink); - - status = device_enum->GetDevice(wstring_device_id.c_str(), &device_p); - } - device.reset(device_p); - - if (FAILED(status)) { - BOOST_LOG(error) << "Couldn't create audio Device [0x"sv << util::hex(status).to_string_view() << ']'; - - return -1; - } - - audio_client_t::pointer audio_client_p{}; - status = device->Activate( - IID_IAudioClient, - CLSCTX_ALL, - nullptr, - (void **) &audio_client_p); - audio_client.reset(audio_client_p); - - if (FAILED(status)) { - BOOST_LOG(error) << "Couldn't activate audio Device [0x"sv << util::hex(status).to_string_view() << ']'; - - return -1; - } - - wave_format_t::pointer wave_format_p{}; - status = audio_client->GetMixFormat(&wave_format_p); - wave_format.reset(wave_format_p); - - if (FAILED(status)) { - BOOST_LOG(error) << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']'; - - return -1; - } - - wave_format->nChannels = 2; - wave_format->wBitsPerSample = 16; - wave_format->nSamplesPerSec = sample_rate; - wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8; - wave_format->nAvgBytesPerSec = wave_format->nSamplesPerSec * wave_format->nBlockAlign; - - switch(wave_format->wFormatTag) { - case WAVE_FORMAT_PCM: - break; - case WAVE_FORMAT_IEEE_FLOAT: - wave_format->wFormatTag = WAVE_FORMAT_PCM; - break; - case WAVE_FORMAT_EXTENSIBLE: { - auto wave_ex = (PWAVEFORMATEXTENSIBLE) wave_format.get(); - if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) { - wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - wave_ex->Samples.wValidBitsPerSample = 16; - break; - } - - BOOST_LOG(error) << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']'; - return -1; - } - default: - BOOST_LOG(error) << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']'; - return -1; - }; - - REFERENCE_TIME default_latency; - audio_client->GetDevicePeriod(&default_latency, nullptr); - default_latency_ms = default_latency / 1000; - - status = audio_client->Initialize( - AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK, - 0, 0, - wave_format.get(), - nullptr); - - if (FAILED(status)) { - BOOST_LOG(error) << "Couldn't initialize audio client [0x"sv << util::hex(status).to_string_view() << ']'; - - return -1; - } - - std::uint32_t frames; - status = audio_client->GetBufferSize(&frames); - if (FAILED(status)) { - BOOST_LOG(error) << "Couldn't acquire the number of audio frames [0x"sv << util::hex(status).to_string_view() << ']'; - - return -1; - } - - sample_buf = util::buffer_t { frames }; - sample_buf_pos = std::begin(sample_buf); - - audio_capture_t::pointer audio_capture_p {}; - status = audio_client->GetService(IID_IAudioCaptureClient, (void**)&audio_capture_p); - audio_capture.reset(audio_capture_p); - if (FAILED(status)) { - BOOST_LOG(error) << "Couldn't initialize audio capture client [0x"sv << util::hex(status).to_string_view() << ']'; - - return -1; - } - - status = audio_client->SetEventHandle(audio_event.get()); - if (FAILED(status)) { - BOOST_LOG(error) << "Couldn't set event handle [0x"sv << util::hex(status).to_string_view() << ']'; - - return -1; - } - - status = audio_client->Start(); - if (FAILED(status)) { - BOOST_LOG(error) << "Couldn't start recording [0x"sv << util::hex(status).to_string_view() << ']'; - - return -1; - } - - return 0; - } - - ~mic_wasapi_t() override { - if(audio_client) { - audio_client->Stop(); - } - } -private: - capture_e _fill_buffer() { - HRESULT status; - - // Total number of samples - struct sample_aligned_t { - std::uint32_t uninitialized; - std::int16_t *samples; - } sample_aligned; - - // number of samples / number of channels - struct block_aligned_t { - std::uint32_t audio_sample_size; - } block_aligned; - - status = WaitForSingleObjectEx(audio_event.get(), default_latency_ms, FALSE); - switch (status) { - case WAIT_OBJECT_0: - break; - case WAIT_TIMEOUT: - return capture_e::timeout; - default: - BOOST_LOG(error) << "Couldn't wait for audio event: [0x"sv << util::hex(status).to_string_view() << ']'; - return capture_e::error; - } - - std::uint32_t packet_size{}; - for ( - status = audio_capture->GetNextPacketSize(&packet_size); - SUCCEEDED(status) && packet_size > 0; - status = audio_capture->GetNextPacketSize(&packet_size) - ) { - DWORD buffer_flags; - status = audio_capture->GetBuffer( - (BYTE **) &sample_aligned.samples, - &block_aligned.audio_sample_size, - &buffer_flags, - nullptr, nullptr); - - switch (status) { - case S_OK: - break; - case AUDCLNT_E_DEVICE_INVALIDATED: - return capture_e::reinit; - default: - BOOST_LOG(error) << "Couldn't capture audio [0x"sv << util::hex(status).to_string_view() << ']'; - return capture_e::error; - } - - sample_aligned.uninitialized = std::end(sample_buf) - sample_buf_pos; - auto n = std::min(sample_aligned.uninitialized, block_aligned.audio_sample_size * wave_format->nChannels); - - if (buffer_flags & AUDCLNT_BUFFERFLAGS_SILENT) { - std::fill_n(sample_buf_pos, n, 0); - } else { - std::copy_n(sample_aligned.samples, n, sample_buf_pos); - } - - sample_buf_pos += n; - - audio_capture->ReleaseBuffer(block_aligned.audio_sample_size); - } - - if (status == AUDCLNT_E_DEVICE_INVALIDATED) { - return capture_e::reinit; - } - - if (FAILED(status)) { - return capture_e::error; - } - - return capture_e::ok; - } -public: - handle_t audio_event; - - device_enum_t device_enum; - device_t device; - audio_client_t audio_client; - wave_format_t wave_format; - audio_capture_t audio_capture; - - REFERENCE_TIME default_latency_ms; - - util::buffer_t sample_buf; - std::int16_t *sample_buf_pos; -}; -} - -namespace platf { -std::unique_ptr microphone(std::uint32_t sample_rate) { - auto mic = std::make_unique(); - - if(mic->init(sample_rate)) { - return nullptr; - } - - return mic; -} - -std::unique_ptr init() { - return std::make_unique(); -} -} +// +// Created by loki on 1/12/20. +// + +#include +#include +#include + +#include + +#include + +#define INITGUID +#include +#undef INITGUID + +#include "sunshine/config.h" +#include "sunshine/main.h" +#include "sunshine/platform/common.h" + +// Must be the last included file +// clang-format off +#include "PolicyConfig.h" +// clang-format on + +DEFINE_PROPERTYKEY(PKEY_Device_DeviceDesc, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 2); // DEVPROP_TYPE_STRING +DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); // DEVPROP_TYPE_STRING +DEFINE_PROPERTYKEY(PKEY_DeviceInterface_FriendlyName, 0x026e516e, 0xb814, 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22, 2); + +const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); +const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); +const IID IID_IAudioClient = __uuidof(IAudioClient); +const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); + +using namespace std::literals; +namespace platf::audio { +constexpr auto SAMPLE_RATE = 48000; + +template +void Release(T *p) { + p->Release(); +} + +template +void co_task_free(T *p) { + CoTaskMemFree((LPVOID)p); +} + +using device_enum_t = util::safe_ptr>; +using device_t = util::safe_ptr>; +using collection_t = util::safe_ptr>; +using audio_client_t = util::safe_ptr>; +using audio_capture_t = util::safe_ptr>; +using wave_format_t = util::safe_ptr>; +using wstring_t = util::safe_ptr>; +using handle_t = util::safe_ptr_v2; +using policy_t = util::safe_ptr>; +using prop_t = util::safe_ptr>; + +class co_init_t : public deinit_t { +public: + co_init_t() { + CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY); + } + + ~co_init_t() override { + CoUninitialize(); + } +}; + +class prop_var_t { +public: + prop_var_t() { + PropVariantInit(&prop); + } + + ~prop_var_t() { + PropVariantClear(&prop); + } + + PROPVARIANT prop; +}; + +class audio_pipe_t { +public: + static constexpr auto stereo = 2; + static constexpr auto channels51 = 6; + static constexpr auto channels71 = 8; + + using samples_t = std::vector; + using buf_t = util::buffer_t; + + virtual void to_stereo(samples_t &out, const buf_t &in) = 0; + virtual void to_51(samples_t &out, const buf_t &in) = 0; + virtual void to_71(samples_t &out, const buf_t &in) = 0; +}; + +class mono_t : public audio_pipe_t { +public: + void to_stereo(samples_t &out, const buf_t &in) override { + auto sample_in_pos = std::begin(in); + auto sample_end = std::begin(out) + out.size(); + + for(auto sample_out_p = std::begin(out); sample_out_p != sample_end;) { + *sample_out_p++ = *sample_in_pos * 7 / 10; + *sample_out_p++ = *sample_in_pos++ * 7 / 10; + } + } + + void to_51(samples_t &out, const buf_t &in) override { + using namespace speaker; + + auto sample_in_pos = std::begin(in); + auto sample_end = std::begin(out) + out.size(); + + for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += channels51) { + int left = *sample_in_pos++; + + auto fl = (left * 7 / 10); + + sample_out_p[FRONT_LEFT] = fl; + sample_out_p[FRONT_RIGHT] = fl; + sample_out_p[FRONT_CENTER] = fl * 6; + sample_out_p[LOW_FREQUENCY] = fl / 10; + sample_out_p[BACK_LEFT] = left * 4 / 10; + sample_out_p[BACK_RIGHT] = left * 4 / 10; + } + } + + void to_71(samples_t &out, const buf_t &in) override { + using namespace speaker; + + auto sample_in_pos = std::begin(in); + auto sample_end = std::begin(out) + out.size(); + + for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += channels71) { + int left = *sample_in_pos++; + + auto fl = (left * 7 / 10); + + sample_out_p[FRONT_LEFT] = fl; + sample_out_p[FRONT_RIGHT] = fl; + sample_out_p[FRONT_CENTER] = fl * 6; + sample_out_p[LOW_FREQUENCY] = fl / 10; + sample_out_p[BACK_LEFT] = left * 4 / 10; + sample_out_p[BACK_RIGHT] = left * 4 / 10; + sample_out_p[SIDE_LEFT] = left * 5 / 10; + sample_out_p[SIDE_RIGHT] = left * 5 / 10; + } + } +}; + +class stereo_t : public audio_pipe_t { +public: + void to_stereo(samples_t &out, const buf_t &in) override { + std::copy_n(std::begin(in), out.size(), std::begin(out)); + } + + void to_51(samples_t &out, const buf_t &in) override { + using namespace speaker; + + auto sample_in_pos = std::begin(in); + auto sample_end = std::begin(out) + out.size(); + + for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += channels51) { + int left = sample_in_pos[speaker::FRONT_LEFT]; + int right = sample_in_pos[speaker::FRONT_RIGHT]; + + sample_in_pos += 2; + + auto fl = (left * 7 / 10); + auto fr = (right * 7 / 10); + + auto mix = (fl + fr) / 2; + + sample_out_p[FRONT_LEFT] = fl; + sample_out_p[FRONT_RIGHT] = fr; + sample_out_p[FRONT_CENTER] = mix; + sample_out_p[LOW_FREQUENCY] = mix / 2; + sample_out_p[BACK_LEFT] = left * 4 / 10; + sample_out_p[BACK_RIGHT] = right * 4 / 10; + } + } + + void to_71(samples_t &out, const buf_t &in) override { + using namespace speaker; + + auto sample_in_pos = std::begin(in); + auto sample_end = std::begin(out) + out.size(); + + for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += channels71) { + int left = sample_in_pos[speaker::FRONT_LEFT]; + int right = sample_in_pos[speaker::FRONT_RIGHT]; + + sample_in_pos += 2; + + auto fl = (left * 7 / 10); + auto fr = (right * 7 / 10); + + auto mix = (fl + fr) / 2; + + sample_out_p[FRONT_LEFT] = fl; + sample_out_p[FRONT_RIGHT] = fr; + sample_out_p[FRONT_CENTER] = mix; + sample_out_p[LOW_FREQUENCY] = mix / 2; + sample_out_p[BACK_LEFT] = left * 4 / 10; + sample_out_p[BACK_RIGHT] = right * 4 / 10; + sample_out_p[SIDE_LEFT] = left * 5 / 10; + sample_out_p[SIDE_RIGHT] = right * 5 / 10; + } + } +}; + +class surr51_t : public audio_pipe_t { +public: + void to_stereo(samples_t &out, const buf_t &in) { + using namespace speaker; + + auto sample_in_pos = std::begin(in); + auto sample_end = std::begin(out) + out.size(); + + for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += stereo) { + int left {}, right {}; + + left += sample_in_pos[FRONT_LEFT]; + left += sample_in_pos[FRONT_CENTER] * 9 / 10; + left += sample_in_pos[LOW_FREQUENCY] * 3 / 10; + left += sample_in_pos[BACK_LEFT] * 7 / 10; + left += sample_in_pos[BACK_RIGHT] * 3 / 10; + + right += sample_in_pos[FRONT_RIGHT]; + right += sample_in_pos[FRONT_CENTER] * 9 / 10; + right += sample_in_pos[LOW_FREQUENCY] * 3 / 10; + right += sample_in_pos[BACK_LEFT] * 3 / 10; + right += sample_in_pos[BACK_RIGHT] * 7 / 10; + + sample_out_p[0] = left; + sample_out_p[1] = right; + + sample_in_pos += channels51; + } + } + + void to_51(samples_t &out, const buf_t &in) override { + std::copy_n(std::begin(in), out.size(), std::begin(out)); + } + + void to_71(samples_t &out, const buf_t &in) override { + using namespace speaker; + + auto sample_in_pos = std::begin(in); + auto sample_end = std::begin(out) + out.size(); + + for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += channels71) { + int fl = sample_in_pos[FRONT_LEFT]; + int fr = sample_in_pos[FRONT_RIGHT]; + int bl = sample_in_pos[BACK_LEFT]; + int br = sample_in_pos[BACK_RIGHT]; + + auto mix_l = (fl + bl) / 2; + auto mix_r = (bl + br) / 2; + + sample_out_p[FRONT_LEFT] = fl; + sample_out_p[FRONT_RIGHT] = fr; + sample_out_p[FRONT_CENTER] = sample_in_pos[FRONT_CENTER]; + sample_out_p[LOW_FREQUENCY] = sample_in_pos[LOW_FREQUENCY]; + sample_out_p[BACK_LEFT] = bl; + sample_out_p[BACK_RIGHT] = br; + sample_out_p[SIDE_LEFT] = mix_l; + sample_out_p[SIDE_RIGHT] = mix_r; + + sample_in_pos += channels51; + } + } +}; + +class surr71_t : public audio_pipe_t { +public: + void to_stereo(samples_t &out, const buf_t &in) { + using namespace speaker; + + auto sample_in_pos = std::begin(in); + auto sample_end = std::begin(out) + out.size(); + + for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += stereo) { + int left {}, right {}; + + left += sample_in_pos[FRONT_LEFT]; + left += sample_in_pos[FRONT_CENTER] * 9 / 10; + left += sample_in_pos[LOW_FREQUENCY] * 3 / 10; + left += sample_in_pos[BACK_LEFT] * 7 / 10; + left += sample_in_pos[BACK_RIGHT] * 3 / 10; + left += sample_in_pos[SIDE_LEFT]; + + right += sample_in_pos[FRONT_RIGHT]; + right += sample_in_pos[FRONT_CENTER] * 9 / 10; + right += sample_in_pos[LOW_FREQUENCY] * 3 / 10; + right += sample_in_pos[BACK_LEFT] * 3 / 10; + right += sample_in_pos[BACK_RIGHT] * 7 / 10; + right += sample_in_pos[SIDE_RIGHT]; + + sample_out_p[0] = left; + sample_out_p[1] = right; + + sample_in_pos += channels71; + } + } + + void to_51(samples_t &out, const buf_t &in) override { + using namespace speaker; + + auto sample_in_pos = std::begin(in); + auto sample_end = std::begin(out) + out.size(); + + for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += channels51) { + auto sl = (int)sample_out_p[SIDE_LEFT] * 3 / 10; + auto sr = (int)sample_out_p[SIDE_RIGHT] * 3 / 10; + + sample_out_p[FRONT_LEFT] = sample_in_pos[FRONT_LEFT] + sl; + sample_out_p[FRONT_RIGHT] = sample_in_pos[FRONT_RIGHT] + sr; + sample_out_p[FRONT_CENTER] = sample_in_pos[FRONT_CENTER]; + sample_out_p[LOW_FREQUENCY] = sample_in_pos[LOW_FREQUENCY]; + sample_out_p[BACK_LEFT] = sample_in_pos[BACK_LEFT] + sl; + sample_out_p[BACK_RIGHT] = sample_in_pos[BACK_RIGHT] + sr; + + sample_in_pos += channels71; + } + } + + void to_71(samples_t &out, const buf_t &in) override { + std::copy_n(std::begin(in), out.size(), std::begin(out)); + } +}; + +static std::wstring_convert, wchar_t> converter; +struct format_t { + enum type_e : int { + none, + mono, + stereo, + surr51, + surr71, + } type; + + std::string_view name; + int channels; + int channel_mask; +} formats[] { + { + format_t::mono, + "Mono"sv, + 1, + SPEAKER_FRONT_CENTER, + }, + { + format_t::stereo, + "Stereo"sv, + 2, + SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT, + }, + { + format_t::surr51, + "Surround 5.1"sv, + 6, + SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT, + }, + { + format_t::surr71, + "Surround 7.1"sv, + 8, + SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT | + SPEAKER_SIDE_LEFT | + SPEAKER_SIDE_RIGHT, + }, +}; + +static format_t surround_51_side_speakers { + format_t::surr51, + "Surround 5.1"sv, + 6, + SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_SIDE_LEFT | + SPEAKER_SIDE_RIGHT, +}; + +void set_wave_format(audio::wave_format_t &wave_format, const format_t &format) { + wave_format->nChannels = format.channels; + wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8; + wave_format->nAvgBytesPerSec = wave_format->nSamplesPerSec * wave_format->nBlockAlign; + + if(wave_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + ((PWAVEFORMATEXTENSIBLE)wave_format.get())->dwChannelMask = format.channel_mask; + } +} + +int init_wave_format(audio::wave_format_t &wave_format, DWORD sample_rate) { + wave_format->wBitsPerSample = 16; + wave_format->nSamplesPerSec = sample_rate; + switch(wave_format->wFormatTag) { + case WAVE_FORMAT_PCM: + break; + case WAVE_FORMAT_IEEE_FLOAT: + break; + case WAVE_FORMAT_EXTENSIBLE: { + auto wave_ex = (PWAVEFORMATEXTENSIBLE)wave_format.get(); + if(IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) { + wave_ex->Samples.wValidBitsPerSample = 16; + wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + break; + } + + BOOST_LOG(error) << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']'; + } + default: + BOOST_LOG(error) << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']'; + return -1; + }; + + return 0; +} + +audio_client_t make_audio_client(device_t &device, const format_t &format, int sample_rate) { + audio_client_t audio_client; + auto status = device->Activate( + IID_IAudioClient, + CLSCTX_ALL, + nullptr, + (void **)&audio_client); + + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't activate Device: [0x"sv << util::hex(status).to_string_view() << ']'; + + return nullptr; + } + + wave_format_t wave_format; + status = audio_client->GetMixFormat(&wave_format); + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']'; + + return nullptr; + } + + if(init_wave_format(wave_format, sample_rate)) { + return nullptr; + } + set_wave_format(wave_format, format); + + status = audio_client->Initialize( + AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + 0, 0, + wave_format.get(), + nullptr); + + if(status) { + BOOST_LOG(debug) << "Couldn't initialize audio client for ["sv << format.name << "]: [0x"sv << util::hex(status).to_string_view() << ']'; + return nullptr; + } + + return audio_client; +} + +const wchar_t *no_null(const wchar_t *str) { + return str ? str : L"Unknown"; +} + +format_t::type_e validate_device(device_t &device, int sample_rate) { + for(const auto &format : formats) { + // Ensure WaveFromat is compatible + auto audio_client = make_audio_client(device, format, sample_rate); + + BOOST_LOG(debug) << format.name << ": "sv << (!audio_client ? "unsupported"sv : "supported"sv); + + if(audio_client) { + return format.type; + } + } + + return format_t::none; +} + +device_t default_device(device_enum_t &device_enum) { + device_t device; + HRESULT status; + status = device_enum->GetDefaultAudioEndpoint( + eRender, + eConsole, + &device); + + + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't create audio Device [0x"sv << util::hex(status).to_string_view() << ']'; + + return nullptr; + } + + return device; +} + +class mic_wasapi_t : public mic_t { +public: + capture_e sample(std::vector &sample_out) override { + auto sample_size = sample_out.size() / channels_out * channels_in; + while(sample_buf_pos - std::begin(sample_buf) < sample_size) { + //FIXME: Use IAudioClient3 instead of IAudioClient, that would allows for adjusting the latency of the audio samples + auto capture_result = _fill_buffer(); + + if(capture_result != capture_e::ok) { + return capture_result; + } + } + + switch(channels_out) { + case 2: + pipe->to_stereo(sample_out, sample_buf); + break; + case 6: + pipe->to_51(sample_out, sample_buf); + break; + case 8: + pipe->to_71(sample_out, sample_buf); + break; + default: + BOOST_LOG(error) << "converting to ["sv << channels_out << "] channels is not supported"sv; + return capture_e::error; + } + + // The excess samples should be in front of the queue + std::move(&sample_buf[sample_size], sample_buf_pos, std::begin(sample_buf)); + sample_buf_pos -= sample_size; + + return capture_e::ok; + } + + + int init(std::uint32_t sample_rate, std::uint32_t frame_size, std::uint32_t channels_out) { + audio_event.reset(CreateEventA(nullptr, FALSE, FALSE, nullptr)); + if(!audio_event) { + BOOST_LOG(error) << "Couldn't create Event handle"sv; + + return -1; + } + + HRESULT status; + + status = CoCreateInstance( + CLSID_MMDeviceEnumerator, + nullptr, + CLSCTX_ALL, + IID_IMMDeviceEnumerator, + (void **)&device_enum); + + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't create Device Enumerator [0x"sv << util::hex(status).to_string_view() << ']'; + + return -1; + } + + auto device = default_device(device_enum); + if(!device) { + return -1; + } + + for(auto &format : formats) { + BOOST_LOG(debug) << "Trying audio format ["sv << format.name << ']'; + audio_client = make_audio_client(device, format, sample_rate); + + if(audio_client) { + BOOST_LOG(debug) << "Found audio format ["sv << format.name << ']'; + channels_in = format.channels; + this->channels_out = channels_out; + + switch(channels_in) { + case 1: + pipe = std::make_unique(); + break; + case 2: + pipe = std::make_unique(); + break; + case 6: + pipe = std::make_unique(); + break; + case 8: + pipe = std::make_unique(); + break; + default: + BOOST_LOG(error) << "converting from ["sv << channels_in << "] channels is not supported"sv; + return -1; + } + break; + } + } + + if(!audio_client) { + BOOST_LOG(error) << "Couldn't find supported format for audio"sv; + return -1; + } + + REFERENCE_TIME default_latency; + audio_client->GetDevicePeriod(&default_latency, nullptr); + default_latency_ms = default_latency / 1000; + + std::uint32_t frames; + status = audio_client->GetBufferSize(&frames); + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't acquire the number of audio frames [0x"sv << util::hex(status).to_string_view() << ']'; + + return -1; + } + + // *2 --> needs to fit double + sample_buf = util::buffer_t { std::max(frames, frame_size) * 2 * channels_in }; + sample_buf_pos = std::begin(sample_buf); + + status = audio_client->GetService(IID_IAudioCaptureClient, (void **)&audio_capture); + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't initialize audio capture client [0x"sv << util::hex(status).to_string_view() << ']'; + + return -1; + } + + status = audio_client->SetEventHandle(audio_event.get()); + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't set event handle [0x"sv << util::hex(status).to_string_view() << ']'; + + return -1; + } + + status = audio_client->Start(); + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't start recording [0x"sv << util::hex(status).to_string_view() << ']'; + + return -1; + } + + return 0; + } + + ~mic_wasapi_t() override { + if(audio_client) { + audio_client->Stop(); + } + } + +private: + capture_e _fill_buffer() { + HRESULT status; + + // Total number of samples + struct sample_aligned_t { + std::uint32_t uninitialized; + std::int16_t *samples; + } sample_aligned; + + // number of samples / number of channels + struct block_aligned_t { + std::uint32_t audio_sample_size; + } block_aligned; + + status = WaitForSingleObjectEx(audio_event.get(), default_latency_ms, FALSE); + switch(status) { + case WAIT_OBJECT_0: + break; + case WAIT_TIMEOUT: + return capture_e::timeout; + default: + BOOST_LOG(error) << "Couldn't wait for audio event: [0x"sv << util::hex(status).to_string_view() << ']'; + return capture_e::error; + } + + std::uint32_t packet_size {}; + for( + status = audio_capture->GetNextPacketSize(&packet_size); + SUCCEEDED(status) && packet_size > 0; + status = audio_capture->GetNextPacketSize(&packet_size)) { + DWORD buffer_flags; + status = audio_capture->GetBuffer( + (BYTE **)&sample_aligned.samples, + &block_aligned.audio_sample_size, + &buffer_flags, + nullptr, nullptr); + + switch(status) { + case S_OK: + break; + case AUDCLNT_E_DEVICE_INVALIDATED: + return capture_e::reinit; + default: + BOOST_LOG(error) << "Couldn't capture audio [0x"sv << util::hex(status).to_string_view() << ']'; + return capture_e::error; + } + + sample_aligned.uninitialized = std::end(sample_buf) - sample_buf_pos; + auto n = std::min(sample_aligned.uninitialized, block_aligned.audio_sample_size * channels_in); + + if(buffer_flags & AUDCLNT_BUFFERFLAGS_SILENT) { + std::fill_n(sample_buf_pos, n, 0); + } + else { + std::copy_n(sample_aligned.samples, n, sample_buf_pos); + } + + sample_buf_pos += n; + + audio_capture->ReleaseBuffer(block_aligned.audio_sample_size); + } + + if(status == AUDCLNT_E_DEVICE_INVALIDATED) { + return capture_e::reinit; + } + + if(FAILED(status)) { + return capture_e::error; + } + + return capture_e::ok; + } + +public: + handle_t audio_event; + + device_enum_t device_enum; + device_t device; + audio_client_t audio_client; + audio_capture_t audio_capture; + + REFERENCE_TIME default_latency_ms; + + util::buffer_t sample_buf; + std::int16_t *sample_buf_pos; + + // out --> our audio output + int channels_out; + // in --> our wasapi input + int channels_in; + + std::unique_ptr pipe; +}; + +class audio_control_t : public ::platf::audio_control_t { +public: + std::optional sink_info() override { + auto virtual_adapter_name = L"Steam Streaming Speakers"sv; + + sink_t sink; + + audio::device_enum_t device_enum; + auto status = CoCreateInstance( + CLSID_MMDeviceEnumerator, + nullptr, + CLSCTX_ALL, + IID_IMMDeviceEnumerator, + (void **)&device_enum); + + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't create Device Enumerator: [0x"sv << util::hex(status).to_string_view() << ']'; + + return std::nullopt; + } + + auto device = default_device(device_enum); + if(!device) { + return std::nullopt; + } + + audio::wstring_t wstring; + device->GetId(&wstring); + + sink.host = converter.to_bytes(wstring.get()); + + collection_t collection; + status = device_enum->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &collection); + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't enumerate: [0x"sv << util::hex(status).to_string_view() << ']'; + + return std::nullopt; + } + + UINT count; + collection->GetCount(&count); + + std::string virtual_device_id = config::audio.virtual_sink; + for(auto x = 0; x < count; ++x) { + audio::device_t device; + collection->Item(x, &device); + + auto type = validate_device(device, SAMPLE_RATE); + if(type == format_t::none) { + continue; + } + + audio::wstring_t wstring; + device->GetId(&wstring); + + audio::prop_t prop; + device->OpenPropertyStore(STGM_READ, &prop); + + prop_var_t adapter_friendly_name; + prop_var_t device_friendly_name; + prop_var_t device_desc; + + prop->GetValue(PKEY_Device_FriendlyName, &device_friendly_name.prop); + prop->GetValue(PKEY_DeviceInterface_FriendlyName, &adapter_friendly_name.prop); + prop->GetValue(PKEY_Device_DeviceDesc, &device_desc.prop); + + auto adapter_name = no_null((LPWSTR)adapter_friendly_name.prop.pszVal); + BOOST_LOG(verbose) + << L"===== Device ====="sv << std::endl + << L"Device ID : "sv << wstring.get() << std::endl + << L"Device name : "sv << no_null((LPWSTR)device_friendly_name.prop.pszVal) << std::endl + << L"Adapter name : "sv << adapter_name << std::endl + << L"Device description : "sv << no_null((LPWSTR)device_desc.prop.pszVal) << std::endl + << std::endl; + + if(virtual_device_id.empty() && adapter_name == virtual_adapter_name) { + virtual_device_id = converter.to_bytes(wstring.get()); + } + } + + if(!virtual_device_id.empty()) { + sink.null = std::make_optional(sink_t::null_t { + "virtual-"s.append(formats[format_t::stereo - 1].name) + virtual_device_id, + "virtual-"s.append(formats[format_t::surr51 - 1].name) + virtual_device_id, + "virtual-"s.append(formats[format_t::surr71 - 1].name) + virtual_device_id, + }); + } + + return sink; + } + + std::unique_ptr microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) override { + auto mic = std::make_unique(); + + if(mic->init(sample_rate, frame_size, channels)) { + return nullptr; + } + + return mic; + } + + /** + * If the requested sink is a virtual sink, meaning no speakers attached to + * the host, then we can seamlessly set the format to stereo and surround sound. + * + * Any virtual sink detected will be prefixed by: + * virtual-(format name) + * If it doesn't contain that prefix, then the format will not be changed + */ + std::optional set_format(const std::string &sink) { + std::string_view sv { sink.c_str(), sink.size() }; + + format_t::type_e type = format_t::none; + // sink format: + // [virtual-(format name)]device_id + auto prefix = "virtual-"sv; + if(sv.find(prefix) == 0) { + sv = sv.substr(prefix.size(), sv.size() - prefix.size()); + + for(auto &format : formats) { + auto &name = format.name; + if(sv.find(name) == 0) { + type = format.type; + sv = sv.substr(name.size(), sv.size() - name.size()); + + break; + } + } + } + + auto wstring_device_id = converter.from_bytes(sv.data()); + + if(type == format_t::none) { + // wstring_device_id does not contain virtual-(format name) + // It's a simple deviceId, just pass it back + return std::make_optional(std::move(wstring_device_id)); + } + + wave_format_t wave_format; + auto status = policy->GetMixFormat(wstring_device_id.c_str(), &wave_format); + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']'; + + return std::nullopt; + } + + if(init_wave_format(wave_format, SAMPLE_RATE)) { + return std::nullopt; + } + set_wave_format(wave_format, formats[(int)type - 1]); + + WAVEFORMATEXTENSIBLE p {}; + status = policy->SetDeviceFormat(wstring_device_id.c_str(), wave_format.get(), (WAVEFORMATEX *)&p); + + // Surround 5.1 might contain side-{left, right} instead of speaker in the back + // Try again with different speaker mask. + if(status == 0x88890008 && type == format_t::surr51) { + set_wave_format(wave_format, surround_51_side_speakers); + status = policy->SetDeviceFormat(wstring_device_id.c_str(), wave_format.get(), (WAVEFORMATEX *)&p); + } + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't set Wave Format [0x"sv << util::hex(status).to_string_view() << ']'; + + return std::nullopt; + } + + return std::make_optional(std::move(wstring_device_id)); + } + + int set_sink(const std::string &sink) override { + auto wstring_device_id = set_format(sink); + if(!wstring_device_id) { + return -1; + } + + int failure {}; + for(int x = 0; x < (int)ERole_enum_count; ++x) { + auto status = policy->SetDefaultEndpoint(wstring_device_id->c_str(), (ERole)x); + if(status) { + BOOST_LOG(warning) << "Couldn't set ["sv << sink << "] to role ["sv << x << ']'; + + ++failure; + } + } + + return failure; + } + + int init() { + auto status = CoCreateInstance( + CLSID_CPolicyConfigClient, + nullptr, + CLSCTX_ALL, + IID_IPolicyConfig, + (void **)&policy); + + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't create audio policy config: [0x"sv << util::hex(status).to_string_view() << ']'; + + return -1; + } + + return 0; + } + + ~audio_control_t() override {} + + policy_t policy; +}; +} // namespace platf::audio + +namespace platf { + +// It's not big enough to justify it's own source file :/ +namespace dxgi { +int init(); +} + +std::unique_ptr audio_control() { + auto control = std::make_unique(); + + if(control->init()) { + return nullptr; + } + + return control; +} + +std::unique_ptr init() { + if(dxgi::init()) { + return nullptr; + } + return std::make_unique(); +} +} // namespace platf diff --git a/sunshine/platform/windows/display.h b/sunshine/platform/windows/display.h index 662115e8..bd920499 100644 --- a/sunshine/platform/windows/display.h +++ b/sunshine/platform/windows/display.h @@ -5,14 +5,14 @@ #ifndef SUNSHINE_DISPLAY_H #define SUNSHINE_DISPLAY_H -#include #include #include #include +#include #include -#include "sunshine/utility.h" #include "sunshine/platform/common.h" +#include "sunshine/utility.h" namespace platf::dxgi { extern const char *format_str[]; @@ -32,6 +32,7 @@ using output_t = util::safe_ptr>; using output1_t = util::safe_ptr>; using dup_t = util::safe_ptr>; using texture2d_t = util::safe_ptr>; +using texture1d_t = util::safe_ptr>; using resource_t = util::safe_ptr>; using multithread_t = util::safe_ptr>; @@ -42,7 +43,7 @@ using processor_t = util::safe_ptr>; using processor_in_t = util::safe_ptr>; using processor_enum_t = util::safe_ptr>; -} +} // namespace video class hwdevice_t; struct cursor_t { @@ -84,6 +85,17 @@ public: DXGI_FORMAT format; D3D_FEATURE_LEVEL feature_level; + + typedef enum _D3DKMT_SCHEDULINGPRIORITYCLASS { + D3DKMT_SCHEDULINGPRIORITYCLASS_IDLE, + D3DKMT_SCHEDULINGPRIORITYCLASS_BELOW_NORMAL, + D3DKMT_SCHEDULINGPRIORITYCLASS_NORMAL, + D3DKMT_SCHEDULINGPRIORITYCLASS_ABOVE_NORMAL, + D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH, + D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME + } D3DKMT_SCHEDULINGPRIORITYCLASS; + + typedef NTSTATUS WINAPI (*PD3DKMTSetProcessSchedulingPriorityClass)(HANDLE, D3DKMT_SCHEDULINGPRIORITYCLASS); }; class display_ram_t : public display_base_t { @@ -106,11 +118,11 @@ public: std::shared_ptr alloc_img() override; int dummy_img(img_t *img_base) override; - std::shared_ptr make_hwdevice(int width, int height, pix_fmt_e pix_fmt) override; + std::shared_ptr make_hwdevice(pix_fmt_e pix_fmt) override; gpu_cursor_t cursor; - std::vector hwdevices; + std::vector hwdevices; }; -} +} // namespace platf::dxgi #endif \ No newline at end of file diff --git a/sunshine/platform/windows/display_base.cpp b/sunshine/platform/windows/display_base.cpp index 4b42880c..240c17f9 100644 --- a/sunshine/platform/windows/display_base.cpp +++ b/sunshine/platform/windows/display_base.cpp @@ -23,18 +23,18 @@ capture_e duplication_t::next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::ch auto status = dup->AcquireNextFrame(timeout.count(), &frame_info, res_p); switch(status) { - case S_OK: - has_frame = true; - return capture_e::ok; - case DXGI_ERROR_WAIT_TIMEOUT: - return capture_e::timeout; - case WAIT_ABANDONED: - case DXGI_ERROR_ACCESS_LOST: - case DXGI_ERROR_ACCESS_DENIED: - return capture_e::reinit; - default: - BOOST_LOG(error) << "Couldn't acquire next frame [0x"sv << util::hex(status).to_string_view(); - return capture_e::error; + case S_OK: + has_frame = true; + return capture_e::ok; + case DXGI_ERROR_WAIT_TIMEOUT: + return capture_e::timeout; + case WAIT_ABANDONED: + case DXGI_ERROR_ACCESS_LOST: + case DXGI_ERROR_ACCESS_DENIED: + return capture_e::reinit; + default: + BOOST_LOG(error) << "Couldn't acquire next frame [0x"sv << util::hex(status).to_string_view(); + return capture_e::error; } } @@ -52,20 +52,20 @@ capture_e duplication_t::release_frame() { } auto status = dup->ReleaseFrame(); - switch (status) { - case S_OK: - has_frame = false; - return capture_e::ok; - case DXGI_ERROR_WAIT_TIMEOUT: - return capture_e::timeout; - case WAIT_ABANDONED: - case DXGI_ERROR_ACCESS_LOST: - case DXGI_ERROR_ACCESS_DENIED: - has_frame = false; - return capture_e::reinit; - default: - BOOST_LOG(error) << "Couldn't release frame [0x"sv << util::hex(status).to_string_view(); - return capture_e::error; + switch(status) { + case S_OK: + has_frame = false; + return capture_e::ok; + case DXGI_ERROR_WAIT_TIMEOUT: + return capture_e::timeout; + case WAIT_ABANDONED: + case DXGI_ERROR_ACCESS_LOST: + case DXGI_ERROR_ACCESS_DENIED: + has_frame = false; + return capture_e::reinit; + default: + BOOST_LOG(error) << "Couldn't release frame [0x"sv << util::hex(status).to_string_view(); + return capture_e::error; } } @@ -74,7 +74,7 @@ duplication_t::~duplication_t() { } int display_base_t::init() { -/* Uncomment when use of IDXGIOutput5 is implemented + /* Uncomment when use of IDXGIOutput5 is implemented std::call_once(windows_cpp_once_flag, []() { DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); const auto DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = ((DPI_AWARENESS_CONTEXT)-4); @@ -90,16 +90,14 @@ int display_base_t::init() { FreeLibrary(user32); }); */ - dxgi::factory1_t::pointer factory_p {}; - dxgi::adapter_t::pointer adapter_p {}; - dxgi::output_t::pointer output_p {}; - dxgi::device_t::pointer device_p {}; - dxgi::device_ctx_t::pointer device_ctx_p {}; + + // Get rectangle of full desktop for absolute mouse coordinates + env_width = GetSystemMetrics(SM_CXVIRTUALSCREEN); + env_height = GetSystemMetrics(SM_CYVIRTUALSCREEN); HRESULT status; - status = CreateDXGIFactory1(IID_IDXGIFactory1, (void**)&factory_p); - factory.reset(factory_p); + status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **)&factory); if(FAILED(status)) { BOOST_LOG(error) << "Failed to create DXGIFactory1 [0x"sv << util::hex(status).to_string_view() << ']'; return -1; @@ -108,9 +106,10 @@ int display_base_t::init() { std::wstring_convert, wchar_t> converter; auto adapter_name = converter.from_bytes(config::video.adapter_name); - auto output_name = converter.from_bytes(config::video.output_name); + auto output_name = converter.from_bytes(config::video.output_name); - for(int x = 0; factory_p->EnumAdapters1(x, &adapter_p) != DXGI_ERROR_NOT_FOUND; ++x) { + adapter_t::pointer adapter_p; + for(int x = 0; factory->EnumAdapters1(x, &adapter_p) != DXGI_ERROR_NOT_FOUND; ++x) { dxgi::adapter_t adapter_tmp { adapter_p }; DXGI_ADAPTER_DESC1 adapter_desc; @@ -120,8 +119,9 @@ int display_base_t::init() { continue; } + dxgi::output_t::pointer output_p; for(int y = 0; adapter_tmp->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) { - dxgi::output_t output_tmp {output_p }; + dxgi::output_t output_tmp { output_p }; DXGI_OUTPUT_DESC desc; output_tmp->GetDesc(&desc); @@ -133,8 +133,15 @@ int display_base_t::init() { if(desc.AttachedToDesktop) { output = std::move(output_tmp); - width = desc.DesktopCoordinates.right - desc.DesktopCoordinates.left; - height = desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top; + offset_x = desc.DesktopCoordinates.left; + offset_y = desc.DesktopCoordinates.top; + width = desc.DesktopCoordinates.right - offset_x; + height = desc.DesktopCoordinates.bottom - offset_y; + + // left and bottom may be negative, yet absolute mouse coordinates start at 0x0 + // Ensure offset starts at 0x0 + offset_x -= GetSystemMetrics(SM_XVIRTUALSCREEN); + offset_y -= GetSystemMetrics(SM_YVIRTUALSCREEN); } } @@ -150,8 +157,6 @@ int display_base_t::init() { } D3D_FEATURE_LEVEL featureLevels[] { - D3D_FEATURE_LEVEL_12_1, - D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, @@ -161,10 +166,9 @@ int display_base_t::init() { D3D_FEATURE_LEVEL_9_1 }; - status = adapter->QueryInterface(IID_IDXGIAdapter, (void**)&adapter_p); + status = adapter->QueryInterface(IID_IDXGIAdapter, (void **)&adapter_p); if(FAILED(status)) { BOOST_LOG(error) << "Failed to query IDXGIAdapter interface"sv; - return -1; } @@ -175,14 +179,12 @@ int display_base_t::init() { D3D11_CREATE_DEVICE_VIDEO_SUPPORT, featureLevels, sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL), D3D11_SDK_VERSION, - &device_p, + &device, &feature_level, - &device_ctx_p); + &device_ctx); adapter_p->Release(); - device.reset(device_p); - device_ctx.reset(device_ctx_p); if(FAILED(status)) { BOOST_LOG(error) << "Failed to create D3D11 device [0x"sv << util::hex(status).to_string_view() << ']'; @@ -202,16 +204,46 @@ int display_base_t::init() { << "Device Sys Mem : "sv << adapter_desc.DedicatedSystemMemory / 1048576 << " MiB"sv << std::endl << "Share Sys Mem : "sv << adapter_desc.SharedSystemMemory / 1048576 << " MiB"sv << std::endl << "Feature Level : 0x"sv << util::hex(feature_level).to_string_view() << std::endl - << "Capture size : "sv << width << 'x' << height; + << "Capture size : "sv << width << 'x' << height << std::endl + << "Offset : "sv << offset_x << 'x' << offset_y << std::endl + << "Virtual Desktop : "sv << env_width << 'x' << env_height; // Bump up thread priority { - dxgi::dxgi_t::pointer dxgi_p {}; - status = device->QueryInterface(IID_IDXGIDevice, (void**)&dxgi_p); - dxgi::dxgi_t dxgi { dxgi_p }; + const DWORD flags = TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY; + TOKEN_PRIVILEGES tp; + HANDLE token; + LUID val; + if(OpenProcessToken(GetCurrentProcess(), flags, &token) && + !!LookupPrivilegeValue(NULL, SE_INC_BASE_PRIORITY_NAME, &val)) { + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = val; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + if(!AdjustTokenPrivileges(token, false, &tp, sizeof(tp), NULL, NULL)) { + BOOST_LOG(warning) << "Could not set privilege to increase GPU priority"; + } + } + + CloseHandle(token); + + HMODULE gdi32 = GetModuleHandleA("GDI32"); + if(gdi32) { + PD3DKMTSetProcessSchedulingPriorityClass fn = + (PD3DKMTSetProcessSchedulingPriorityClass)GetProcAddress(gdi32, "D3DKMTSetProcessSchedulingPriorityClass"); + if(fn) { + status = fn(GetCurrentProcess(), D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME); + if(FAILED(status)) { + BOOST_LOG(warning) << "Failed to set realtime GPU priority. Please run application as administrator for optimal performance."; + } + } + } + + dxgi::dxgi_t dxgi; + status = device->QueryInterface(IID_IDXGIDevice, (void **)&dxgi); if(FAILED(status)) { - BOOST_LOG(error) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']'; + BOOST_LOG(warning) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']'; return -1; } @@ -220,25 +252,24 @@ int display_base_t::init() { // Try to reduce latency { - dxgi::dxgi1_t::pointer dxgi_p {}; - status = device->QueryInterface(IID_IDXGIDevice, (void**)&dxgi_p); - dxgi::dxgi1_t dxgi { dxgi_p }; - + dxgi::dxgi1_t dxgi {}; + status = device->QueryInterface(IID_IDXGIDevice, (void **)&dxgi); if(FAILED(status)) { BOOST_LOG(error) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']'; return -1; } - dxgi->SetMaximumFrameLatency(1); + status = dxgi->SetMaximumFrameLatency(1); + if(FAILED(status)) { + BOOST_LOG(warning) << "Failed to set maximum frame latency [0x"sv << util::hex(status).to_string_view() << ']'; + } } //FIXME: Duplicate output on RX580 in combination with DOOM (2016) --> BSOD //TODO: Use IDXGIOutput5 for improved performance { - dxgi::output1_t::pointer output1_p {}; - status = output->QueryInterface(IID_IDXGIOutput1, (void**)&output1_p); - dxgi::output1_t output1 {output1_p }; - + dxgi::output1_t output1 {}; + status = output->QueryInterface(IID_IDXGIOutput1, (void **)&output1); if(FAILED(status)) { BOOST_LOG(error) << "Failed to query IDXGIOutput1 from the output"sv; return -1; @@ -246,10 +277,8 @@ int display_base_t::init() { // We try this twice, in case we still get an error on reinitialization for(int x = 0; x < 2; ++x) { - dxgi::dup_t::pointer dup_p {}; - status = output1->DuplicateOutput((IUnknown*)device.get(), &dup_p); + status = output1->DuplicateOutput((IUnknown *)device.get(), &dup.dup); if(SUCCEEDED(status)) { - dup.reset(dup_p); break; } std::this_thread::sleep_for(200ms); @@ -396,18 +425,18 @@ const char *format_str[] = { "DXGI_FORMAT_V408" }; -} +} // namespace platf::dxgi namespace platf { -std::shared_ptr display(dev_type_e hwdevice_type) { - if(hwdevice_type == dev_type_e::dxgi) { +std::shared_ptr display(mem_type_e hwdevice_type) { + if(hwdevice_type == mem_type_e::dxgi) { auto disp = std::make_shared(); if(!disp->init()) { return disp; } } - else if(hwdevice_type == dev_type_e::none) { + else if(hwdevice_type == mem_type_e::system) { auto disp = std::make_shared(); if(!disp->init()) { @@ -417,4 +446,4 @@ std::shared_ptr display(dev_type_e hwdevice_type) { return nullptr; } -} +} // namespace platf diff --git a/sunshine/platform/windows/display_ram.cpp b/sunshine/platform/windows/display_ram.cpp index 15d608fe..cb3930d4 100644 --- a/sunshine/platform/windows/display_ram.cpp +++ b/sunshine/platform/windows/display_ram.cpp @@ -1,12 +1,12 @@ -#include "sunshine/main.h" #include "display.h" +#include "sunshine/main.h" namespace platf { using namespace std::literals; } namespace platf::dxgi { -struct img_t : public ::platf::img_t { +struct img_t : public ::platf::img_t { ~img_t() override { delete[] data; data = nullptr; @@ -22,29 +22,29 @@ void blend_cursor_monochrome(const cursor_t &cursor, img_t &img) { auto cursor_skip_y = -std::min(0, cursor.y); auto cursor_skip_x = -std::min(0, cursor.x); - // img cursor.{x,y} > img.{x,y}, truncate parts of the cursor.img_data + // img cursor.{x,y} > img.{x,y}, truncate parts of the cursor.img_data auto cursor_truncate_y = std::max(0, cursor.y - img.height); auto cursor_truncate_x = std::max(0, cursor.x - img.width); - auto cursor_width = width - cursor_skip_x - cursor_truncate_x; + auto cursor_width = width - cursor_skip_x - cursor_truncate_x; auto cursor_height = height - cursor_skip_y - cursor_truncate_y; if(cursor_height > height || cursor_width > width) { return; } - auto img_skip_y = std::max(0, cursor.y); - auto img_skip_x = std::max(0, cursor.x); + auto img_skip_y = std::max(0, cursor.y); + auto img_skip_x = std::max(0, cursor.x); auto cursor_img_data = cursor.img_data.data() + cursor_skip_y * pitch; int delta_height = std::min(cursor_height - cursor_truncate_y, std::max(0, img.height - img_skip_y)); - int delta_width = std::min(cursor_width - cursor_truncate_x, std::max(0, img.width - img_skip_x)); + int delta_width = std::min(cursor_width - cursor_truncate_x, std::max(0, img.width - img_skip_x)); auto pixels_per_byte = width / pitch; - auto bytes_per_row = delta_width / pixels_per_byte; + auto bytes_per_row = delta_width / pixels_per_byte; - auto img_data = (int*)img.data; + auto img_data = (int *)img.data; for(int i = 0; i < delta_height; ++i) { auto and_mask = &cursor_img_data[i * pitch]; auto xor_mask = &cursor_img_data[(i + height) * pitch]; @@ -76,8 +76,8 @@ void blend_cursor_monochrome(const cursor_t &cursor, img_t &img) { } void apply_color_alpha(int *img_pixel_p, int cursor_pixel) { - auto colors_out = (std::uint8_t*)&cursor_pixel; - auto colors_in = (std::uint8_t*)img_pixel_p; + auto colors_out = (std::uint8_t *)&cursor_pixel; + auto colors_in = (std::uint8_t *)img_pixel_p; //TODO: When use of IDXGIOutput5 is implemented, support different color formats auto alpha = colors_out[3]; @@ -85,15 +85,15 @@ void apply_color_alpha(int *img_pixel_p, int cursor_pixel) { *img_pixel_p = cursor_pixel; } else { - colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) + 255/2) / 255; - colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255/2) / 255; - colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255/2) / 255; + colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) + 255 / 2) / 255; + colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255 / 2) / 255; + colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255 / 2) / 255; } } void apply_color_masked(int *img_pixel_p, int cursor_pixel) { //TODO: When use of IDXGIOutput5 is implemented, support different color formats - auto alpha = ((std::uint8_t*)&cursor_pixel)[3]; + auto alpha = ((std::uint8_t *)&cursor_pixel)[3]; if(alpha == 0xFF) { *img_pixel_p ^= cursor_pixel; } @@ -111,30 +111,30 @@ void blend_cursor_color(const cursor_t &cursor, img_t &img, const bool masked) { auto cursor_skip_y = -std::min(0, cursor.y); auto cursor_skip_x = -std::min(0, cursor.x); - // img cursor.{x,y} > img.{x,y}, truncate parts of the cursor.img_data + // img cursor.{x,y} > img.{x,y}, truncate parts of the cursor.img_data auto cursor_truncate_y = std::max(0, cursor.y - img.height); auto cursor_truncate_x = std::max(0, cursor.x - img.width); - auto img_skip_y = std::max(0, cursor.y); - auto img_skip_x = std::max(0, cursor.x); + auto img_skip_y = std::max(0, cursor.y); + auto img_skip_x = std::max(0, cursor.x); - auto cursor_width = width - cursor_skip_x - cursor_truncate_x; + auto cursor_width = width - cursor_skip_x - cursor_truncate_x; auto cursor_height = height - cursor_skip_y - cursor_truncate_y; if(cursor_height > height || cursor_width > width) { return; } - auto cursor_img_data = (int*)&cursor.img_data[cursor_skip_y * pitch]; + auto cursor_img_data = (int *)&cursor.img_data[cursor_skip_y * pitch]; int delta_height = std::min(cursor_height - cursor_truncate_y, std::max(0, img.height - img_skip_y)); - int delta_width = std::min(cursor_width - cursor_truncate_x, std::max(0, img.width - img_skip_x)); + int delta_width = std::min(cursor_width - cursor_truncate_x, std::max(0, img.width - img_skip_x)); - auto img_data = (int*)img.data; + auto img_data = (int *)img.data; for(int i = 0; i < delta_height; ++i) { auto cursor_begin = &cursor_img_data[i * cursor.shape_info.Width + cursor_skip_x]; - auto cursor_end = &cursor_begin[delta_width]; + auto cursor_end = &cursor_begin[delta_width]; auto img_pixel_p = &img_data[(i + img_skip_y) * (img.row_pitch / img.pixel_pitch) + img_skip_x]; std::for_each(cursor_begin, cursor_end, [&](int cursor_pixel) { @@ -151,22 +151,22 @@ void blend_cursor_color(const cursor_t &cursor, img_t &img, const bool masked) { void blend_cursor(const cursor_t &cursor, img_t &img) { switch(cursor.shape_info.Type) { - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: - blend_cursor_color(cursor, img, false); - break; - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME: - blend_cursor_monochrome(cursor, img); - break; - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: - blend_cursor_color(cursor, img, true); - break; - default: - BOOST_LOG(warning) << "Unsupported cursor format ["sv << cursor.shape_info.Type << ']'; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: + blend_cursor_color(cursor, img, false); + break; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME: + blend_cursor_monochrome(cursor, img); + break; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: + blend_cursor_color(cursor, img, true); + break; + default: + BOOST_LOG(warning) << "Unsupported cursor format ["sv << cursor.shape_info.Type << ']'; } } capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::milliseconds timeout, bool cursor_visible) { - auto img = (img_t*)img_base; + auto img = (img_t *)img_base; HRESULT status; @@ -174,9 +174,9 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise resource_t::pointer res_p {}; auto capture_status = dup.next_frame(frame_info, timeout, &res_p); - resource_t res{res_p}; + resource_t res { res_p }; - if (capture_status != capture_e::ok) { + if(capture_status != capture_e::ok) { return capture_status; } @@ -187,7 +187,7 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise UINT dummy; status = dup.dup->GetFramePointerShape(img_data.size(), img_data.data(), &dummy, &cursor.shape_info); - if (FAILED(status)) { + if(FAILED(status)) { BOOST_LOG(error) << "Failed to get new pointer shape [0x"sv << util::hex(status).to_string_view() << ']'; return capture_e::error; @@ -195,19 +195,18 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise } if(frame_info.LastMouseUpdateTime.QuadPart) { - cursor.x = frame_info.PointerPosition.Position.x; - cursor.y = frame_info.PointerPosition.Position.y; + cursor.x = frame_info.PointerPosition.Position.x; + cursor.y = frame_info.PointerPosition.Position.y; cursor.visible = frame_info.PointerPosition.Visible; } // If frame has been updated - if (frame_info.LastPresentTime.QuadPart != 0) { + if(frame_info.LastPresentTime.QuadPart != 0) { { - texture2d_t::pointer src_p {}; - status = res->QueryInterface(IID_ID3D11Texture2D, (void **)&src_p); - texture2d_t src{src_p}; + texture2d_t src {}; + status = res->QueryInterface(IID_ID3D11Texture2D, (void **)&src); - if (FAILED(status)) { + if(FAILED(status)) { BOOST_LOG(error) << "Couldn't query interface [0x"sv << util::hex(status).to_string_view() << ']'; return capture_e::error; } @@ -222,14 +221,14 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise } status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, &img_info); - if (FAILED(status)) { + if(FAILED(status)) { BOOST_LOG(error) << "Failed to map texture [0x"sv << util::hex(status).to_string_view() << ']'; return capture_e::error; } } - const bool mouse_update = + const bool mouse_update = (frame_info.LastMouseUpdateTime.QuadPart || frame_info.PointerShapeBufferSize > 0) && (cursor_visible && cursor.visible); @@ -239,7 +238,7 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise return capture_e::timeout; } - std::copy_n((std::uint8_t*)img_info.pData, height * img_info.RowPitch, (std::uint8_t*)img->data); + std::copy_n((std::uint8_t *)img_info.pData, height * img_info.RowPitch, (std::uint8_t *)img->data); if(cursor_visible && cursor.visible) { blend_cursor(cursor, *img); @@ -251,11 +250,11 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise std::shared_ptr display_ram_t::alloc_img() { auto img = std::make_shared(); - img->pixel_pitch = 4; - img->row_pitch = img_info.RowPitch; - img->width = width; - img->height = height; - img->data = new std::uint8_t[img->row_pitch * height]; + img->pixel_pitch = 4; + img->row_pitch = img_info.RowPitch; + img->width = width; + img->height = height; + img->data = new std::uint8_t[img->row_pitch * height]; return img; } @@ -270,19 +269,16 @@ int display_ram_t::init() { } D3D11_TEXTURE2D_DESC t {}; - t.Width = width; - t.Height = height; - t.MipLevels = 1; - t.ArraySize = 1; + t.Width = width; + t.Height = height; + t.MipLevels = 1; + t.ArraySize = 1; t.SampleDesc.Count = 1; - t.Usage = D3D11_USAGE_STAGING; - t.Format = format; - t.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + t.Usage = D3D11_USAGE_STAGING; + t.Format = format; + t.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - dxgi::texture2d_t::pointer tex_p {}; - auto status = device->CreateTexture2D(&t, nullptr, &tex_p); - - texture.reset(tex_p); + auto status = device->CreateTexture2D(&t, nullptr, &texture); if(FAILED(status)) { BOOST_LOG(error) << "Failed to create texture [0x"sv << util::hex(status).to_string_view() << ']'; @@ -298,4 +294,4 @@ int display_ram_t::init() { return 0; } -} +} // namespace platf::dxgi diff --git a/sunshine/platform/windows/display_vram.cpp b/sunshine/platform/windows/display_vram.cpp index 55fce9a4..bb77bbda 100644 --- a/sunshine/platform/windows/display_vram.cpp +++ b/sunshine/platform/windows/display_vram.cpp @@ -1,91 +1,183 @@ -#include "sunshine/main.h" -#include "display.h" +#include +#include + +#include +#include + +extern "C" { +#include +#include +} + +#include "display.h" +#include "sunshine/main.h" +#include "sunshine/video.h" + + +#define SUNSHINE_SHADERS_DIR SUNSHINE_ASSETS_DIR "/shaders/directx" namespace platf { using namespace std::literals; } +static void free_frame(AVFrame *frame) { + av_frame_free(&frame); +} + +using frame_t = util::safe_ptr; + namespace platf::dxgi { +using input_layout_t = util::safe_ptr>; +using render_target_t = util::safe_ptr>; +using shader_res_t = util::safe_ptr>; +using buf_t = util::safe_ptr>; +using blend_t = util::safe_ptr>; +using raster_state_t = util::safe_ptr>; +using sampler_state_t = util::safe_ptr>; +using vs_t = util::safe_ptr>; +using ps_t = util::safe_ptr>; +using blob_t = util::safe_ptr>; +using depth_stencil_state_t = util::safe_ptr>; +using depth_stencil_view_t = util::safe_ptr>; + +template +buf_t make_buffer(device_t::pointer device, const T &t) { + static_assert(sizeof(T) % 16 == 0, "Buffer needs to be aligned on a 16-byte alignment"); + + D3D11_BUFFER_DESC buffer_desc { + sizeof(T), + D3D11_USAGE_IMMUTABLE, + D3D11_BIND_CONSTANT_BUFFER + }; + + D3D11_SUBRESOURCE_DATA init_data { + &t + }; + + buf_t::pointer buf_p; + auto status = device->CreateBuffer(&buffer_desc, &init_data, &buf_p); + if(status) { + BOOST_LOG(error) << "Failed to create buffer: [0x"sv << util::hex(status).to_string_view() << ']'; + return nullptr; + } + + return buf_t { buf_p }; +} + +blend_t make_blend(device_t::pointer device, bool enable) { + D3D11_BLEND_DESC bdesc {}; + auto &rt = bdesc.RenderTarget[0]; + rt.BlendEnable = enable; + rt.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + + if(enable) { + rt.BlendOp = D3D11_BLEND_OP_ADD; + rt.BlendOpAlpha = D3D11_BLEND_OP_ADD; + + rt.SrcBlend = D3D11_BLEND_SRC_ALPHA; + rt.DestBlend = D3D11_BLEND_INV_SRC_ALPHA; + + rt.SrcBlendAlpha = D3D11_BLEND_ZERO; + rt.DestBlendAlpha = D3D11_BLEND_ZERO; + } + + blend_t blend; + auto status = device->CreateBlendState(&bdesc, &blend); + if(status) { + BOOST_LOG(error) << "Failed to create blend state: [0x"sv << util::hex(status).to_string_view() << ']'; + return nullptr; + } + + return blend; +} + +blob_t convert_UV_vs_hlsl; +blob_t convert_UV_ps_hlsl; +blob_t scene_vs_hlsl; +blob_t convert_Y_ps_hlsl; +blob_t scene_ps_hlsl; + struct img_d3d_t : public platf::img_t { - std::shared_ptr display; + shader_res_t input_res; texture2d_t texture; + std::shared_ptr display; ~img_d3d_t() override = default; }; -util::buffer_t make_cursor_image(util::buffer_t &&img_data, DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info) { - constexpr std::uint32_t black = 0xFF000000; - constexpr std::uint32_t white = 0xFFFFFFFF; +util::buffer_t make_cursor_image(util::buffer_t &&img_data, DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info) { + constexpr std::uint32_t black = 0xFF000000; + constexpr std::uint32_t white = 0xFFFFFFFF; constexpr std::uint32_t transparent = 0; switch(shape_info.Type) { - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: - std::for_each((std::uint32_t*)std::begin(img_data), (std::uint32_t*)std::end(img_data), [](auto &pixel) { - if(pixel & 0xFF000000) { - pixel = transparent; - } - }); - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: - return std::move(img_data); - default: - break; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: + std::for_each((std::uint32_t *)std::begin(img_data), (std::uint32_t *)std::end(img_data), [](auto &pixel) { + if(pixel & 0xFF000000) { + pixel = transparent; + } + }); + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: + return std::move(img_data); + default: + break; } shape_info.Height /= 2; util::buffer_t cursor_img { shape_info.Width * shape_info.Height * 4 }; - auto bytes = shape_info.Pitch * shape_info.Height; - auto pixel_begin = (std::uint32_t*)std::begin(cursor_img); - auto pixel_data = pixel_begin; - auto and_mask = std::begin(img_data); - auto xor_mask = std::begin(img_data) + bytes; + auto bytes = shape_info.Pitch * shape_info.Height; + auto pixel_begin = (std::uint32_t *)std::begin(cursor_img); + auto pixel_data = pixel_begin; + auto and_mask = std::begin(img_data); + auto xor_mask = std::begin(img_data) + bytes; - for(auto x = 0; x < bytes; ++x) { + for(auto x = 0; x < bytes; ++x) { for(auto c = 7; c >= 0; --c) { - auto bit = 1 << c; + auto bit = 1 << c; auto color_type = ((*and_mask & bit) ? 1 : 0) + ((*xor_mask & bit) ? 2 : 0); switch(color_type) { - case 0: //black - *pixel_data = black; - break; - case 2: //white - *pixel_data = white; - break; - case 1: //transparent - { - *pixel_data = transparent; + case 0: //black + *pixel_data = black; + break; + case 2: //white + *pixel_data = white; + break; + case 1: //transparent + { + *pixel_data = transparent; - break; + break; + } + case 3: //inverse + { + auto top_p = pixel_data - shape_info.Width; + auto left_p = pixel_data - 1; + auto right_p = pixel_data + 1; + auto bottom_p = pixel_data + shape_info.Width; + + // Get the x coordinate of the pixel + auto column = (pixel_data - pixel_begin) % shape_info.Width != 0; + + if(top_p >= pixel_begin && *top_p == transparent) { + *top_p = black; } - case 3: //inverse - { - auto top_p = pixel_data - shape_info.Width; - auto left_p = pixel_data - 1; - auto right_p = pixel_data + 1; - auto bottom_p = pixel_data + shape_info.Width; - // Get the x coordinate of the pixel - auto column = (pixel_data - pixel_begin) % shape_info.Width != 0; - - if(top_p >= pixel_begin && *top_p == transparent) { - *top_p = black; - } - - if(column != 0 && left_p >= pixel_begin && *left_p == transparent) { - *left_p = black; - } - - if(bottom_p < (std::uint32_t*)std::end(cursor_img)) { - *bottom_p = black; - } - - if(column != shape_info.Width -1) { - *right_p = black; - } - *pixel_data = white; + if(column != 0 && left_p >= pixel_begin && *left_p == transparent) { + *left_p = black; } + + if(bottom_p < (std::uint32_t *)std::end(cursor_img)) { + *bottom_p = black; + } + + if(column != shape_info.Width - 1) { + *right_p = black; + } + *pixel_data = white; + } } ++pixel_data; @@ -97,9 +189,44 @@ util::buffer_t make_cursor_image(util::buffer_t &&im return cursor_img; } +blob_t compile_shader(LPCSTR file, LPCSTR entrypoint, LPCSTR shader_model) { + blob_t::pointer msg_p = nullptr; + blob_t::pointer compiled_p; + + DWORD flags = D3DCOMPILE_ENABLE_STRICTNESS; + +#ifndef NDEBUG + flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; +#endif + std::wstring_convert, wchar_t> converter; + + auto wFile = converter.from_bytes(file); + auto status = D3DCompileFromFile(wFile.c_str(), nullptr, nullptr, entrypoint, shader_model, flags, 0, &compiled_p, &msg_p); + + if(msg_p) { + BOOST_LOG(warning) << std::string_view { (const char *)msg_p->GetBufferPointer(), msg_p->GetBufferSize() - 1 }; + msg_p->Release(); + } + + if(status) { + BOOST_LOG(error) << "Couldn't compile ["sv << file << "] [0x"sv << util::hex(status).to_string_view() << ']'; + return nullptr; + } + + return blob_t { compiled_p }; +} + +blob_t compile_pixel_shader(LPCSTR file) { + return compile_shader(file, "main_ps", "ps_5_0"); +} + +blob_t compile_vertex_shader(LPCSTR file) { + return compile_shader(file, "main_vs", "vs_5_0"); +} + class hwdevice_t : public platf::hwdevice_t { public: - hwdevice_t(std::vector *hwdevices_p) : hwdevices_p { hwdevices_p } {} + hwdevice_t(std::vector *hwdevices_p) : hwdevices_p { hwdevices_p } {} hwdevice_t() = delete; void set_cursor_pos(LONG rel_x, LONG rel_y, bool visible) { @@ -109,183 +236,346 @@ public: return; } - LONG x = ((double)rel_x) * out_width / (double)in_width; - LONG y = ((double)rel_y) * out_height / (double)in_height; + auto x = ((float)rel_x); + auto y = ((float)rel_y); - // Ensure it's within bounds - auto left_out = std::min(out_width, std::max(0, x)); - auto top_out = std::min(out_height, std::max(0, y)); - auto right_out = std::max(0, std::min(out_width, x + cursor_scaled_width)); - auto bottom_out = std::max(0, std::min(out_height, y + cursor_scaled_height)); - - auto left_in = std::max(0, -rel_x); - auto top_in = std::max(0, -rel_y); - auto right_in = std::min(in_width - rel_x, cursor_width); - auto bottom_in = std::min(in_height - rel_y, cursor_height); - - RECT rect_in { left_in, top_in, right_in, bottom_in }; - RECT rect_out { left_out, top_out, right_out, bottom_out }; - - ctx->VideoProcessorSetStreamSourceRect(processor.get(), 1, TRUE, &rect_in); - ctx->VideoProcessorSetStreamDestRect(processor.get(), 1, TRUE, &rect_out); + cursor_view.TopLeftX = x; + cursor_view.TopLeftY = y; } int set_cursor_texture(texture2d_t::pointer texture, LONG width, LONG height) { - D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC input_desc = { 0, (D3D11_VPIV_DIMENSION)D3D11_VPIV_DIMENSION_TEXTURE2D, { 0, 0 } }; + auto device = (device_t::pointer)data; - video::processor_in_t::pointer processor_in_p; - auto status = device->CreateVideoProcessorInputView(texture, processor_e.get(), &input_desc, &processor_in_p); + cursor_view.Width = width; + cursor_view.Height = height; + + D3D11_SHADER_RESOURCE_VIEW_DESC desc { + DXGI_FORMAT_B8G8R8A8_UNORM, + D3D11_SRV_DIMENSION_TEXTURE2D + }; + desc.Texture2D.MipLevels = 1; + + auto status = device->CreateShaderResourceView(texture, &desc, &img.input_res); if(FAILED(status)) { - BOOST_LOG(error) << "Failed to create cursor VideoProcessorInputView [0x"sv << util::hex(status).to_string_view() << ']'; + BOOST_LOG(error) << "Failed to create cursor shader resource view [0x"sv << util::hex(status).to_string_view() << ']'; return -1; } - cursor_in.reset(processor_in_p); - - cursor_width = width; - cursor_height = height; - cursor_scaled_width = ((double)width) / in_width * out_width; - cursor_scaled_height = ((double)height) / in_height * out_height; - return 0; } int convert(platf::img_t &img_base) override { - auto &img = (img_d3d_t&)img_base; + auto &img = (img_d3d_t &)img_base; - auto it = texture_to_processor_in.find(img.texture.get()); - if(it == std::end(texture_to_processor_in)) { - D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC input_desc = { 0, (D3D11_VPIV_DIMENSION)D3D11_VPIV_DIMENSION_TEXTURE2D, { 0, 0 } }; + if(!img.input_res) { + auto device = (device_t::pointer)data; - video::processor_in_t::pointer processor_in_p; - auto status = device->CreateVideoProcessorInputView(img.texture.get(), processor_e.get(), &input_desc, &processor_in_p); + D3D11_SHADER_RESOURCE_VIEW_DESC desc { + DXGI_FORMAT_B8G8R8A8_UNORM, + D3D11_SRV_DIMENSION_TEXTURE2D + }; + desc.Texture2D.MipLevels = 1; + + auto status = device->CreateShaderResourceView(img.texture.get(), &desc, &img.input_res); if(FAILED(status)) { - BOOST_LOG(error) << "Failed to create VideoProcessorInputView [0x"sv << util::hex(status).to_string_view() << ']'; + BOOST_LOG(error) << "Failed to create input shader resource view [0x"sv << util::hex(status).to_string_view() << ']'; return -1; } - it = texture_to_processor_in.emplace(img.texture.get(), processor_in_p).first; } - auto &processor_in = it->second; - D3D11_VIDEO_PROCESSOR_STREAM stream[] { - { TRUE, 0, 0, 0, 0, nullptr, processor_in.get(), nullptr }, - { TRUE, 0, 0, 0, 0, nullptr, cursor_in.get(), nullptr } - }; + auto input_res_p = img.input_res.get(); - auto status = ctx->VideoProcessorBlt(processor.get(), processor_out.get(), 0, cursor_visible ? 2 : 1, stream); - if(FAILED(status)) { - BOOST_LOG(error) << "Failed size and color conversion [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; + if(cursor_visible) { + _init_view_port(img.width, img.height); + + device_ctx_p->OMSetRenderTargets(1, &scene_rt, nullptr); + device_ctx_p->VSSetShader(scene_vs.get(), nullptr, 0); + device_ctx_p->PSSetShader(scene_ps.get(), nullptr, 0); + device_ctx_p->PSSetShaderResources(0, 1, &input_res_p); + + device_ctx_p->Draw(3, 0); + + device_ctx_p->OMSetBlendState(blend_enable.get(), nullptr, 0xFFFFFFFFu); + device_ctx_p->RSSetViewports(1, &cursor_view); + device_ctx_p->PSSetShaderResources(0, 1, &this->img.input_res); + device_ctx_p->Draw(3, 0); + device_ctx_p->OMSetBlendState(blend_disable.get(), nullptr, 0xFFFFFFFFu); + + input_res_p = scene_sr.get(); } + _init_view_port(this->img.width, this->img.height); + device_ctx_p->OMSetRenderTargets(1, &nv12_Y_rt, nullptr); + device_ctx_p->VSSetShader(scene_vs.get(), nullptr, 0); + device_ctx_p->PSSetShader(convert_Y_ps.get(), nullptr, 0); + device_ctx_p->PSSetShaderResources(0, 1, &back_img.input_res); + device_ctx_p->Draw(3, 0); + + device_ctx_p->RSSetViewports(1, &outY_view); + device_ctx_p->PSSetShaderResources(0, 1, &input_res_p); + device_ctx_p->Draw(3, 0); + + // Artifacts start appearing on the rendered image if Sunshine doesn't flush + // before rendering on the UV part of the image. + device_ctx_p->Flush(); + + _init_view_port(this->img.width / 2, this->img.height / 2); + device_ctx_p->OMSetRenderTargets(1, &nv12_UV_rt, nullptr); + device_ctx_p->VSSetShader(convert_UV_vs.get(), nullptr, 0); + device_ctx_p->PSSetShader(convert_UV_ps.get(), nullptr, 0); + device_ctx_p->PSSetShaderResources(0, 1, &back_img.input_res); + device_ctx_p->Draw(3, 0); + + device_ctx_p->RSSetViewports(1, &outUV_view); + device_ctx_p->PSSetShaderResources(0, 1, &input_res_p); + device_ctx_p->Draw(3, 0); + device_ctx_p->Flush(); + return 0; } void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) override { - colorspace |= (color_range >> 4); - ctx->VideoProcessorSetOutputColorSpace(processor.get(), (D3D11_VIDEO_PROCESSOR_COLOR_SPACE*)&colorspace); + switch(colorspace) { + case 5: // SWS_CS_SMPTE170M + color_p = &::video::colors[0]; + break; + case 1: // SWS_CS_ITU709 + color_p = &::video::colors[2]; + break; + case 9: // SWS_CS_BT2020 + default: + BOOST_LOG(warning) << "Colorspace: ["sv << colorspace << "] not yet supported: switching to default"sv; + color_p = &::video::colors[0]; + }; + + if(color_range > 1) { + // Full range + ++color_p; + } + + auto color_matrix = make_buffer((device_t::pointer)data, *color_p); + if(!color_matrix) { + BOOST_LOG(warning) << "Failed to create color matrix"sv; + return; + } + + device_ctx_p->PSSetConstantBuffers(0, 1, &color_matrix); + this->color_matrix = std::move(color_matrix); + } + + int set_frame(AVFrame *frame) { + this->hwframe.reset(frame); + this->frame = frame; + + auto device_p = (device_t::pointer)data; + + auto out_width = frame->width; + auto out_height = frame->height; + + float in_width = img.display->width; + float in_height = img.display->height; + + // // Ensure aspect ratio is maintained + auto scalar = std::fminf(out_width / in_width, out_height / in_height); + auto out_width_f = in_width * scalar; + auto out_height_f = in_height * scalar; + + // result is always positive + auto offsetX = (out_width - out_width_f) / 2; + auto offsetY = (out_height - out_height_f) / 2; + + outY_view = D3D11_VIEWPORT { offsetX, offsetY, out_width_f, out_height_f, 0.0f, 1.0f }; + outUV_view = D3D11_VIEWPORT { offsetX / 2, offsetY / 2, out_width_f / 2, out_height_f / 2, 0.0f, 1.0f }; + + D3D11_TEXTURE2D_DESC t {}; + t.Width = out_width; + t.Height = out_height; + t.MipLevels = 1; + t.ArraySize = 1; + t.SampleDesc.Count = 1; + t.Usage = D3D11_USAGE_DEFAULT; + t.Format = format; + t.BindFlags = D3D11_BIND_RENDER_TARGET; + + auto status = device_p->CreateTexture2D(&t, nullptr, &img.texture); + if(FAILED(status)) { + BOOST_LOG(error) << "Failed to create render target texture [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + img.width = out_width; + img.height = out_height; + img.data = (std::uint8_t *)img.texture.get(); + img.row_pitch = out_width; + img.pixel_pitch = 1; + + float info_in[16 / sizeof(float)] { 1.0f / (float)out_width }; //aligned to 16-byte + info_scene = make_buffer(device_p, info_in); + + if(!info_in) { + BOOST_LOG(error) << "Failed to create info scene buffer"sv; + return -1; + } + + D3D11_RENDER_TARGET_VIEW_DESC nv12_rt_desc { + DXGI_FORMAT_R8_UNORM, + D3D11_RTV_DIMENSION_TEXTURE2D + }; + + status = device_p->CreateRenderTargetView(img.texture.get(), &nv12_rt_desc, &nv12_Y_rt); + if(FAILED(status)) { + BOOST_LOG(error) << "Failed to create render target view [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + nv12_rt_desc.Format = DXGI_FORMAT_R8G8_UNORM; + status = device_p->CreateRenderTargetView(img.texture.get(), &nv12_rt_desc, &nv12_UV_rt); + if(FAILED(status)) { + BOOST_LOG(error) << "Failed to create render target view [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + // Need to have something refcounted + if(!frame->buf[0]) { + frame->buf[0] = av_buffer_allocz(sizeof(AVD3D11FrameDescriptor)); + } + + auto desc = (AVD3D11FrameDescriptor *)frame->buf[0]->data; + desc->texture = (ID3D11Texture2D *)img.data; + desc->index = 0; + + frame->data[0] = img.data; + frame->data[1] = 0; + + frame->linesize[0] = img.row_pitch; + + frame->height = img.height; + frame->width = img.width; + + return 0; } int init( std::shared_ptr display, device_t::pointer device_p, device_ctx_t::pointer device_ctx_p, - int in_width, int in_height, int out_width, int out_height, - pix_fmt_e pix_fmt - ) { + pix_fmt_e pix_fmt) { + HRESULT status; - cursor_visible = false; - - platf::hwdevice_t::img = &img; - - this->out_width = out_width; - this->out_height = out_height; - this->in_width = in_width; - this->in_height = in_height; - - video::device_t::pointer vdevice_p; - status = device_p->QueryInterface(IID_ID3D11VideoDevice, (void**)&vdevice_p); - if(FAILED(status)) { - BOOST_LOG(error) << "Failed to query ID3D11VideoDevice interface [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - device.reset(vdevice_p); - - video::ctx_t::pointer ctx_p; - status = device_ctx_p->QueryInterface(IID_ID3D11VideoContext, (void**)&ctx_p); - if(FAILED(status)) { - BOOST_LOG(error) << "Failed to query ID3D11VideoContext interface [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - ctx.reset(ctx_p); - - D3D11_VIDEO_PROCESSOR_CONTENT_DESC contentDesc { - D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE, - { 1, 1 }, (UINT)in_width, (UINT)in_height, - { 1, 1 }, (UINT)out_width, (UINT)out_height, - D3D11_VIDEO_USAGE_OPTIMAL_QUALITY - }; - - video::processor_enum_t::pointer vp_e_p; - status = device->CreateVideoProcessorEnumerator(&contentDesc, &vp_e_p); - if(FAILED(status)) { - BOOST_LOG(error) << "Failed to create video processor enumerator [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - processor_e.reset(vp_e_p); - - video::processor_t::pointer processor_p; - status = device->CreateVideoProcessor(processor_e.get(), 0, &processor_p); - if(FAILED(status)) { - BOOST_LOG(error) << "Failed to create video processor [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - processor.reset(processor_p); - - D3D11_TEXTURE2D_DESC t {}; - t.Width = out_width; - t.Height = out_height; - t.MipLevels = 1; - t.ArraySize = 1; - t.SampleDesc.Count = 1; - t.Usage = D3D11_USAGE_DEFAULT; - t.Format = pix_fmt == pix_fmt_e::nv12 ? DXGI_FORMAT_NV12 : DXGI_FORMAT_P010; - t.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_VIDEO_ENCODER; - - dxgi::texture2d_t::pointer tex_p {}; - status = device_p->CreateTexture2D(&t, nullptr, &tex_p); - if(FAILED(status)) { - BOOST_LOG(error) << "Failed to create video output texture [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - - img.texture.reset(tex_p); - img.display = std::move(display); - img.width = out_width; - img.height = out_height; - img.data = (std::uint8_t*)tex_p; - img.row_pitch = out_width; - img.pixel_pitch = 1; - - D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC output_desc { D3D11_VPOV_DIMENSION_TEXTURE2D, 0 }; - video::processor_out_t::pointer processor_out_p; - status = device->CreateVideoProcessorOutputView(img.texture.get(), processor_e.get(), &output_desc, &processor_out_p); - if(FAILED(status)) { - BOOST_LOG(error) << "Failed to create VideoProcessorOutputView [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - processor_out.reset(processor_out_p); - - // Tell video processor alpha values need to be enabled - ctx->VideoProcessorSetStreamAlpha(processor.get(), 1, TRUE, 1.0f); - device_p->AddRef(); data = device_p; + + this->device_ctx_p = device_ctx_p; + + cursor_visible = false; + cursor_view.MinDepth = 0.0f; + cursor_view.MaxDepth = 1.0f; + + format = (pix_fmt == pix_fmt_e::nv12 ? DXGI_FORMAT_NV12 : DXGI_FORMAT_P010); + status = device_p->CreateVertexShader(scene_vs_hlsl->GetBufferPointer(), scene_vs_hlsl->GetBufferSize(), nullptr, &scene_vs); + if(status) { + BOOST_LOG(error) << "Failed to create scene vertex shader [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + status = device_p->CreatePixelShader(convert_Y_ps_hlsl->GetBufferPointer(), convert_Y_ps_hlsl->GetBufferSize(), nullptr, &convert_Y_ps); + if(status) { + BOOST_LOG(error) << "Failed to create convertY pixel shader [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + status = device_p->CreatePixelShader(convert_UV_ps_hlsl->GetBufferPointer(), convert_UV_ps_hlsl->GetBufferSize(), nullptr, &convert_UV_ps); + if(status) { + BOOST_LOG(error) << "Failed to create convertUV pixel shader [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + status = device_p->CreateVertexShader(convert_UV_vs_hlsl->GetBufferPointer(), convert_UV_vs_hlsl->GetBufferSize(), nullptr, &convert_UV_vs); + if(status) { + BOOST_LOG(error) << "Failed to create convertUV vertex shader [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + status = device_p->CreatePixelShader(scene_ps_hlsl->GetBufferPointer(), scene_ps_hlsl->GetBufferSize(), nullptr, &scene_ps); + if(status) { + BOOST_LOG(error) << "Failed to create scene pixel shader [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + blend_disable = make_blend(device_p, false); + blend_enable = make_blend(device_p, true); + + if(!blend_disable || !blend_enable) { + return -1; + } + + if(_init_rt(scene_sr, scene_rt, display->width, display->height, DXGI_FORMAT_B8G8R8A8_UNORM)) { + return -1; + } + + color_matrix = make_buffer(device_p, ::video::colors[0]); + if(!color_matrix) { + BOOST_LOG(error) << "Failed to create color matrix buffer"sv; + return -1; + } + + D3D11_INPUT_ELEMENT_DESC layout_desc { + "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 + }; + + status = device_p->CreateInputLayout( + &layout_desc, 1, + convert_UV_vs_hlsl->GetBufferPointer(), convert_UV_vs_hlsl->GetBufferSize(), + &input_layout); + + img.display = std::move(display); + + D3D11_SAMPLER_DESC sampler_desc {}; + sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; + sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; + sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + sampler_desc.ComparisonFunc = D3D11_COMPARISON_NEVER; + sampler_desc.MinLOD = 0; + sampler_desc.MaxLOD = D3D11_FLOAT32_MAX; + + status = device_p->CreateSamplerState(&sampler_desc, &sampler_linear); + if(FAILED(status)) { + BOOST_LOG(error) << "Failed to create point sampler state [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + // Color the background black, so that the padding for keeping the aspect ratio + // is black + if(img.display->dummy_img(&back_img)) { + BOOST_LOG(warning) << "Couldn't create an image to set background color to black"sv; + return -1; + } + + D3D11_SHADER_RESOURCE_VIEW_DESC desc { + DXGI_FORMAT_B8G8R8A8_UNORM, + D3D11_SRV_DIMENSION_TEXTURE2D + }; + desc.Texture2D.MipLevels = 1; + + status = device_p->CreateShaderResourceView(back_img.texture.get(), &desc, &back_img.input_res); + if(FAILED(status)) { + BOOST_LOG(error) << "Failed to create input shader resource view [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + device_ctx_p->OMSetBlendState(blend_disable.get(), nullptr, 0xFFFFFFFFu); + device_ctx_p->PSSetSamplers(0, 1, &sampler_linear); + device_ctx_p->PSSetConstantBuffers(0, 1, &color_matrix); + device_ctx_p->VSSetConstantBuffers(0, 1, &info_scene); + device_ctx_p->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + device_ctx_p->IASetInputLayout(input_layout.get()); + return 0; } ~hwdevice_t() override { if(data) { - ((ID3D11Device*)data)->Release(); + ((ID3D11Device *)data)->Release(); } auto it = std::find(std::begin(*hwdevices_p), std::end(*hwdevices_p), this); @@ -294,29 +584,117 @@ public: } } +private: + void _init_view_port(float x, float y, float width, float height) { + D3D11_VIEWPORT view { + x, y, + width, height, + 0.0f, 1.0f + }; + + device_ctx_p->RSSetViewports(1, &view); + } + + void _init_view_port(float width, float height) { + _init_view_port(0.0f, 0.0f, width, height); + } + + int _init_rt(shader_res_t &shader_res, render_target_t &render_target, int width, int height, DXGI_FORMAT format) { + D3D11_TEXTURE2D_DESC desc {}; + + desc.Width = width; + desc.Height = height; + desc.Format = format; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.SampleDesc.Count = 1; + + auto device = (device_t::pointer)data; + + texture2d_t tex; + auto status = device->CreateTexture2D(&desc, nullptr, &tex); + if(status) { + BOOST_LOG(error) << "Failed to create render target texture for luma [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + + D3D11_SHADER_RESOURCE_VIEW_DESC shader_resource_desc { + format, + D3D11_SRV_DIMENSION_TEXTURE2D + }; + shader_resource_desc.Texture2D.MipLevels = 1; + + device->CreateShaderResourceView(tex.get(), &shader_resource_desc, &shader_res); + if(status) { + BOOST_LOG(error) << "Failed to create render target texture for luma [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + D3D11_RENDER_TARGET_VIEW_DESC render_target_desc { + format, + D3D11_RTV_DIMENSION_TEXTURE2D + }; + + device->CreateRenderTargetView(tex.get(), &render_target_desc, &render_target); + if(status) { + BOOST_LOG(error) << "Failed to create render target view [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + return 0; + } + +public: + frame_t hwframe; + + ::video::color_t *color_p; + + blend_t blend_enable; + blend_t blend_disable; + + buf_t info_scene; + buf_t color_matrix; + + sampler_state_t sampler_linear; + + input_layout_t input_layout; + + render_target_t nv12_Y_rt; + render_target_t nv12_UV_rt; + + render_target_t scene_rt; + shader_res_t scene_sr; + img_d3d_t img; - video::device_t device; - video::ctx_t ctx; - video::processor_enum_t processor_e; - video::processor_t processor; - video::processor_out_t processor_out; - std::unordered_map texture_to_processor_in; - video::processor_in_t cursor_in; + // Clear nv12 render target to black + img_d3d_t back_img; + vs_t convert_UV_vs; + ps_t convert_UV_ps; + ps_t convert_Y_ps; + ps_t scene_ps; + vs_t scene_vs; + + D3D11_VIEWPORT outY_view; + D3D11_VIEWPORT outUV_view; + + D3D11_VIEWPORT cursor_view; bool cursor_visible; - LONG cursor_width, cursor_height; - LONG cursor_scaled_width, cursor_scaled_height; + DXGI_FORMAT format; - LONG in_width, in_height; - double out_width, out_height; + device_ctx_t::pointer device_ctx_p; - std::vector *hwdevices_p; + // The destructor will remove itself from the list of hardware devices, this is done synchronously + std::vector *hwdevices_p; }; capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::milliseconds timeout, bool cursor_visible) { - auto img = (img_d3d_t*)img_base; + auto img = (img_d3d_t *)img_base; HRESULT status; @@ -324,7 +702,7 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec resource_t::pointer res_p {}; auto capture_status = dup.next_frame(frame_info, timeout, &res_p); - resource_t res{ res_p }; + resource_t res { res_p }; if(capture_status != capture_e::ok) { return capture_status; @@ -332,7 +710,7 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec const bool mouse_update_flag = frame_info.LastMouseUpdateTime.QuadPart != 0 || frame_info.PointerShapeBufferSize > 0; const bool frame_update_flag = frame_info.AccumulatedFrames != 0 || frame_info.LastPresentTime.QuadPart != 0; - const bool update_flag = mouse_update_flag || frame_update_flag; + const bool update_flag = mouse_update_flag || frame_update_flag; if(!update_flag) { return capture_e::timeout; @@ -345,7 +723,7 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec UINT dummy; status = dup.dup->GetFramePointerShape(img_data.size(), std::begin(img_data), &dummy, &shape_info); - if (FAILED(status)) { + if(FAILED(status)) { BOOST_LOG(error) << "Failed to get new pointer shape [0x"sv << util::hex(status).to_string_view() << ']'; return capture_e::error; @@ -361,25 +739,24 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec // Create texture for cursor D3D11_TEXTURE2D_DESC t {}; - t.Width = shape_info.Width; - t.Height = cursor_img.size() / data.SysMemPitch; - t.MipLevels = 1; - t.ArraySize = 1; + t.Width = shape_info.Width; + t.Height = cursor_img.size() / data.SysMemPitch; + t.MipLevels = 1; + t.ArraySize = 1; t.SampleDesc.Count = 1; - t.Usage = D3D11_USAGE_DEFAULT; - t.Format = DXGI_FORMAT_B8G8R8A8_UNORM; - t.BindFlags = D3D11_BIND_RENDER_TARGET; + t.Usage = D3D11_USAGE_DEFAULT; + t.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + t.BindFlags = D3D11_BIND_SHADER_RESOURCE; - dxgi::texture2d_t::pointer tex_p {}; - auto status = device->CreateTexture2D(&t, &data, &tex_p); + texture2d_t texture; + auto status = device->CreateTexture2D(&t, &data, &texture); if(FAILED(status)) { - BOOST_LOG(error) << "Failed to create dummy texture [0x"sv << util::hex(status).to_string_view() << ']'; + BOOST_LOG(error) << "Failed to create mouse texture [0x"sv << util::hex(status).to_string_view() << ']'; return capture_e::error; } - texture2d_t texture { tex_p }; for(auto *hwdevice : hwdevices) { - if(hwdevice->set_cursor_texture(tex_p, t.Width, t.Height)) { + if(hwdevice->set_cursor_texture(texture.get(), t.Width, t.Height)) { return capture_e::error; } } @@ -396,15 +773,14 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec } if(frame_update_flag) { - texture2d_t::pointer src_p {}; - status = res->QueryInterface(IID_ID3D11Texture2D, (void **)&src_p); + texture2d_t src; + status = res->QueryInterface(IID_ID3D11Texture2D, (void **)&src); if(FAILED(status)) { BOOST_LOG(error) << "Couldn't query interface [0x"sv << util::hex(status).to_string_view() << ']'; return capture_e::error; } - texture2d_t src { src_p }; device_ctx->CopyResource(img->texture.get(), src.get()); } @@ -415,63 +791,61 @@ std::shared_ptr display_vram_t::alloc_img() { auto img = std::make_shared(); D3D11_TEXTURE2D_DESC t {}; - t.Width = width; - t.Height = height; - t.MipLevels = 1; - t.ArraySize = 1; + t.Width = width; + t.Height = height; + t.MipLevels = 1; + t.ArraySize = 1; t.SampleDesc.Count = 1; - t.Usage = D3D11_USAGE_DEFAULT; - t.Format = format; - t.BindFlags = D3D11_BIND_RENDER_TARGET; + t.Usage = D3D11_USAGE_DEFAULT; + t.Format = format; + t.BindFlags = D3D11_BIND_SHADER_RESOURCE; - dxgi::texture2d_t::pointer tex_p {}; - auto status = device->CreateTexture2D(&t, nullptr, &tex_p); + auto status = device->CreateTexture2D(&t, nullptr, &img->texture); if(FAILED(status)) { BOOST_LOG(error) << "Failed to create img buf texture [0x"sv << util::hex(status).to_string_view() << ']'; return nullptr; } - img->data = (std::uint8_t*)tex_p; + img->data = (std::uint8_t *)img->texture.get(); img->row_pitch = 0; img->pixel_pitch = 4; img->width = 0; img->height = 0; - img->texture.reset(tex_p); img->display = shared_from_this(); return img; } int display_vram_t::dummy_img(platf::img_t *img_base) { - auto img = (img_d3d_t*)img_base; + auto img = (img_d3d_t *)img_base; - img->row_pitch = width * 4; + img->row_pitch = width * 4; auto dummy_data = std::make_unique(width * height); D3D11_SUBRESOURCE_DATA data { dummy_data.get(), - (UINT)img->row_pitch, - 0 + (UINT)img->row_pitch }; + std::fill_n(dummy_data.get(), width * height, 0); D3D11_TEXTURE2D_DESC t {}; - t.Width = width; - t.Height = height; - t.MipLevels = 1; - t.ArraySize = 1; + t.Width = width; + t.Height = height; + t.MipLevels = 1; + t.ArraySize = 1; t.SampleDesc.Count = 1; - t.Usage = D3D11_USAGE_DEFAULT; - t.Format = format; - t.BindFlags = D3D11_BIND_RENDER_TARGET; + t.Usage = D3D11_USAGE_DEFAULT; + t.Format = format; + t.BindFlags = D3D11_BIND_SHADER_RESOURCE; - dxgi::texture2d_t::pointer tex_p {}; - auto status = device->CreateTexture2D(&t, &data, &tex_p); + dxgi::texture2d_t tex; + auto status = device->CreateTexture2D(&t, &data, &tex); if(FAILED(status)) { BOOST_LOG(error) << "Failed to create dummy texture [0x"sv << util::hex(status).to_string_view() << ']'; return -1; } - img->data = (std::uint8_t*)tex_p; - img->texture.reset(tex_p); + img->texture = std::move(tex); + img->data = (std::uint8_t *)img->texture.get(); img->height = height; img->width = width; img->pixel_pitch = 4; @@ -479,9 +853,9 @@ int display_vram_t::dummy_img(platf::img_t *img_base) { return 0; } -std::shared_ptr display_vram_t::make_hwdevice(int width, int height, pix_fmt_e pix_fmt) { - if(pix_fmt != platf::pix_fmt_e::nv12 && pix_fmt != platf::pix_fmt_e::p010) { - BOOST_LOG(error) << "display_vram_t doesn't support pixel format ["sv << (int)pix_fmt << ']'; +std::shared_ptr display_vram_t::make_hwdevice(pix_fmt_e pix_fmt) { + if(pix_fmt != platf::pix_fmt_e::nv12) { + BOOST_LOG(error) << "display_vram_t doesn't support pixel format ["sv << from_pix_fmt(pix_fmt) << ']'; return nullptr; } @@ -492,8 +866,6 @@ std::shared_ptr display_vram_t::make_hwdevice(int width, int shared_from_this(), device.get(), device_ctx.get(), - this->width, this->height, - width, height, pix_fmt); if(ret) { @@ -508,4 +880,35 @@ std::shared_ptr display_vram_t::make_hwdevice(int width, int return hwdevice; } -} \ No newline at end of file + +int init() { + BOOST_LOG(info) << "Compiling shaders..."sv; + scene_vs_hlsl = compile_vertex_shader(SUNSHINE_SHADERS_DIR "/SceneVS.hlsl"); + if(!scene_vs_hlsl) { + return -1; + } + + convert_Y_ps_hlsl = compile_pixel_shader(SUNSHINE_SHADERS_DIR "/ConvertYPS.hlsl"); + if(!convert_Y_ps_hlsl) { + return -1; + } + + convert_UV_ps_hlsl = compile_pixel_shader(SUNSHINE_SHADERS_DIR "/ConvertUVPS.hlsl"); + if(!convert_UV_ps_hlsl) { + return -1; + } + + convert_UV_vs_hlsl = compile_vertex_shader(SUNSHINE_SHADERS_DIR "/ConvertUVVS.hlsl"); + if(!convert_UV_vs_hlsl) { + return -1; + } + + scene_ps_hlsl = compile_pixel_shader(SUNSHINE_SHADERS_DIR "/ScenePS.hlsl"); + if(!scene_ps_hlsl) { + return -1; + } + BOOST_LOG(info) << "Compiled shaders"sv; + + return 0; +} +} // namespace platf::dxgi \ No newline at end of file diff --git a/sunshine/platform/windows/input.cpp b/sunshine/platform/windows/input.cpp index 60120738..9eb6667b 100755 --- a/sunshine/platform/windows/input.cpp +++ b/sunshine/platform/windows/input.cpp @@ -1,11 +1,6 @@ -#include -#include - -#include -#include #include -#include -#include + +#include #include @@ -15,7 +10,13 @@ namespace platf { using namespace std::literals; -using adapteraddrs_t = util::c_ptr; +volatile HDESK _lastKnownInputDesktop = NULL; +constexpr touch_port_t target_touch_port { + 0, 0, + 65535, 65535 +}; + +HDESK pairInputDesktop(); class vigem_t { public: @@ -86,75 +87,10 @@ public: client_t client; }; -std::string from_sockaddr(const sockaddr *const socket_address) { - char data[INET6_ADDRSTRLEN]; - - auto family = socket_address->sa_family; - if(family == AF_INET6) { - inet_ntop(AF_INET6, &((sockaddr_in6*)socket_address)->sin6_addr, data, INET6_ADDRSTRLEN); - } - - if(family == AF_INET) { - inet_ntop(AF_INET, &((sockaddr_in*)socket_address)->sin_addr, data, INET_ADDRSTRLEN); - } - - return std::string { data }; -} - -std::pair from_sockaddr_ex(const sockaddr *const ip_addr) { - char data[INET6_ADDRSTRLEN]; - - auto family = ip_addr->sa_family; - std::uint16_t port; - if(family == AF_INET6) { - inet_ntop(AF_INET6, &((sockaddr_in6*)ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN); - port = ((sockaddr_in6*)ip_addr)->sin6_port; - } - - if(family == AF_INET) { - inet_ntop(AF_INET, &((sockaddr_in*)ip_addr)->sin_addr, data, INET_ADDRSTRLEN); - port = ((sockaddr_in*)ip_addr)->sin_port; - } - - return { port, std::string { data } }; -} - -adapteraddrs_t get_adapteraddrs() { - adapteraddrs_t info { nullptr }; - ULONG size = 0; - - while(GetAdaptersAddresses(AF_UNSPEC, 0, nullptr, info.get(), &size) == ERROR_BUFFER_OVERFLOW) { - info.reset((PIP_ADAPTER_ADDRESSES)malloc(size)); - } - - return info; -} - -std::string get_mac_address(const std::string_view &address) { - adapteraddrs_t info = get_adapteraddrs(); - for(auto adapter_pos = info.get(); adapter_pos != nullptr; adapter_pos = adapter_pos->Next) { - for(auto addr_pos = adapter_pos->FirstUnicastAddress; addr_pos != nullptr; addr_pos = addr_pos->Next) { - if(adapter_pos->PhysicalAddressLength != 0 && address == from_sockaddr(addr_pos->Address.lpSockaddr)) { - std::stringstream mac_addr; - mac_addr << std::hex; - for(int i = 0; i < adapter_pos->PhysicalAddressLength; i++) { - if(i > 0) { - mac_addr << ':'; - } - mac_addr << std::setw(2) << std::setfill('0') << (int)adapter_pos->PhysicalAddress[i]; - } - return mac_addr.str(); - } - } - } - BOOST_LOG(warning) << "Unable to find MAC address for "sv << address; - return "00:00:00:00:00:00"s; -} - input_t input() { input_t result { new vigem_t {} }; - auto vigem = (vigem_t*)result.get(); + auto vigem = (vigem_t *)result.get(); if(vigem->init()) { return nullptr; } @@ -162,55 +98,86 @@ input_t input() { return result; } +void send_input(INPUT &i) { +retry: + auto send = SendInput(1, &i, sizeof(INPUT)); + if(send != 1) { + auto hDesk = pairInputDesktop(); + if(_lastKnownInputDesktop != hDesk) { + _lastKnownInputDesktop = hDesk; + goto retry; + } + BOOST_LOG(warning) << "Couldn't send input"sv; + } +} +void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) { + INPUT i {}; + + i.type = INPUT_MOUSE; + auto &mi = i.mi; + + mi.dwFlags = + MOUSEEVENTF_MOVE | + MOUSEEVENTF_ABSOLUTE | + + // MOUSEEVENTF_VIRTUALDESK maps to the entirety of the desktop rather than the primary desktop + MOUSEEVENTF_VIRTUALDESK; + + auto scaled_x = std::lround((x + touch_port.offset_x) * ((float)target_touch_port.width / (float)touch_port.width)); + auto scaled_y = std::lround((y + touch_port.offset_y) * ((float)target_touch_port.height / (float)touch_port.height)); + + mi.dx = scaled_x; + mi.dy = scaled_y; + + send_input(i); +} + void move_mouse(input_t &input, int deltaX, int deltaY) { INPUT i {}; - i.type = INPUT_MOUSE; + i.type = INPUT_MOUSE; auto &mi = i.mi; mi.dwFlags = MOUSEEVENTF_MOVE; - mi.dx = deltaX; - mi.dy = deltaY; + mi.dx = deltaX; + mi.dy = deltaY; - auto send = SendInput(1, &i, sizeof(INPUT)); - if(send != 1) { - BOOST_LOG(warning) << "Couldn't send mouse movement input"sv; - } + send_input(i); } void button_mouse(input_t &input, int button, bool release) { - constexpr SHORT KEY_STATE_DOWN = 0x8000; + constexpr auto KEY_STATE_DOWN = (SHORT)0x8000; INPUT i {}; - i.type = INPUT_MOUSE; + i.type = INPUT_MOUSE; auto &mi = i.mi; int mouse_button; if(button == 1) { - mi.dwFlags = release ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_LEFTDOWN; + mi.dwFlags = release ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_LEFTDOWN; mouse_button = VK_LBUTTON; } else if(button == 2) { - mi.dwFlags = release ? MOUSEEVENTF_MIDDLEUP : MOUSEEVENTF_MIDDLEDOWN; + mi.dwFlags = release ? MOUSEEVENTF_MIDDLEUP : MOUSEEVENTF_MIDDLEDOWN; mouse_button = VK_MBUTTON; } else if(button == 3) { - mi.dwFlags = release ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_RIGHTDOWN; + mi.dwFlags = release ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_RIGHTDOWN; mouse_button = VK_RBUTTON; } else if(button == 4) { - mi.dwFlags = release ? MOUSEEVENTF_XUP : MOUSEEVENTF_XDOWN; + mi.dwFlags = release ? MOUSEEVENTF_XUP : MOUSEEVENTF_XDOWN; mi.mouseData = XBUTTON1; mouse_button = VK_XBUTTON1; } else { - mi.dwFlags = release ? MOUSEEVENTF_XUP : MOUSEEVENTF_XDOWN; + mi.dwFlags = release ? MOUSEEVENTF_XUP : MOUSEEVENTF_XDOWN; mi.mouseData = XBUTTON2; mouse_button = VK_XBUTTON2; } - auto key_state = GetAsyncKeyState(mouse_button); + auto key_state = GetAsyncKeyState(mouse_button); bool key_state_down = (key_state & KEY_STATE_DOWN) != 0; if(key_state_down != release) { BOOST_LOG(warning) << "Button state of mouse_button ["sv << button << "] does not match the desired state"sv; @@ -218,25 +185,19 @@ void button_mouse(input_t &input, int button, bool release) { return; } - auto send = SendInput(1, &i, sizeof(INPUT)); - if(send != 1) { - BOOST_LOG(warning) << "Couldn't send mouse button input"sv; - } + send_input(i); } void scroll(input_t &input, int distance) { INPUT i {}; - i.type = INPUT_MOUSE; + i.type = INPUT_MOUSE; auto &mi = i.mi; - mi.dwFlags = MOUSEEVENTF_WHEEL; + mi.dwFlags = MOUSEEVENTF_WHEEL; mi.mouseData = distance; - auto send = SendInput(1, &i, sizeof(INPUT)); - if(send != 1) { - BOOST_LOG(warning) << "Couldn't send moue movement input"sv; - } + send_input(i); } void keyboard(input_t &input, uint16_t modcode, bool release) { @@ -245,12 +206,12 @@ void keyboard(input_t &input, uint16_t modcode, bool release) { } INPUT i {}; - i.type = INPUT_KEYBOARD; + i.type = INPUT_KEYBOARD; auto &ki = i.ki; // For some reason, MapVirtualKey(VK_LWIN, MAPVK_VK_TO_VSC) doesn't seem to work :/ if(modcode != VK_LWIN && modcode != VK_RWIN && modcode != VK_PAUSE) { - ki.wScan = MapVirtualKey(modcode, MAPVK_VK_TO_VSC); + ki.wScan = MapVirtualKey(modcode, MAPVK_VK_TO_VSC); ki.dwFlags = KEYEVENTF_SCANCODE; } else { @@ -259,33 +220,30 @@ void keyboard(input_t &input, uint16_t modcode, bool release) { // https://docs.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input#keystroke-message-flags switch(modcode) { - case VK_RMENU: - case VK_RCONTROL: - case VK_INSERT: - case VK_DELETE: - case VK_HOME: - case VK_END: - case VK_PRIOR: - case VK_NEXT: - case VK_UP: - case VK_DOWN: - case VK_LEFT: - case VK_RIGHT: - case VK_DIVIDE: - ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; - break; - default: - break; + case VK_RMENU: + case VK_RCONTROL: + case VK_INSERT: + case VK_DELETE: + case VK_HOME: + case VK_END: + case VK_PRIOR: + case VK_NEXT: + case VK_UP: + case VK_DOWN: + case VK_LEFT: + case VK_RIGHT: + case VK_DIVIDE: + ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; + break; + default: + break; } if(release) { ki.dwFlags |= KEYEVENTF_KEYUP; } - auto send = SendInput(1, &i, sizeof(INPUT)); - if(send != 1) { - BOOST_LOG(warning) << "Couldn't send moue movement input"sv; - } + send_input(i); } int alloc_gamepad(input_t &input, int nr) { @@ -293,7 +251,7 @@ int alloc_gamepad(input_t &input, int nr) { return 0; } - return ((vigem_t*)input.get())->alloc_x360(nr); + return ((vigem_t *)input.get())->alloc_x360(nr); } void free_gamepad(input_t &input, int nr) { @@ -301,7 +259,7 @@ void free_gamepad(input_t &input, int nr) { return; } - ((vigem_t*)input.get())->free_target(nr); + ((vigem_t *)input.get())->free_target(nr); } void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) { // If there is no gamepad support @@ -309,7 +267,7 @@ void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) { return; } - auto vigem = (vigem_t*)input.get(); + auto vigem = (vigem_t *)input.get(); auto &xusb = *(PXUSB_REPORT)&gamepad_state; auto &x360 = vigem->x360s[nr]; @@ -323,13 +281,32 @@ void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) { } } -int thread_priority() { +int thread_priority() { return SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST) ? 0 : 1; } +HDESK pairInputDesktop() { + auto hDesk = OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, FALSE, GENERIC_ALL); + if(NULL == hDesk) { + auto err = GetLastError(); + BOOST_LOG(error) << "Failed to OpenInputDesktop [0x"sv << util::hex(err).to_string_view() << ']'; + } + else { + BOOST_LOG(info) << std::endl + << "Opened desktop [0x"sv << util::hex(hDesk).to_string_view() << ']'; + if(!SetThreadDesktop(hDesk)) { + auto err = GetLastError(); + BOOST_LOG(error) << "Failed to SetThreadDesktop [0x"sv << util::hex(err).to_string_view() << ']'; + } + CloseDesktop(hDesk); + } + + return hDesk; +} + void freeInput(void *p) { - auto vigem = (vigem_t*)p; + auto vigem = (vigem_t *)p; delete vigem; } -} +} // namespace platf diff --git a/sunshine/platform/windows/misc.cpp b/sunshine/platform/windows/misc.cpp new file mode 100644 index 00000000..90a59689 --- /dev/null +++ b/sunshine/platform/windows/misc.cpp @@ -0,0 +1,90 @@ +#include +#include +#include + + +// prevent clang format from "optimizing" the header include order +// clang-format off +#include +#include +#include +#include +#include +// clang-format on + +#include "sunshine/main.h" +#include "sunshine/utility.h" + +using namespace std::literals; +namespace platf { +using adapteraddrs_t = util::c_ptr; + +std::filesystem::path appdata() { + return L"."sv; +} + +std::string from_sockaddr(const sockaddr *const socket_address) { + char data[INET6_ADDRSTRLEN]; + + auto family = socket_address->sa_family; + if(family == AF_INET6) { + inet_ntop(AF_INET6, &((sockaddr_in6 *)socket_address)->sin6_addr, data, INET6_ADDRSTRLEN); + } + + if(family == AF_INET) { + inet_ntop(AF_INET, &((sockaddr_in *)socket_address)->sin_addr, data, INET_ADDRSTRLEN); + } + + return std::string { data }; +} + +std::pair from_sockaddr_ex(const sockaddr *const ip_addr) { + char data[INET6_ADDRSTRLEN]; + + auto family = ip_addr->sa_family; + std::uint16_t port; + if(family == AF_INET6) { + inet_ntop(AF_INET6, &((sockaddr_in6 *)ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN); + port = ((sockaddr_in6 *)ip_addr)->sin6_port; + } + + if(family == AF_INET) { + inet_ntop(AF_INET, &((sockaddr_in *)ip_addr)->sin_addr, data, INET_ADDRSTRLEN); + port = ((sockaddr_in *)ip_addr)->sin_port; + } + + return { port, std::string { data } }; +} + +adapteraddrs_t get_adapteraddrs() { + adapteraddrs_t info { nullptr }; + ULONG size = 0; + + while(GetAdaptersAddresses(AF_UNSPEC, 0, nullptr, info.get(), &size) == ERROR_BUFFER_OVERFLOW) { + info.reset((PIP_ADAPTER_ADDRESSES)malloc(size)); + } + + return info; +} + +std::string get_mac_address(const std::string_view &address) { + adapteraddrs_t info = get_adapteraddrs(); + for(auto adapter_pos = info.get(); adapter_pos != nullptr; adapter_pos = adapter_pos->Next) { + for(auto addr_pos = adapter_pos->FirstUnicastAddress; addr_pos != nullptr; addr_pos = addr_pos->Next) { + if(adapter_pos->PhysicalAddressLength != 0 && address == from_sockaddr(addr_pos->Address.lpSockaddr)) { + std::stringstream mac_addr; + mac_addr << std::hex; + for(int i = 0; i < adapter_pos->PhysicalAddressLength; i++) { + if(i > 0) { + mac_addr << ':'; + } + mac_addr << std::setw(2) << std::setfill('0') << (int)adapter_pos->PhysicalAddress[i]; + } + return mac_addr.str(); + } + } + } + BOOST_LOG(warning) << "Unable to find MAC address for "sv << address; + return "00:00:00:00:00:00"s; +} +} // namespace platf \ No newline at end of file diff --git a/sunshine/process.cpp b/sunshine/process.cpp index ec2aa2fe..697868d8 100644 --- a/sunshine/process.cpp +++ b/sunshine/process.cpp @@ -2,16 +2,18 @@ // Created by loki on 12/14/19. // +#define BOOST_BIND_GLOBAL_PLACEHOLDERS + #include "process.h" -#include #include +#include -#include #include +#include -#include "utility.h" #include "main.h" +#include "utility.h" namespace proc { using namespace std::literals; @@ -57,11 +59,11 @@ int proc_t::execute(int app_id) { // Ensure starting from a clean slate terminate(); - _app_id = app_id; + _app_id = app_id; auto &proc = _apps[app_id]; _undo_begin = std::begin(proc.prep_cmds); - _undo_it = _undo_begin; + _undo_it = _undo_begin; if(!proc.output.empty() && proc.output != "null"sv) { _pipe.reset(fopen(proc.output.c_str(), "a")); @@ -80,17 +82,31 @@ int proc_t::execute(int app_id) { auto ret = exe(cmd, _env, _pipe, ec); if(ec) { - BOOST_LOG(error) << "System: "sv << ec.message(); + BOOST_LOG(error) << "Couldn't run ["sv << cmd << "]: System: "sv << ec.message(); return -1; } if(ret != 0) { - BOOST_LOG(error) << "Return code ["sv << ret << ']'; + BOOST_LOG(error) << '[' << cmd << "] failed with code ["sv << ret << ']'; return -1; } } - BOOST_LOG(debug) << "Starting ["sv << proc.cmd << ']'; + for(auto &cmd : proc.detached) { + BOOST_LOG(info) << "Spawning ["sv << cmd << ']'; + if(proc.output.empty()) { + bp::spawn(cmd, _env, bp::std_out > bp::null, bp::std_err > bp::null, ec); + } + else { + bp::spawn(cmd, _env, bp::std_out > _pipe.get(), bp::std_err > _pipe.get(), ec); + } + + if(ec) { + BOOST_LOG(warning) << "Couldn't spawn ["sv << cmd << "]: System: "sv << ec.message(); + } + } + + BOOST_LOG(info) << "Executing: ["sv << proc.cmd << ']'; if(proc.cmd.empty()) { placebo = true; } @@ -102,7 +118,7 @@ int proc_t::execute(int app_id) { } if(ec) { - BOOST_LOG(info) << "System: "sv << ec.message(); + BOOST_LOG(warning) << "Couldn't run ["sv << proc.cmd << "]: System: "sv << ec.message(); return -1; } @@ -133,7 +149,7 @@ void proc_t::terminate() { std::abort(); } - for(;_undo_it != _undo_begin; --_undo_it) { + for(; _undo_it != _undo_begin; --_undo_it) { auto &cmd = (_undo_it - 1)->undo_cmd; if(cmd.empty()) { @@ -178,9 +194,11 @@ std::string_view::iterator find_match(std::string_view::iterator begin, std::str do { ++begin; switch(*begin) { - case '(': ++stack; + case '(': + ++stack; break; - case ')': --stack; + case ')': + --stack; } } while(begin != end && stack != 0); @@ -191,7 +209,7 @@ std::string_view::iterator find_match(std::string_view::iterator begin, std::str } std::string parse_env_val(bp::native_environment &env, const std::string_view &val_raw) { - auto pos = std::begin(val_raw); + auto pos = std::begin(val_raw); auto dollar = std::find(pos, std::end(val_raw), '$'); std::stringstream ss; @@ -200,23 +218,23 @@ std::string parse_env_val(bp::native_environment &env, const std::string_view &v auto next = dollar + 1; if(next != std::end(val_raw)) { switch(*next) { - case '(': { - ss.write(pos, (dollar - pos)); - auto var_begin = next + 1; - auto var_end = find_match(next, std::end(val_raw)); + case '(': { + ss.write(pos, (dollar - pos)); + auto var_begin = next + 1; + auto var_end = find_match(next, std::end(val_raw)); - ss << env[std::string { var_begin, var_end }].to_string(); + ss << env[std::string { var_begin, var_end }].to_string(); - pos = var_end + 1; - next = var_end; + pos = var_end + 1; + next = var_end; - break; - } - case '$': - ss.write(pos, (next - pos)); - pos = next + 1; - ++next; - break; + break; + } + case '$': + ss.write(pos, (next - pos)); + pos = next + 1; + ++next; + break; } dollar = std::find(next, std::end(val_raw), '$'); @@ -231,7 +249,7 @@ std::string parse_env_val(bp::native_environment &env, const std::string_view &v return ss.str(); } -std::optional parse(const std::string& file_name) { +std::optional parse(const std::string &file_name) { pt::ptree tree; try { @@ -242,27 +260,27 @@ std::optional parse(const std::string& file_name) { auto this_env = boost::this_process::environment(); - for(auto &[name,val] : env_vars) { + for(auto &[name, val] : env_vars) { this_env[name] = parse_env_val(this_env, val.get_value()); } std::vector apps; - for(auto &[_,app_node] : apps_node) { + for(auto &[_, app_node] : apps_node) { proc::ctx_t ctx; - auto prep_nodes_opt = app_node.get_child_optional("prep-cmd"s); - auto output = app_node.get_optional("output"s); - auto name = parse_env_val(this_env, app_node.get("name"s)); - auto cmd = app_node.get_optional("cmd"s); + auto prep_nodes_opt = app_node.get_child_optional("prep-cmd"s); + auto detached_nodes_opt = app_node.get_child_optional("detached"s); + auto output = app_node.get_optional("output"s); + auto name = parse_env_val(this_env, app_node.get("name"s)); + auto cmd = app_node.get_optional("cmd"s); std::vector prep_cmds; - if(prep_nodes_opt) { auto &prep_nodes = *prep_nodes_opt; prep_cmds.reserve(prep_nodes.size()); for(auto &[_, prep_node] : prep_nodes) { - auto do_cmd = parse_env_val(this_env, prep_node.get("do"s)); + auto do_cmd = parse_env_val(this_env, prep_node.get("do"s)); auto undo_cmd = prep_node.get_optional("undo"s); if(undo_cmd) { @@ -274,6 +292,16 @@ std::optional parse(const std::string& file_name) { } } + std::vector detached; + if(detached_nodes_opt) { + auto &detached_nodes = *detached_nodes_opt; + + detached.reserve(detached_nodes.size()); + for(auto &[_, detached_val] : detached_nodes) { + detached.emplace_back(parse_env_val(this_env, detached_val.get_value())); + } + } + if(output) { ctx.output = parse_env_val(this_env, *output); } @@ -282,8 +310,9 @@ std::optional parse(const std::string& file_name) { ctx.cmd = parse_env_val(this_env, *cmd); } - ctx.name = std::move(name); + ctx.name = std::move(name); ctx.prep_cmds = std::move(prep_cmds); + ctx.detached = std::move(detached); apps.emplace_back(std::move(ctx)); } @@ -291,7 +320,8 @@ std::optional parse(const std::string& file_name) { return proc::proc_t { std::move(this_env), std::move(apps) }; - } catch (std::exception &e) { + } + catch(std::exception &e) { BOOST_LOG(error) << e.what(); } @@ -302,7 +332,12 @@ void refresh(const std::string &file_name) { auto proc_opt = proc::parse(file_name); if(proc_opt) { + { + proc::ctx_t ctx; + ctx.name = "Desktop"s; + proc_opt->get_apps().emplace(std::begin(proc_opt->get_apps()), std::move(ctx)); + } proc = std::move(*proc_opt); } } -} +} // namespace proc diff --git a/sunshine/process.h b/sunshine/process.h index 37c17acd..39d7cff6 100644 --- a/sunshine/process.h +++ b/sunshine/process.h @@ -9,8 +9,8 @@ #define __kernel_entry #endif -#include #include +#include #include @@ -30,6 +30,7 @@ struct cmd_t { }; /* * pre_cmds -- guaranteed to be executed unless any of the commands fail. + * detached -- commands detached from Sunshine * cmd -- Runs indefinitely until: * No session is running and a different set of commands it to be executed * Command exits @@ -41,6 +42,14 @@ struct cmd_t { struct ctx_t { std::vector prep_cmds; + /** + * Some applications, such as Steam, + * either exit quickly, or keep running indefinitely. + * Steam.exe is one such application. + * That is why some applications need be run and forgotten about + */ + std::vector detached; + std::string name; std::string cmd; std::string output; @@ -52,10 +61,9 @@ public: proc_t( boost::process::environment &&env, - std::vector &&apps) : - _app_id(-1), - _env(std::move(env)), - _apps(std::move(apps)) {} + std::vector &&apps) : _app_id(-1), + _env(std::move(env)), + _apps(std::move(apps)) {} int execute(int app_id); @@ -68,7 +76,7 @@ public: const std::vector &get_apps() const; std::vector &get_apps(); - + void terminate(); private: @@ -89,8 +97,8 @@ private: }; void refresh(const std::string &file_name); -std::optional parse(const std::string& file_name); +std::optional parse(const std::string &file_name); extern proc_t proc; -} +} // namespace proc #endif //SUNSHINE_PROCESS_H diff --git a/sunshine/publish.cpp b/sunshine/publish.cpp index 6494919e..8fc57e86 100644 --- a/sunshine/publish.cpp +++ b/sunshine/publish.cpp @@ -9,20 +9,20 @@ #include #include -#include "publish.h" -#include "nvhttp.h" #include "main.h" +#include "nvhttp.h" +#include "publish.h" namespace publish { -AvahiEntryGroup *group = NULL; +AvahiEntryGroup *group = NULL; AvahiSimplePoll *simple_poll = NULL; -char *name = NULL; +char *name = NULL; void create_services(AvahiClient *c); void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) { assert(g == group || group == NULL); group = g; - switch (state) { + switch(state) { case AVAHI_ENTRY_GROUP_ESTABLISHED: BOOST_LOG(info) << "Avahi service " << name << " successfully established."; break; @@ -39,8 +39,7 @@ void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_ avahi_simple_poll_quit(simple_poll); break; case AVAHI_ENTRY_GROUP_UNCOMMITED: - case AVAHI_ENTRY_GROUP_REGISTERING: - ; + case AVAHI_ENTRY_GROUP_REGISTERING:; } } @@ -48,22 +47,22 @@ void create_services(AvahiClient *c) { char *n; int ret; assert(c); - if (!group) { - if (!(group = avahi_entry_group_new(c, entry_group_callback, NULL))) { + if(!group) { + if(!(group = avahi_entry_group_new(c, entry_group_callback, NULL))) { BOOST_LOG(error) << "avahi_entry_group_new() failed: " << avahi_strerror(avahi_client_errno(c)); goto fail; } } - if (avahi_entry_group_is_empty(group)) { + if(avahi_entry_group_is_empty(group)) { BOOST_LOG(info) << "Adding avahi service " << name; - if ((ret = avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AvahiPublishFlags(0), name, SERVICE_TYPE, NULL, NULL, nvhttp::PORT_HTTP, NULL)) < 0) { - if (ret == AVAHI_ERR_COLLISION) + if((ret = avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AvahiPublishFlags(0), name, SERVICE_TYPE, NULL, NULL, nvhttp::PORT_HTTP, NULL)) < 0) { + if(ret == AVAHI_ERR_COLLISION) goto collision; BOOST_LOG(error) << "Failed to add " << SERVICE_TYPE << " service: " << avahi_strerror(ret); goto fail; } - if ((ret = avahi_entry_group_commit(group)) < 0) { + if((ret = avahi_entry_group_commit(group)) < 0) { BOOST_LOG(error) << "Failed to commit entry group: " << avahi_strerror(ret); goto fail; } @@ -74,7 +73,7 @@ collision: n = avahi_alternative_service_name(name); avahi_free(name); name = n; - BOOST_LOG(info) << "Service name collision, renaming service to " << name; + BOOST_LOG(info) << "Service name collision, renaming service to " << name; avahi_entry_group_reset(group); create_services(c); return; @@ -84,7 +83,7 @@ fail: void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void *userdata) { assert(c); - switch (state) { + switch(state) { case AVAHI_CLIENT_S_RUNNING: create_services(c); break; @@ -94,24 +93,25 @@ void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED vo break; case AVAHI_CLIENT_S_COLLISION: case AVAHI_CLIENT_S_REGISTERING: - if (group) + if(group) avahi_entry_group_reset(group); break; - case AVAHI_CLIENT_CONNECTING: - ; + case AVAHI_CLIENT_CONNECTING:; } } -void start(std::shared_ptr shutdown_event) { +void start() { + auto shutdown_event = mail::man->event(mail::shutdown); + AvahiClient *client = NULL; int avhi_error; - if (!(simple_poll = avahi_simple_poll_new())) { + if(!(simple_poll = avahi_simple_poll_new())) { BOOST_LOG(error) << "Failed to create simple poll object."; return; } - name = avahi_strdup(SERVICE_NAME); + name = avahi_strdup(SERVICE_NAME); client = avahi_client_new(avahi_simple_poll_get(simple_poll), AvahiClientFlags(0), client_callback, NULL, &avhi_error); - if (!client) { + if(!client) { BOOST_LOG(error) << "Failed to create client: " << avahi_strerror(avhi_error); avahi_simple_poll_free(simple_poll); return; @@ -125,7 +125,7 @@ void start(std::shared_ptr shutdown_event) { avahi_simple_poll_quit(simple_poll); poll_thread.join(); - + avahi_client_free(client); avahi_simple_poll_free(simple_poll); avahi_free(name); diff --git a/sunshine/publish.h b/sunshine/publish.h index ed84031c..27ee1cd1 100644 --- a/sunshine/publish.h +++ b/sunshine/publish.h @@ -7,7 +7,7 @@ #define SERVICE_TYPE "_nvstream._tcp" namespace publish { -void start(std::shared_ptr shutdown_event); +void start(); } #endif //SUNSHINE_PUBLISH_H diff --git a/sunshine/round_robin.h b/sunshine/round_robin.h index 47e125db..e522d16c 100644 --- a/sunshine/round_robin.h +++ b/sunshine/round_robin.h @@ -10,12 +10,12 @@ public: typedef T iterator; typedef typename std::iterator::value_type class_t; - typedef class_t& reference; - typedef class_t* pointer; + typedef class_t &reference; + typedef class_t *pointer; typedef std::ptrdiff_t diff_t; - iterator operator += (diff_t step) { + iterator operator+=(diff_t step) { while(step-- > 0) { ++_this(); } @@ -23,7 +23,7 @@ public: return _this(); } - iterator operator -= (diff_t step) { + iterator operator-=(diff_t step) { while(step-- > 0) { --_this(); } @@ -31,19 +31,19 @@ public: return _this(); } - iterator operator +(diff_t step) { + iterator operator+(diff_t step) { iterator new_ = _this(); return new_ += step; } - iterator operator -(diff_t step) { + iterator operator-(diff_t step) { iterator new_ = _this(); return new_ -= step; } - diff_t operator -(iterator first) { + diff_t operator-(iterator first) { diff_t step = 0; while(first != _this()) { ++step; @@ -53,8 +53,14 @@ public: return step; } - iterator operator++() { _this().inc(); return _this(); } - iterator operator--() { _this().dec(); return _this(); } + iterator operator++() { + _this().inc(); + return _this(); + } + iterator operator--() { + _this().dec(); + return _this(); + } iterator operator++(int) { iterator new_ = _this(); @@ -78,35 +84,35 @@ public: pointer operator->() { return &*_this(); } const pointer operator->() const { return &*_this(); } - bool operator != (const iterator &other) const { + bool operator!=(const iterator &other) const { return !(_this() == other); } - bool operator < (const iterator &other) const { + bool operator<(const iterator &other) const { return !(_this() >= other); } - bool operator >= (const iterator &other) const { + bool operator>=(const iterator &other) const { return _this() == other || _this() > other; } - bool operator <= (const iterator &other) const { + bool operator<=(const iterator &other) const { return _this() == other || _this() < other; } - bool operator == (const iterator &other) const { return _this().eq(other); }; - bool operator > (const iterator &other) const { return _this().gt(other); } -private: + bool operator==(const iterator &other) const { return _this().eq(other); }; + bool operator>(const iterator &other) const { return _this().gt(other); } - iterator &_this() { return *static_cast(this); } - const iterator &_this() const { return *static_cast(this); } +private: + iterator &_this() { return *static_cast(this); } + const iterator &_this() const { return *static_cast(this); } }; template class round_robin_t : public it_wrap_t> { public: using iterator = It; - using pointer = V*; + using pointer = V *; round_robin_t(iterator begin, iterator end) : _begin(begin), _end(end), _pos(begin) {} @@ -119,10 +125,10 @@ public: } void dec() { - if(_pos == _begin) { + if(_pos == _begin) { _pos = _end; } - + --_pos; } @@ -133,6 +139,7 @@ public: pointer get() const { return &*_pos; } + private: It _begin; It _end; @@ -144,6 +151,6 @@ template round_robin_t make_round_robin(It begin, It end) { return round_robin_t(begin, end); } -} +} // namespace util #endif diff --git a/sunshine/rtsp.cpp b/sunshine/rtsp.cpp index 77d1179e..89197de3 100644 --- a/sunshine/rtsp.cpp +++ b/sunshine/rtsp.cpp @@ -7,10 +7,10 @@ extern "C" { } #include "config.h" +#include "input.h" #include "main.h" #include "network.h" #include "rtsp.h" -#include "input.h" #include "stream.h" #include "sync.h" @@ -25,7 +25,7 @@ namespace stream { //FIXME: Quick and dirty workaround for bug in MinGW 9.3 causing a linker error when using std::to_string template -std::string to_string(T && t) { +std::string to_string(T &&t) { std::stringstream ss; ss << std::forward(t); return ss.str(); @@ -41,11 +41,11 @@ void free_msg(PRTSP_MESSAGE msg) { class rtsp_server_t; -using msg_t = util::safe_ptr; -using cmd_func_t = std::function; +using msg_t = util::safe_ptr; +using cmd_func_t = std::function; void print_msg(PRTSP_MESSAGE msg); -void cmd_not_found(net::host_t::pointer host, net::peer_t peer, msg_t&& req); +void cmd_not_found(net::host_t::pointer host, net::peer_t peer, msg_t &&req); class rtsp_server_t { public: @@ -69,6 +69,7 @@ public: } void session_raise(launch_session_t launch_session) { + //FIXME: If client abandons us at this stage, _slot_count won't be raised again. --_slot_count; launch_event.raise(launch_session); } @@ -82,61 +83,61 @@ public: ENetEvent event; auto res = enet_host_service(_host.get(), &event, std::chrono::floor(timeout).count()); - if (res > 0) { - switch (event.type) { - case ENET_EVENT_TYPE_RECEIVE: { - net::packet_t packet{event.packet}; - net::peer_t peer{event.peer}; + if(res > 0) { + switch(event.type) { + case ENET_EVENT_TYPE_RECEIVE: { + net::packet_t packet { event.packet }; + net::peer_t peer { event.peer }; - msg_t req { new msg_t::element_type }; + msg_t req { new msg_t::element_type }; - //TODO: compare addresses of the peers - if (_queue_packet.second == nullptr) { - parseRtspMessage(req.get(), (char *) packet->data, packet->dataLength); - for (auto option = req->options; option != nullptr; option = option->next) { - if ("Content-length"sv == option->option) { - _queue_packet = std::make_pair(peer, std::move(packet)); - return; - } + //TODO: compare addresses of the peers + if(_queue_packet.second == nullptr) { + parseRtspMessage(req.get(), (char *)packet->data, packet->dataLength); + for(auto option = req->options; option != nullptr; option = option->next) { + if("Content-length"sv == option->option) { + _queue_packet = std::make_pair(peer, std::move(packet)); + return; } } - else { - std::vector full_payload; - - auto old_msg = std::move(_queue_packet); - auto &old_packet = old_msg.second; - - std::string_view new_payload{(char *) packet->data, packet->dataLength}; - std::string_view old_payload{(char *) old_packet->data, old_packet->dataLength}; - full_payload.resize(new_payload.size() + old_payload.size()); - - std::copy(std::begin(old_payload), std::end(old_payload), std::begin(full_payload)); - std::copy(std::begin(new_payload), std::end(new_payload), std::begin(full_payload) + old_payload.size()); - - parseRtspMessage(req.get(), full_payload.data(), full_payload.size()); - } - - print_msg(req.get()); - - msg_t resp; - auto func = _map_cmd_cb.find(req->message.request.command); - if (func != std::end(_map_cmd_cb)) { - func->second(this, peer, std::move(req)); - } - else { - cmd_not_found(host(), peer, std::move(req)); - } - - return; } - case ENET_EVENT_TYPE_CONNECT: - BOOST_LOG(info) << "CLIENT CONNECTED TO RTSP"sv; - break; - case ENET_EVENT_TYPE_DISCONNECT: - BOOST_LOG(info) << "CLIENT DISCONNECTED FROM RTSP"sv; - break; - case ENET_EVENT_TYPE_NONE: - break; + else { + std::vector full_payload; + + auto old_msg = std::move(_queue_packet); + auto &old_packet = old_msg.second; + + std::string_view new_payload { (char *)packet->data, packet->dataLength }; + std::string_view old_payload { (char *)old_packet->data, old_packet->dataLength }; + full_payload.resize(new_payload.size() + old_payload.size()); + + std::copy(std::begin(old_payload), std::end(old_payload), std::begin(full_payload)); + std::copy(std::begin(new_payload), std::end(new_payload), std::begin(full_payload) + old_payload.size()); + + parseRtspMessage(req.get(), full_payload.data(), full_payload.size()); + } + + print_msg(req.get()); + + msg_t resp; + auto func = _map_cmd_cb.find(req->message.request.command); + if(func != std::end(_map_cmd_cb)) { + func->second(this, peer, std::move(req)); + } + else { + cmd_not_found(host(), peer, std::move(req)); + } + + return; + } + case ENET_EVENT_TYPE_CONNECT: + BOOST_LOG(info) << "CLIENT CONNECTED TO RTSP"sv; + break; + case ENET_EVENT_TYPE_DISCONNECT: + BOOST_LOG(info) << "CLIENT DISCONNECTED FROM RTSP"sv; + break; + case ENET_EVENT_TYPE_NONE: + break; } } } @@ -149,7 +150,7 @@ public: auto lg = _session_slots.lock(); for(auto &slot : *_session_slots) { - if (slot && (all || session::state(*slot) == session::state_e::STOPPING)) { + if(slot && (all || session::state(*slot) == session::state_e::STOPPING)) { session::stop(*slot); session::join(*slot); @@ -194,7 +195,6 @@ public: safe::event_t launch_event; private: - // named _queue_packet because I want to make it an actual queue // It's like this for convenience sake std::pair _queue_packet; @@ -225,11 +225,11 @@ void respond(net::host_t::pointer host, net::peer_t peer, msg_t &resp) { auto payload = std::make_pair(resp->payload, resp->payloadLength); auto lg = util::fail_guard([&]() { - resp->payload = payload.first; + resp->payload = payload.first; resp->payloadLength = payload.second; }); - resp->payload = nullptr; + resp->payload = nullptr; resp->payloadLength = 0; int serialized_len; @@ -264,45 +264,69 @@ void respond(net::host_t::pointer host, net::peer_t peer, msg_t &resp) { void respond(net::host_t::pointer host, net::peer_t peer, POPTION_ITEM options, int statuscode, const char *status_msg, int seqn, const std::string_view &payload) { msg_t resp { new msg_t::element_type }; - createRtspResponse(resp.get(), nullptr, 0, const_cast("RTSP/1.0"), statuscode, const_cast(status_msg), seqn, options, const_cast(payload.data()), (int)payload.size()); + createRtspResponse(resp.get(), nullptr, 0, const_cast("RTSP/1.0"), statuscode, const_cast(status_msg), seqn, options, const_cast(payload.data()), (int)payload.size()); respond(host, peer, resp); } -void cmd_not_found(net::host_t::pointer host, net::peer_t peer, msg_t&& req) { +void cmd_not_found(net::host_t::pointer host, net::peer_t peer, msg_t &&req) { respond(host, peer, nullptr, 404, "NOT FOUND", req->sequenceNumber, {}); } -void cmd_option(rtsp_server_t *server, net::peer_t peer, msg_t&& req) { +void cmd_option(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { OPTION_ITEM option {}; // I know these string literals will not be modified - option.option = const_cast("CSeq"); + option.option = const_cast("CSeq"); - auto seqn_str = to_string(req->sequenceNumber); - option.content = const_cast(seqn_str.c_str()); + auto seqn_str = to_string(req->sequenceNumber); + option.content = const_cast(seqn_str.c_str()); respond(server->host(), peer, &option, 200, "OK", req->sequenceNumber, {}); } -void cmd_describe(rtsp_server_t *server, net::peer_t peer, msg_t&& req) { +void cmd_describe(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { OPTION_ITEM option {}; // I know these string literals will not be modified - option.option = const_cast("CSeq"); + option.option = const_cast("CSeq"); - auto seqn_str = to_string(req->sequenceNumber); - option.content = const_cast(seqn_str.c_str()); + auto seqn_str = to_string(req->sequenceNumber); + option.content = const_cast(seqn_str.c_str()); - std::string_view payload; - if(config::video.hevc_mode == 1) { - payload = "surround-params=NONE"sv; - } - else { - payload = "sprop-parameter-sets=AAAAAU;surround-params=NONE"sv; + std::stringstream ss; + if(config::video.hevc_mode != 1) { + ss << "sprop-parameter-sets=AAAAAU"sv << std::endl; } - respond(server->host(), peer, &option, 200, "OK", req->sequenceNumber, payload); + for(int x = 0; x < audio::MAX_STREAM_CONFIG; ++x) { + auto &stream_config = audio::stream_configs[x]; + std::uint8_t mapping[platf::speaker::MAX_SPEAKERS]; + + auto mapping_p = stream_config.mapping; + + /** + * GFE advertises incorrect mapping for normal quality configurations, + * as a result, Moonlight rotates all channels from index '3' to the right + * To work around this, rotate channels to the left from index '3' + */ + if(x == audio::SURROUND51 || x == audio::SURROUND71) { + std::copy_n(mapping_p, stream_config.channelCount, mapping); + std::rotate(mapping + 3, mapping + 4, mapping + audio::MAX_STREAM_CONFIG); + + mapping_p = mapping; + } + + ss << "a=fmtp:97 surround-params="sv << stream_config.channelCount << stream_config.streams << stream_config.coupledStreams; + + std::for_each_n(mapping_p, stream_config.channelCount, [&ss](std::uint8_t digit) { + ss << (char)(digit + '0'); + }); + + ss << std::endl; + } + + respond(server->host(), peer, &option, 200, "OK", req->sequenceNumber, ss.str()); } void cmd_setup(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { @@ -311,10 +335,10 @@ void cmd_setup(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { auto &seqn = options[0]; auto &session_option = options[1]; - seqn.option = const_cast("CSeq"); + seqn.option = const_cast("CSeq"); auto seqn_str = to_string(req->sequenceNumber); - seqn.content = const_cast(seqn_str.c_str()); + seqn.content = const_cast(seqn_str.c_str()); std::string_view target { req->message.request.target }; auto begin = std::find(std::begin(target), std::end(target), '=') + 1; @@ -324,8 +348,8 @@ void cmd_setup(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { if(type == "audio"sv) { seqn.next = &session_option; - session_option.option = const_cast("Session"); - session_option.content = const_cast("DEADBEEFCAFE;timeout = 90"); + session_option.option = const_cast("Session"); + session_option.content = const_cast("DEADBEEFCAFE;timeout = 90"); } else if(type != "video"sv && type != "control"sv) { cmd_not_found(server->host(), peer, std::move(req)); @@ -340,10 +364,10 @@ void cmd_announce(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { OPTION_ITEM option {}; // I know these string literals will not be modified - option.option = const_cast("CSeq"); + option.option = const_cast("CSeq"); - auto seqn_str = to_string(req->sequenceNumber); - option.content = const_cast(seqn_str.c_str()); + auto seqn_str = to_string(req->sequenceNumber); + option.content = const_cast(seqn_str.c_str()); if(!server->launch_event.peek()) { // /launch has not been used @@ -362,10 +386,10 @@ void cmd_announce(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { }; { - auto pos = std::begin(payload); + auto pos = std::begin(payload); auto begin = pos; - while (pos != std::end(payload)) { - if (whitespace(*pos++)) { + while(pos != std::end(payload)) { + if(whitespace(*pos++)) { lines.emplace_back(begin, pos - begin - 1); while(pos != std::end(payload) && whitespace(*pos)) { ++pos; } @@ -386,10 +410,10 @@ void cmd_announce(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { auto pos = line.find(':'); auto name = line.substr(2, pos - 2); - auto val = line.substr(pos + 1); + auto val = line.substr(pos + 1); - if(val[val.size() -1] == ' ') { - val = val.substr(0, val.size() -1); + if(val[val.size() - 1] == ' ') { + val = val.substr(0, val.size() - 1); } args.emplace(name, val); } @@ -402,11 +426,16 @@ void cmd_announce(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { args.try_emplace("x-nv-aqos.packetDuration"sv, "5"sv); config_t config; + + config.audio.flags[audio::config_t::HOST_AUDIO] = launch_session->host_audio; try { config.audio.channels = util::from_view(args.at("x-nv-audio.surround.numChannels"sv)); config.audio.mask = util::from_view(args.at("x-nv-audio.surround.channelMask"sv)); config.audio.packetDuration = util::from_view(args.at("x-nv-aqos.packetDuration"sv)); + config.audio.flags[audio::config_t::HIGH_QUALITY] = + util::from_view(args.at("x-nv-audio.surround.AudioQuality"sv)); + config.packetsize = util::from_view(args.at("x-nv-video[0].packetSize"sv)); config.monitor.height = util::from_view(args.at("x-nv-video[0].clientViewportHt"sv)); @@ -418,8 +447,8 @@ void cmd_announce(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { config.monitor.encoderCscMode = util::from_view(args.at("x-nv-video[0].encoderCscMode"sv)); config.monitor.videoFormat = util::from_view(args.at("x-nv-vqos[0].bitStreamFormat"sv)); config.monitor.dynamicRange = util::from_view(args.at("x-nv-video[0].dynamicRangeMode"sv)); - - } catch(std::out_of_range &) { + } + catch(std::out_of_range &) { respond(server->host(), peer, &option, 400, "BAD REQUEST", req->sequenceNumber, {}); return; @@ -442,7 +471,7 @@ void cmd_announce(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { return; } - if(session::start(*session, platf::from_sockaddr((sockaddr*)&peer->address.address))) { + if(session::start(*session, platf::from_sockaddr((sockaddr *)&peer->address.address))) { BOOST_LOG(error) << "Failed to start a streaming session"sv; server->clear(slot); @@ -457,15 +486,18 @@ void cmd_play(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { OPTION_ITEM option {}; // I know these string literals will not be modified - option.option = const_cast("CSeq"); + option.option = const_cast("CSeq"); - auto seqn_str = to_string(req->sequenceNumber); - option.content = const_cast(seqn_str.c_str()); + auto seqn_str = to_string(req->sequenceNumber); + option.content = const_cast(seqn_str.c_str()); respond(server->host(), peer, &option, 200, "OK", req->sequenceNumber, {}); } -void rtpThread(std::shared_ptr shutdown_event) { +void rtpThread() { + auto shutdown_event = mail::man->event(mail::shutdown); + auto broadcast_shutdown_event = mail::man->event(mail::broadcast_shutdown); + server.map("OPTIONS"sv, &cmd_option); server.map("DESCRIBE"sv, &cmd_describe); server.map("SETUP"sv, &cmd_setup); @@ -483,7 +515,7 @@ void rtpThread(std::shared_ptr shutdown_event) { while(!shutdown_event->peek()) { server.iterate(std::min(500ms, config::stream.ping_timeout)); - if(broadcast_shutdown_event.peek()) { + if(broadcast_shutdown_event->peek()) { server.clear(); } else { @@ -518,7 +550,7 @@ void print_msg(PRTSP_MESSAGE msg) { BOOST_LOG(debug) << "status :: "sv << status; } else { - auto& req = msg->message.request; + auto &req = msg->message.request; std::string_view command { req.command }; std::string_view target { req.target }; @@ -534,6 +566,8 @@ void print_msg(PRTSP_MESSAGE msg) { BOOST_LOG(debug) << name << " :: "sv << content; } - BOOST_LOG(debug) << "---Begin MessageBuffer---"sv << std::endl << messageBuffer << std::endl << "---End MessageBuffer---"sv << std::endl; -} + BOOST_LOG(debug) << "---Begin MessageBuffer---"sv << std::endl + << messageBuffer << std::endl + << "---End MessageBuffer---"sv << std::endl; } +} // namespace stream diff --git a/sunshine/rtsp.h b/sunshine/rtsp.h index ac60e8d6..64d3cc85 100644 --- a/sunshine/rtsp.h +++ b/sunshine/rtsp.h @@ -14,13 +14,15 @@ namespace stream { struct launch_session_t { crypto::aes_t gcm_key; crypto::aes_t iv; + + bool host_audio; }; void launch_session_raise(launch_session_t launch_session); int session_count(); -void rtpThread(std::shared_ptr shutdown_event); +void rtpThread(); -} +} // namespace stream #endif //SUNSHINE_RTSP_H diff --git a/sunshine/stream.cpp b/sunshine/stream.cpp index f6d4ef21..eedd7328 100644 --- a/sunshine/stream.cpp +++ b/sunshine/stream.cpp @@ -4,8 +4,8 @@ #include "process.h" -#include #include +#include #include #include @@ -15,14 +15,14 @@ extern "C" { #include } -#include "network.h" #include "config.h" -#include "utility.h" -#include "stream.h" -#include "thread_safe.h" -#include "sync.h" #include "input.h" #include "main.h" +#include "network.h" +#include "stream.h" +#include "sync.h" +#include "thread_safe.h" +#include "utility.h" #define IDX_START_A 0 #define IDX_REQUEST_IDR_FRAME 0 @@ -45,7 +45,7 @@ static const short packetTypes[] = { }; constexpr auto VIDEO_STREAM_PORT = 47998; -constexpr auto CONTROL_PORT = 47999; +constexpr auto CONTROL_PORT = 47999; constexpr auto AUDIO_STREAM_PORT = 48000; namespace asio = boost::asio; @@ -89,7 +89,7 @@ using rh_t = util::safe_ptr; using video_packet_t = util::c_ptr; using audio_packet_t = util::c_ptr; -using message_queue_t = std::shared_ptr>>; +using message_queue_t = std::shared_ptr>>; using message_queue_queue_t = std::shared_ptr>>; static inline void while_starting_do_nothing(std::atomic &state) { @@ -124,7 +124,7 @@ public: // Therefore, iterate is implemented further down the source file void iterate(std::chrono::milliseconds timeout); - void map(uint16_t type, std::function cb) { + void map(uint16_t type, std::function cb) { _map_type_cb.emplace(type, std::move(cb)); } @@ -140,19 +140,16 @@ public: } // Callbacks - std::unordered_map> _map_type_cb; + std::unordered_map> _map_type_cb; // Mapping ip:port to session - util::sync_t>> _map_addr_session; + util::sync_t>> _map_addr_session; ENetAddress _addr; net::host_t _host; }; struct broadcast_ctx_t { - video::packet_queue_t video_packets; - audio::packet_queue_t audio_packets; - message_queue_queue_t message_queue_queue; std::thread recv_thread; @@ -169,6 +166,9 @@ struct broadcast_ctx_t { struct session_t { config_t config; + + safe::mail_t mail; + std::shared_ptr input; std::thread audioThread; @@ -181,7 +181,7 @@ struct session_t { struct { int lowseq; udp::endpoint peer; - video::idr_event_t idr_events; + safe::mail_raw_t::event_t idr_events; } video; struct { @@ -196,7 +196,7 @@ struct session_t { crypto::aes_t gcm_key; crypto::aes_t iv; - safe::signal_t shutdown_event; + safe::mail_raw_t::event_t shutdown_event; safe::signal_t controlEnd; std::atomic state; @@ -207,10 +207,9 @@ void end_broadcast(broadcast_ctx_t &ctx); static auto broadcast = safe::make_shared(start_broadcast, end_broadcast); -safe::signal_t broadcast_shutdown_event; session_t *control_server_t::get_session(const net::peer_t peer) { - TUPLE_2D(port, addr_string, platf::from_sockaddr_ex((sockaddr*)&peer->address.address)); + TUPLE_2D(port, addr_string, platf::from_sockaddr_ex((sockaddr *)&peer->address.address)); auto lg = _map_addr_session.lock(); TUPLE_2D(begin, end, _map_addr_session->equal_range(addr_string)); @@ -231,7 +230,7 @@ session_t *control_server_t::get_session(const net::peer_t peer) { TUPLE_2D_REF(session_port, session_p, it->second); session_p->control.peer = peer; - session_port = port; + session_port = port; return session_p; } @@ -246,7 +245,7 @@ void control_server_t::iterate(std::chrono::milliseconds timeout) { if(res > 0) { auto session = get_session(event.peer); if(!session) { - BOOST_LOG(warning) << "Rejected connection from ["sv << platf::from_sockaddr((sockaddr*)&event.peer->address.address) << "]: it's not properly set up"sv; + BOOST_LOG(warning) << "Rejected connection from ["sv << platf::from_sockaddr((sockaddr *)&event.peer->address.address) << "]: it's not properly set up"sv; enet_peer_disconnect_now(event.peer, 0); return; @@ -255,37 +254,37 @@ void control_server_t::iterate(std::chrono::milliseconds timeout) { session->pingTimeout = std::chrono::steady_clock::now() + config::stream.ping_timeout; switch(event.type) { - case ENET_EVENT_TYPE_RECEIVE: - { - net::packet_t packet { event.packet }; + case ENET_EVENT_TYPE_RECEIVE: { + net::packet_t packet { event.packet }; - auto type = (std::uint16_t *)packet->data; - std::string_view payload { (char*)packet->data + sizeof(*type), packet->dataLength - sizeof(*type) }; + auto type = (std::uint16_t *)packet->data; + std::string_view payload { (char *)packet->data + sizeof(*type), packet->dataLength - sizeof(*type) }; - auto cb = _map_type_cb.find(*type); - if(cb == std::end(_map_type_cb)) { - BOOST_LOG(warning) - << "type [Unknown] { "sv << util::hex(*type).to_string_view() << " }"sv << std::endl - << "---data---"sv << std::endl << util::hex_vec(payload) << std::endl << "---end data---"sv; - } - - else { - cb->second(session, payload); - } + auto cb = _map_type_cb.find(*type); + if(cb == std::end(_map_type_cb)) { + BOOST_LOG(warning) + << "type [Unknown] { "sv << util::hex(*type).to_string_view() << " }"sv << std::endl + << "---data---"sv << std::endl + << util::hex_vec(payload) << std::endl + << "---end data---"sv; } - break; - case ENET_EVENT_TYPE_CONNECT: - BOOST_LOG(info) << "CLIENT CONNECTED"sv; - break; - case ENET_EVENT_TYPE_DISCONNECT: - BOOST_LOG(info) << "CLIENT DISCONNECTED"sv; - // No more clients to send video data to ^_^ - if(session->state == session::state_e::RUNNING) { - session::stop(*session); - } - break; - case ENET_EVENT_TYPE_NONE: - break; + + else { + cb->second(session, payload); + } + } break; + case ENET_EVENT_TYPE_CONNECT: + BOOST_LOG(info) << "CLIENT CONNECTED"sv; + break; + case ENET_EVENT_TYPE_DISCONNECT: + BOOST_LOG(info) << "CLIENT DISCONNECTED"sv; + // No more clients to send video data to ^_^ + if(session->state == session::state_e::RUNNING) { + session::stop(*session); + } + break; + case ENET_EVENT_TYPE_NONE: + break; } } } @@ -302,11 +301,11 @@ struct fec_t { util::buffer_t shards; char *data(size_t el) { - return &shards[el*blocksize]; + return &shards[el * blocksize]; } std::string_view operator[](size_t el) const { - return { &shards[el*blocksize], blocksize }; + return { &shards[el * blocksize], blocksize }; } size_t size() const { @@ -314,38 +313,42 @@ struct fec_t { } }; -fec_t encode(const std::string_view &payload, size_t blocksize, size_t fecpercentage) { +static fec_t encode(const std::string_view &payload, size_t blocksize, size_t fecpercentage) { auto payload_size = payload.size(); auto pad = payload_size % blocksize != 0; auto data_shards = payload_size / blocksize + (pad ? 1 : 0); auto parity_shards = (data_shards * fecpercentage + 99) / 100; - auto nr_shards = data_shards + parity_shards; + auto nr_shards = data_shards + parity_shards; if(nr_shards > DATA_SHARDS_MAX) { - BOOST_LOG(error) + BOOST_LOG(warning) << "Number of fragments for reed solomon exceeds DATA_SHARDS_MAX"sv << std::endl - << nr_shards << " > "sv << DATA_SHARDS_MAX; + << nr_shards << " > "sv << DATA_SHARDS_MAX + << ", skipping error correction"sv; - return { 0 }; + nr_shards = data_shards; + fecpercentage = 0; } util::buffer_t shards { nr_shards * blocksize }; - util::buffer_t shards_p { nr_shards }; + util::buffer_t shards_p { nr_shards }; // copy payload + padding auto next = std::copy(std::begin(payload), std::end(payload), std::begin(shards)); std::fill(next, std::end(shards), 0); // padding with zero for(auto x = 0; x < nr_shards; ++x) { - shards_p[x] = (uint8_t*)&shards[x * blocksize]; + shards_p[x] = (uint8_t *)&shards[x * blocksize]; } - // packets = parity_shards + data_shards - rs_t rs { reed_solomon_new(data_shards, parity_shards) }; + if(data_shards + parity_shards <= DATA_SHARDS_MAX) { + // packets = parity_shards + data_shards + rs_t rs { reed_solomon_new(data_shards, parity_shards) }; - reed_solomon_encode(rs.get(), shards_p.begin(), nr_shards, blocksize); + reed_solomon_encode(rs.get(), shards_p.begin(), nr_shards, blocksize); + } return { data_shards, @@ -355,11 +358,11 @@ fec_t encode(const std::string_view &payload, size_t blocksize, size_t fecpercen std::move(shards) }; } -} +} // namespace fec template std::vector insert(uint64_t insert_size, uint64_t slice_size, const std::string_view &data, F &&f) { - auto pad = data.size() % slice_size != 0; + auto pad = data.size() % slice_size != 0; auto elements = data.size() / slice_size + (pad ? 1 : 0); std::vector result; @@ -367,20 +370,20 @@ std::vector insert(uint64_t insert_size, uint64_t slice_size, const std auto next = std::begin(data); for(auto x = 0; x < elements - 1; ++x) { - void *p = &result[x*(insert_size + slice_size)]; + void *p = &result[x * (insert_size + slice_size)]; f(p, x, elements); - std::copy(next, next + slice_size, (char*)p + insert_size); + std::copy(next, next + slice_size, (char *)p + insert_size); next += slice_size; } - auto x = elements - 1; - void *p = &result[x*(insert_size + slice_size)]; + auto x = elements - 1; + void *p = &result[x * (insert_size + slice_size)]; f(p, x, elements); - std::copy(next, std::end(data), (char*)p + insert_size); + std::copy(next, std::end(data), (char *)p + insert_size); return result; } @@ -389,7 +392,7 @@ std::vector replace(const std::string_view &original, const std::string std::vector replaced; auto begin = std::begin(original); - auto next = std::search(begin, std::end(original), std::begin(old), std::end(old)); + auto next = std::search(begin, std::end(original), std::begin(old), std::end(old)); std::copy(begin, next, std::back_inserter(replaced)); std::copy(std::begin(_new), std::end(_new), std::back_inserter(replaced)); @@ -398,7 +401,7 @@ std::vector replace(const std::string_view &original, const std::string return replaced; } -void controlBroadcastThread(safe::signal_t *shutdown_event, control_server_t *server) { +void controlBroadcastThread(control_server_t *server) { server->map(packetTypes[IDX_START_A], [&](session_t *session, const std::string_view &payload) { BOOST_LOG(debug) << "type [IDX_START_A]"sv; }); @@ -408,8 +411,8 @@ void controlBroadcastThread(safe::signal_t *shutdown_event, control_server_t *se }); server->map(packetTypes[IDX_LOSS_STATS], [&](session_t *session, const std::string_view &payload) { - int32_t *stats = (int32_t*)payload.data(); - auto count = stats[0]; + int32_t *stats = (int32_t *)payload.data(); + auto count = stats[0]; std::chrono::milliseconds t { stats[1] }; auto lastGoodFrame = stats[3]; @@ -424,9 +427,9 @@ void controlBroadcastThread(safe::signal_t *shutdown_event, control_server_t *se }); server->map(packetTypes[IDX_INVALIDATE_REF_FRAMES], [&](session_t *session, const std::string_view &payload) { - auto frames = (std::int64_t *)payload.data(); + auto frames = (std::int64_t *)payload.data(); auto firstFrame = frames[0]; - auto lastFrame = frames[1]; + auto lastFrame = frames[1]; BOOST_LOG(debug) << "type [IDX_INVALIDATE_REF_FRAMES]"sv << std::endl @@ -439,7 +442,7 @@ void controlBroadcastThread(safe::signal_t *shutdown_event, control_server_t *se server->map(packetTypes[IDX_INPUT_DATA], [&](session_t *session, const std::string_view &payload) { BOOST_LOG(debug) << "type [IDX_INPUT_DATA]"sv; - int32_t tagged_cipher_length = util::endian::big(*(int32_t*)payload.data()); + int32_t tagged_cipher_length = util::endian::big(*(int32_t *)payload.data()); std::string_view tagged_cipher { payload.data() + sizeof(tagged_cipher_length), (size_t)tagged_cipher_length }; crypto::cipher_t cipher { session->gcm_key }; @@ -462,6 +465,7 @@ void controlBroadcastThread(safe::signal_t *shutdown_event, control_server_t *se input::passthrough(session->input, std::move(plaintext)); }); + auto shutdown_event = mail::man->event(mail::broadcast_shutdown); while(!shutdown_event->peek()) { { auto lg = server->_map_addr_session.lock(); @@ -498,12 +502,12 @@ void controlBroadcastThread(safe::signal_t *shutdown_event, control_server_t *se payload[0] = packetTypes[IDX_TERMINATION]; payload[1] = reason; - server->send(std::string_view {(char*)payload.data(), payload.size()}); + server->send(std::string_view { (char *)payload.data(), payload.size() }); auto lg = server->_map_addr_session.lock(); for(auto pos = std::begin(*server->_map_addr_session); pos != std::end(*server->_map_addr_session); ++pos) { auto session = pos->second.second; - session->shutdown_event.raise(true); + session->shutdown_event->raise(true); } } @@ -518,7 +522,9 @@ void recvThread(broadcast_ctx_t &ctx) { auto &video_sock = ctx.video_sock; auto &audio_sock = ctx.audio_sock; - auto &message_queue_queue = ctx.message_queue_queue; + auto &message_queue_queue = ctx.message_queue_queue; + auto broadcast_shutdown_event = mail::man->event(mail::broadcast_shutdown); + auto &io = ctx.io; udp::endpoint peer; @@ -532,34 +538,34 @@ void recvThread(broadcast_ctx_t &ctx) { TUPLE_3D_REF(socket_type, addr, message_queue, *message_queue_opt); switch(socket_type) { - case socket_e::video: - if(message_queue) { - peer_to_video_session.emplace(addr, message_queue); - } - else { - peer_to_video_session.erase(addr); - } - break; - case socket_e::audio: - if(message_queue) { - peer_to_audio_session.emplace(addr, message_queue); - } - else { - peer_to_audio_session.erase(addr); - } - break; + case socket_e::video: + if(message_queue) { + peer_to_video_session.emplace(addr, message_queue); + } + else { + peer_to_video_session.erase(addr); + } + break; + case socket_e::audio: + if(message_queue) { + peer_to_audio_session.emplace(addr, message_queue); + } + else { + peer_to_audio_session.erase(addr); + } + break; } } }; auto recv_func_init = [&](udp::socket &sock, int buf_elem, std::map &peer_to_session) { - recv_func[buf_elem] = [&,buf_elem](const boost::system::error_code &ec, size_t bytes) { + recv_func[buf_elem] = [&, buf_elem](const boost::system::error_code &ec, size_t bytes) { auto fg = util::fail_guard([&]() { sock.async_receive_from(asio::buffer(buf[buf_elem]), peer, 0, recv_func[buf_elem]); }); auto type_str = buf_elem ? "AUDIO"sv : "VIDEO"sv; - BOOST_LOG(debug) << "Recv: "sv << peer.address().to_string() << ":"sv << peer.port() << " :: " << type_str; + BOOST_LOG(verbose) << "Recv: "sv << peer.address().to_string() << ':' << peer.port() << " :: " << type_str; populate_peer_to_session(); @@ -590,61 +596,58 @@ void recvThread(broadcast_ctx_t &ctx) { video_sock.async_receive_from(asio::buffer(buf[0]), peer, 0, recv_func[0]); audio_sock.async_receive_from(asio::buffer(buf[1]), peer, 0, recv_func[1]); - while(!broadcast_shutdown_event.peek()) { + while(!broadcast_shutdown_event->peek()) { io.run(); } } -void videoBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, video::packet_queue_t packets) { +void videoBroadcastThread(udp::socket &sock) { + auto shutdown_event = mail::man->event(mail::broadcast_shutdown); + auto packets = mail::man->queue(mail::video_packets); + while(auto packet = packets->pop()) { if(shutdown_event->peek()) { break; } - auto session = (session_t*)packet->channel_data; - auto lowseq = session->video.lowseq; + auto session = (session_t *)packet->channel_data; + auto lowseq = session->video.lowseq; - std::string_view payload{(char *) packet->data, (size_t) packet->size}; + std::string_view payload { (char *)packet->data, (size_t)packet->size }; std::vector payload_new; auto nv_packet_header = "\0017charss"sv; std::copy(std::begin(nv_packet_header), std::end(nv_packet_header), std::back_inserter(payload_new)); std::copy(std::begin(payload), std::end(payload), std::back_inserter(payload_new)); - payload = {(char *) payload_new.data(), payload_new.size()}; + payload = { (char *)payload_new.data(), payload_new.size() }; - // make sure moonlight recognizes the nalu code for IDR frames - if (packet->flags & AV_PKT_FLAG_KEY) { - // TODO: Not all encoders encode their IDR frames with the 4 byte NALU prefix - std::string_view frame_old = "\000\000\001e"sv; - std::string_view frame_new = "\000\000\000\001e"sv; - if(session->config.monitor.videoFormat != 0) { - frame_old = "\000\000\001("sv; - frame_new = "\000\000\000\001("sv; + if(packet->flags & AV_PKT_FLAG_KEY) { + for(auto &replacement : *packet->replacements) { + auto frame_old = replacement.old; + auto frame_new = replacement._new; + + payload_new = replace(payload, frame_old, frame_new); + payload = { (char *)payload_new.data(), payload_new.size() }; } - - payload_new = replace(payload, frame_old, frame_new); - payload = {(char *) payload_new.data(), payload_new.size()}; } // insert packet headers - auto blocksize = session->config.packetsize + MAX_RTP_HEADER_SIZE; + auto blocksize = session->config.packetsize + MAX_RTP_HEADER_SIZE; auto payload_blocksize = blocksize - sizeof(video_packet_raw_t); auto fecPercentage = config::stream.fec_percentage; payload_new = insert(sizeof(video_packet_raw_t), payload_blocksize, - payload, [&](void *p, int fecIndex, int end) { + payload, [&](void *p, int fecIndex, int end) { video_packet_raw_t *video_packet = (video_packet_raw_t *)p; - video_packet->packet.flags = FLAG_CONTAINS_PIC_DATA; - video_packet->packet.frameIndex = packet->pts; + video_packet->packet.flags = FLAG_CONTAINS_PIC_DATA; + video_packet->packet.frameIndex = packet->pts; video_packet->packet.streamPacketIndex = ((uint32_t)lowseq + fecIndex) << 8; - video_packet->packet.fecInfo = ( - fecIndex << 12 | - end << 22 | - fecPercentage << 4 - ); + video_packet->packet.fecInfo = (fecIndex << 12 | + end << 22 | + fecPercentage << 4); if(fecIndex == 0) { video_packet->packet.flags |= FLAG_SOF; @@ -654,11 +657,11 @@ void videoBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, vid video_packet->packet.flags |= FLAG_EOF; } - video_packet->rtp.header = FLAG_EXTENSION; + video_packet->rtp.header = FLAG_EXTENSION; video_packet->rtp.sequenceNumber = util::endian::big(lowseq + fecIndex); }); - payload = {(char *) payload_new.data(), payload_new.size()}; + payload = { (char *)payload_new.data(), payload_new.size() }; auto shards = fec::encode(payload, blocksize, fecPercentage); if(shards.data_shards == 0) { @@ -666,17 +669,15 @@ void videoBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, vid continue; } - for (auto x = shards.data_shards; x < shards.size(); ++x) { + for(auto x = shards.data_shards; x < shards.size(); ++x) { auto *inspect = (video_packet_raw_t *)shards.data(x); inspect->packet.frameIndex = packet->pts; - inspect->packet.fecInfo = ( - x << 12 | - shards.data_shards << 22 | - fecPercentage << 4 - ); + inspect->packet.fecInfo = (x << 12 | + shards.data_shards << 22 | + shards.percentage << 4); - inspect->rtp.header = FLAG_EXTENSION; + inspect->rtp.header = FLAG_EXTENSION; inspect->rtp.sequenceNumber = util::endian::big(lowseq + x); } @@ -697,28 +698,31 @@ void videoBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, vid shutdown_event->raise(true); } -void audioBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, audio::packet_queue_t packets) { - while (auto packet = packets->pop()) { +void audioBroadcastThread(udp::socket &sock) { + auto shutdown_event = mail::man->event(mail::broadcast_shutdown); + auto packets = mail::man->queue(mail::audio_packets); + + while(auto packet = packets->pop()) { if(shutdown_event->peek()) { break; } TUPLE_2D_REF(channel_data, packet_data, *packet); - auto session = (session_t*)channel_data; + auto session = (session_t *)channel_data; auto frame = session->audio.frame++; - audio_packet_t audio_packet { (audio_packet_raw_t*)malloc(sizeof(audio_packet_raw_t) + packet_data.size()) }; + audio_packet_t audio_packet { (audio_packet_raw_t *)malloc(sizeof(audio_packet_raw_t) + packet_data.size()) }; - audio_packet->rtp.header = 0; - audio_packet->rtp.packetType = 97; + audio_packet->rtp.header = 0; + audio_packet->rtp.packetType = 97; audio_packet->rtp.sequenceNumber = util::endian::big(frame); - audio_packet->rtp.timestamp = 0; - audio_packet->rtp.ssrc = 0; + audio_packet->rtp.timestamp = 0; + audio_packet->rtp.ssrc = 0; std::copy(std::begin(packet_data), std::end(packet_data), audio_packet->payload()); - sock.send_to(asio::buffer((char*)audio_packet.get(), sizeof(audio_packet_raw_t) + packet_data.size()), session->audio.peer); + sock.send_to(asio::buffer((char *)audio_packet.get(), sizeof(audio_packet_raw_t) + packet_data.size()), session->audio.peer); BOOST_LOG(verbose) << "Audio ["sv << frame << "] :: send..."sv; } @@ -761,13 +765,11 @@ int start_broadcast(broadcast_ctx_t &ctx) { return -1; } - ctx.video_packets = std::make_shared(30); - ctx.audio_packets = std::make_shared(30); ctx.message_queue_queue = std::make_shared(30); - ctx.video_thread = std::thread { videoBroadcastThread, &broadcast_shutdown_event, std::ref(ctx.video_sock), ctx.video_packets }; - ctx.audio_thread = std::thread { audioBroadcastThread, &broadcast_shutdown_event, std::ref(ctx.audio_sock), ctx.audio_packets }; - ctx.control_thread = std::thread { controlBroadcastThread, &broadcast_shutdown_event, &ctx.control_server }; + ctx.video_thread = std::thread { videoBroadcastThread, std::ref(ctx.video_sock) }; + ctx.audio_thread = std::thread { audioBroadcastThread, std::ref(ctx.audio_sock) }; + ctx.control_thread = std::thread { controlBroadcastThread, &ctx.control_server }; ctx.recv_thread = std::thread { recvThread, std::ref(ctx) }; @@ -775,11 +777,16 @@ int start_broadcast(broadcast_ctx_t &ctx) { } void end_broadcast(broadcast_ctx_t &ctx) { - broadcast_shutdown_event.raise(true); + auto broadcast_shutdown_event = mail::man->event(mail::broadcast_shutdown); + + broadcast_shutdown_event->raise(true); + + auto video_packets = mail::man->queue(mail::video_packets); + auto audio_packets = mail::man->queue(mail::audio_packets); // Minimize delay stopping video/audio threads - ctx.video_packets->stop(); - ctx.audio_packets->stop(); + video_packets->stop(); + audio_packets->stop(); ctx.message_queue_queue->stop(); ctx.io.stop(); @@ -787,8 +794,8 @@ void end_broadcast(broadcast_ctx_t &ctx) { ctx.video_sock.close(); ctx.audio_sock.close(); - ctx.video_packets.reset(); - ctx.audio_packets.reset(); + video_packets.reset(); + audio_packets.reset(); BOOST_LOG(debug) << "Waiting for main listening thread to end..."sv; ctx.recv_thread.join(); @@ -800,7 +807,7 @@ void end_broadcast(broadcast_ctx_t &ctx) { ctx.control_thread.join(); BOOST_LOG(debug) << "All broadcasting threads ended"sv; - broadcast_shutdown_event.reset(); + broadcast_shutdown_event->reset(); } int recv_ping(decltype(broadcast)::ptr_t ref, socket_e type, asio::ip::address &addr, std::chrono::milliseconds timeout) { @@ -842,7 +849,7 @@ void videoThread(session_t *session, std::string addr_str) { while_starting_do_nothing(session->state); auto addr = asio::ip::make_address(addr_str); - auto ref = broadcast.ref(); + auto ref = broadcast.ref(); auto port = recv_ping(ref, socket_e::video, addr, config::stream.ping_timeout); if(port < 0) { return; @@ -852,7 +859,7 @@ void videoThread(session_t *session, std::string addr_str) { session->video.peer.port(port); BOOST_LOG(debug) << "Start capturing Video"sv; - video::capture(&session->shutdown_event, ref->video_packets, session->video.idr_events, session->config.monitor, session); + video::capture(session->mail, session->config.monitor, session); } void audioThread(session_t *session, std::string addr_str) { @@ -864,7 +871,7 @@ void audioThread(session_t *session, std::string addr_str) { auto addr = asio::ip::make_address(addr_str); - auto ref = broadcast.ref(); + auto ref = broadcast.ref(); auto port = recv_ping(ref, socket_e::audio, addr, config::stream.ping_timeout); if(port < 0) { return; @@ -874,7 +881,7 @@ void audioThread(session_t *session, std::string addr_str) { session->audio.peer.port(port); BOOST_LOG(debug) << "Start capturing Audio"sv; - audio::capture(&session->shutdown_event, ref->audio_packets, session->config.audio, session); + audio::capture(session->mail, session->config.audio, session); } namespace session { @@ -884,14 +891,13 @@ state_e state(session_t &session) { void stop(session_t &session) { while_starting_do_nothing(session.state); - - auto expected = state_e::RUNNING; + auto expected = state_e::RUNNING; auto already_stopping = !session.state.compare_exchange_strong(expected, state_e::STOPPING); if(already_stopping) { return; } - session.shutdown_event.raise(true); + session.shutdown_event->raise(true); } void join(session_t &session) { @@ -901,11 +907,14 @@ void join(session_t &session) { session.audioThread.join(); BOOST_LOG(debug) << "Waiting for control to end..."sv; session.controlEnd.view(); + //Reset input on session stop to avoid stuck repeated keys + BOOST_LOG(debug) << "Resetting Input..."sv; + input::reset(session.input); BOOST_LOG(debug) << "Session ended"sv; } int start(session_t &session, const std::string &addr_string) { - session.input = input::alloc(); + session.input = input::alloc(session.mail); session.broadcast_ref = broadcast.ref(); if(!session.broadcast_ref) { @@ -916,8 +925,8 @@ int start(session_t &session, const std::string &addr_string) { session.pingTimeout = std::chrono::steady_clock::now() + config::stream.ping_timeout; - session.audioThread = std::thread {audioThread, &session, addr_string}; - session.videoThread = std::thread {videoThread, &session, addr_string}; + session.audioThread = std::thread { audioThread, &session, addr_string }; + session.videoThread = std::thread { videoThread, &session, addr_string }; session.state.store(state_e::RUNNING, std::memory_order_relaxed); @@ -927,19 +936,25 @@ int start(session_t &session, const std::string &addr_string) { std::shared_ptr alloc(config_t &config, crypto::aes_t &gcm_key, crypto::aes_t &iv) { auto session = std::make_shared(); - session->config = config; - session->gcm_key = gcm_key; - session->iv = iv; + auto mail = std::make_shared(); - session->video.idr_events = std::make_shared(); - session->video.lowseq = 0; + session->shutdown_event = mail->event(mail::shutdown); + + session->config = config; + session->gcm_key = gcm_key; + session->iv = iv; + + session->video.idr_events = mail->event(mail::idr); + session->video.lowseq = 0; session->audio.frame = 1; session->control.peer = nullptr; session->state.store(state_e::STOPPED, std::memory_order_relaxed); + session->mail = std::move(mail); + return session; } -} -} +} // namespace session +} // namespace stream diff --git a/sunshine/stream.h b/sunshine/stream.h index cd63fcb2..ed526ce8 100644 --- a/sunshine/stream.h +++ b/sunshine/stream.h @@ -7,9 +7,9 @@ #include -#include "video.h" #include "audio.h" #include "crypto.h" +#include "video.h" namespace stream { struct session_t; @@ -18,7 +18,6 @@ struct config_t { video::config_t monitor; int packetsize; - bool sops; std::optional gcmap; }; @@ -35,9 +34,7 @@ int start(session_t &session, const std::string &addr_string); void stop(session_t &session); void join(session_t &session); state_e state(session_t &session); -} - -extern safe::signal_t broadcast_shutdown_event; -} +} // namespace session +} // namespace stream #endif //SUNSHINE_STREAM_H diff --git a/sunshine/sync.h b/sunshine/sync.h index 4689e595..8d67de2a 100644 --- a/sunshine/sync.h +++ b/sunshine/sync.h @@ -5,9 +5,9 @@ #ifndef SUNSHINE_SYNC_H #define SUNSHINE_SYNC_H -#include -#include #include +#include +#include namespace util { @@ -21,8 +21,8 @@ public: return std::lock_guard { _lock }; } - template - sync_t(Args&&... args) : raw {std::forward(args)... } {} + template + sync_t(Args &&...args) : raw { std::forward(args)... } {} sync_t &operator=(sync_t &&other) noexcept { std::lock(_lock, other._lock); @@ -84,11 +84,12 @@ public: } value_t raw; + private: mutex_t _lock; }; -} +} // namespace util #endif //T_MAN_SYNC_H diff --git a/sunshine/task_pool.h b/sunshine/task_pool.h index 83fe8596..fc07e8ab 100644 --- a/sunshine/task_pool.h +++ b/sunshine/task_pool.h @@ -1,18 +1,18 @@ #ifndef KITTY_TASK_POOL_H #define KITTY_TASK_POOL_H -#include -#include -#include #include -#include +#include #include +#include #include -#include #include +#include +#include +#include -#include "utility.h" #include "move_by_copy.h" +#include "utility.h" namespace util { class _ImplBase { @@ -29,8 +29,7 @@ class _Impl : public _ImplBase { Function _func; public: - - _Impl(Function&& f) : _func(std::forward(f)) { } + _Impl(Function &&f) : _func(std::forward(f)) {} void run() override { _func(); @@ -40,7 +39,7 @@ public: class TaskPool { public: typedef std::unique_ptr<_ImplBase> __task; - typedef _ImplBase* task_id_t; + typedef _ImplBase *task_id_t; typedef std::chrono::steady_clock::time_point __time_point; @@ -53,9 +52,10 @@ public: timer_task_t(task_id_t task_id, std::future &future) : task_id { task_id }, future { std::move(future) } {} }; + protected: std::deque<__task> _tasks; - std::vector> _timer_tasks; + std::vector> _timer_tasks; std::mutex _task_mutex; public: @@ -70,8 +70,8 @@ public: } template - auto push(Function && newTask, Args &&... args) { - static_assert(std::is_invocable_v, "arguments don't match the function"); + auto push(Function &&newTask, Args &&...args) { + static_assert(std::is_invocable_v, "arguments don't match the function"); using __return = std::invoke_result_t; using task_t = std::packaged_task<__return()>; @@ -81,12 +81,12 @@ public: }; task_t task(std::move(bind)); - + auto future = task.get_future(); - + std::lock_guard lg(_task_mutex); _tasks.emplace_back(toRunnable(std::move(task))); - + return future; } @@ -107,14 +107,14 @@ public: * @return an id to potentially delay the task */ template - auto pushDelayed(Function &&newTask, std::chrono::duration duration, Args &&... args) { - static_assert(std::is_invocable_v, "arguments don't match the function"); + auto pushDelayed(Function &&newTask, std::chrono::duration duration, Args &&...args) { + static_assert(std::is_invocable_v, "arguments don't match the function"); using __return = std::invoke_result_t; using task_t = std::packaged_task<__return()>; __time_point time_point; - if constexpr (std::is_floating_point_v) { + if constexpr(std::is_floating_point_v) { time_point = std::chrono::steady_clock::now() + std::chrono::duration_cast(duration); } else { @@ -127,7 +127,7 @@ public: task_t task(std::move(bind)); - auto future = task.get_future(); + auto future = task.get_future(); auto runnable = toRunnable(std::move(task)); task_id_t task_id = &*runnable; @@ -160,13 +160,14 @@ public: } // smaller time goes to the back - auto prev = it -1; + auto prev = it - 1; while(it > _timer_tasks.cbegin()) { if(std::get<0>(*it) > std::get<0>(*prev)) { std::swap(*it, *prev); } - --prev; --it; + --prev; + --it; } } @@ -201,20 +202,20 @@ public: std::optional<__task> pop() { std::lock_guard lg(_task_mutex); - + if(!_tasks.empty()) { __task task = std::move(_tasks.front()); _tasks.pop_front(); return std::move(task); } - + if(!_timer_tasks.empty() && std::get<0>(_timer_tasks.back()) <= std::chrono::steady_clock::now()) { __task task = std::move(std::get<1>(_timer_tasks.back())); _timer_tasks.pop_back(); - + return std::move(task); } - + return std::nullopt; } @@ -233,12 +234,12 @@ public: return std::get<0>(_timer_tasks.back()); } -private: +private: template std::unique_ptr<_ImplBase> toRunnable(Function &&f) { - return std::make_unique<_Impl>(std::forward(f)); + return std::make_unique<_Impl>(std::forward(f)); } }; -} +} // namespace util #endif diff --git a/sunshine/thread_pool.h b/sunshine/thread_pool.h index be3f4eb9..8048e6d0 100644 --- a/sunshine/thread_pool.h +++ b/sunshine/thread_pool.h @@ -1,8 +1,8 @@ #ifndef KITTY_THREAD_POOL_H #define KITTY_THREAD_POOL_H -#include #include "task_pool.h" +#include namespace util { /* @@ -12,32 +12,33 @@ namespace util { class ThreadPool : public TaskPool { public: typedef TaskPool::__task __task; - + private: std::vector _thread; std::condition_variable _cv; std::mutex _lock; - + bool _continue; + public: ThreadPool() : _continue { false } {} explicit ThreadPool(int threads) : _thread(threads), _continue { true } { - for (auto &t : _thread) { + for(auto &t : _thread) { t = std::thread(&ThreadPool::_main, this); } } ~ThreadPool() noexcept { - if (!_continue) return; + if(!_continue) return; stop(); join(); } template - auto push(Function && newTask, Args &&... args) { + auto push(Function &&newTask, Args &&...args) { std::lock_guard lg(_lock); auto future = TaskPool::push(std::forward(newTask), std::forward(args)...); @@ -52,7 +53,7 @@ public: } template - auto pushDelayed(Function &&newTask, std::chrono::duration duration, Args &&... args) { + auto pushDelayed(Function &&newTask, std::chrono::duration duration, Args &&...args) { std::lock_guard lg(_lock); auto future = TaskPool::pushDelayed(std::forward(newTask), duration, std::forward(args)...); @@ -79,15 +80,14 @@ public: } void join() { - for (auto & t : _thread) { + for(auto &t : _thread) { t.join(); } } public: - void _main() { - while (_continue) { + while(_continue) { if(auto task = this->pop()) { (*task)->run(); } @@ -117,5 +117,5 @@ public: } } }; -} +} // namespace util #endif diff --git a/sunshine/thread_safe.h b/sunshine/thread_safe.h index d166cc0f..01a122fe 100644 --- a/sunshine/thread_safe.h +++ b/sunshine/thread_safe.h @@ -5,28 +5,29 @@ #ifndef SUNSHINE_THREAD_SAFE_H #define SUNSHINE_THREAD_SAFE_H -#include -#include -#include #include +#include #include +#include +#include +#include #include "utility.h" namespace safe { template class event_t { +public: using status_t = util::optional_t; -public: - template + template void raise(Args &&...args) { std::lock_guard lg { _lock }; if(!_continue) { return; } - if constexpr (std::is_same_v, status_t>) { + if constexpr(std::is_same_v, status_t>) { _status = std::make_optional(std::forward(args)...); } else { @@ -38,42 +39,42 @@ public: // pop and view shoud not be used interchangebly status_t pop() { - std::unique_lock ul{ _lock }; + std::unique_lock ul { _lock }; - if (!_continue) { + if(!_continue) { return util::false_v; } - while (!_status) { + while(!_status) { _cv.wait(ul); - if (!_continue) { + if(!_continue) { return util::false_v; } } auto val = std::move(_status); - _status = util::false_v; + _status = util::false_v; return val; } // pop and view shoud not be used interchangebly template status_t pop(std::chrono::duration delay) { - std::unique_lock ul{ _lock }; + std::unique_lock ul { _lock }; - if (!_continue) { + if(!_continue) { return util::false_v; } - while (!_status) { - if (!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) { + while(!_status) { + if(!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) { return util::false_v; } } auto val = std::move(_status); - _status = util::false_v; + _status = util::false_v; return val; } @@ -81,14 +82,14 @@ public: const status_t &view() { std::unique_lock ul { _lock }; - if (!_continue) { + if(!_continue) { return util::false_v; } - while (!_status) { + while(!_status) { _cv.wait(ul); - if (!_continue) { + if(!_continue) { return util::false_v; } } @@ -97,13 +98,11 @@ public: } bool peek() { - std::lock_guard lg { _lock }; - return _continue && (bool)_status; } void stop() { - std::lock_guard lg{ _lock }; + std::lock_guard lg { _lock }; _continue = false; @@ -111,7 +110,7 @@ public: } void reset() { - std::lock_guard lg{ _lock }; + std::lock_guard lg { _lock }; _continue = true; @@ -121,8 +120,8 @@ public: [[nodiscard]] bool running() const { return _continue; } -private: +private: bool _continue { true }; status_t _status { util::false_v }; @@ -131,14 +130,101 @@ private: }; template -class queue_t { +class alarm_raw_t { +public: using status_t = util::optional_t; + alarm_raw_t() : _status { util::false_v } {} + + void ring(const status_t &status) { + std::lock_guard lg(_lock); + + _status = status; + _cv.notify_one(); + } + + void ring(status_t &&status) { + std::lock_guard lg(_lock); + + _status = std::move(status); + _cv.notify_one(); + } + + template + auto wait_for(const std::chrono::duration &rel_time) { + std::unique_lock ul(_lock); + + return _cv.wait_for(ul, rel_time, [this]() { return (bool)status(); }); + } + + template + auto wait_for(const std::chrono::duration &rel_time, Pred &&pred) { + std::unique_lock ul(_lock); + + return _cv.wait_for(ul, rel_time, [this, &pred]() { return (bool)status() || pred(); }); + } + + template + auto wait_until(const std::chrono::duration &rel_time) { + std::unique_lock ul(_lock); + + return _cv.wait_until(ul, rel_time, [this]() { return (bool)status(); }); + } + + template + auto wait_until(const std::chrono::duration &rel_time, Pred &&pred) { + std::unique_lock ul(_lock); + + return _cv.wait_until(ul, rel_time, [this, &pred]() { return (bool)status() || pred(); }); + } + + auto wait() { + std::unique_lock ul(_lock); + _cv.wait(ul, [this]() { return (bool)status(); }); + } + + template + auto wait(Pred &&pred) { + std::unique_lock ul(_lock); + _cv.wait(ul, [this, &pred]() { return (bool)status() || pred(); }); + } + + const status_t &status() const { + return _status; + } + + status_t &status() { + return _status; + } + + void reset() { + _status = status_t {}; + } + +private: + std::mutex _lock; + std::condition_variable _cv; + + status_t _status; +}; + +template +using alarm_t = std::shared_ptr>; + +template +alarm_t make_alarm() { + return std::make_shared>(); +} + +template +class queue_t { public: + using status_t = util::optional_t; + queue_t(std::uint32_t max_elements) : _max_elements { max_elements } {} - template - void raise(Args &&... args) { + template + void raise(Args &&...args) { std::lock_guard ul { _lock }; if(!_continue) { @@ -155,8 +241,6 @@ public: } bool peek() { - std::lock_guard lg { _lock }; - return _continue && !_queue.empty(); } @@ -164,12 +248,12 @@ public: status_t pop(std::chrono::duration delay) { std::unique_lock ul { _lock }; - if (!_continue) { + if(!_continue) { return util::false_v; } - while (_queue.empty()) { - if (!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) { + while(_queue.empty()) { + if(!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) { return util::false_v; } } @@ -183,14 +267,14 @@ public: status_t pop() { std::unique_lock ul { _lock }; - if (!_continue) { + if(!_continue) { return util::false_v; } - while (_queue.empty()) { + while(_queue.empty()) { _cv.wait(ul); - if (!_continue) { + if(!_continue) { return util::false_v; } } @@ -202,7 +286,6 @@ public: } std::vector &unsafe() { - std::lock_guard { _lock }; return _queue; } @@ -219,7 +302,6 @@ public: } private: - bool _continue { true }; std::uint32_t _max_elements; @@ -235,7 +317,7 @@ public: using element_type = T; using construct_f = std::function; - using destruct_f = std::function; + using destruct_f = std::function; struct ptr_t { shared_t *owner; @@ -252,7 +334,7 @@ public: return; } - auto tmp = ptr.owner->ref(); + auto tmp = ptr.owner->ref(); tmp.owner = nullptr; } @@ -282,7 +364,7 @@ public: } } - operator bool () const { + operator bool() const { return owner != nullptr; } @@ -298,22 +380,22 @@ public: } element_type *get() const { - return reinterpret_cast(owner->_object_buf.data()); + return reinterpret_cast(owner->_object_buf.data()); } element_type *operator->() { - return reinterpret_cast(owner->_object_buf.data()); + return reinterpret_cast(owner->_object_buf.data()); } }; template - shared_t(FC && fc, FD &&fd) : _construct { std::forward(fc) }, _destruct { std::forward(fd) } {} + shared_t(FC &&fc, FD &&fd) : _construct { std::forward(fc) }, _destruct { std::forward(fd) } {} [[nodiscard]] ptr_t ref() { std::lock_guard lg { _lock }; if(!_count) { new(_object_buf.data()) element_type; - if(_construct(*reinterpret_cast(_object_buf.data()))) { + if(_construct(*reinterpret_cast(_object_buf.data()))) { return ptr_t { nullptr }; } } @@ -322,6 +404,7 @@ public: return ptr_t { this }; } + private: construct_f _construct; destruct_f _destruct; @@ -340,6 +423,89 @@ auto make_shared(F_Construct &&fc, F_Destruct &&fd) { } using signal_t = event_t; + +class mail_raw_t; +using mail_t = std::shared_ptr; + +void cleanup(mail_raw_t *); +template +class post_t : public T { +public: + template + post_t(mail_t mail, Args &&...args) : T(std::forward(args)...), mail { std::move(mail) } {} + + mail_t mail; + + ~post_t() { + cleanup(mail.get()); + } +}; + +template +inline auto lock(const std::weak_ptr &wp) { + return std::reinterpret_pointer_cast(wp.lock()); } +class mail_raw_t : public std::enable_shared_from_this { +public: + template + using event_t = std::shared_ptr>>; + + template + using queue_t = std::shared_ptr>>; + + template + event_t event(const std::string_view &id) { + std::lock_guard lg { mutex }; + + auto it = id_to_post.find(id); + if(it != std::end(id_to_post)) { + return lock>(it->second); + } + + auto post = std::make_shared::element_type>(shared_from_this()); + id_to_post.emplace(std::pair> { std::string { id }, post }); + + return post; + } + + template + queue_t queue(const std::string_view &id) { + std::lock_guard lg { mutex }; + + auto it = id_to_post.find(id); + if(it != std::end(id_to_post)) { + return lock>(it->second); + } + + auto post = std::make_shared::element_type>(shared_from_this(), 32); + id_to_post.emplace(std::pair> { std::string { id }, post }); + + return post; + } + + void cleanup() { + std::lock_guard lg { mutex }; + + for(auto it = std::begin(id_to_post); it != std::end(id_to_post); ++it) { + auto &weak = it->second; + + if(weak.expired()) { + id_to_post.erase(it); + + return; + } + } + } + + std::mutex mutex; + + std::map, std::less<>> id_to_post; +}; + +inline void cleanup(mail_raw_t *mail) { + mail->cleanup(); +} +} // namespace safe + #endif //SUNSHINE_THREAD_SAFE_H diff --git a/sunshine/utility.h b/sunshine/utility.h index 0c03c51d..d991bad4 100644 --- a/sunshine/utility.h +++ b/sunshine/utility.h @@ -1,63 +1,109 @@ #ifndef UTILITY_H #define UTILITY_H +#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#define KITTY_WHILE_LOOP(x, y, z) { x;while(y) z } -#define KITTY_DECL_CONSTR(x)\ - x(x&&) noexcept = default;\ - x&operator=(x&&) noexcept = default;\ +#define KITTY_WHILE_LOOP(x, y, z) \ + { \ + x; \ + while(y) z \ + } + +template +struct argument_type; + +template +struct argument_type { typedef U type; }; + +#define KITTY_USING_MOVE_T(move_t, t, init_val, z) \ + class move_t { \ + public: \ + using element_type = typename argument_type::type; \ + \ + move_t() : el { init_val } {} \ + template \ + move_t(Args &&...args) : el { std::forward(args)... } {} \ + move_t(const move_t &) = delete; \ + \ + move_t(move_t &&other) noexcept : el { std::move(other.el) } { \ + other.el = element_type { init_val }; \ + } \ + \ + move_t &operator=(const move_t &) = delete; \ + \ + move_t &operator=(move_t &&other) { \ + std::swap(el, other.el); \ + return *this; \ + } \ + element_type *operator->() { return ⪙ } \ + const element_type *operator->() const { return ⪙ } \ + \ + ~move_t() z \ + \ + element_type el; \ + } + +#define KITTY_DECL_CONSTR(x) \ + x(x &&) noexcept = default; \ + x &operator=(x &&) noexcept = default; \ x(); -#define KITTY_DEFAULT_CONSTR(x)\ - x(x&&) noexcept = default;\ - x&operator=(x&&) noexcept = default;\ - x() = default; +#define KITTY_DEFAULT_CONSTR(x) \ + x(x &&) noexcept = default; \ + x &operator=(x &&) noexcept = default; \ + x() = default; -#define KITTY_DEFAULT_CONSTR_THROW(x)\ - x(x&&) = default;\ - x&operator=(x&&) = default;\ - x() = default; +#define KITTY_DEFAULT_CONSTR_THROW(x) \ + x(x &&) = default; \ + x &operator=(x &&) = default; \ + x() = default; -#define TUPLE_2D(a,b, expr)\ - decltype(expr) a##_##b = expr;\ - auto &a = std::get<0>(a##_##b);\ - auto &b = std::get<1>(a##_##b) +#define TUPLE_2D(a, b, expr) \ + decltype(expr) a##_##b = expr; \ + auto &a = std::get<0>(a##_##b); \ + auto &b = std::get<1>(a##_##b) -#define TUPLE_2D_REF(a,b, expr)\ - auto &a##_##b = expr;\ - auto &a = std::get<0>(a##_##b);\ - auto &b = std::get<1>(a##_##b) +#define TUPLE_2D_REF(a, b, expr) \ + auto &a##_##b = expr; \ + auto &a = std::get<0>(a##_##b); \ + auto &b = std::get<1>(a##_##b) -#define TUPLE_3D(a,b,c, expr)\ - decltype(expr) a##_##b##_##c = expr;\ - auto &a = std::get<0>(a##_##b##_##c);\ - auto &b = std::get<1>(a##_##b##_##c);\ - auto &c = std::get<2>(a##_##b##_##c) +#define TUPLE_3D(a, b, c, expr) \ + decltype(expr) a##_##b##_##c = expr; \ + auto &a = std::get<0>(a##_##b##_##c); \ + auto &b = std::get<1>(a##_##b##_##c); \ + auto &c = std::get<2>(a##_##b##_##c) -#define TUPLE_3D_REF(a,b,c, expr)\ - auto &a##_##b##_##c = expr;\ - auto &a = std::get<0>(a##_##b##_##c);\ - auto &b = std::get<1>(a##_##b##_##c);\ - auto &c = std::get<2>(a##_##b##_##c) +#define TUPLE_3D_REF(a, b, c, expr) \ + auto &a##_##b##_##c = expr; \ + auto &a = std::get<0>(a##_##b##_##c); \ + auto &b = std::get<1>(a##_##b##_##c); \ + auto &c = std::get<2>(a##_##b##_##c) + +#define TUPLE_EL(a, b, expr) \ + decltype(expr) a##_ = expr; \ + auto &a = std::get(a##_) + +#define TUPLE_EL_REF(a, b, expr) \ + auto &a = std::get(expr) namespace util { -template class X, class...Y> +template class X, class... Y> struct __instantiation_of : public std::false_type {}; template class X, class... Y> struct __instantiation_of> : public std::true_type {}; -template class X, class T, class...Y> +template class X, class T, class... Y> static constexpr auto instantiation_of_v = __instantiation_of::value; template @@ -76,45 +122,16 @@ struct __either { template using either_t = typename __either::type; -template -struct __false_v; - -template -struct __false_v>> { - static constexpr std::nullopt_t value = std::nullopt; -}; - -template -struct __false_v || instantiation_of_v || instantiation_of_v) - >> { - static constexpr std::nullptr_t value = nullptr; -}; - -template -struct __false_v>> { - static constexpr bool value = false; -}; - -template -static constexpr auto false_v = __false_v::value; - -template -using optional_t = either_t< - (std::is_same_v || - instantiation_of_v || - instantiation_of_v || - std::is_pointer_v), - T, std::optional>; - -template struct overloaded : Ts... { using Ts::operator()...; }; -template overloaded(Ts...) -> overloaded; +template +struct overloaded : Ts... { using Ts::operator()...; }; +template +overloaded(Ts...) -> overloaded; template class FailGuard { public: FailGuard() = delete; - FailGuard(T && f) noexcept : _func { std::forward(f) } {} + FailGuard(T &&f) noexcept : _func { std::forward(f) } {} FailGuard(FailGuard &&other) noexcept : _func { std::move(other._func) } { this->failure = other.failure; @@ -134,12 +151,13 @@ public: void disable() { failure = false; } bool failure { true }; + private: T _func; }; template -[[nodiscard]] auto fail_guard(T && f) { +[[nodiscard]] auto fail_guard(T &&f) { return FailGuard { std::forward(f) }; } @@ -149,9 +167,9 @@ void append_struct(std::vector &buf, const T &_struct) { buf.reserve(data_len); - auto *data = (uint8_t *) & _struct; + auto *data = (uint8_t *)&_struct; - for (size_t x = 0; x < data_len; ++x) { + for(size_t x = 0; x < data_len; ++x) { buf.push_back(data[x]); } } @@ -160,24 +178,26 @@ template class Hex { public: typedef T elem_type; + private: const char _bits[16] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; char _hex[sizeof(elem_type) * 2]; + public: Hex(const elem_type &elem, bool rev) { if(!rev) { const uint8_t *data = reinterpret_cast(&elem) + sizeof(elem_type) - 1; - for (auto it = begin(); it < cend();) { + for(auto it = begin(); it < cend();) { *it++ = _bits[*data / 16]; *it++ = _bits[*data-- % 16]; } } else { const uint8_t *data = reinterpret_cast(&elem); - for (auto it = begin(); it < cend();) { + for(auto it = begin(); it < cend();) { *it++ = _bits[*data / 16]; *it++ = _bits[*data++ % 16]; } @@ -209,7 +229,7 @@ Hex hex(const T &elem, bool rev = false) { template std::string hex_vec(It begin, It end, bool rev = false) { - auto str_size = 2*std::distance(begin, end); + auto str_size = 2 * std::distance(begin, end); std::string hex; @@ -220,14 +240,14 @@ std::string hex_vec(It begin, It end, bool rev = false) { }; if(rev) { - for (auto it = std::begin(hex); it < std::end(hex);) { + for(auto it = std::begin(hex); it < std::end(hex);) { *it++ = _bits[((uint8_t)*begin) / 16]; *it++ = _bits[((uint8_t)*begin++) % 16]; } } else { --end; - for (auto it = std::begin(hex); it < std::end(hex);) { + for(auto it = std::begin(hex); it < std::end(hex);) { *it++ = _bits[((uint8_t)*end) / 16]; *it++ = _bits[((uint8_t)*end--) % 16]; } @@ -238,7 +258,7 @@ std::string hex_vec(It begin, It end, bool rev = false) { } template -std::string hex_vec(C&& c, bool rev = false) { +std::string hex_vec(C &&c, bool rev = false) { return hex_vec(std::begin(c), std::end(c), rev); } @@ -247,7 +267,7 @@ std::optional from_hex(const std::string_view &hex, bool rev = false) { std::uint8_t buf[sizeof(T)]; static char constexpr shift_bit = 'a' - 'A'; - auto is_convertable = [] (char ch) -> bool { + auto is_convertable = [](char ch) -> bool { if(isdigit(ch)) { return true; } @@ -266,9 +286,9 @@ std::optional from_hex(const std::string_view &hex, bool rev = false) { return std::nullopt; } - const char *data = hex.data() + hex.size() -1; + const char *data = hex.data() + hex.size() - 1; - auto convert = [] (char ch) -> std::uint8_t { + auto convert = [](char ch) -> std::uint8_t { if(ch >= '0' && ch <= '9') { return (std::uint8_t)ch - '0'; } @@ -297,7 +317,7 @@ inline std::string from_hex_vec(const std::string &hex, bool rev = false) { std::string buf; static char constexpr shift_bit = 'a' - 'A'; - auto is_convertable = [] (char ch) -> bool { + auto is_convertable = [](char ch) -> bool { if(isdigit(ch)) { return true; } @@ -314,9 +334,9 @@ inline std::string from_hex_vec(const std::string &hex, bool rev = false) { auto buf_size = std::count_if(std::begin(hex), std::end(hex), is_convertable) / 2; buf.resize(buf_size); - const char *data = hex.data() + hex.size() -1; + const char *data = hex.data() + hex.size() - 1; - auto convert = [] (char ch) -> std::uint8_t { + auto convert = [](char ch) -> std::uint8_t { if(ch >= '0' && ch <= '9') { return (std::uint8_t)ch - '0'; } @@ -348,49 +368,20 @@ public: std::size_t operator()(const value_type &value) const { const auto *p = reinterpret_cast(&value); - return std::hash{}(std::string_view { p, sizeof(value_type) }); + return std::hash {}(std::string_view { p, sizeof(value_type) }); } }; template -auto enm(const T& val) -> const std::underlying_type_t& { - return *reinterpret_cast*>(&val); +auto enm(const T &val) -> const std::underlying_type_t & { + return *reinterpret_cast *>(&val); } template -auto enm(T& val) -> std::underlying_type_t& { - return *reinterpret_cast*>(&val); +auto enm(T &val) -> std::underlying_type_t & { + return *reinterpret_cast *>(&val); } -template -struct Function { - typedef ReturnType (*type)(Args...); -}; - -template::type function> -struct Destroy { - typedef T pointer; - - void operator()(pointer p) { - function(p); - } -}; - -template::type function> -using safe_ptr = std::unique_ptr>; - -// You cannot specialize an alias -template::type function> -using safe_ptr_v2 = std::unique_ptr>; - -template -void c_free(T *p) { - free(p); -} - -template -using c_ptr = safe_ptr>; - inline std::int64_t from_chars(const char *begin, const char *end) { std::int64_t res {}; std::int64_t mul = 1; @@ -436,13 +427,171 @@ public: } }; +// Compared to std::unique_ptr, it adds the ability to get the address of the pointer itself +template> +class uniq_ptr { +public: + using element_type = T; + using pointer = element_type *; + using deleter_type = D; + + constexpr uniq_ptr() noexcept : _p { nullptr } {} + constexpr uniq_ptr(std::nullptr_t) noexcept : _p { nullptr } {} + + uniq_ptr(const uniq_ptr &other) noexcept = delete; + uniq_ptr &operator=(const uniq_ptr &other) noexcept = delete; + + template + uniq_ptr(V *p) noexcept : _p { p } { + static_assert(std::is_same_v || std::is_same_v || std::is_base_of_v, "element_type must be base class of V"); + } + + template + uniq_ptr(std::unique_ptr &&uniq) noexcept : _p { uniq.release() } { + static_assert(std::is_same_v || std::is_same_v || std::is_base_of_v, "element_type must be base class of V"); + } + + template + uniq_ptr(uniq_ptr &&other) noexcept : _p { other.release() } { + static_assert(std::is_same_v || std::is_same_v || std::is_base_of_v, "element_type must be base class of V"); + } + + template + uniq_ptr &operator=(uniq_ptr &&other) noexcept { + static_assert(std::is_same_v || std::is_same_v || std::is_base_of_v, "element_type must be base class of V"); + reset(other.release()); + + return *this; + } + + template + uniq_ptr &operator=(std::unique_ptr &&uniq) noexcept { + static_assert(std::is_same_v || std::is_same_v || std::is_base_of_v, "element_type must be base class of V"); + + reset(uniq.release()); + + return *this; + } + + ~uniq_ptr() { + reset(); + } + + void reset(pointer p = pointer()) { + if(_p) { + _deleter(_p); + } + + _p = p; + } + + pointer release() { + auto tmp = _p; + _p = nullptr; + return tmp; + } + + pointer get() { + return _p; + } + + const pointer get() const { + return _p; + } + + const std::add_lvalue_reference_t operator*() const { + return *_p; + } + std::add_lvalue_reference_t operator*() { + return *_p; + } + const pointer operator->() const { + return _p; + } + pointer operator->() { + return _p; + } + pointer *operator&() const { + return &_p; + } + + pointer *operator&() { + return &_p; + } + + deleter_type &get_deleter() { + return _deleter; + } + + const deleter_type &get_deleter() const { + return _deleter; + } + + explicit operator bool() const { + return _p != nullptr; + } + +protected: + pointer _p; + deleter_type _deleter; +}; + +template +bool operator==(const uniq_ptr &x, const uniq_ptr &y) { + return x.get() == y.get(); +} + +template +bool operator!=(const uniq_ptr &x, const uniq_ptr &y) { + return x.get() != y.get(); +} + +template +bool operator==(const std::unique_ptr &x, const uniq_ptr &y) { + return x.get() == y.get(); +} + +template +bool operator!=(const std::unique_ptr &x, const uniq_ptr &y) { + return x.get() != y.get(); +} + +template +bool operator==(const uniq_ptr &x, const std::unique_ptr &y) { + return x.get() == y.get(); +} + +template +bool operator!=(const uniq_ptr &x, const std::unique_ptr &y) { + return x.get() != y.get(); +} + +template +bool operator==(const uniq_ptr &x, std::nullptr_t) { + return !(bool)x; +} + +template +bool operator!=(const uniq_ptr &x, std::nullptr_t) { + return (bool)x; +} + +template +bool operator==(std::nullptr_t, const uniq_ptr &y) { + return !(bool)y; +} + +template +bool operator!=(std::nullptr_t, const uniq_ptr &y) { + return (bool)y; +} template class wrap_ptr { public: using element_type = T; - using pointer = element_type*; - using reference = element_type&; + using pointer = element_type *; + using reference = element_type &; wrap_ptr() : _own_ptr { false }, _p { nullptr } {} wrap_ptr(pointer p) : _own_ptr { false }, _p { p } {} @@ -458,7 +607,7 @@ public: _p = other._p; - _own_ptr = other._own_ptr; + _own_ptr = other._own_ptr; other._own_ptr = false; return *this; @@ -468,7 +617,7 @@ public: wrap_ptr &operator=(std::unique_ptr &&uniq_ptr) { static_assert(std::is_base_of_v, "element_type must be base class of V"); _own_ptr = true; - _p = uniq_ptr.release(); + _p = uniq_ptr.release(); return *this; } @@ -478,7 +627,7 @@ public: delete _p; } - _p = p; + _p = p; _own_ptr = false; return *this; @@ -510,12 +659,52 @@ private: pointer _p; }; +template +constexpr bool is_pointer_v = + instantiation_of_v || + instantiation_of_v || + instantiation_of_v || + std::is_pointer_v; + +template +struct __false_v; + +template +struct __false_v>> { + static constexpr std::nullopt_t value = std::nullopt; +}; + +template +struct __false_v>> { + static constexpr std::nullptr_t value = nullptr; +}; + +template +struct __false_v>> { + static constexpr bool value = false; +}; + +template +static constexpr auto false_v = __false_v::value; + +template +using optional_t = either_t< + (std::is_same_v || is_pointer_v), + T, std::optional>; + template class buffer_t { public: buffer_t() : _els { 0 } {}; - buffer_t(buffer_t&&) noexcept = default; - buffer_t &operator=(buffer_t&& other) noexcept = default; + buffer_t(buffer_t &&o) noexcept : _els { o._els }, _buf { std::move(o._buf) } { + o._els = 0; + } + buffer_t &operator=(buffer_t &&o) noexcept { + std::swap(_els, o._els); + std::swap(_buf, o._buf); + + return *this; + }; explicit buffer_t(size_t elements) : _els { elements }, _buf { std::make_unique(elements) } {} explicit buffer_t(size_t elements, const T &t) : _els { elements }, _buf { std::make_unique(elements) } { @@ -559,7 +748,6 @@ private: std::unique_ptr _buf; }; - template T either(std::optional &&l, T &&r) { if(l) { @@ -569,27 +757,77 @@ T either(std::optional &&l, T &&r) { return std::forward(r); } +template +struct Function { + typedef ReturnType (*type)(Args...); +}; + +template::type function> +struct Destroy { + typedef T pointer; + + void operator()(pointer p) { + function(p); + } +}; + +template::type function> +using safe_ptr = uniq_ptr>; + +// You cannot specialize an alias +template::type function> +using safe_ptr_v2 = uniq_ptr>; + +template +void c_free(T *p) { + free(p); +} + +template +void dynamic(T *p) { + (*function)(p); +} + +template +using dyn_safe_ptr = safe_ptr>; + +template +using dyn_safe_ptr_v2 = safe_ptr>; + +template +using c_ptr = safe_ptr>; + +template +std::string_view view(It begin, It end) { + return std::string_view { (const char *)begin, (std::size_t)(end - begin) }; +} + +template +std::string_view view(const T &data) { + return std::string_view((const char *)&data, sizeof(T)); +} + namespace endian { template struct endianness { enum : bool { #if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \ - defined(__BIG_ENDIAN__) || \ - defined(__ARMEB__) || \ - defined(__THUMBEB__) || \ - defined(__AARCH64EB__) || \ - defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) + defined(__BIG_ENDIAN__) || \ + defined(__ARMEB__) || \ + defined(__THUMBEB__) || \ + defined(__AARCH64EB__) || \ + defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) // It's a big-endian target architecture little = false, #elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || \ - defined(__LITTLE_ENDIAN__) || \ - defined(__ARMEL__) || \ - defined(__THUMBEL__) || \ - defined(__AARCH64EL__) || \ - defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || \ - defined(_WIN32) + defined(__LITTLE_ENDIAN__) || \ + defined(__ARMEL__) || \ + defined(__THUMBEL__) || \ + defined(__AARCH64EL__) || \ + defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || \ + defined(_WIN32) // It's a little-endian target architecture - little = true, + little = true, #else #error "Unknown Endianness" #endif @@ -598,15 +836,14 @@ struct endianness { }; template -struct endian_helper { }; +struct endian_helper {}; template struct endian_helper) ->> { + !(instantiation_of_v)>> { static inline T big(T x) { - if constexpr (endianness::little) { - uint8_t *data = reinterpret_cast(&x); + if constexpr(endianness::little) { + uint8_t *data = reinterpret_cast(&x); std::reverse(data, data + sizeof(x)); } @@ -615,8 +852,8 @@ struct endian_helper::big) { - uint8_t *data = reinterpret_cast(&x); + if constexpr(endianness::big) { + uint8_t *data = reinterpret_cast(&x); std::reverse(data, data + sizeof(x)); } @@ -627,32 +864,31 @@ struct endian_helper struct endian_helper ->> { -static inline T little(T x) { - if(!x) return x; + instantiation_of_v>> { + static inline T little(T x) { + if(!x) return x; - if constexpr (endianness::big) { - auto *data = reinterpret_cast(&*x); + if constexpr(endianness::big) { + auto *data = reinterpret_cast(&*x); - std::reverse(data, data + sizeof(*x)); + std::reverse(data, data + sizeof(*x)); + } + + return x; } - return x; -} + static inline T big(T x) { + if(!x) return x; -static inline T big(T x) { - if(!x) return x; + if constexpr(endianness::big) { + auto *data = reinterpret_cast(&*x); - if constexpr (endianness::big) { - auto *data = reinterpret_cast(&*x); + std::reverse(data, data + sizeof(*x)); + } - std::reverse(data, data + sizeof(*x)); + return x; } - - return x; -} }; template @@ -660,7 +896,6 @@ inline auto little(T x) { return endian_helper::little(x); } template inline auto big(T x) { return endian_helper::big(x); } -} /* endian */ - -} /* util */ +} // namespace endian +} // namespace util #endif diff --git a/sunshine/uuid.h b/sunshine/uuid.h index 01fad1d6..6d8abe80 100644 --- a/sunshine/uuid.h +++ b/sunshine/uuid.h @@ -18,12 +18,12 @@ union uuid_t { std::uniform_int_distribution dist(0, std::numeric_limits::max()); uuid_t buf; - for (auto &el : buf.b8) { + for(auto &el : buf.b8) { el = dist(engine); } - buf.b8[7] &= (std::uint8_t) 0b00101111; - buf.b8[9] &= (std::uint8_t) 0b10011111; + buf.b8[7] &= (std::uint8_t)0b00101111; + buf.b8[9] &= (std::uint8_t)0b10011111; return buf; } @@ -31,7 +31,7 @@ union uuid_t { static uuid_t generate() { std::random_device r; - std::default_random_engine engine{r()}; + std::default_random_engine engine { r() }; return generate(engine); } @@ -41,7 +41,7 @@ union uuid_t { result.reserve(sizeof(uuid_t) * 2 + 4); - auto hex = util::hex(*this, true); + auto hex = util::hex(*this, true); auto hex_view = hex.to_string_view(); std::string_view slices[] = { @@ -75,5 +75,5 @@ union uuid_t { return (b64[0] > other.b64[0] || (b64[0] == other.b64[0] && b64[1] > other.b64[1])); } }; -} +} // namespace util #endif //T_MAN_UUID_H diff --git a/sunshine/video.cpp b/sunshine/video.cpp index f32cd60f..1807edca 100644 --- a/sunshine/video.cpp +++ b/sunshine/video.cpp @@ -3,19 +3,21 @@ // #include -#include #include +#include extern "C" { #include } +#include "cbs.h" +#include "config.h" +#include "input.h" +#include "main.h" #include "platform/common.h" #include "round_robin.h" #include "sync.h" -#include "config.h" #include "video.h" -#include "main.h" #ifdef _WIN32 extern "C" { @@ -23,8 +25,11 @@ extern "C" { } #endif -namespace video { using namespace std::literals; +namespace video { + +constexpr auto hevc_nalu = "\000\000\000\001("sv; +constexpr auto h264_nalu = "\000\000\000\001e"sv; void free_ctx(AVCodecContext *ctx) { avcodec_free_context(&ctx); @@ -38,9 +43,11 @@ void free_buffer(AVBufferRef *ref) { av_buffer_unref(&ref); } -void free_packet(AVPacket *packet) { - av_packet_free(&packet); -} +using ctx_t = util::safe_ptr; +using frame_t = util::safe_ptr; +using buffer_t = util::safe_ptr; +using sws_t = util::safe_ptr; +using img_event_t = std::shared_ptr>>; namespace nv { @@ -56,38 +63,118 @@ enum class profile_hevc_e : int { main_10, rext, }; -} +} // namespace nv -using ctx_t = util::safe_ptr; -using frame_t = util::safe_ptr; -using buffer_t = util::safe_ptr; -using sws_t = util::safe_ptr; -using img_event_t = std::shared_ptr>>; -platf::dev_type_e map_dev_type(AVHWDeviceType type); +platf::mem_type_e map_dev_type(AVHWDeviceType type); platf::pix_fmt_e map_pix_fmt(AVPixelFormat fmt); -void sw_img_to_frame(const platf::img_t &img, frame_t &frame); -void nv_d3d_img_to_frame(const platf::img_t &img, frame_t &frame); -util::Either nv_d3d_make_hwdevice_ctx(platf::hwdevice_t *hwdevice_ctx); +util::Either dxgi_make_hwdevice_ctx(platf::hwdevice_t *hwdevice_ctx); +util::Either vaapi_make_hwdevice_ctx(platf::hwdevice_t *hwdevice_ctx); -util::Either make_hwdevice_ctx(AVHWDeviceType type, void *hwdevice_ctx); int hwframe_ctx(ctx_t &ctx, buffer_t &hwdevice, AVPixelFormat format); class swdevice_t : public platf::hwdevice_t { public: int convert(platf::img_t &img) override { - auto frame = (AVFrame *)data; - - av_frame_make_writable(frame); + av_frame_make_writable(sw_frame.get()); const int linesizes[2] { img.row_pitch, 0 }; - int ret = sws_scale(sws.get(), (std::uint8_t*const*)&img.data, linesizes, 0, img.height, frame->data, frame->linesize); + std::uint8_t *data[4]; + + data[0] = sw_frame->data[0] + offsetY; + if(sw_frame->format == AV_PIX_FMT_NV12) { + data[1] = sw_frame->data[1] + offsetUV; + data[2] = nullptr; + } + else { + data[1] = sw_frame->data[1] + offsetUV; + data[2] = sw_frame->data[2] + offsetUV; + data[3] = nullptr; + } + + int ret = sws_scale(sws.get(), (std::uint8_t *const *)&img.data, linesizes, 0, img.height, data, sw_frame->linesize); if(ret <= 0) { - BOOST_LOG(fatal) << "Couldn't convert image to required format and/or size"sv; + BOOST_LOG(error) << "Couldn't convert image to required format and/or size"sv; + + return -1; + } + + // If frame is not a software frame, it means we still need to transfer from main memory + // to vram memory + if(frame->hw_frames_ctx) { + auto status = av_hwframe_transfer_data(frame, sw_frame.get(), 0); + if(status < 0) { + char string[AV_ERROR_MAX_STRING_SIZE]; + BOOST_LOG(error) << "Failed to transfer image data to hardware frame: "sv << av_make_error_string(string, AV_ERROR_MAX_STRING_SIZE, status); + return -1; + } + } + + return 0; + } + + int set_frame(AVFrame *frame) { + this->frame = frame; + + // If it's a hwframe, allocate buffers for hardware + if(frame->hw_frames_ctx) { + hw_frame.reset(frame); + + if(av_hwframe_get_buffer(frame->hw_frames_ctx, frame, 0)) return -1; + } + + if(!frame->hw_frames_ctx) { + sw_frame.reset(frame); + } + + return 0; + } + + void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) override { + sws_setColorspaceDetails(sws.get(), + sws_getCoefficients(SWS_CS_DEFAULT), 0, + sws_getCoefficients(colorspace), color_range - 1, + 0, 1 << 16, 1 << 16); + } + + /** + * When preserving aspect ratio, ensure that padding is black + */ + int prefill() { + auto frame = sw_frame ? sw_frame.get() : this->frame; + auto width = frame->width; + auto height = frame->height; + + av_frame_get_buffer(frame, 0); + sws_t sws { + sws_getContext( + width, height, AV_PIX_FMT_BGR0, + width, height, (AVPixelFormat)frame->format, + SWS_LANCZOS | SWS_ACCURATE_RND, + nullptr, nullptr, nullptr) + }; + + if(!sws) { + return -1; + } + + util::buffer_t img { (std::size_t)(width * height) }; + std::fill(std::begin(img), std::end(img), 0); + + const int linesizes[2] { + width, 0 + }; + + av_frame_make_writable(frame); + + auto data = img.begin(); + int ret = sws_scale(sws.get(), (std::uint8_t *const *)&data, linesizes, 0, height, frame->data, frame->linesize); + if(ret <= 0) { + BOOST_LOG(error) << "Couldn't convert image to required format and/or size"sv; return -1; } @@ -95,49 +182,106 @@ public: return 0; } - virtual void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) { - sws_setColorspaceDetails(sws.get(), - sws_getCoefficients(SWS_CS_DEFAULT), 0, - sws_getCoefficients(colorspace), color_range -1, - 0, 1 << 16, 1 << 16 - ); - } + int init(int in_width, int in_height, AVFrame *frame, AVPixelFormat format) { + // If the device used is hardware, yet the image resides on main memory + if(frame->hw_frames_ctx) { + sw_frame.reset(av_frame_alloc()); + + sw_frame->width = frame->width; + sw_frame->height = frame->height; + sw_frame->format = format; + } + else { + this->frame = frame; + } + + if(prefill()) { + return -1; + } + + auto out_width = frame->width; + auto out_height = frame->height; + + // Ensure aspect ratio is maintained + auto scalar = std::fminf((float)out_width / in_width, (float)out_height / in_height); + out_width = in_width * scalar; + out_height = in_height * scalar; + + // result is always positive + auto offsetW = (frame->width - out_width) / 2; + auto offsetH = (frame->height - out_height) / 2; + offsetUV = (offsetW + offsetH * frame->width / 2) / 2; + offsetY = offsetW + offsetH * frame->width; - int init(int in_width, int in_height, int out_width, int out_height, AVFrame *frame, AVPixelFormat format) { sws.reset(sws_getContext( in_width, in_height, AV_PIX_FMT_BGR0, out_width, out_height, format, SWS_LANCZOS | SWS_ACCURATE_RND, - nullptr, nullptr, nullptr - )); - data = frame; + nullptr, nullptr, nullptr)); return sws ? 0 : -1; } ~swdevice_t() override {} + // Store ownsership when frame is hw_frame + frame_t hw_frame; + + frame_t sw_frame; sws_t sws; + + // offset of input image to output frame in pixels + int offsetUV; + int offsetY; +}; + +enum flag_e { + DEFAULT = 0x00, + SYSTEM_MEMORY = 0x01, + H264_ONLY = 0x02, + LIMITED_GOP_SIZE = 0x04, }; struct encoder_t { std::string_view name; enum flag_e { - PASSED, // Is supported - REF_FRAMES_RESTRICT, // Set maximum reference frames + PASSED, // Is supported + REF_FRAMES_RESTRICT, // Set maximum reference frames REF_FRAMES_AUTOSELECT, // Allow encoder to select maximum reference frames (If !REF_FRAMES_RESTRICT --> REF_FRAMES_AUTOSELECT) - DYNAMIC_RANGE, + SLICE, // Allow frame to be partitioned into multiple slices + DYNAMIC_RANGE, // hdr + VUI_PARAMETERS, // AMD encoder with VAAPI doesn't add VUI parameters to SPS + NALU_PREFIX_5b, // libx264/libx265 have a 3-byte nalu prefix instead of 4-byte nalu prefix MAX_FLAGS }; + static std::string_view from_flag(flag_e flag) { +#define _CONVERT(x) \ + case flag_e::x: \ + return #x##sv + switch(flag) { + _CONVERT(PASSED); + _CONVERT(REF_FRAMES_RESTRICT); + _CONVERT(REF_FRAMES_AUTOSELECT); + _CONVERT(SLICE); + _CONVERT(DYNAMIC_RANGE); + _CONVERT(VUI_PARAMETERS); + _CONVERT(NALU_PREFIX_5b); + _CONVERT(MAX_FLAGS); + } +#undef _CONVERT + + return "unknown"sv; + } + struct option_t { KITTY_DEFAULT_CONSTR(option_t) option_t(const option_t &) = default; std::string name; - std::variant*, std::string, std::string*> value; + std::variant *, std::string, std::string *> value; - option_t(std::string &&name, decltype(value) &&value) : name { std::move(name) }, value { std::move(value) } {} + option_t(std::string &&name, decltype(value) &&value) : name { std::move(name) }, value { std::move(value) } {} }; struct { @@ -168,41 +312,50 @@ struct encoder_t { } } hevc, h264; - bool system_memory; - bool hevc_mode; + int flags; - std::function img_to_frame; std::function(platf::hwdevice_t *hwdevice)> make_hwdevice_ctx; }; class session_t { public: session_t() = default; - session_t(ctx_t &&ctx, frame_t &&frame, util::wrap_ptr &&device) : - ctx { std::move(ctx) }, frame { std::move(frame) }, device { std::move(device) } {} + session_t(ctx_t &&ctx, util::wrap_ptr &&device, int inject) : ctx { std::move(ctx) }, device { std::move(device) }, inject { inject } {} - session_t(session_t &&other) : - ctx { std::move(other.ctx) }, frame { std::move(other.frame) }, device { std::move(other.device) } {} + session_t(session_t &&other) noexcept = default; // Ensure objects are destroyed in the correct order session_t &operator=(session_t &&other) { - device = std::move(other.device); - frame = std::move(other.frame); - ctx = std::move(other.ctx); + device = std::move(other.device); + ctx = std::move(other.ctx); + replacements = std::move(other.replacements); + sps = std::move(other.sps); + vps = std::move(other.vps); + + inject = other.inject; return *this; } ctx_t ctx; - frame_t frame; util::wrap_ptr device; + + std::vector replacements; + + cbs::nal_t sps; + cbs::nal_t vps; + + // inject sps/vps data into idr pictures + int inject; }; struct sync_session_ctx_t { - safe::signal_t *shutdown_event; safe::signal_t *join_event; - packet_queue_t packets; - idr_event_t idr_events; + safe::mail_raw_t::event_t shutdown_event; + safe::mail_raw_t::queue_t packets; + safe::mail_raw_t::event_t idr_events; + safe::mail_raw_t::event_t touch_port_events; + config_t config; int frame_nr; int key_frame_nr; @@ -211,7 +364,7 @@ struct sync_session_ctx_t { struct sync_session_t { sync_session_ctx_t *ctx; - + std::chrono::steady_clock::time_point next_frame; std::chrono::nanoseconds delay; @@ -221,7 +374,7 @@ struct sync_session_t { }; using encode_session_ctx_queue_t = safe::queue_t; -using encode_e = platf::capture_e; +using encode_e = platf::capture_e; struct capture_ctx_t { img_event_t images; @@ -248,7 +401,7 @@ void end_capture_async(capture_thread_async_ctx_t &ctx); // Keep a reference counter to ensure the capture thread only runs when other threads have a reference to the capture thread auto capture_thread_async = safe::make_shared(start_capture_async, end_capture_async); -auto capture_thread_sync = safe::make_shared(start_capture_sync, end_capture_sync); +auto capture_thread_sync = safe::make_shared(start_capture_sync, end_capture_sync); #ifdef _WIN32 static encoder_t nvenc { @@ -262,9 +415,10 @@ static encoder_t nvenc { { "forced-idr"s, 1 }, { "zerolatency"s, 1 }, { "preset"s, &config::video.nv.preset }, - { "rc"s, &config::video.nv.rc } + { "rc"s, &config::video.nv.rc }, }, - std::nullopt, std::nullopt, + std::nullopt, + std::nullopt, "hevc_nvenc"s, }, { @@ -273,16 +427,47 @@ static encoder_t nvenc { { "zerolatency"s, 1 }, { "preset"s, &config::video.nv.preset }, { "rc"s, &config::video.nv.rc }, - { "coder"s, &config::video.nv.coder } + { "coder"s, &config::video.nv.coder }, }, - std::nullopt, std::make_optional({"qp"s, &config::video.qp}), - "h264_nvenc"s + std::nullopt, + std::make_optional({ "qp"s, &config::video.qp }), + "h264_nvenc"s, }, - false, - true, + DEFAULT, + dxgi_make_hwdevice_ctx +}; - nv_d3d_img_to_frame, - nv_d3d_make_hwdevice_ctx +static encoder_t amdvce { + "amdvce"sv, + { FF_PROFILE_H264_HIGH, FF_PROFILE_HEVC_MAIN }, + AV_HWDEVICE_TYPE_D3D11VA, + AV_PIX_FMT_D3D11, + AV_PIX_FMT_NV12, AV_PIX_FMT_P010, + { + { + { "header_insertion_mode"s, "idr"s }, + { "gops_per_idr"s, 30 }, + { "usage"s, "ultralowlatency"s }, + { "quality"s, &config::video.amd.quality }, + { "rc"s, &config::video.amd.rc }, + }, + std::nullopt, + std::make_optional({ "qp"s, &config::video.qp }), + "hevc_amf"s, + }, + { + { + { "usage"s, "ultralowlatency"s }, + { "quality"s, &config::video.amd.quality }, + { "rc"s, &config::video.amd.rc }, + { "log_to_dbg"s, "1"s }, + }, + std::nullopt, + std::make_optional({ "qp"s, &config::video.qp }), + "h264_amf"s, + }, + DEFAULT, + dxgi_make_hwdevice_ctx }; #endif @@ -298,31 +483,67 @@ static encoder_t software { // It also looks like gop_size isn't passed on to x265, so we have to set // 'keyint=-1' in the parameters ourselves. { + { "forced-idr"s, 1 }, { "x265-params"s, "info=0:keyint=-1"s }, { "preset"s, &config::video.sw.preset }, - { "tune"s, &config::video.sw.tune } + { "tune"s, &config::video.sw.tune }, }, - std::make_optional("crf"s, &config::video.crf), std::make_optional("qp"s, &config::video.qp), - "libx265"s + std::make_optional("crf"s, &config::video.crf), + std::make_optional("qp"s, &config::video.qp), + "libx265"s, }, { { { "preset"s, &config::video.sw.preset }, - { "tune"s, &config::video.sw.tune } + { "tune"s, &config::video.sw.tune }, }, - std::make_optional("crf"s, &config::video.crf), std::make_optional("qp"s, &config::video.qp), - "libx264"s + std::make_optional("crf"s, &config::video.crf), + std::make_optional("qp"s, &config::video.qp), + "libx264"s, }, - true, - false, + H264_ONLY | SYSTEM_MEMORY, - sw_img_to_frame, nullptr }; +#ifdef __linux__ +static encoder_t vaapi { + "vaapi"sv, + { FF_PROFILE_H264_HIGH, FF_PROFILE_HEVC_MAIN, FF_PROFILE_HEVC_MAIN_10 }, + AV_HWDEVICE_TYPE_VAAPI, + AV_PIX_FMT_VAAPI, + AV_PIX_FMT_NV12, AV_PIX_FMT_YUV420P10, + { + { + { "sei"s, 0 }, + { "idr_interval"s, std::numeric_limits::max() }, + }, + std::nullopt, + std::nullopt, + "hevc_vaapi"s, + }, + { + { + { "sei"s, 0 }, + { "idr_interval"s, std::numeric_limits::max() }, + }, + std::nullopt, + std::nullopt, + "h264_vaapi"s, + }, + LIMITED_GOP_SIZE | SYSTEM_MEMORY, + + vaapi_make_hwdevice_ctx +}; +#endif + static std::vector encoders { #ifdef _WIN32 nvenc, + amdvce, +#endif +#ifdef __linux__ + vaapi, #endif software }; @@ -335,7 +556,7 @@ void reset_display(std::shared_ptr &disp, AVHWDeviceType type) if(disp) { break; } - + std::this_thread::sleep_for(200ms); } } @@ -344,8 +565,7 @@ void captureThread( std::shared_ptr> capture_ctx_queue, util::sync_t> &display_wp, safe::signal_t &reinit_event, - const encoder_t &encoder - ) { + const encoder_t &encoder) { std::vector capture_ctxs; auto fg = util::fail_guard([&]() { @@ -379,7 +599,7 @@ void captureThread( } } - if(auto capture_ctx = capture_ctx_queue->pop()) { + if(auto capture_ctx = capture_ctx_queue->pop()) { capture_ctxs.emplace_back(std::move(*capture_ctx)); delay = capture_ctxs.back().delay; @@ -399,57 +619,64 @@ void captureThread( while(img.use_count() > 1) {} auto status = disp->snapshot(img.get(), 1000ms, display_cursor); - switch (status) { - case platf::capture_e::reinit: { - reinit_event.raise(true); + switch(status) { + case platf::capture_e::reinit: { + reinit_event.raise(true); - // Some classes of images contain references to the display --> display won't delete unless img is deleted - for(auto &img : imgs) { - img.reset(); - } + // Some classes of images contain references to the display --> display won't delete unless img is deleted + for(auto &img : imgs) { + img.reset(); + } - // Some classes of display cannot have multiple instances at once - disp.reset(); + // Some classes of display cannot have multiple instances at once + disp.reset(); - // display_wp is modified in this thread only - while(!display_wp->expired()) { - std::this_thread::sleep_for(100ms); - } + // display_wp is modified in this thread only + while(!display_wp->expired()) { + std::this_thread::sleep_for(100ms); + } + while(capture_ctx_queue->running()) { reset_display(disp, encoder.dev_type); - if(!disp) { + + if(disp) { + break; + } + std::this_thread::sleep_for(200ms); + } + if(!disp) { + return; + } + + display_wp = disp; + // Re-allocate images + for(auto &img : imgs) { + img = disp->alloc_img(); + if(!img) { + BOOST_LOG(error) << "Couldn't initialize an image"sv; return; } - - display_wp = disp; - // Re-allocate images - for(auto &img : imgs) { - img = disp->alloc_img(); - if(!img) { - BOOST_LOG(error) << "Couldn't initialize an image"sv; - return; - } - } - - reinit_event.reset(); - continue; } - case platf::capture_e::error: - return; - case platf::capture_e::timeout: - std::this_thread::sleep_for(1ms); - continue; - case platf::capture_e::ok: - break; - default: - BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']'; - return; + + reinit_event.reset(); + continue; + } + case platf::capture_e::error: + return; + case platf::capture_e::timeout: + std::this_thread::sleep_for(1ms); + continue; + case platf::capture_e::ok: + break; + default: + BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']'; + return; } KITTY_WHILE_LOOP(auto capture_ctx = std::begin(capture_ctxs), capture_ctx != std::end(capture_ctxs), { if(!capture_ctx->images->running()) { auto tmp_delay = capture_ctx->delay; - capture_ctx = capture_ctxs.erase(capture_ctx); + capture_ctx = capture_ctxs.erase(capture_ctx); if(tmp_delay == delay) { delay = std::min_element(std::begin(capture_ctxs), std::end(capture_ctxs), [](const auto &l, const auto &r) { @@ -470,29 +697,60 @@ void captureThread( } } -int encode(int64_t frame_nr, ctx_t &ctx, frame_t &frame, packet_queue_t &packets, void *channel_data) { +int encode(int64_t frame_nr, session_t &session, frame_t::pointer frame, safe::mail_raw_t::queue_t &packets, void *channel_data) { frame->pts = frame_nr; + auto &ctx = session.ctx; + + auto &sps = session.sps; + auto &vps = session.vps; + /* send the frame to the encoder */ - auto ret = avcodec_send_frame(ctx.get(), frame.get()); - if (ret < 0) { - char err_str[AV_ERROR_MAX_STRING_SIZE] {0}; + auto ret = avcodec_send_frame(ctx.get(), frame); + if(ret < 0) { + char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; BOOST_LOG(error) << "Could not send a frame for encoding: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, ret); return -1; } - while (ret >= 0) { + while(ret >= 0) { auto packet = std::make_unique(nullptr); ret = avcodec_receive_packet(ctx.get(), packet.get()); - if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { return 0; } - else if (ret < 0) { + else if(ret < 0) { return ret; } + if(session.inject) { + if(session.inject == 1) { + auto h264 = cbs::make_sps_h264(ctx.get(), packet.get()); + + sps = std::move(h264.sps); + } + else { + auto hevc = cbs::make_sps_hevc(ctx.get(), packet.get()); + + sps = std::move(hevc.sps); + vps = std::move(hevc.vps); + + session.replacements.emplace_back( + std::string_view((char *)std::begin(vps.old), vps.old.size()), + std::string_view((char *)std::begin(vps._new), vps._new.size())); + } + + session.inject = 0; + + + session.replacements.emplace_back( + std::string_view((char *)std::begin(sps.old), sps.old.size()), + std::string_view((char *)std::begin(sps._new), sps._new.size())); + } + + packet->replacements = &session.replacements; packet->channel_data = channel_data; packets->raise(std::move(packet)); } @@ -505,7 +763,7 @@ std::optional make_session(const encoder_t &encoder, const config_t & auto &video_format = config.videoFormat == 0 ? encoder.h264 : encoder.hevc; if(!video_format[encoder_t::PASSED]) { - BOOST_LOG(error) << encoder.name << ": "sv << video_format.name << " mode not supported"sv; + BOOST_LOG(error) << encoder.name << ": "sv << video_format.name << " mode not supported"sv; return std::nullopt; } @@ -522,10 +780,10 @@ std::optional make_session(const encoder_t &encoder, const config_t & } ctx_t ctx { avcodec_alloc_context3(codec) }; - ctx->width = config.width; - ctx->height = config.height; - ctx->time_base = AVRational{1, config.framerate}; - ctx->framerate = AVRational{config.framerate, 1}; + ctx->width = config.width; + ctx->height = config.height; + ctx->time_base = AVRational { 1, config.framerate }; + ctx->framerate = AVRational { config.framerate, 1 }; if(config.videoFormat == 0) { ctx->profile = encoder.profile.h264_high; @@ -541,8 +799,11 @@ std::optional make_session(const encoder_t &encoder, const config_t & ctx->max_b_frames = 0; // Use an infinite GOP length since I-frames are generated on demand - ctx->gop_size = std::numeric_limits::max(); - ctx->keyint_min = ctx->gop_size; + ctx->gop_size = encoder.flags & LIMITED_GOP_SIZE ? + std::numeric_limits::max() : + std::numeric_limits::max(); + + ctx->keyint_min = std::numeric_limits::max(); if(config.numRefFrames == 0) { ctx->refs = video_format[encoder_t::REF_FRAMES_AUTOSELECT] ? 0 : 16; @@ -558,32 +819,36 @@ std::optional make_session(const encoder_t &encoder, const config_t & ctx->color_range = (config.encoderCscMode & 0x1) ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; int sws_color_space; - switch (config.encoderCscMode >> 1) { - case 0: - default: - // Rec. 601 - ctx->color_primaries = AVCOL_PRI_SMPTE170M; - ctx->color_trc = AVCOL_TRC_SMPTE170M; - ctx->colorspace = AVCOL_SPC_SMPTE170M; - sws_color_space = SWS_CS_SMPTE170M; - break; + switch(config.encoderCscMode >> 1) { + case 0: + default: + // Rec. 601 + BOOST_LOG(info) << "Color coding [Rec. 601]"sv; + ctx->color_primaries = AVCOL_PRI_SMPTE170M; + ctx->color_trc = AVCOL_TRC_SMPTE170M; + ctx->colorspace = AVCOL_SPC_SMPTE170M; + sws_color_space = SWS_CS_SMPTE170M; + break; - case 1: - // Rec. 709 - ctx->color_primaries = AVCOL_PRI_BT709; - ctx->color_trc = AVCOL_TRC_BT709; - ctx->colorspace = AVCOL_SPC_BT709; - sws_color_space = SWS_CS_ITU709; - break; + case 1: + // Rec. 709 + BOOST_LOG(info) << "Color coding [Rec. 709]"sv; + ctx->color_primaries = AVCOL_PRI_BT709; + ctx->color_trc = AVCOL_TRC_BT709; + ctx->colorspace = AVCOL_SPC_BT709; + sws_color_space = SWS_CS_ITU709; + break; - case 2: - // Rec. 2020 - ctx->color_primaries = AVCOL_PRI_BT2020; - ctx->color_trc = AVCOL_TRC_BT2020_10; - ctx->colorspace = AVCOL_SPC_BT2020_NCL; - sws_color_space = SWS_CS_BT2020; - break; + case 2: + // Rec. 2020 + BOOST_LOG(info) << "Color coding [Rec. 2020]"sv; + ctx->color_primaries = AVCOL_PRI_BT2020; + ctx->color_trc = AVCOL_TRC_BT2020_10; + ctx->colorspace = AVCOL_SPC_BT2020_NCL; + sws_color_space = SWS_CS_BT2020; + break; } + BOOST_LOG(info) << "Color range: ["sv << ((config.encoderCscMode & 0x1) ? "JPEG"sv : "MPEG"sv) << ']'; AVPixelFormat sw_fmt; if(config.dynamicRange == 0) { @@ -593,6 +858,9 @@ std::optional make_session(const encoder_t &encoder, const config_t & sw_fmt = encoder.dynamic_pix_fmt; } + // Used by cbs::make_sps_hevc + ctx->sw_pix_fmt = sw_fmt; + buffer_t hwdevice_ctx; if(hardware) { ctx->pix_fmt = encoder.dev_pix_fmt; @@ -618,18 +886,23 @@ std::optional make_session(const encoder_t &encoder, const config_t & ctx->slices = std::max(config.slicesPerFrame, config::video.min_threads); } - ctx->thread_type = FF_THREAD_SLICE; + if(!video_format[encoder_t::SLICE]) { + ctx->slices = 1; + } + + ctx->thread_type = FF_THREAD_SLICE; ctx->thread_count = ctx->slices; - AVDictionary *options {nullptr}; + AVDictionary *options { nullptr }; auto handle_option = [&options](const encoder_t::option_t &option) { - std::visit(util::overloaded { - [&](int v) { av_dict_set_int(&options, option.name.c_str(), v, 0); }, - [&](int *v) { av_dict_set_int(&options, option.name.c_str(), *v, 0); }, - [&](std::optional *v) { if(*v) av_dict_set_int(&options, option.name.c_str(), **v, 0); }, - [&](const std::string &v) { av_dict_set(&options, option.name.c_str(), v.c_str(), 0); }, - [&](std::string *v) { if(!v->empty()) av_dict_set(&options, option.name.c_str(), v->c_str(), 0); } - }, option.value); + std::visit( + util::overloaded { + [&](int v) { av_dict_set_int(&options, option.name.c_str(), v, 0); }, + [&](int *v) { av_dict_set_int(&options, option.name.c_str(), *v, 0); }, + [&](std::optional *v) { if(*v) av_dict_set_int(&options, option.name.c_str(), **v, 0); }, + [&](const std::string &v) { av_dict_set(&options, option.name.c_str(), v.c_str(), 0); }, + [&](std::string *v) { if(!v->empty()) av_dict_set(&options, option.name.c_str(), v->c_str(), 0); } }, + option.value); }; for(auto &option : video_format.options) { @@ -637,11 +910,11 @@ std::optional make_session(const encoder_t &encoder, const config_t & } if(config.bitrate > 500) { - auto bitrate = config.bitrate * 1000; - ctx->rc_max_rate = bitrate; + auto bitrate = config.bitrate * 1000; + ctx->rc_max_rate = bitrate; ctx->rc_buffer_size = bitrate / config.framerate; - ctx->bit_rate = bitrate; - ctx->rc_min_rate = bitrate; + ctx->bit_rate = bitrate; + ctx->rc_min_rate = bitrate; } else if(video_format.crf && config::video.crf != 0) { handle_option(*video_format.crf); @@ -654,9 +927,17 @@ std::optional make_session(const encoder_t &encoder, const config_t & return std::nullopt; } - avcodec_open2(ctx.get(), codec, &options); + if(auto status = avcodec_open2(ctx.get(), codec, &options)) { + char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; + BOOST_LOG(error) + << "Could not open codec ["sv + << video_format.name << "]: "sv + << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, status); - frame_t frame {av_frame_alloc() }; + return std::nullopt; + } + + frame_t frame { av_frame_alloc() }; frame->format = ctx->pix_fmt; frame->width = ctx->width; frame->height = ctx->height; @@ -665,16 +946,13 @@ std::optional make_session(const encoder_t &encoder, const config_t & if(hardware) { frame->hw_frames_ctx = av_buffer_ref(ctx->hw_frames_ctx); } - else /* software */ { - av_frame_get_buffer(frame.get(), 0); - } util::wrap_ptr device; if(!hwdevice->data) { auto device_tmp = std::make_unique(); - if(device_tmp->init(width, height, config.width, config.height, frame.get(), sw_fmt)) { + if(device_tmp->init(width, height, frame.get(), sw_fmt)) { return std::nullopt; } @@ -684,19 +962,32 @@ std::optional make_session(const encoder_t &encoder, const config_t & device = hwdevice; } + if(device->set_frame(frame.release())) { + return std::nullopt; + } + device->set_colorspace(sws_color_space, ctx->color_range); - return std::make_optional(session_t { + + session_t session { std::move(ctx), - std::move(frame), - std::move(device) - }); + std::move(device), + + // 0 ==> don't inject, 1 ==> inject for h264, 2 ==> inject for hevc + (1 - (int)video_format[encoder_t::VUI_PARAMETERS]) * (1 + config.videoFormat), + }; + + if(!video_format[encoder_t::NALU_PREFIX_5b]) { + auto nalu_prefix = config.videoFormat ? hevc_nalu : h264_nalu; + + session.replacements.emplace_back(nalu_prefix.substr(1), nalu_prefix); + } + + return std::make_optional(std::move(session)); } void encode_run( int &frame_nr, int &key_frame_nr, // Store progress of the frame number - safe::signal_t* shutdown_event, // Signal for shutdown event of the session - packet_queue_t packets, - idr_event_t idr_events, + safe::mail_t mail, img_event_t images, config_t config, int width, int height, @@ -713,36 +1004,45 @@ void encode_run( auto delay = std::chrono::floor(1s) / config.framerate; auto next_frame = std::chrono::steady_clock::now(); + + auto frame = session->device->frame; + + auto shutdown_event = mail->event(mail::shutdown); + auto packets = mail::man->queue(mail::video_packets); + auto idr_events = mail->event(mail::idr); + while(true) { if(shutdown_event->peek() || reinit_event.peek() || !images->running()) { break; } if(idr_events->peek()) { - session->frame->pict_type = AV_PICTURE_TYPE_I; + frame->pict_type = AV_PICTURE_TYPE_I; + frame->key_frame = 1; auto event = idr_events->pop(); if(!event) { return; } - auto end = event->second; - frame_nr = end; + auto end = event->second; + frame_nr = end; key_frame_nr = end + config.framerate; } else if(frame_nr == key_frame_nr) { - session->frame->pict_type = AV_PICTURE_TYPE_I; + auto frame = session->device->frame; + + frame->pict_type = AV_PICTURE_TYPE_I; + frame->key_frame = 1; } std::this_thread::sleep_until(next_frame); next_frame += delay; // When Moonlight request an IDR frame, send frames even if there is no new captured frame - if(frame_nr > (key_frame_nr + config.framerate) || images->peek()) { + if(frame_nr > key_frame_nr || images->peek()) { if(auto img = images->pop(delay)) { session->device->convert(*img); - - encoder.img_to_frame(*session->device->img, session->frame); } else if(images->running()) { continue; @@ -751,38 +1051,65 @@ void encode_run( break; } } - - if(encode(frame_nr++, session->ctx, session->frame, packets, channel_data)) { + + if(encode(frame_nr++, *session, frame, packets, channel_data)) { BOOST_LOG(error) << "Could not encode video packet"sv; return; } - session->frame->pict_type = AV_PICTURE_TYPE_NONE; + frame->pict_type = AV_PICTURE_TYPE_NONE; + frame->key_frame = 0; } } +input::touch_port_t make_port(platf::display_t *display, const config_t &config) { + float wd = display->width; + float hd = display->height; + + float wt = config.width; + float ht = config.height; + + auto scalar = std::fminf(wt / wd, ht / hd); + + auto w2 = scalar * wd; + auto h2 = scalar * hd; + + return input::touch_port_t { + display->offset_x, + display->offset_y, + (int)w2, + (int)h2, + display->env_width, + display->env_height, + 1.0f / scalar, + }; +} + std::optional make_synced_session(platf::display_t *disp, const encoder_t &encoder, platf::img_t &img, sync_session_ctx_t &ctx) { sync_session_t encode_session; - encode_session.ctx = &ctx; + encode_session.ctx = &ctx; encode_session.next_frame = std::chrono::steady_clock::now(); encode_session.delay = std::chrono::nanoseconds { 1s } / ctx.config.framerate; - auto pix_fmt = ctx.config.dynamicRange == 0 ? map_pix_fmt(encoder.static_pix_fmt) : map_pix_fmt(encoder.dynamic_pix_fmt); - auto hwdevice = disp->make_hwdevice(ctx.config.width, ctx.config.height, pix_fmt); + auto pix_fmt = ctx.config.dynamicRange == 0 ? map_pix_fmt(encoder.static_pix_fmt) : map_pix_fmt(encoder.dynamic_pix_fmt); + auto hwdevice = disp->make_hwdevice(pix_fmt); if(!hwdevice) { return std::nullopt; } + // absolute mouse coordinates require that the dimensions of the screen are known + ctx.touch_port_events->raise(make_port(disp, ctx.config)); + auto session = make_session(encoder, ctx.config, img.width, img.height, hwdevice.get()); if(!session) { return std::nullopt; } - encode_session.img_tmp = &img; + encode_session.img_tmp = &img; encode_session.hwdevice = std::move(hwdevice); - encode_session.session = std::move(*session); + encode_session.session = std::move(*session); return std::move(encode_session); } @@ -791,7 +1118,16 @@ encode_e encode_run_sync(std::vector> &synce const auto &encoder = encoders.front(); std::shared_ptr disp; - reset_display(disp, encoder.dev_type); + + while(encode_session_ctx_queue.running()) { + reset_display(disp, encoder.dev_type); + if(disp) { + break; + } + + std::this_thread::sleep_for(200ms); + } + if(!disp) { return encode_e::error; } @@ -817,7 +1153,7 @@ encode_e encode_run_sync(std::vector> &synce while(encode_session_ctx_queue.running()) { while(encode_session_ctx_queue.peek()) { auto encode_session_ctx = encode_session_ctx_queue.pop(); - if(!encode_session_ctx) { + if(!encode_session_ctx) { return encode_e::ok; } @@ -836,28 +1172,29 @@ encode_e encode_run_sync(std::vector> &synce auto delay = std::max(0ms, std::chrono::duration_cast(next_frame - std::chrono::steady_clock::now())); auto status = disp->snapshot(img.get(), delay, display_cursor); - switch(status) { - case platf::capture_e::reinit: - case platf::capture_e::error: - return status; - case platf::capture_e::timeout: - break; - case platf::capture_e::ok: - img_tmp = img.get(); - break; + switch(status) { + case platf::capture_e::reinit: + case platf::capture_e::error: + return status; + case platf::capture_e::timeout: + break; + case platf::capture_e::ok: + img_tmp = img.get(); + break; } - + auto now = std::chrono::steady_clock::now(); - + next_frame = now + 1s; KITTY_WHILE_LOOP(auto pos = std::begin(synced_sessions), pos != std::end(synced_sessions), { - auto ctx = pos->ctx; + auto frame = pos->session.device->frame; + auto ctx = pos->ctx; if(ctx->shutdown_event->peek()) { // Let waiting thread know it can delete shutdown_event ctx->join_event->raise(true); - + pos = synced_sessions.erase(pos); - synced_session_ctxs.erase(std::find_if(std::begin(synced_session_ctxs), std::end(synced_session_ctxs), [&ctx_p=ctx](auto &ctx) { + synced_session_ctxs.erase(std::find_if(std::begin(synced_session_ctxs), std::end(synced_session_ctxs), [&ctx_p = ctx](auto &ctx) { return ctx.get() == ctx_p; })); @@ -869,16 +1206,18 @@ encode_e encode_run_sync(std::vector> &synce } if(ctx->idr_events->peek()) { - pos->session.frame->pict_type = AV_PICTURE_TYPE_I; + frame->pict_type = AV_PICTURE_TYPE_I; + frame->key_frame = 1; auto event = ctx->idr_events->pop(); - auto end = event->second; + auto end = event->second; - ctx->frame_nr = end; + ctx->frame_nr = end; ctx->key_frame_nr = end + ctx->config.framerate; } else if(ctx->frame_nr == ctx->key_frame_nr) { - pos->session.frame->pict_type = AV_PICTURE_TYPE_I; + frame->pict_type = AV_PICTURE_TYPE_I; + frame->key_frame = 1; } if(img_tmp) { @@ -905,18 +1244,17 @@ encode_e encode_run_sync(std::vector> &synce continue; } pos->img_tmp = nullptr; - - encoder.img_to_frame(*pos->hwdevice->img, pos->session.frame); } - if(encode(ctx->frame_nr++, pos->session.ctx, pos->session.frame, ctx->packets, ctx->channel_data)) { + if(encode(ctx->frame_nr++, pos->session, frame, ctx->packets, ctx->channel_data)) { BOOST_LOG(error) << "Could not encode video packet"sv; ctx->shutdown_event->raise(true); continue; } - pos->session.frame->pict_type = AV_PICTURE_TYPE_NONE; + frame->pict_type = AV_PICTURE_TYPE_NONE; + frame->key_frame = 0; ++pos; }) @@ -933,7 +1271,7 @@ void captureThreadSync() { std::vector> synced_session_ctxs; auto &ctx = ref->encode_session_ctx_queue; - auto lg = util::fail_guard([&]() { + auto lg = util::fail_guard([&]() { ctx.stop(); for(auto &ctx : synced_session_ctxs) { @@ -947,18 +1285,18 @@ void captureThreadSync() { } }); - while(encode_run_sync(synced_session_ctxs, ctx) == encode_e::reinit); + while(encode_run_sync(synced_session_ctxs, ctx) == encode_e::reinit) {} } void capture_async( - safe::signal_t *shutdown_event, - packet_queue_t &packets, - idr_event_t &idr_events, + safe::mail_t mail, config_t &config, void *channel_data) { + auto shutdown_event = mail->event(mail::shutdown); + auto images = std::make_shared(); - auto lg = util::fail_guard([&]() { + auto lg = util::fail_guard([&]() { images->stop(); shutdown_event->raise(true); }); @@ -970,16 +1308,17 @@ void capture_async( auto delay = std::chrono::floor(1s) / config.framerate; ref->capture_ctx_queue->raise(capture_ctx_t { - images, delay - }); + images, delay }); if(!ref->capture_ctx_queue->running()) { return; } - int frame_nr = 1; + int frame_nr = 1; int key_frame_nr = 1; + auto touch_port_event = mail->event(mail::touch_port); + while(!shutdown_event->peek() && images->running()) { // Wait for the main capture event when the display is being reinitialized if(ref->reinit_event.peek()) { @@ -997,8 +1336,8 @@ void capture_async( display = ref->display_wp->lock(); } - auto pix_fmt = config.dynamicRange == 0 ? platf::pix_fmt_e::yuv420p : platf::pix_fmt_e::yuv420p10; - auto hwdevice = display->make_hwdevice(config.width, config.height, pix_fmt); + auto pix_fmt = config.dynamicRange == 0 ? platf::pix_fmt_e::yuv420p : platf::pix_fmt_e::yuv420p10; + auto hwdevice = display->make_hwdevice(pix_fmt); if(!hwdevice) { return; } @@ -1007,12 +1346,15 @@ void capture_async( if(display->dummy_img(dummy_img.get())) { return; } + images->raise(std::move(dummy_img)); + // absolute mouse coordinates require that the dimensions of the screen are known + touch_port_event->raise(make_port(display.get(), config)); + encode_run( frame_nr, key_frame_nr, - shutdown_event, - packets, idr_events, images, + mail, images, config, display->width, display->height, hwdevice.get(), ref->reinit_event, *ref->encoder_p, @@ -1021,108 +1363,164 @@ void capture_async( } void capture( - safe::signal_t *shutdown_event, - packet_queue_t packets, - idr_event_t idr_events, + safe::mail_t mail, config_t config, void *channel_data) { + auto idr_events = mail->event(mail::idr); + idr_events->raise(std::make_pair(0, 1)); - if(encoders.front().system_memory) { - capture_async(shutdown_event, packets, idr_events, config, channel_data); + if(encoders.front().flags & SYSTEM_MEMORY) { + capture_async(std::move(mail), config, channel_data); } else { safe::signal_t join_event; auto ref = capture_thread_sync.ref(); ref->encode_session_ctx_queue.raise(sync_session_ctx_t { - shutdown_event, &join_event, packets, idr_events, config, 1, 1, channel_data - }); + &join_event, + mail->event(mail::shutdown), + mail::man->queue(mail::video_packets), + std::move(idr_events), + mail->event(mail::touch_port), + config, + 1, + 1, + channel_data, + }); // Wait for join signal join_event.view(); } } -bool validate_config(std::shared_ptr &disp, const encoder_t &encoder, const config_t &config) { +enum validate_flag_e { + VUI_PARAMS = 0x01, + NALU_PREFIX_5b = 0x02, +}; + +int validate_config(std::shared_ptr &disp, const encoder_t &encoder, const config_t &config) { reset_display(disp, encoder.dev_type); if(!disp) { - return false; + return -1; } - auto pix_fmt = config.dynamicRange == 0 ? map_pix_fmt(encoder.static_pix_fmt) : map_pix_fmt(encoder.dynamic_pix_fmt); - auto hwdevice = disp->make_hwdevice(config.width, config.height, pix_fmt); + auto pix_fmt = config.dynamicRange == 0 ? map_pix_fmt(encoder.static_pix_fmt) : map_pix_fmt(encoder.dynamic_pix_fmt); + auto hwdevice = disp->make_hwdevice(pix_fmt); if(!hwdevice) { - return false; + return -1; } auto session = make_session(encoder, config, disp->width, disp->height, hwdevice.get()); if(!session) { - return false; + return -1; } auto img = disp->alloc_img(); if(disp->dummy_img(img.get())) { - return false; + return -1; } if(session->device->convert(*img)) { - return false; + return -1; } - encoder.img_to_frame(*hwdevice->img, session->frame); + auto frame = session->device->frame; - session->frame->pict_type = AV_PICTURE_TYPE_I; + frame->pict_type = AV_PICTURE_TYPE_I; - auto packets = std::make_shared(30); - if(encode(1, session->ctx, session->frame, packets, nullptr)) { - return false; + auto packets = mail::man->queue(mail::video_packets); + while(!packets->peek()) { + if(encode(1, *session, frame, packets, nullptr)) { + return -1; + } } - return true; + auto packet = packets->pop(); + if(!(packet->flags & AV_PKT_FLAG_KEY)) { + BOOST_LOG(error) << "First packet type is not an IDR frame"sv; + + return -1; + } + + int flag = 0; + if(cbs::validate_sps(&*packet, config.videoFormat ? AV_CODEC_ID_H265 : AV_CODEC_ID_H264)) { + flag |= VUI_PARAMS; + } + + auto nalu_prefix = config.videoFormat ? hevc_nalu : h264_nalu; + std::string_view payload { (char *)packet->data, (std::size_t)packet->size }; + if(std::search(std::begin(payload), std::end(payload), std::begin(nalu_prefix), std::end(nalu_prefix)) != std::end(payload)) { + flag |= NALU_PREFIX_5b; + } + + return flag; } bool validate_encoder(encoder_t &encoder) { std::shared_ptr disp; + BOOST_LOG(info) << "Trying encoder ["sv << encoder.name << ']'; + auto fg = util::fail_guard([&]() { + BOOST_LOG(info) << "Encoder ["sv << encoder.name << "] failed"sv; + }); + auto force_hevc = config::video.hevc_mode >= 2; - auto test_hevc = force_hevc || (config::video.hevc_mode == 0 && encoder.hevc_mode); + auto test_hevc = force_hevc || (config::video.hevc_mode == 0 && !(encoder.flags & H264_ONLY)); encoder.h264.capabilities.set(); encoder.hevc.capabilities.set(); + encoder.hevc[encoder_t::PASSED] = test_hevc; + // First, test encoder viability config_t config_max_ref_frames { 1920, 1080, 60, 1000, 1, 1, 1, 0, 0 }; - config_t config_autoselect { 1920, 1080, 60, 1000, 1, 0, 1, 0, 0 }; + config_t config_autoselect { 1920, 1080, 60, 1000, 1, 0, 1, 0, 0 }; auto max_ref_frames_h264 = validate_config(disp, encoder, config_max_ref_frames); auto autoselect_h264 = validate_config(disp, encoder, config_autoselect); - if(!max_ref_frames_h264 && !autoselect_h264) { + if(max_ref_frames_h264 < 0 && autoselect_h264 < 0) { return false; } - encoder.h264[encoder_t::REF_FRAMES_RESTRICT] = max_ref_frames_h264; - encoder.h264[encoder_t::REF_FRAMES_AUTOSELECT] = autoselect_h264; - encoder.h264[encoder_t::PASSED] = true; + std::vector> packet_deficiencies { + { VUI_PARAMS, encoder_t::VUI_PARAMETERS }, + { NALU_PREFIX_5b, encoder_t::NALU_PREFIX_5b }, + }; + for(auto [validate_flag, encoder_flag] : packet_deficiencies) { + encoder.h264[encoder_flag] = (max_ref_frames_h264 & validate_flag && autoselect_h264 & validate_flag); + } + + encoder.h264[encoder_t::REF_FRAMES_RESTRICT] = max_ref_frames_h264 >= 0; + encoder.h264[encoder_t::REF_FRAMES_AUTOSELECT] = autoselect_h264 >= 0; + encoder.h264[encoder_t::PASSED] = true; + + encoder.h264[encoder_t::SLICE] = validate_config(disp, encoder, config_max_ref_frames); if(test_hevc) { config_max_ref_frames.videoFormat = 1; - config_autoselect.videoFormat = 1; + config_autoselect.videoFormat = 1; auto max_ref_frames_hevc = validate_config(disp, encoder, config_max_ref_frames); auto autoselect_hevc = validate_config(disp, encoder, config_autoselect); // If HEVC must be supported, but it is not supported - if(force_hevc && !max_ref_frames_hevc && !autoselect_hevc) { + if(force_hevc && max_ref_frames_hevc < 0 && autoselect_hevc < 0) { return false; } - encoder.hevc[encoder_t::REF_FRAMES_RESTRICT] = max_ref_frames_hevc; - encoder.hevc[encoder_t::REF_FRAMES_AUTOSELECT] = autoselect_hevc; + for(auto [validate_flag, encoder_flag] : packet_deficiencies) { + encoder.hevc[encoder_flag] = (max_ref_frames_hevc & validate_flag && autoselect_hevc & validate_flag); + } + + encoder.hevc[encoder_t::REF_FRAMES_RESTRICT] = max_ref_frames_hevc >= 0; + encoder.hevc[encoder_t::REF_FRAMES_AUTOSELECT] = autoselect_hevc >= 0; + + encoder.hevc[encoder_t::PASSED] = max_ref_frames_hevc >= 0 || autoselect_hevc >= 0; } - encoder.hevc[encoder_t::PASSED] = test_hevc; std::vector> configs { - { encoder_t::DYNAMIC_RANGE, { 1920, 1080, 60, 1000, 1, 0, 1, 1, 1 } } + { encoder_t::DYNAMIC_RANGE, { 1920, 1080, 60, 1000, 1, 0, 3, 1, 1 } }, + { encoder_t::SLICE, { 1920, 1080, 60, 1000, 2, 1, 1, 0, 0 } }, }; for(auto &[flag, config] : configs) { auto h264 = config; @@ -1131,22 +1529,46 @@ bool validate_encoder(encoder_t &encoder) { h264.videoFormat = 0; hevc.videoFormat = 1; - encoder.h264[flag] = validate_config(disp, encoder, h264); - if(test_hevc && encoder.hevc[encoder_t::PASSED]) { - encoder.hevc[flag] = validate_config(disp, encoder, hevc); + encoder.h264[flag] = validate_config(disp, encoder, h264) >= 0; + if(encoder.hevc[encoder_t::PASSED]) { + encoder.hevc[flag] = validate_config(disp, encoder, hevc) >= 0; } } + encoder.h264[encoder_t::VUI_PARAMETERS] = encoder.h264[encoder_t::VUI_PARAMETERS] && !config::sunshine.flags[config::flag::FORCE_VIDEO_HEADER_REPLACE]; + encoder.hevc[encoder_t::VUI_PARAMETERS] = encoder.hevc[encoder_t::VUI_PARAMETERS] && !config::sunshine.flags[config::flag::FORCE_VIDEO_HEADER_REPLACE]; + + if(!encoder.h264[encoder_t::VUI_PARAMETERS]) { + BOOST_LOG(warning) << encoder.name << ": h264 missing sps->vui parameters"sv; + } + if(encoder.hevc[encoder_t::PASSED] && !encoder.hevc[encoder_t::VUI_PARAMETERS]) { + BOOST_LOG(warning) << encoder.name << ": hevc missing sps->vui parameters"sv; + } + + if(!encoder.h264[encoder_t::NALU_PREFIX_5b]) { + BOOST_LOG(warning) << encoder.name << ": h264: replacing nalu prefix data"sv; + } + if(encoder.hevc[encoder_t::PASSED] && !encoder.hevc[encoder_t::NALU_PREFIX_5b]) { + BOOST_LOG(warning) << encoder.name << ": hevc: replacing nalu prefix data"sv; + } + + fg.disable(); return true; } int init() { + BOOST_LOG(info) << "//////////////////////////////////////////////////////////////////"sv; + BOOST_LOG(info) << "// //"sv; + BOOST_LOG(info) << "// Testing for available encoders, this may generate errors. //"sv; + BOOST_LOG(info) << "// You can safely ignore those errors. //"sv; + BOOST_LOG(info) << "// //"sv; + BOOST_LOG(info) << "//////////////////////////////////////////////////////////////////"sv; + KITTY_WHILE_LOOP(auto pos = std::begin(encoders), pos != std::end(encoders), { if( - (!config::video.encoder.empty() && pos->name != config::video.encoder) || - !validate_encoder(*pos) || - (config::video.hevc_mode == 3 && !pos->hevc[encoder_t::DYNAMIC_RANGE]) - ) { + (!config::video.encoder.empty() && pos->name != config::video.encoder) || + !validate_encoder(*pos) || + (config::video.hevc_mode == 3 && !pos->hevc[encoder_t::DYNAMIC_RANGE])) { pos = encoders.erase(pos); continue; @@ -1155,8 +1577,16 @@ int init() { break; }) + BOOST_LOG(info); + BOOST_LOG(info) << "//////////////////////////////////////////////////////////////"sv; + BOOST_LOG(info) << "// //"sv; + BOOST_LOG(info) << "// Ignore any errors mentioned above, they are not relevant //"sv; + BOOST_LOG(info) << "// //"sv; + BOOST_LOG(info) << "//////////////////////////////////////////////////////////////"sv; + BOOST_LOG(info); + if(encoders.empty()) { - if(config::video.encoder.empty()) { + if(config::video.encoder.empty()) { BOOST_LOG(fatal) << "Couldn't find any encoder"sv; } else { @@ -1167,11 +1597,26 @@ int init() { } auto &encoder = encoders.front(); + + BOOST_LOG(debug) << "------ h264 ------"sv; + for(int x = 0; x < encoder_t::MAX_FLAGS; ++x) { + auto flag = (encoder_t::flag_e)x; + BOOST_LOG(debug) << encoder_t::from_flag(flag) << (encoder.h264[flag] ? ": supported"sv : ": unsupported"sv); + } + BOOST_LOG(debug) << "-------------------"sv; + if(encoder.hevc[encoder_t::PASSED]) { + BOOST_LOG(debug) << "------ hevc ------"sv; + for(int x = 0; x < encoder_t::MAX_FLAGS; ++x) { + auto flag = (encoder_t::flag_e)x; + BOOST_LOG(debug) << encoder_t::from_flag(flag) << (encoder.hevc[flag] ? ": supported"sv : ": unsupported"sv); + } + BOOST_LOG(debug) << "-------------------"sv; + BOOST_LOG(info) << "Found encoder "sv << encoder.name << ": ["sv << encoder.h264.name << ", "sv << encoder.hevc.name << ']'; } else { - BOOST_LOG(info) << "Found encoder "sv << encoder.name << ": ["sv << encoder.h264.name << ']'; + BOOST_LOG(info) << "Found encoder "sv << encoder.name << ": ["sv << encoder.h264.name << ']'; } if(config::video.hevc_mode == 0) { @@ -1181,37 +1626,14 @@ int init() { return 0; } -util::Either make_hwdevice_ctx(AVHWDeviceType type, void *hwdevice) { - buffer_t ctx; - - int err; - if(hwdevice) { - ctx.reset(av_hwdevice_ctx_alloc(type)); - ((AVHWDeviceContext*)ctx.get())->hwctx = hwdevice; - - err = av_hwdevice_ctx_init(ctx.get()); - } - else { - AVBufferRef *ref {}; - err = av_hwdevice_ctx_create(&ref, type, nullptr, nullptr, 0); - ctx.reset(ref); - } - - if(err < 0) { - return err; - } - - return ctx; -} - int hwframe_ctx(ctx_t &ctx, buffer_t &hwdevice, AVPixelFormat format) { - buffer_t frame_ref { av_hwframe_ctx_alloc(hwdevice.get())}; + buffer_t frame_ref { av_hwframe_ctx_alloc(hwdevice.get()) }; - auto frame_ctx = (AVHWFramesContext*)frame_ref->data; - frame_ctx->format = ctx->pix_fmt; - frame_ctx->sw_format = format; - frame_ctx->height = ctx->height; - frame_ctx->width = ctx->width; + auto frame_ctx = (AVHWFramesContext *)frame_ref->data; + frame_ctx->format = ctx->pix_fmt; + frame_ctx->sw_format = format; + frame_ctx->height = ctx->height; + frame_ctx->width = ctx->width; frame_ctx->initial_pool_size = 0; if(auto err = av_hwframe_ctx_init(frame_ref.get()); err < 0) { @@ -1223,46 +1645,58 @@ int hwframe_ctx(ctx_t &ctx, buffer_t &hwdevice, AVPixelFormat format) { return 0; } -void sw_img_to_frame(const platf::img_t &img, frame_t &frame) {} +// Linux only declaration +typedef int (*vaapi_make_hwdevice_ctx_fn)(platf::hwdevice_t *base, AVBufferRef **hw_device_buf); -#ifdef _WIN32 -void nv_d3d_img_to_frame(const platf::img_t &img, frame_t &frame) { - if(img.data == frame->data[0]) { - return; - } - - // Need to have something refcounted - if(!frame->buf[0]) { - frame->buf[0] = av_buffer_allocz(sizeof(AVD3D11FrameDescriptor)); +util::Either vaapi_make_hwdevice_ctx(platf::hwdevice_t *base) { + buffer_t hw_device_buf; + + // If an egl hwdevice + if(base->data) { + if(((vaapi_make_hwdevice_ctx_fn)base->data)(base, &hw_device_buf)) { + return -1; + } + + return hw_device_buf; } - auto desc = (AVD3D11FrameDescriptor*)frame->buf[0]->data; - desc->texture = (ID3D11Texture2D*)img.data; - desc->index = 0; + auto render_device = config::video.adapter_name.empty() ? nullptr : config::video.adapter_name.c_str(); - frame->data[0] = img.data; - frame->data[1] = 0; + auto status = av_hwdevice_ctx_create(&hw_device_buf, AV_HWDEVICE_TYPE_VAAPI, render_device, nullptr, 0); + if(status < 0) { + char string[AV_ERROR_MAX_STRING_SIZE]; + BOOST_LOG(error) << "Failed to create a VAAPI device: "sv << av_make_error_string(string, AV_ERROR_MAX_STRING_SIZE, status); + return -1; + } - frame->linesize[0] = img.row_pitch; - - frame->height = img.height; - frame->width = img.width; + return hw_device_buf; } -util::Either nv_d3d_make_hwdevice_ctx(platf::hwdevice_t *hwdevice_ctx) { - buffer_t ctx_buf { av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_D3D11VA) }; - auto ctx = (AVD3D11VADeviceContext*)((AVHWDeviceContext*)ctx_buf->data)->hwctx; - - std::fill_n((std::uint8_t*)ctx, sizeof(AVD3D11VADeviceContext), 0); +#ifdef _WIN32 +} + +void do_nothing(void *) {} + +namespace video { +util::Either dxgi_make_hwdevice_ctx(platf::hwdevice_t *hwdevice_ctx) { + buffer_t ctx_buf { av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_D3D11VA) }; + auto ctx = (AVD3D11VADeviceContext *)((AVHWDeviceContext *)ctx_buf->data)->hwctx; + + std::fill_n((std::uint8_t *)ctx, sizeof(AVD3D11VADeviceContext), 0); + + auto device = (ID3D11Device *)hwdevice_ctx->data; - auto device = (ID3D11Device*)hwdevice_ctx->data; device->AddRef(); ctx->device = device; + ctx->lock_ctx = (void *)1; + ctx->lock = do_nothing; + ctx->unlock = do_nothing; + auto err = av_hwdevice_ctx_init(ctx_buf.get()); if(err) { - char err_str[AV_ERROR_MAX_STRING_SIZE] {0}; - BOOST_LOG(error) << "Failed to create FFMpeg nvenc: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); + char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; + BOOST_LOG(error) << "Failed to create FFMpeg hardware device context: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); return err; } @@ -1299,33 +1733,62 @@ int start_capture_sync(capture_thread_sync_ctx_t &ctx) { } void end_capture_sync(capture_thread_sync_ctx_t &ctx) {} -platf::dev_type_e map_dev_type(AVHWDeviceType type) { +platf::mem_type_e map_dev_type(AVHWDeviceType type) { switch(type) { - case AV_HWDEVICE_TYPE_D3D11VA: - return platf::dev_type_e::dxgi; - case AV_PICTURE_TYPE_NONE: - return platf::dev_type_e::none; - default: - return platf::dev_type_e::unknown; + case AV_HWDEVICE_TYPE_D3D11VA: + return platf::mem_type_e::dxgi; + case AV_HWDEVICE_TYPE_VAAPI: + return platf::mem_type_e::vaapi; + case AV_PICTURE_TYPE_NONE: + return platf::mem_type_e::system; + default: + return platf::mem_type_e::unknown; } - return platf::dev_type_e::unknown; + return platf::mem_type_e::unknown; } platf::pix_fmt_e map_pix_fmt(AVPixelFormat fmt) { switch(fmt) { - case AV_PIX_FMT_YUV420P10: - return platf::pix_fmt_e::yuv420p10; - case AV_PIX_FMT_YUV420P: - return platf::pix_fmt_e::yuv420p; - case AV_PIX_FMT_NV12: - return platf::pix_fmt_e::nv12; - case AV_PIX_FMT_P010: - return platf::pix_fmt_e::p010; - default: - return platf::pix_fmt_e::unknown; + case AV_PIX_FMT_YUV420P10: + return platf::pix_fmt_e::yuv420p10; + case AV_PIX_FMT_YUV420P: + return platf::pix_fmt_e::yuv420p; + case AV_PIX_FMT_NV12: + return platf::pix_fmt_e::nv12; + case AV_PIX_FMT_P010: + return platf::pix_fmt_e::p010; + default: + return platf::pix_fmt_e::unknown; } return platf::pix_fmt_e::unknown; } + +color_t make_color_matrix(float Cr, float Cb, float U_max, float V_max, float add_Y, float add_UV, const float2 &range_Y, const float2 &range_UV) { + float Cg = 1.0f - Cr - Cb; + + float Cr_i = 1.0f - Cr; + float Cb_i = 1.0f - Cb; + + float shift_y = range_Y[0] / 256.0f; + float shift_uv = range_UV[0] / 256.0f; + + float scale_y = (range_Y[1] - range_Y[0]) / 256.0f; + float scale_uv = (range_UV[1] - range_UV[0]) / 256.0f; + return { + { Cr, Cg, Cb, add_Y }, + { -(Cr * U_max / Cb_i), -(Cg * U_max / Cb_i), U_max, add_UV }, + { V_max, -(Cg * V_max / Cr_i), -(Cb * V_max / Cr_i), add_UV }, + { scale_y, shift_y }, + { scale_uv, shift_uv }, + }; +} + +color_t colors[] { + make_color_matrix(0.299f, 0.114f, 0.436f, 0.615f, 0.0625, 0.5f, { 16.0f, 235.0f }, { 16.0f, 240.0f }), // BT601 MPEG + make_color_matrix(0.299f, 0.114f, 0.5f, 0.5f, 0.0f, 0.5f, { 0.0f, 255.0f }, { 0.0f, 255.0f }), // BT601 JPEG + make_color_matrix(0.2126f, 0.0722f, 0.436f, 0.615f, 0.0625, 0.5f, { 16.0f, 235.0f }, { 16.0f, 240.0f }), // BT701 MPEG + make_color_matrix(0.2126f, 0.0722f, 0.5f, 0.5f, 0.0f, 0.5f, { 0.0f, 255.0f }, { 0.0f, 255.0f }), // BT701 JPEG +}; } diff --git a/sunshine/video.h b/sunshine/video.h index e4560e05..1c142147 100644 --- a/sunshine/video.h +++ b/sunshine/video.h @@ -5,8 +5,9 @@ #ifndef SUNSHINE_VIDEO_H #define SUNSHINE_VIDEO_H -#include "thread_safe.h" +#include "input.h" #include "platform/common.h" +#include "thread_safe.h" extern "C" { #include @@ -14,29 +15,49 @@ extern "C" { struct AVPacket; namespace video { -void free_packet(AVPacket *packet); struct packet_raw_t : public AVPacket { - template - explicit packet_raw_t(P *user_data) : channel_data { user_data } { - av_init_packet(this); + void init_packet() { + pts = AV_NOPTS_VALUE; + dts = AV_NOPTS_VALUE; + pos = -1; + duration = 0; + flags = 0; + stream_index = 0; + buf = nullptr; + side_data = nullptr; + side_data_elems = 0; } - explicit packet_raw_t(std::nullptr_t null) : channel_data { nullptr } { - av_init_packet(this); + template + explicit packet_raw_t(P *user_data) : channel_data { user_data } { + init_packet(); + } + + explicit packet_raw_t(std::nullptr_t) : channel_data { nullptr } { + init_packet(); } ~packet_raw_t() { av_packet_unref(this); } + struct replace_t { + std::string_view old; + std::string_view _new; + + KITTY_DEFAULT_CONSTR(replace_t) + + replace_t(std::string_view old, std::string_view _new) noexcept : old { std::move(old) }, _new { std::move(_new) } {} + }; + + std::vector *replacements; + void *channel_data; }; -using packet_t = std::unique_ptr; -using packet_queue_t = std::shared_ptr>; -using idr_event_t = std::shared_ptr>>; -using img_event_t = std::shared_ptr>>; +using packet_t = std::unique_ptr; +using idr_t = std::pair; struct config_t { int width; @@ -50,14 +71,26 @@ struct config_t { int dynamicRange; }; +using float4 = float[4]; +using float3 = float[3]; +using float2 = float[2]; + +struct __attribute__((__aligned__(16))) color_t { + float4 color_vec_y; + float4 color_vec_u; + float4 color_vec_v; + float2 range_y; + float2 range_uv; +}; + +extern color_t colors[4]; + void capture( - safe::signal_t *shutdown_event, - packet_queue_t packets, - idr_event_t idr_events, + safe::mail_t mail, config_t config, void *channel_data); int init(); -} +} // namespace video #endif //SUNSHINE_VIDEO_H diff --git a/Simple-Web-Server b/third-party/Simple-Web-Server similarity index 100% rename from Simple-Web-Server rename to third-party/Simple-Web-Server diff --git a/ViGEmClient b/third-party/ViGEmClient similarity index 100% rename from ViGEmClient rename to third-party/ViGEmClient diff --git a/third-party/cbs/CMakeLists.txt b/third-party/cbs/CMakeLists.txt new file mode 100644 index 00000000..b30a8031 --- /dev/null +++ b/third-party/cbs/CMakeLists.txt @@ -0,0 +1,69 @@ +cmake_minimum_required(VERSION 3.0) + +project(CBS) + +SET(CBS_SOURCE_FILES +include/cbs/av1.h +include/cbs/cbs_av1.h +include/cbs/cbs_bsf.h +include/cbs/cbs.h +include/cbs/cbs_h2645.h +include/cbs/cbs_h264.h +include/cbs/cbs_h265.h +include/cbs/cbs_jpeg.h +include/cbs/cbs_mpeg2.h +include/cbs/cbs_sei.h +include/cbs/cbs_vp9.h +include/cbs/h2645_parse.h +include/cbs/h264.h +include/cbs/hevc.h +include/cbs/sei.h +include/cbs/video_levels.h + +cbs.c +cbs_h2645.c +cbs_av1.c +cbs_vp9.c +cbs_mpeg2.c +cbs_jpeg.c +cbs_sei.c +h2645_parse.c +video_levels.c + +bytestream.h +cbs_internal.h +defs.h +get_bits.h +h264_ps.h +h264_sei.h +hevc_sei.h +intmath.h +mathops.h +put_bits.h +vlc.h +config.h +) + +include_directories(include) + +if(DEFINED FFMPEG_INCLUDE_DIRS) +include_directories(${FFMPEG_INCLUDE_DIRS}) +endif() + +add_compile_definitions( + HAVE_THREADS=1 + HAVE_FAST_UNALIGNED + + PIC=1 + + CONFIG_CBS_AV1=1 + CONFIG_CBS_H264=1 + CONFIG_CBS_H265=1 + CONFIG_CBS_JPEG=1 + CONFIG_CBS_MPEG2=1 + CONFIG_CBS_VP9=1 + ) + + +add_library(cbs ${CBS_SOURCE_FILES}) +target_compile_options(cbs PRIVATE -Wall -Wno-incompatible-pointer-types -Wno-maybe-uninitialized -Wno-format -Wno-format-extra-args) \ No newline at end of file diff --git a/third-party/cbs/bytestream.h b/third-party/cbs/bytestream.h new file mode 100644 index 00000000..7892af92 --- /dev/null +++ b/third-party/cbs/bytestream.h @@ -0,0 +1,351 @@ +/* + * Bytestream functions + * copyright (c) 2006 Baptiste Coudurier + * Copyright (c) 2012 Aneesh Dogra (lionaneesh) + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_BYTESTREAM_H +#define AVCODEC_BYTESTREAM_H + +#include "config.h" + +#include +#include + +#include +#include +#include + +typedef struct GetByteContext { + const uint8_t *buffer, *buffer_end, *buffer_start; +} GetByteContext; + +typedef struct PutByteContext { + uint8_t *buffer, *buffer_end, *buffer_start; + int eof; +} PutByteContext; + +#define DEF(type, name, bytes, read, write) \ + static av_always_inline type bytestream_get_##name(const uint8_t **b) { \ + (*b) += bytes; \ + return read(*b - bytes); \ + } \ + static av_always_inline void bytestream_put_##name(uint8_t **b, \ + const type value) { \ + write(*b, value); \ + (*b) += bytes; \ + } \ + static av_always_inline void bytestream2_put_##name##u(PutByteContext *p, \ + const type value) { \ + bytestream_put_##name(&p->buffer, value); \ + } \ + static av_always_inline void bytestream2_put_##name(PutByteContext *p, \ + const type value) { \ + if(!p->eof && (p->buffer_end - p->buffer >= bytes)) { \ + write(p->buffer, value); \ + p->buffer += bytes; \ + } \ + else \ + p->eof = 1; \ + } \ + static av_always_inline type bytestream2_get_##name##u(GetByteContext *g) { \ + return bytestream_get_##name(&g->buffer); \ + } \ + static av_always_inline type bytestream2_get_##name(GetByteContext *g) { \ + if(g->buffer_end - g->buffer < bytes) { \ + g->buffer = g->buffer_end; \ + return 0; \ + } \ + return bytestream2_get_##name##u(g); \ + } \ + static av_always_inline type bytestream2_peek_##name##u(GetByteContext *g) { \ + return read(g->buffer); \ + } \ + static av_always_inline type bytestream2_peek_##name(GetByteContext *g) { \ + if(g->buffer_end - g->buffer < bytes) \ + return 0; \ + return bytestream2_peek_##name##u(g); \ + } + +DEF(uint64_t, le64, 8, AV_RL64, AV_WL64) +DEF(unsigned int, le32, 4, AV_RL32, AV_WL32) +DEF(unsigned int, le24, 3, AV_RL24, AV_WL24) +DEF(unsigned int, le16, 2, AV_RL16, AV_WL16) +DEF(uint64_t, be64, 8, AV_RB64, AV_WB64) +DEF(unsigned int, be32, 4, AV_RB32, AV_WB32) +DEF(unsigned int, be24, 3, AV_RB24, AV_WB24) +DEF(unsigned int, be16, 2, AV_RB16, AV_WB16) +DEF(unsigned int, byte, 1, AV_RB8, AV_WB8) + +#if AV_HAVE_BIGENDIAN +#define bytestream2_get_ne16 bytestream2_get_be16 +#define bytestream2_get_ne24 bytestream2_get_be24 +#define bytestream2_get_ne32 bytestream2_get_be32 +#define bytestream2_get_ne64 bytestream2_get_be64 +#define bytestream2_get_ne16u bytestream2_get_be16u +#define bytestream2_get_ne24u bytestream2_get_be24u +#define bytestream2_get_ne32u bytestream2_get_be32u +#define bytestream2_get_ne64u bytestream2_get_be64u +#define bytestream2_put_ne16 bytestream2_put_be16 +#define bytestream2_put_ne24 bytestream2_put_be24 +#define bytestream2_put_ne32 bytestream2_put_be32 +#define bytestream2_put_ne64 bytestream2_put_be64 +#define bytestream2_peek_ne16 bytestream2_peek_be16 +#define bytestream2_peek_ne24 bytestream2_peek_be24 +#define bytestream2_peek_ne32 bytestream2_peek_be32 +#define bytestream2_peek_ne64 bytestream2_peek_be64 +#else +#define bytestream2_get_ne16 bytestream2_get_le16 +#define bytestream2_get_ne24 bytestream2_get_le24 +#define bytestream2_get_ne32 bytestream2_get_le32 +#define bytestream2_get_ne64 bytestream2_get_le64 +#define bytestream2_get_ne16u bytestream2_get_le16u +#define bytestream2_get_ne24u bytestream2_get_le24u +#define bytestream2_get_ne32u bytestream2_get_le32u +#define bytestream2_get_ne64u bytestream2_get_le64u +#define bytestream2_put_ne16 bytestream2_put_le16 +#define bytestream2_put_ne24 bytestream2_put_le24 +#define bytestream2_put_ne32 bytestream2_put_le32 +#define bytestream2_put_ne64 bytestream2_put_le64 +#define bytestream2_peek_ne16 bytestream2_peek_le16 +#define bytestream2_peek_ne24 bytestream2_peek_le24 +#define bytestream2_peek_ne32 bytestream2_peek_le32 +#define bytestream2_peek_ne64 bytestream2_peek_le64 +#endif + +static av_always_inline void bytestream2_init(GetByteContext *g, + const uint8_t *buf, + int buf_size) { + av_assert0(buf_size >= 0); + g->buffer = buf; + g->buffer_start = buf; + g->buffer_end = buf + buf_size; +} + +static av_always_inline void bytestream2_init_writer(PutByteContext *p, + uint8_t *buf, + int buf_size) { + av_assert0(buf_size >= 0); + p->buffer = buf; + p->buffer_start = buf; + p->buffer_end = buf + buf_size; + p->eof = 0; +} + +static av_always_inline int bytestream2_get_bytes_left(GetByteContext *g) { + return g->buffer_end - g->buffer; +} + +static av_always_inline int bytestream2_get_bytes_left_p(PutByteContext *p) { + return p->buffer_end - p->buffer; +} + +static av_always_inline void bytestream2_skip(GetByteContext *g, + unsigned int size) { + g->buffer += FFMIN(g->buffer_end - g->buffer, size); +} + +static av_always_inline void bytestream2_skipu(GetByteContext *g, + unsigned int size) { + g->buffer += size; +} + +static av_always_inline void bytestream2_skip_p(PutByteContext *p, + unsigned int size) { + int size2; + if(p->eof) + return; + size2 = FFMIN(p->buffer_end - p->buffer, size); + if(size2 != size) + p->eof = 1; + p->buffer += size2; +} + +static av_always_inline int bytestream2_tell(GetByteContext *g) { + return (int)(g->buffer - g->buffer_start); +} + +static av_always_inline int bytestream2_tell_p(PutByteContext *p) { + return (int)(p->buffer - p->buffer_start); +} + +static av_always_inline int bytestream2_size(GetByteContext *g) { + return (int)(g->buffer_end - g->buffer_start); +} + +static av_always_inline int bytestream2_size_p(PutByteContext *p) { + return (int)(p->buffer_end - p->buffer_start); +} + +static av_always_inline int bytestream2_seek(GetByteContext *g, + int offset, + int whence) { + switch(whence) { + case SEEK_CUR: + offset = av_clip(offset, -(g->buffer - g->buffer_start), + g->buffer_end - g->buffer); + g->buffer += offset; + break; + case SEEK_END: + offset = av_clip(offset, -(g->buffer_end - g->buffer_start), 0); + g->buffer = g->buffer_end + offset; + break; + case SEEK_SET: + offset = av_clip(offset, 0, g->buffer_end - g->buffer_start); + g->buffer = g->buffer_start + offset; + break; + default: + return AVERROR(EINVAL); + } + return bytestream2_tell(g); +} + +static av_always_inline int bytestream2_seek_p(PutByteContext *p, + int offset, + int whence) { + p->eof = 0; + switch(whence) { + case SEEK_CUR: + if(p->buffer_end - p->buffer < offset) + p->eof = 1; + offset = av_clip(offset, -(p->buffer - p->buffer_start), + p->buffer_end - p->buffer); + p->buffer += offset; + break; + case SEEK_END: + if(offset > 0) + p->eof = 1; + offset = av_clip(offset, -(p->buffer_end - p->buffer_start), 0); + p->buffer = p->buffer_end + offset; + break; + case SEEK_SET: + if(p->buffer_end - p->buffer_start < offset) + p->eof = 1; + offset = av_clip(offset, 0, p->buffer_end - p->buffer_start); + p->buffer = p->buffer_start + offset; + break; + default: + return AVERROR(EINVAL); + } + return bytestream2_tell_p(p); +} + +static av_always_inline unsigned int bytestream2_get_buffer(GetByteContext *g, + uint8_t *dst, + unsigned int size) { + int size2 = FFMIN(g->buffer_end - g->buffer, size); + memcpy(dst, g->buffer, size2); + g->buffer += size2; + return size2; +} + +static av_always_inline unsigned int bytestream2_get_bufferu(GetByteContext *g, + uint8_t *dst, + unsigned int size) { + memcpy(dst, g->buffer, size); + g->buffer += size; + return size; +} + +static av_always_inline unsigned int bytestream2_put_buffer(PutByteContext *p, + const uint8_t *src, + unsigned int size) { + int size2; + if(p->eof) + return 0; + size2 = FFMIN(p->buffer_end - p->buffer, size); + if(size2 != size) + p->eof = 1; + memcpy(p->buffer, src, size2); + p->buffer += size2; + return size2; +} + +static av_always_inline unsigned int bytestream2_put_bufferu(PutByteContext *p, + const uint8_t *src, + unsigned int size) { + memcpy(p->buffer, src, size); + p->buffer += size; + return size; +} + +static av_always_inline void bytestream2_set_buffer(PutByteContext *p, + const uint8_t c, + unsigned int size) { + int size2; + if(p->eof) + return; + size2 = FFMIN(p->buffer_end - p->buffer, size); + if(size2 != size) + p->eof = 1; + memset(p->buffer, c, size2); + p->buffer += size2; +} + +static av_always_inline void bytestream2_set_bufferu(PutByteContext *p, + const uint8_t c, + unsigned int size) { + memset(p->buffer, c, size); + p->buffer += size; +} + +static av_always_inline unsigned int bytestream2_get_eof(PutByteContext *p) { + return p->eof; +} + +static av_always_inline unsigned int bytestream2_copy_bufferu(PutByteContext *p, + GetByteContext *g, + unsigned int size) { + memcpy(p->buffer, g->buffer, size); + p->buffer += size; + g->buffer += size; + return size; +} + +static av_always_inline unsigned int bytestream2_copy_buffer(PutByteContext *p, + GetByteContext *g, + unsigned int size) { + int size2; + + if(p->eof) + return 0; + size = FFMIN(g->buffer_end - g->buffer, size); + size2 = FFMIN(p->buffer_end - p->buffer, size); + if(size2 != size) + p->eof = 1; + + return bytestream2_copy_bufferu(p, g, size2); +} + +static av_always_inline unsigned int bytestream_get_buffer(const uint8_t **b, + uint8_t *dst, + unsigned int size) { + memcpy(dst, *b, size); + (*b) += size; + return size; +} + +static av_always_inline void bytestream_put_buffer(uint8_t **b, + const uint8_t *src, + unsigned int size) { + memcpy(*b, src, size); + (*b) += size; +} + +#endif /* AVCODEC_BYTESTREAM_H */ diff --git a/third-party/cbs/cbs.c b/third-party/cbs/cbs.c new file mode 100644 index 00000000..456cac20 --- /dev/null +++ b/third-party/cbs/cbs.c @@ -0,0 +1,1050 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include +#include + +#include + +#include "cbs/cbs.h" +#include "cbs_internal.h" + +#include "get_bits.h" + + +static const CodedBitstreamType *const cbs_type_table[] = { +#if CONFIG_CBS_AV1 + &ff_cbs_type_av1, +#endif +#if CONFIG_CBS_H264 + &ff_cbs_type_h264, +#endif +#if CONFIG_CBS_H265 + &ff_cbs_type_h265, +#endif +#if CONFIG_CBS_JPEG + &ff_cbs_type_jpeg, +#endif +#if CONFIG_CBS_MPEG2 + &ff_cbs_type_mpeg2, +#endif +#if CONFIG_CBS_VP9 + &ff_cbs_type_vp9, +#endif +}; + +const enum AVCodecID ff_cbs_all_codec_ids[] = { +#if CONFIG_CBS_AV1 + AV_CODEC_ID_AV1, +#endif +#if CONFIG_CBS_H264 + AV_CODEC_ID_H264, +#endif +#if CONFIG_CBS_H265 + AV_CODEC_ID_H265, +#endif +#if CONFIG_CBS_JPEG + AV_CODEC_ID_MJPEG, +#endif +#if CONFIG_CBS_MPEG2 + AV_CODEC_ID_MPEG2VIDEO, +#endif +#if CONFIG_CBS_VP9 + AV_CODEC_ID_VP9, +#endif + AV_CODEC_ID_NONE +}; + +int ff_cbs_init(CodedBitstreamContext **ctx_ptr, + enum AVCodecID codec_id, void *log_ctx) { + CodedBitstreamContext *ctx; + const CodedBitstreamType *type; + int i; + + type = NULL; + for(i = 0; i < FF_ARRAY_ELEMS(cbs_type_table); i++) { + if(cbs_type_table[i]->codec_id == codec_id) { + type = cbs_type_table[i]; + break; + } + } + if(!type) + return AVERROR(EINVAL); + + ctx = av_mallocz(sizeof(*ctx)); + if(!ctx) + return AVERROR(ENOMEM); + + ctx->log_ctx = log_ctx; + ctx->codec = type; /* Must be before any error */ + + if(type->priv_data_size) { + ctx->priv_data = av_mallocz(ctx->codec->priv_data_size); + if(!ctx->priv_data) { + av_freep(&ctx); + return AVERROR(ENOMEM); + } + if(type->priv_class) { + *(const AVClass **)ctx->priv_data = type->priv_class; + av_opt_set_defaults(ctx->priv_data); + } + } + + ctx->decompose_unit_types = NULL; + + ctx->trace_enable = 0; + ctx->trace_level = AV_LOG_TRACE; + + *ctx_ptr = ctx; + return 0; +} + +void ff_cbs_flush(CodedBitstreamContext *ctx) { + if(ctx->codec->flush) + ctx->codec->flush(ctx); +} + +void ff_cbs_close(CodedBitstreamContext **ctx_ptr) { + CodedBitstreamContext *ctx = *ctx_ptr; + + if(!ctx) + return; + + if(ctx->codec->close) + ctx->codec->close(ctx); + + av_freep(&ctx->write_buffer); + + if(ctx->codec->priv_class && ctx->priv_data) + av_opt_free(ctx->priv_data); + + av_freep(&ctx->priv_data); + av_freep(ctx_ptr); +} + +static void cbs_unit_uninit(CodedBitstreamUnit *unit) { + av_buffer_unref(&unit->content_ref); + unit->content = NULL; + + av_buffer_unref(&unit->data_ref); + unit->data = NULL; + unit->data_size = 0; + unit->data_bit_padding = 0; +} + +void ff_cbs_fragment_reset(CodedBitstreamFragment *frag) { + int i; + + for(i = 0; i < frag->nb_units; i++) + cbs_unit_uninit(&frag->units[i]); + frag->nb_units = 0; + + av_buffer_unref(&frag->data_ref); + frag->data = NULL; + frag->data_size = 0; + frag->data_bit_padding = 0; +} + +void ff_cbs_fragment_free(CodedBitstreamFragment *frag) { + ff_cbs_fragment_reset(frag); + + av_freep(&frag->units); + frag->nb_units_allocated = 0; +} + +static int cbs_read_fragment_content(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag) { + int err, i, j; + + for(i = 0; i < frag->nb_units; i++) { + CodedBitstreamUnit *unit = &frag->units[i]; + + if(ctx->decompose_unit_types) { + for(j = 0; j < ctx->nb_decompose_unit_types; j++) { + if(ctx->decompose_unit_types[j] == unit->type) + break; + } + if(j >= ctx->nb_decompose_unit_types) + continue; + } + + av_buffer_unref(&unit->content_ref); + unit->content = NULL; + + av_assert0(unit->data && unit->data_ref); + + err = ctx->codec->read_unit(ctx, unit); + if(err == AVERROR(ENOSYS)) { + av_log(ctx->log_ctx, AV_LOG_VERBOSE, + "Decomposition unimplemented for unit %d " + "(type %" PRIu32 ").\n", + i, unit->type); + } + else if(err == AVERROR(EAGAIN)) { + av_log(ctx->log_ctx, AV_LOG_VERBOSE, + "Skipping decomposition of unit %d " + "(type %" PRIu32 ").\n", + i, unit->type); + av_buffer_unref(&unit->content_ref); + unit->content = NULL; + } + else if(err < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to read unit %d " + "(type %" PRIu32 ").\n", + i, unit->type); + return err; + } + } + + return 0; +} + +static int cbs_fill_fragment_data(CodedBitstreamFragment *frag, + const uint8_t *data, size_t size) { + av_assert0(!frag->data && !frag->data_ref); + + frag->data_ref = + av_buffer_alloc(size + AV_INPUT_BUFFER_PADDING_SIZE); + if(!frag->data_ref) + return AVERROR(ENOMEM); + + frag->data = frag->data_ref->data; + frag->data_size = size; + + memcpy(frag->data, data, size); + memset(frag->data + size, 0, + AV_INPUT_BUFFER_PADDING_SIZE); + + return 0; +} + +static int cbs_read_data(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + AVBufferRef *buf, + const uint8_t *data, size_t size, + int header) { + int err; + + if(buf) { + frag->data_ref = av_buffer_ref(buf); + if(!frag->data_ref) + return AVERROR(ENOMEM); + + frag->data = (uint8_t *)data; + frag->data_size = size; + } + else { + err = cbs_fill_fragment_data(frag, data, size); + if(err < 0) + return err; + } + + err = ctx->codec->split_fragment(ctx, frag, header); + if(err < 0) + return err; + + return cbs_read_fragment_content(ctx, frag); +} + +int ff_cbs_read_extradata(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + const AVCodecParameters *par) { + return cbs_read_data(ctx, frag, NULL, + par->extradata, + par->extradata_size, 1); +} + +int ff_cbs_read_extradata_from_codec(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + const AVCodecContext *avctx) { + return cbs_read_data(ctx, frag, NULL, + avctx->extradata, + avctx->extradata_size, 1); +} + +int ff_cbs_read_packet(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + const AVPacket *pkt) { + return cbs_read_data(ctx, frag, pkt->buf, + pkt->data, pkt->size, 0); +} + +int ff_cbs_read(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + const uint8_t *data, size_t size) { + return cbs_read_data(ctx, frag, NULL, + data, size, 0); +} + +static int cbs_write_unit_data(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) { + PutBitContext pbc; + int ret; + + if(!ctx->write_buffer) { + // Initial write buffer size is 1MB. + ctx->write_buffer_size = 1024 * 1024; + + reallocate_and_try_again: + ret = av_reallocp(&ctx->write_buffer, ctx->write_buffer_size); + if(ret < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Unable to allocate a " + "sufficiently large write buffer (last attempt " + "%zu bytes).\n", + ctx->write_buffer_size); + return ret; + } + } + + init_put_bits(&pbc, ctx->write_buffer, ctx->write_buffer_size); + + ret = ctx->codec->write_unit(ctx, unit, &pbc); + if(ret < 0) { + if(ret == AVERROR(ENOSPC)) { + // Overflow. + if(ctx->write_buffer_size == INT_MAX / 8) + return AVERROR(ENOMEM); + ctx->write_buffer_size = FFMIN(2 * ctx->write_buffer_size, INT_MAX / 8); + goto reallocate_and_try_again; + } + // Write failed for some other reason. + return ret; + } + + // Overflow but we didn't notice. + av_assert0(put_bits_count(&pbc) <= 8 * ctx->write_buffer_size); + + if(put_bits_count(&pbc) % 8) + unit->data_bit_padding = 8 - put_bits_count(&pbc) % 8; + else + unit->data_bit_padding = 0; + + flush_put_bits(&pbc); + + ret = ff_cbs_alloc_unit_data(unit, put_bytes_output(&pbc)); + if(ret < 0) + return ret; + + memcpy(unit->data, ctx->write_buffer, unit->data_size); + + return 0; +} + +int ff_cbs_write_fragment_data(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag) { + int err, i; + + for(i = 0; i < frag->nb_units; i++) { + CodedBitstreamUnit *unit = &frag->units[i]; + + if(!unit->content) + continue; + + av_buffer_unref(&unit->data_ref); + unit->data = NULL; + + err = cbs_write_unit_data(ctx, unit); + if(err < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to write unit %d " + "(type %" PRIu32 ").\n", + i, unit->type); + return err; + } + av_assert0(unit->data && unit->data_ref); + } + + av_buffer_unref(&frag->data_ref); + frag->data = NULL; + + err = ctx->codec->assemble_fragment(ctx, frag); + if(err < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to assemble fragment.\n"); + return err; + } + av_assert0(frag->data && frag->data_ref); + + return 0; +} + +int ff_cbs_write_extradata(CodedBitstreamContext *ctx, + AVCodecParameters *par, + CodedBitstreamFragment *frag) { + int err; + + err = ff_cbs_write_fragment_data(ctx, frag); + if(err < 0) + return err; + + av_freep(&par->extradata); + + par->extradata = av_malloc(frag->data_size + + AV_INPUT_BUFFER_PADDING_SIZE); + if(!par->extradata) + return AVERROR(ENOMEM); + + memcpy(par->extradata, frag->data, frag->data_size); + memset(par->extradata + frag->data_size, 0, + AV_INPUT_BUFFER_PADDING_SIZE); + par->extradata_size = frag->data_size; + + return 0; +} + +int ff_cbs_write_packet(CodedBitstreamContext *ctx, + AVPacket *pkt, + CodedBitstreamFragment *frag) { + AVBufferRef *buf; + int err; + + err = ff_cbs_write_fragment_data(ctx, frag); + if(err < 0) + return err; + + buf = av_buffer_ref(frag->data_ref); + if(!buf) + return AVERROR(ENOMEM); + + av_buffer_unref(&pkt->buf); + + pkt->buf = buf; + pkt->data = frag->data; + pkt->size = frag->data_size; + + return 0; +} + + +void ff_cbs_trace_header(CodedBitstreamContext *ctx, + const char *name) { + if(!ctx->trace_enable) + return; + + av_log(ctx->log_ctx, ctx->trace_level, "%s\n", name); +} + +void ff_cbs_trace_syntax_element(CodedBitstreamContext *ctx, int position, + const char *str, const int *subscripts, + const char *bits, int64_t value) { + char name[256]; + size_t name_len, bits_len; + int pad, subs, i, j, k, n; + + if(!ctx->trace_enable) + return; + + av_assert0(value >= INT_MIN && value <= UINT32_MAX); + + subs = subscripts ? subscripts[0] : 0; + n = 0; + for(i = j = 0; str[i];) { + if(str[i] == '[') { + if(n < subs) { + ++n; + k = snprintf(name + j, sizeof(name) - j, "[%d", subscripts[n]); + av_assert0(k > 0 && j + k < sizeof(name)); + j += k; + for(++i; str[i] && str[i] != ']'; i++) + ; + av_assert0(str[i] == ']'); + } + else { + while(str[i] && str[i] != ']') + name[j++] = str[i++]; + av_assert0(str[i] == ']'); + } + } + else { + av_assert0(j + 1 < sizeof(name)); + name[j++] = str[i++]; + } + } + av_assert0(j + 1 < sizeof(name)); + name[j] = 0; + av_assert0(n == subs); + + name_len = strlen(name); + bits_len = strlen(bits); + + if(name_len + bits_len > 60) + pad = bits_len + 2; + else + pad = 61 - name_len; + + av_log(ctx->log_ctx, ctx->trace_level, "%-10d %s%*s = %" PRId64 "\n", + position, name, pad, bits, value); +} + +int ff_cbs_read_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc, + int width, const char *name, + const int *subscripts, uint32_t *write_to, + uint32_t range_min, uint32_t range_max) { + uint32_t value; + int position; + + av_assert0(width > 0 && width <= 32); + + if(get_bits_left(gbc) < width) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid value at " + "%s: bitstream ended.\n", + name); + return AVERROR_INVALIDDATA; + } + + if(ctx->trace_enable) + position = get_bits_count(gbc); + + value = get_bits_long(gbc, width); + + if(ctx->trace_enable) { + char bits[33]; + int i; + for(i = 0; i < width; i++) + bits[i] = value >> (width - i - 1) & 1 ? '1' : '0'; + bits[i] = 0; + + ff_cbs_trace_syntax_element(ctx, position, name, subscripts, + bits, value); + } + + if(value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%" PRIu32 ", but must be in [%" PRIu32 ",%" PRIu32 "].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + + *write_to = value; + return 0; +} + +int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc, + int width, const char *name, + const int *subscripts, uint32_t value, + uint32_t range_min, uint32_t range_max) { + av_assert0(width > 0 && width <= 32); + + if(value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%" PRIu32 ", but must be in [%" PRIu32 ",%" PRIu32 "].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + + if(put_bits_left(pbc) < width) + return AVERROR(ENOSPC); + + if(ctx->trace_enable) { + char bits[33]; + int i; + for(i = 0; i < width; i++) + bits[i] = value >> (width - i - 1) & 1 ? '1' : '0'; + bits[i] = 0; + + ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), + name, subscripts, bits, value); + } + + if(width < 32) + put_bits(pbc, width, value); + else + put_bits32(pbc, value); + + return 0; +} + +int ff_cbs_read_signed(CodedBitstreamContext *ctx, GetBitContext *gbc, + int width, const char *name, + const int *subscripts, int32_t *write_to, + int32_t range_min, int32_t range_max) { + int32_t value; + int position; + + av_assert0(width > 0 && width <= 32); + + if(get_bits_left(gbc) < width) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid value at " + "%s: bitstream ended.\n", + name); + return AVERROR_INVALIDDATA; + } + + if(ctx->trace_enable) + position = get_bits_count(gbc); + + value = get_sbits_long(gbc, width); + + if(ctx->trace_enable) { + char bits[33]; + int i; + for(i = 0; i < width; i++) + bits[i] = value & (1U << (width - i - 1)) ? '1' : '0'; + bits[i] = 0; + + ff_cbs_trace_syntax_element(ctx, position, name, subscripts, + bits, value); + } + + if(value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%" PRId32 ", but must be in [%" PRId32 ",%" PRId32 "].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + + *write_to = value; + return 0; +} + +int ff_cbs_write_signed(CodedBitstreamContext *ctx, PutBitContext *pbc, + int width, const char *name, + const int *subscripts, int32_t value, + int32_t range_min, int32_t range_max) { + av_assert0(width > 0 && width <= 32); + + if(value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%" PRId32 ", but must be in [%" PRId32 ",%" PRId32 "].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + + if(put_bits_left(pbc) < width) + return AVERROR(ENOSPC); + + if(ctx->trace_enable) { + char bits[33]; + int i; + for(i = 0; i < width; i++) + bits[i] = value & (1U << (width - i - 1)) ? '1' : '0'; + bits[i] = 0; + + ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), + name, subscripts, bits, value); + } + + if(width < 32) + put_sbits(pbc, width, value); + else + put_bits32(pbc, value); + + return 0; +} + + +int ff_cbs_alloc_unit_content(CodedBitstreamUnit *unit, + size_t size, + void (*free)(void *opaque, uint8_t *data)) { + av_assert0(!unit->content && !unit->content_ref); + + unit->content = av_mallocz(size); + if(!unit->content) + return AVERROR(ENOMEM); + + unit->content_ref = av_buffer_create(unit->content, size, + free, NULL, 0); + if(!unit->content_ref) { + av_freep(&unit->content); + return AVERROR(ENOMEM); + } + + return 0; +} + +int ff_cbs_alloc_unit_data(CodedBitstreamUnit *unit, + size_t size) { + av_assert0(!unit->data && !unit->data_ref); + + unit->data_ref = av_buffer_alloc(size + AV_INPUT_BUFFER_PADDING_SIZE); + if(!unit->data_ref) + return AVERROR(ENOMEM); + + unit->data = unit->data_ref->data; + unit->data_size = size; + + memset(unit->data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + + return 0; +} + +static int cbs_insert_unit(CodedBitstreamFragment *frag, + int position) { + CodedBitstreamUnit *units; + + if(frag->nb_units < frag->nb_units_allocated) { + units = frag->units; + + if(position < frag->nb_units) + memmove(units + position + 1, units + position, + (frag->nb_units - position) * sizeof(*units)); + } + else { + units = av_malloc_array(frag->nb_units * 2 + 1, sizeof(*units)); + if(!units) + return AVERROR(ENOMEM); + + frag->nb_units_allocated = 2 * frag->nb_units_allocated + 1; + + if(position > 0) + memcpy(units, frag->units, position * sizeof(*units)); + + if(position < frag->nb_units) + memcpy(units + position + 1, frag->units + position, + (frag->nb_units - position) * sizeof(*units)); + } + + memset(units + position, 0, sizeof(*units)); + + if(units != frag->units) { + av_free(frag->units); + frag->units = units; + } + + ++frag->nb_units; + + return 0; +} + +int ff_cbs_insert_unit_content(CodedBitstreamFragment *frag, + int position, + CodedBitstreamUnitType type, + void *content, + AVBufferRef *content_buf) { + CodedBitstreamUnit *unit; + AVBufferRef *content_ref; + int err; + + if(position == -1) + position = frag->nb_units; + av_assert0(position >= 0 && position <= frag->nb_units); + + if(content_buf) { + content_ref = av_buffer_ref(content_buf); + if(!content_ref) + return AVERROR(ENOMEM); + } + else { + content_ref = NULL; + } + + err = cbs_insert_unit(frag, position); + if(err < 0) { + av_buffer_unref(&content_ref); + return err; + } + + unit = &frag->units[position]; + unit->type = type; + unit->content = content; + unit->content_ref = content_ref; + + return 0; +} + +int ff_cbs_insert_unit_data(CodedBitstreamFragment *frag, + int position, + CodedBitstreamUnitType type, + uint8_t *data, size_t data_size, + AVBufferRef *data_buf) { + CodedBitstreamUnit *unit; + AVBufferRef *data_ref; + int err; + + if(position == -1) + position = frag->nb_units; + av_assert0(position >= 0 && position <= frag->nb_units); + + if(data_buf) + data_ref = av_buffer_ref(data_buf); + else + data_ref = av_buffer_create(data, data_size, NULL, NULL, 0); + if(!data_ref) { + if(!data_buf) + av_free(data); + return AVERROR(ENOMEM); + } + + err = cbs_insert_unit(frag, position); + if(err < 0) { + av_buffer_unref(&data_ref); + return err; + } + + unit = &frag->units[position]; + unit->type = type; + unit->data = data; + unit->data_size = data_size; + unit->data_ref = data_ref; + + return 0; +} + +void ff_cbs_delete_unit(CodedBitstreamFragment *frag, + int position) { + av_assert0(0 <= position && position < frag->nb_units && "Unit to be deleted not in fragment."); + + cbs_unit_uninit(&frag->units[position]); + + --frag->nb_units; + + if(frag->nb_units > 0) + memmove(frag->units + position, + frag->units + position + 1, + (frag->nb_units - position) * sizeof(*frag->units)); +} + +static void cbs_default_free_unit_content(void *opaque, uint8_t *data) { + const CodedBitstreamUnitTypeDescriptor *desc = opaque; + if(desc->content_type == CBS_CONTENT_TYPE_INTERNAL_REFS) { + int i; + for(i = 0; i < desc->nb_ref_offsets; i++) { + void **ptr = (void **)(data + desc->ref_offsets[i]); + av_buffer_unref((AVBufferRef **)(ptr + 1)); + } + } + av_free(data); +} + +static const CodedBitstreamUnitTypeDescriptor + * + cbs_find_unit_type_desc(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) { + const CodedBitstreamUnitTypeDescriptor *desc; + int i, j; + + if(!ctx->codec->unit_types) + return NULL; + + for(i = 0;; i++) { + desc = &ctx->codec->unit_types[i]; + if(desc->nb_unit_types == 0) + break; + if(desc->nb_unit_types == CBS_UNIT_TYPE_RANGE) { + if(unit->type >= desc->unit_type_range_start && + unit->type <= desc->unit_type_range_end) + return desc; + } + else { + for(j = 0; j < desc->nb_unit_types; j++) { + if(desc->unit_types[j] == unit->type) + return desc; + } + } + } + return NULL; +} + +int ff_cbs_alloc_unit_content2(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) { + const CodedBitstreamUnitTypeDescriptor *desc; + + av_assert0(!unit->content && !unit->content_ref); + + desc = cbs_find_unit_type_desc(ctx, unit); + if(!desc) + return AVERROR(ENOSYS); + + unit->content = av_mallocz(desc->content_size); + if(!unit->content) + return AVERROR(ENOMEM); + + unit->content_ref = + av_buffer_create(unit->content, desc->content_size, + desc->content_free ? desc->content_free : cbs_default_free_unit_content, + (void *)desc, 0); + if(!unit->content_ref) { + av_freep(&unit->content); + return AVERROR(ENOMEM); + } + + return 0; +} + +static int cbs_clone_unit_content(AVBufferRef **clone_ref, + CodedBitstreamUnit *unit, + const CodedBitstreamUnitTypeDescriptor *desc) { + uint8_t *src, *copy; + uint8_t **src_ptr, **copy_ptr; + AVBufferRef **src_buf, **copy_buf; + int err, i; + + av_assert0(unit->content); + src = unit->content; + + copy = av_memdup(src, desc->content_size); + if(!copy) + return AVERROR(ENOMEM); + + for(i = 0; i < desc->nb_ref_offsets; i++) { + src_ptr = (uint8_t **)(src + desc->ref_offsets[i]); + src_buf = (AVBufferRef **)(src_ptr + 1); + copy_ptr = (uint8_t **)(copy + desc->ref_offsets[i]); + copy_buf = (AVBufferRef **)(copy_ptr + 1); + + if(!*src_ptr) { + av_assert0(!*src_buf); + continue; + } + if(!*src_buf) { + // We can't handle a non-refcounted pointer here - we don't + // have enough information to handle whatever structure lies + // at the other end of it. + err = AVERROR(EINVAL); + goto fail; + } + + // src_ptr is required to point somewhere inside src_buf. If it + // doesn't, there is a bug somewhere. + av_assert0(*src_ptr >= (*src_buf)->data && + *src_ptr < (*src_buf)->data + (*src_buf)->size); + + *copy_buf = av_buffer_ref(*src_buf); + if(!*copy_buf) { + err = AVERROR(ENOMEM); + goto fail; + } + *copy_ptr = (*copy_buf)->data + (*src_ptr - (*src_buf)->data); + } + + *clone_ref = av_buffer_create(copy, desc->content_size, + desc->content_free ? desc->content_free : + cbs_default_free_unit_content, + (void *)desc, 0); + if(!*clone_ref) { + err = AVERROR(ENOMEM); + goto fail; + } + + return 0; + +fail: + for(--i; i >= 0; i--) + av_buffer_unref((AVBufferRef **)(copy + desc->ref_offsets[i])); + av_freep(©); + *clone_ref = NULL; + return err; +} + +int ff_cbs_make_unit_refcounted(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) { + const CodedBitstreamUnitTypeDescriptor *desc; + AVBufferRef *ref; + int err; + + av_assert0(unit->content); + if(unit->content_ref) { + // Already refcounted, nothing to do. + return 0; + } + + desc = cbs_find_unit_type_desc(ctx, unit); + if(!desc) + return AVERROR(ENOSYS); + + switch(desc->content_type) { + case CBS_CONTENT_TYPE_POD: + ref = av_buffer_alloc(desc->content_size); + if(!ref) + return AVERROR(ENOMEM); + memcpy(ref->data, unit->content, desc->content_size); + err = 0; + break; + + case CBS_CONTENT_TYPE_INTERNAL_REFS: + err = cbs_clone_unit_content(&ref, unit, desc); + break; + + case CBS_CONTENT_TYPE_COMPLEX: + if(!desc->content_clone) + return AVERROR_PATCHWELCOME; + err = desc->content_clone(&ref, unit); + break; + + default: + av_assert0(0 && "Invalid content type."); + } + + if(err < 0) + return err; + + unit->content_ref = ref; + unit->content = ref->data; + return 0; +} + +int ff_cbs_make_unit_writable(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) { + const CodedBitstreamUnitTypeDescriptor *desc; + AVBufferRef *ref; + int err; + + // This can only be applied to refcounted units. + err = ff_cbs_make_unit_refcounted(ctx, unit); + if(err < 0) + return err; + av_assert0(unit->content && unit->content_ref); + + if(av_buffer_is_writable(unit->content_ref)) + return 0; + + desc = cbs_find_unit_type_desc(ctx, unit); + if(!desc) + return AVERROR(ENOSYS); + + switch(desc->content_type) { + case CBS_CONTENT_TYPE_POD: + err = av_buffer_make_writable(&unit->content_ref); + break; + + case CBS_CONTENT_TYPE_INTERNAL_REFS: + err = cbs_clone_unit_content(&ref, unit, desc); + break; + + case CBS_CONTENT_TYPE_COMPLEX: + if(!desc->content_clone) + return AVERROR_PATCHWELCOME; + err = desc->content_clone(&ref, unit); + break; + + default: + av_assert0(0 && "Invalid content type."); + } + if(err < 0) + return err; + + if(desc->content_type != CBS_CONTENT_TYPE_POD) { + av_buffer_unref(&unit->content_ref); + unit->content_ref = ref; + } + unit->content = unit->content_ref->data; + return 0; +} + +const uint8_t ff_log2_tab[256] = { + 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 +}; \ No newline at end of file diff --git a/third-party/cbs/cbs_av1.c b/third-party/cbs/cbs_av1.c new file mode 100644 index 00000000..7bb53b93 --- /dev/null +++ b/third-party/cbs/cbs_av1.c @@ -0,0 +1,1323 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include + +#include "cbs/cbs.h" +#include "cbs/cbs_av1.h" + +#include "cbs_internal.h" + + +static int cbs_av1_read_uvlc(CodedBitstreamContext *ctx, GetBitContext *gbc, + const char *name, uint32_t *write_to, + uint32_t range_min, uint32_t range_max) { + uint32_t zeroes, bits_value, value; + int position; + + if(ctx->trace_enable) + position = get_bits_count(gbc); + + zeroes = 0; + while(1) { + if(get_bits_left(gbc) < 1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid uvlc code at " + "%s: bitstream ended.\n", + name); + return AVERROR_INVALIDDATA; + } + + if(get_bits1(gbc)) + break; + ++zeroes; + } + + if(zeroes >= 32) { + value = MAX_UINT_BITS(32); + } + else { + if(get_bits_left(gbc) < zeroes) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid uvlc code at " + "%s: bitstream ended.\n", + name); + return AVERROR_INVALIDDATA; + } + + bits_value = get_bits_long(gbc, zeroes); + value = bits_value + (UINT32_C(1) << zeroes) - 1; + } + + if(ctx->trace_enable) { + char bits[65]; + int i, j, k; + + if(zeroes >= 32) { + while(zeroes > 32) { + k = FFMIN(zeroes - 32, 32); + for(i = 0; i < k; i++) + bits[i] = '0'; + bits[i] = 0; + ff_cbs_trace_syntax_element(ctx, position, name, + NULL, bits, 0); + zeroes -= k; + position += k; + } + } + + for(i = 0; i < zeroes; i++) + bits[i] = '0'; + bits[i++] = '1'; + + if(zeroes < 32) { + for(j = 0; j < zeroes; j++) + bits[i++] = (bits_value >> (zeroes - j - 1) & 1) ? '1' : '0'; + } + + bits[i] = 0; + ff_cbs_trace_syntax_element(ctx, position, name, + NULL, bits, value); + } + + if(value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%" PRIu32 ", but must be in [%" PRIu32 ",%" PRIu32 "].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + + *write_to = value; + return 0; +} + +static int cbs_av1_write_uvlc(CodedBitstreamContext *ctx, PutBitContext *pbc, + const char *name, uint32_t value, + uint32_t range_min, uint32_t range_max) { + uint32_t v; + int position, zeroes; + + if(value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%" PRIu32 ", but must be in [%" PRIu32 ",%" PRIu32 "].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + + if(ctx->trace_enable) + position = put_bits_count(pbc); + + zeroes = av_log2(value + 1); + v = value - (1U << zeroes) + 1; + put_bits(pbc, zeroes, 0); + put_bits(pbc, 1, 1); + put_bits(pbc, zeroes, v); + + if(ctx->trace_enable) { + char bits[65]; + int i, j; + i = 0; + for(j = 0; j < zeroes; j++) + bits[i++] = '0'; + bits[i++] = '1'; + for(j = 0; j < zeroes; j++) + bits[i++] = (v >> (zeroes - j - 1) & 1) ? '1' : '0'; + bits[i++] = 0; + ff_cbs_trace_syntax_element(ctx, position, name, NULL, + bits, value); + } + + return 0; +} + +static int cbs_av1_read_leb128(CodedBitstreamContext *ctx, GetBitContext *gbc, + const char *name, uint64_t *write_to) { + uint64_t value; + int position, err, i; + + if(ctx->trace_enable) + position = get_bits_count(gbc); + + value = 0; + for(i = 0; i < 8; i++) { + int subscript[2] = { 1, i }; + uint32_t byte; + err = ff_cbs_read_unsigned(ctx, gbc, 8, "leb128_byte[i]", subscript, + &byte, 0x00, 0xff); + if(err < 0) + return err; + + value |= (uint64_t)(byte & 0x7f) << (i * 7); + if(!(byte & 0x80)) + break; + } + + if(value > UINT32_MAX) + return AVERROR_INVALIDDATA; + + if(ctx->trace_enable) + ff_cbs_trace_syntax_element(ctx, position, name, NULL, "", value); + + *write_to = value; + return 0; +} + +static int cbs_av1_write_leb128(CodedBitstreamContext *ctx, PutBitContext *pbc, + const char *name, uint64_t value) { + int position, err, len, i; + uint8_t byte; + + len = (av_log2(value) + 7) / 7; + + if(ctx->trace_enable) + position = put_bits_count(pbc); + + for(i = 0; i < len; i++) { + int subscript[2] = { 1, i }; + + byte = value >> (7 * i) & 0x7f; + if(i < len - 1) + byte |= 0x80; + + err = ff_cbs_write_unsigned(ctx, pbc, 8, "leb128_byte[i]", subscript, + byte, 0x00, 0xff); + if(err < 0) + return err; + } + + if(ctx->trace_enable) + ff_cbs_trace_syntax_element(ctx, position, name, NULL, "", value); + + return 0; +} + +static int cbs_av1_read_ns(CodedBitstreamContext *ctx, GetBitContext *gbc, + uint32_t n, const char *name, + const int *subscripts, uint32_t *write_to) { + uint32_t m, v, extra_bit, value; + int position, w; + + av_assert0(n > 0); + + if(ctx->trace_enable) + position = get_bits_count(gbc); + + w = av_log2(n) + 1; + m = (1 << w) - n; + + if(get_bits_left(gbc) < w) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid non-symmetric value at " + "%s: bitstream ended.\n", + name); + return AVERROR_INVALIDDATA; + } + + if(w - 1 > 0) + v = get_bits(gbc, w - 1); + else + v = 0; + + if(v < m) { + value = v; + } + else { + extra_bit = get_bits1(gbc); + value = (v << 1) - m + extra_bit; + } + + if(ctx->trace_enable) { + char bits[33]; + int i; + for(i = 0; i < w - 1; i++) + bits[i] = (v >> i & 1) ? '1' : '0'; + if(v >= m) + bits[i++] = extra_bit ? '1' : '0'; + bits[i] = 0; + + ff_cbs_trace_syntax_element(ctx, position, + name, subscripts, bits, value); + } + + *write_to = value; + return 0; +} + +static int cbs_av1_write_ns(CodedBitstreamContext *ctx, PutBitContext *pbc, + uint32_t n, const char *name, + const int *subscripts, uint32_t value) { + uint32_t w, m, v, extra_bit; + int position; + + if(value > n) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%" PRIu32 ", but must be in [0,%" PRIu32 "].\n", + name, value, n); + return AVERROR_INVALIDDATA; + } + + if(ctx->trace_enable) + position = put_bits_count(pbc); + + w = av_log2(n) + 1; + m = (1 << w) - n; + + if(put_bits_left(pbc) < w) + return AVERROR(ENOSPC); + + if(value < m) { + v = value; + put_bits(pbc, w - 1, v); + } + else { + v = m + ((value - m) >> 1); + extra_bit = (value - m) & 1; + put_bits(pbc, w - 1, v); + put_bits(pbc, 1, extra_bit); + } + + if(ctx->trace_enable) { + char bits[33]; + int i; + for(i = 0; i < w - 1; i++) + bits[i] = (v >> i & 1) ? '1' : '0'; + if(value >= m) + bits[i++] = extra_bit ? '1' : '0'; + bits[i] = 0; + + ff_cbs_trace_syntax_element(ctx, position, + name, subscripts, bits, value); + } + + return 0; +} + +static int cbs_av1_read_increment(CodedBitstreamContext *ctx, GetBitContext *gbc, + uint32_t range_min, uint32_t range_max, + const char *name, uint32_t *write_to) { + uint32_t value; + int position, i; + char bits[33]; + + av_assert0(range_min <= range_max && range_max - range_min < sizeof(bits) - 1); + if(ctx->trace_enable) + position = get_bits_count(gbc); + + for(i = 0, value = range_min; value < range_max;) { + if(get_bits_left(gbc) < 1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid increment value at " + "%s: bitstream ended.\n", + name); + return AVERROR_INVALIDDATA; + } + if(get_bits1(gbc)) { + bits[i++] = '1'; + ++value; + } + else { + bits[i++] = '0'; + break; + } + } + + if(ctx->trace_enable) { + bits[i] = 0; + ff_cbs_trace_syntax_element(ctx, position, + name, NULL, bits, value); + } + + *write_to = value; + return 0; +} + +static int cbs_av1_write_increment(CodedBitstreamContext *ctx, PutBitContext *pbc, + uint32_t range_min, uint32_t range_max, + const char *name, uint32_t value) { + int len; + + av_assert0(range_min <= range_max && range_max - range_min < 32); + if(value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%" PRIu32 ", but must be in [%" PRIu32 ",%" PRIu32 "].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + + if(value == range_max) + len = range_max - range_min; + else + len = value - range_min + 1; + if(put_bits_left(pbc) < len) + return AVERROR(ENOSPC); + + if(ctx->trace_enable) { + char bits[33]; + int i; + for(i = 0; i < len; i++) { + if(range_min + i == value) + bits[i] = '0'; + else + bits[i] = '1'; + } + bits[i] = 0; + ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), + name, NULL, bits, value); + } + + if(len > 0) + put_bits(pbc, len, (1 << len) - 1 - (value != range_max)); + + return 0; +} + +static int cbs_av1_read_subexp(CodedBitstreamContext *ctx, GetBitContext *gbc, + uint32_t range_max, const char *name, + const int *subscripts, uint32_t *write_to) { + uint32_t value; + int position, err; + uint32_t max_len, len, range_offset, range_bits; + + if(ctx->trace_enable) + position = get_bits_count(gbc); + + av_assert0(range_max > 0); + max_len = av_log2(range_max - 1) - 3; + + err = cbs_av1_read_increment(ctx, gbc, 0, max_len, + "subexp_more_bits", &len); + if(err < 0) + return err; + + if(len) { + range_bits = 2 + len; + range_offset = 1 << range_bits; + } + else { + range_bits = 3; + range_offset = 0; + } + + if(len < max_len) { + err = ff_cbs_read_unsigned(ctx, gbc, range_bits, + "subexp_bits", NULL, &value, + 0, MAX_UINT_BITS(range_bits)); + if(err < 0) + return err; + } + else { + err = cbs_av1_read_ns(ctx, gbc, range_max - range_offset, + "subexp_final_bits", NULL, &value); + if(err < 0) + return err; + } + value += range_offset; + + if(ctx->trace_enable) + ff_cbs_trace_syntax_element(ctx, position, + name, subscripts, "", value); + + *write_to = value; + return err; +} + +static int cbs_av1_write_subexp(CodedBitstreamContext *ctx, PutBitContext *pbc, + uint32_t range_max, const char *name, + const int *subscripts, uint32_t value) { + int position, err; + uint32_t max_len, len, range_offset, range_bits; + + if(value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%" PRIu32 ", but must be in [0,%" PRIu32 "].\n", + name, value, range_max); + return AVERROR_INVALIDDATA; + } + + if(ctx->trace_enable) + position = put_bits_count(pbc); + + av_assert0(range_max > 0); + max_len = av_log2(range_max - 1) - 3; + + if(value < 8) { + range_bits = 3; + range_offset = 0; + len = 0; + } + else { + range_bits = av_log2(value); + len = range_bits - 2; + if(len > max_len) { + // The top bin is combined with the one below it. + av_assert0(len == max_len + 1); + --range_bits; + len = max_len; + } + range_offset = 1 << range_bits; + } + + err = cbs_av1_write_increment(ctx, pbc, 0, max_len, + "subexp_more_bits", len); + if(err < 0) + return err; + + if(len < max_len) { + err = ff_cbs_write_unsigned(ctx, pbc, range_bits, + "subexp_bits", NULL, + value - range_offset, + 0, MAX_UINT_BITS(range_bits)); + if(err < 0) + return err; + } + else { + err = cbs_av1_write_ns(ctx, pbc, range_max - range_offset, + "subexp_final_bits", NULL, + value - range_offset); + if(err < 0) + return err; + } + + if(ctx->trace_enable) + ff_cbs_trace_syntax_element(ctx, position, + name, subscripts, "", value); + + return err; +} + + +static int cbs_av1_tile_log2(int blksize, int target) { + int k; + for(k = 0; (blksize << k) < target; k++) + ; + return k; +} + +static int cbs_av1_get_relative_dist(const AV1RawSequenceHeader *seq, + unsigned int a, unsigned int b) { + unsigned int diff, m; + if(!seq->enable_order_hint) + return 0; + diff = a - b; + m = 1 << seq->order_hint_bits_minus_1; + diff = (diff & (m - 1)) - (diff & m); + return diff; +} + +static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc) { + GetBitContext tmp = *gbc; + size_t size = 0; + for(int i = 0; get_bits_left(&tmp) >= 8; i++) { + if(get_bits(&tmp, 8)) + size = i; + } + return size; +} + + +#define HEADER(name) \ + do { \ + ff_cbs_trace_header(ctx, name); \ + } while(0) + +#define CHECK(call) \ + do { \ + err = (call); \ + if(err < 0) \ + return err; \ + } while(0) + +#define FUNC_NAME(rw, codec, name) cbs_##codec##_##rw##_##name +#define FUNC_AV1(rw, name) FUNC_NAME(rw, av1, name) +#define FUNC(name) FUNC_AV1(READWRITE, name) + +#define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]) { subs, __VA_ARGS__ }) : NULL) + +#define fb(width, name) \ + xf(width, name, current->name, 0, MAX_UINT_BITS(width), 0, ) +#define fc(width, name, range_min, range_max) \ + xf(width, name, current->name, range_min, range_max, 0, ) +#define flag(name) fb(1, name) +#define su(width, name) \ + xsu(width, name, current->name, 0, ) + +#define fbs(width, name, subs, ...) \ + xf(width, name, current->name, 0, MAX_UINT_BITS(width), subs, __VA_ARGS__) +#define fcs(width, name, range_min, range_max, subs, ...) \ + xf(width, name, current->name, range_min, range_max, subs, __VA_ARGS__) +#define flags(name, subs, ...) \ + xf(1, name, current->name, 0, 1, subs, __VA_ARGS__) +#define sus(width, name, subs, ...) \ + xsu(width, name, current->name, subs, __VA_ARGS__) + +#define fixed(width, name, value) \ + do { \ + av_unused uint32_t fixed_value = value; \ + xf(width, name, fixed_value, value, value, 0, ); \ + } while(0) + + +#define READ +#define READWRITE read +#define RWContext GetBitContext + +#define xf(width, name, var, range_min, range_max, subs, ...) \ + do { \ + uint32_t value; \ + CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + &value, range_min, range_max)); \ + var = value; \ + } while(0) + +#define xsu(width, name, var, subs, ...) \ + do { \ + int32_t value; \ + CHECK(ff_cbs_read_signed(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), &value, \ + MIN_INT_BITS(width), \ + MAX_INT_BITS(width))); \ + var = value; \ + } while(0) + +#define uvlc(name, range_min, range_max) \ + do { \ + uint32_t value; \ + CHECK(cbs_av1_read_uvlc(ctx, rw, #name, \ + &value, range_min, range_max)); \ + current->name = value; \ + } while(0) + +#define ns(max_value, name, subs, ...) \ + do { \ + uint32_t value; \ + CHECK(cbs_av1_read_ns(ctx, rw, max_value, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), &value)); \ + current->name = value; \ + } while(0) + +#define increment(name, min, max) \ + do { \ + uint32_t value; \ + CHECK(cbs_av1_read_increment(ctx, rw, min, max, #name, &value)); \ + current->name = value; \ + } while(0) + +#define subexp(name, max, subs, ...) \ + do { \ + uint32_t value; \ + CHECK(cbs_av1_read_subexp(ctx, rw, max, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), &value)); \ + current->name = value; \ + } while(0) + +#define delta_q(name) \ + do { \ + uint8_t delta_coded; \ + int8_t delta_q; \ + xf(1, name.delta_coded, delta_coded, 0, 1, 0, ); \ + if(delta_coded) \ + xsu(1 + 6, name.delta_q, delta_q, 0, ); \ + else \ + delta_q = 0; \ + current->name = delta_q; \ + } while(0) + +#define leb128(name) \ + do { \ + uint64_t value; \ + CHECK(cbs_av1_read_leb128(ctx, rw, #name, &value)); \ + current->name = value; \ + } while(0) + +#define infer(name, value) \ + do { \ + current->name = value; \ + } while(0) + +#define byte_alignment(rw) (get_bits_count(rw) % 8) + +#include "cbs_av1_syntax_template.c" + +#undef READ +#undef READWRITE +#undef RWContext +#undef xf +#undef xsu +#undef uvlc +#undef ns +#undef increment +#undef subexp +#undef delta_q +#undef leb128 +#undef infer +#undef byte_alignment + + +#define WRITE +#define READWRITE write +#define RWContext PutBitContext + +#define xf(width, name, var, range_min, range_max, subs, ...) \ + do { \ + CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + var, range_min, range_max)); \ + } while(0) + +#define xsu(width, name, var, subs, ...) \ + do { \ + CHECK(ff_cbs_write_signed(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), var, \ + MIN_INT_BITS(width), \ + MAX_INT_BITS(width))); \ + } while(0) + +#define uvlc(name, range_min, range_max) \ + do { \ + CHECK(cbs_av1_write_uvlc(ctx, rw, #name, current->name, \ + range_min, range_max)); \ + } while(0) + +#define ns(max_value, name, subs, ...) \ + do { \ + CHECK(cbs_av1_write_ns(ctx, rw, max_value, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + current->name)); \ + } while(0) + +#define increment(name, min, max) \ + do { \ + CHECK(cbs_av1_write_increment(ctx, rw, min, max, #name, \ + current->name)); \ + } while(0) + +#define subexp(name, max, subs, ...) \ + do { \ + CHECK(cbs_av1_write_subexp(ctx, rw, max, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + current->name)); \ + } while(0) + +#define delta_q(name) \ + do { \ + xf(1, name.delta_coded, current->name != 0, 0, 1, 0, ); \ + if(current->name) \ + xsu(1 + 6, name.delta_q, current->name, 0, ); \ + } while(0) + +#define leb128(name) \ + do { \ + CHECK(cbs_av1_write_leb128(ctx, rw, #name, current->name)); \ + } while(0) + +#define infer(name, value) \ + do { \ + if(current->name != (value)) { \ + av_log(ctx->log_ctx, AV_LOG_ERROR, \ + "%s does not match inferred value: " \ + "%" PRId64 ", but should be %" PRId64 ".\n", \ + #name, (int64_t)current->name, (int64_t)(value)); \ + return AVERROR_INVALIDDATA; \ + } \ + } while(0) + +#define byte_alignment(rw) (put_bits_count(rw) % 8) + +#include "cbs_av1_syntax_template.c" + +#undef WRITE +#undef READWRITE +#undef RWContext +#undef xf +#undef xsu +#undef uvlc +#undef ns +#undef increment +#undef subexp +#undef delta_q +#undef leb128 +#undef infer +#undef byte_alignment + + +static int cbs_av1_split_fragment(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + int header) { + GetBitContext gbc; + uint8_t *data; + size_t size; + uint64_t obu_length; + int pos, err, trace; + + // Don't include this parsing in trace output. + trace = ctx->trace_enable; + ctx->trace_enable = 0; + + data = frag->data; + size = frag->data_size; + + if(INT_MAX / 8 < size) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid fragment: " + "too large (%zu bytes).\n", + size); + err = AVERROR_INVALIDDATA; + goto fail; + } + + if(header && size && data[0] & 0x80) { + // first bit is nonzero, the extradata does not consist purely of + // OBUs. Expect MP4/Matroska AV1CodecConfigurationRecord + int config_record_version = data[0] & 0x7f; + + if(config_record_version != 1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Unknown version %d of AV1CodecConfigurationRecord " + "found!\n", + config_record_version); + err = AVERROR_INVALIDDATA; + goto fail; + } + + if(size <= 4) { + if(size < 4) { + av_log(ctx->log_ctx, AV_LOG_WARNING, + "Undersized AV1CodecConfigurationRecord v%d found!\n", + config_record_version); + err = AVERROR_INVALIDDATA; + goto fail; + } + + goto success; + } + + // In AV1CodecConfigurationRecord v1, actual OBUs start after + // four bytes. Thus set the offset as required for properly + // parsing them. + data += 4; + size -= 4; + } + + while(size > 0) { + AV1RawOBUHeader header; + uint64_t obu_size; + + init_get_bits(&gbc, data, 8 * size); + + err = cbs_av1_read_obu_header(ctx, &gbc, &header); + if(err < 0) + goto fail; + + if(header.obu_has_size_field) { + if(get_bits_left(&gbc) < 8) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid OBU: fragment " + "too short (%zu bytes).\n", + size); + err = AVERROR_INVALIDDATA; + goto fail; + } + err = cbs_av1_read_leb128(ctx, &gbc, "obu_size", &obu_size); + if(err < 0) + goto fail; + } + else + obu_size = size - 1 - header.obu_extension_flag; + + pos = get_bits_count(&gbc); + av_assert0(pos % 8 == 0 && pos / 8 <= size); + + obu_length = pos / 8 + obu_size; + + if(size < obu_length) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid OBU length: " + "%" PRIu64 ", but only %zu bytes remaining in fragment.\n", + obu_length, size); + err = AVERROR_INVALIDDATA; + goto fail; + } + + err = ff_cbs_insert_unit_data(frag, -1, header.obu_type, + data, obu_length, frag->data_ref); + if(err < 0) + goto fail; + + data += obu_length; + size -= obu_length; + } + +success: + err = 0; +fail: + ctx->trace_enable = trace; + return err; +} + +static int cbs_av1_ref_tile_data(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + GetBitContext *gbc, + AV1RawTileData *td) { + int pos; + + pos = get_bits_count(gbc); + if(pos >= 8 * unit->data_size) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Bitstream ended before " + "any data in tile group (%d bits read).\n", + pos); + return AVERROR_INVALIDDATA; + } + // Must be byte-aligned at this point. + av_assert0(pos % 8 == 0); + + td->data_ref = av_buffer_ref(unit->data_ref); + if(!td->data_ref) + return AVERROR(ENOMEM); + + td->data = unit->data + pos / 8; + td->data_size = unit->data_size - pos / 8; + + return 0; +} + +static int cbs_av1_read_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + AV1RawOBU *obu; + GetBitContext gbc; + int err, start_pos, end_pos; + + err = ff_cbs_alloc_unit_content2(ctx, unit); + if(err < 0) + return err; + obu = unit->content; + + err = init_get_bits(&gbc, unit->data, 8 * unit->data_size); + if(err < 0) + return err; + + err = cbs_av1_read_obu_header(ctx, &gbc, &obu->header); + if(err < 0) + return err; + av_assert0(obu->header.obu_type == unit->type); + + if(obu->header.obu_has_size_field) { + uint64_t obu_size; + err = cbs_av1_read_leb128(ctx, &gbc, "obu_size", &obu_size); + if(err < 0) + return err; + obu->obu_size = obu_size; + } + else { + if(unit->data_size < 1 + obu->header.obu_extension_flag) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid OBU length: " + "unit too short (%zu).\n", + unit->data_size); + return AVERROR_INVALIDDATA; + } + obu->obu_size = unit->data_size - 1 - obu->header.obu_extension_flag; + } + + start_pos = get_bits_count(&gbc); + + if(obu->header.obu_extension_flag) { + if(obu->header.obu_type != AV1_OBU_SEQUENCE_HEADER && + obu->header.obu_type != AV1_OBU_TEMPORAL_DELIMITER && + priv->operating_point_idc) { + int in_temporal_layer = + (priv->operating_point_idc >> priv->temporal_id) & 1; + int in_spatial_layer = + (priv->operating_point_idc >> (priv->spatial_id + 8)) & 1; + if(!in_temporal_layer || !in_spatial_layer) { + return AVERROR(EAGAIN); // drop_obu() + } + } + } + + switch(obu->header.obu_type) { + case AV1_OBU_SEQUENCE_HEADER: { + err = cbs_av1_read_sequence_header_obu(ctx, &gbc, + &obu->obu.sequence_header); + if(err < 0) + return err; + + if(priv->operating_point >= 0) { + AV1RawSequenceHeader *sequence_header = &obu->obu.sequence_header; + + if(priv->operating_point > sequence_header->operating_points_cnt_minus_1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid Operating Point %d requested. " + "Must not be higher than %u.\n", + priv->operating_point, sequence_header->operating_points_cnt_minus_1); + return AVERROR(EINVAL); + } + priv->operating_point_idc = sequence_header->operating_point_idc[priv->operating_point]; + } + + av_buffer_unref(&priv->sequence_header_ref); + priv->sequence_header = NULL; + + priv->sequence_header_ref = av_buffer_ref(unit->content_ref); + if(!priv->sequence_header_ref) + return AVERROR(ENOMEM); + priv->sequence_header = &obu->obu.sequence_header; + } break; + case AV1_OBU_TEMPORAL_DELIMITER: { + err = cbs_av1_read_temporal_delimiter_obu(ctx, &gbc); + if(err < 0) + return err; + } break; + case AV1_OBU_FRAME_HEADER: + case AV1_OBU_REDUNDANT_FRAME_HEADER: { + err = cbs_av1_read_frame_header_obu(ctx, &gbc, + &obu->obu.frame_header, + obu->header.obu_type == + AV1_OBU_REDUNDANT_FRAME_HEADER, + unit->data_ref); + if(err < 0) + return err; + } break; + case AV1_OBU_TILE_GROUP: { + err = cbs_av1_read_tile_group_obu(ctx, &gbc, + &obu->obu.tile_group); + if(err < 0) + return err; + + err = cbs_av1_ref_tile_data(ctx, unit, &gbc, + &obu->obu.tile_group.tile_data); + if(err < 0) + return err; + } break; + case AV1_OBU_FRAME: { + err = cbs_av1_read_frame_obu(ctx, &gbc, &obu->obu.frame, + unit->data_ref); + if(err < 0) + return err; + + err = cbs_av1_ref_tile_data(ctx, unit, &gbc, + &obu->obu.frame.tile_group.tile_data); + if(err < 0) + return err; + } break; + case AV1_OBU_TILE_LIST: { + err = cbs_av1_read_tile_list_obu(ctx, &gbc, + &obu->obu.tile_list); + if(err < 0) + return err; + + err = cbs_av1_ref_tile_data(ctx, unit, &gbc, + &obu->obu.tile_list.tile_data); + if(err < 0) + return err; + } break; + case AV1_OBU_METADATA: { + err = cbs_av1_read_metadata_obu(ctx, &gbc, &obu->obu.metadata); + if(err < 0) + return err; + } break; + case AV1_OBU_PADDING: { + err = cbs_av1_read_padding_obu(ctx, &gbc, &obu->obu.padding); + if(err < 0) + return err; + } break; + default: + return AVERROR(ENOSYS); + } + + end_pos = get_bits_count(&gbc); + av_assert0(end_pos <= unit->data_size * 8); + + if(obu->obu_size > 0 && + obu->header.obu_type != AV1_OBU_TILE_GROUP && + obu->header.obu_type != AV1_OBU_TILE_LIST && + obu->header.obu_type != AV1_OBU_FRAME) { + int nb_bits = obu->obu_size * 8 + start_pos - end_pos; + + if(nb_bits <= 0) + return AVERROR_INVALIDDATA; + + err = cbs_av1_read_trailing_bits(ctx, &gbc, nb_bits); + if(err < 0) + return err; + } + + return 0; +} + +static int cbs_av1_write_obu(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + PutBitContext *pbc) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + AV1RawOBU *obu = unit->content; + PutBitContext pbc_tmp; + AV1RawTileData *td; + size_t header_size; + int err, start_pos, end_pos, data_pos; + + // OBUs in the normal bitstream format must contain a size field + // in every OBU (in annex B it is optional, but we don't support + // writing that). + obu->header.obu_has_size_field = 1; + + err = cbs_av1_write_obu_header(ctx, pbc, &obu->header); + if(err < 0) + return err; + + if(obu->header.obu_has_size_field) { + pbc_tmp = *pbc; + // Add space for the size field to fill later. + put_bits32(pbc, 0); + put_bits32(pbc, 0); + } + + td = NULL; + start_pos = put_bits_count(pbc); + + switch(obu->header.obu_type) { + case AV1_OBU_SEQUENCE_HEADER: { + err = cbs_av1_write_sequence_header_obu(ctx, pbc, + &obu->obu.sequence_header); + if(err < 0) + return err; + + av_buffer_unref(&priv->sequence_header_ref); + priv->sequence_header = NULL; + + err = ff_cbs_make_unit_refcounted(ctx, unit); + if(err < 0) + return err; + + priv->sequence_header_ref = av_buffer_ref(unit->content_ref); + if(!priv->sequence_header_ref) + return AVERROR(ENOMEM); + priv->sequence_header = &obu->obu.sequence_header; + } break; + case AV1_OBU_TEMPORAL_DELIMITER: { + err = cbs_av1_write_temporal_delimiter_obu(ctx, pbc); + if(err < 0) + return err; + } break; + case AV1_OBU_FRAME_HEADER: + case AV1_OBU_REDUNDANT_FRAME_HEADER: { + err = cbs_av1_write_frame_header_obu(ctx, pbc, + &obu->obu.frame_header, + obu->header.obu_type == + AV1_OBU_REDUNDANT_FRAME_HEADER, + NULL); + if(err < 0) + return err; + } break; + case AV1_OBU_TILE_GROUP: { + err = cbs_av1_write_tile_group_obu(ctx, pbc, + &obu->obu.tile_group); + if(err < 0) + return err; + + td = &obu->obu.tile_group.tile_data; + } break; + case AV1_OBU_FRAME: { + err = cbs_av1_write_frame_obu(ctx, pbc, &obu->obu.frame, NULL); + if(err < 0) + return err; + + td = &obu->obu.frame.tile_group.tile_data; + } break; + case AV1_OBU_TILE_LIST: { + err = cbs_av1_write_tile_list_obu(ctx, pbc, &obu->obu.tile_list); + if(err < 0) + return err; + + td = &obu->obu.tile_list.tile_data; + } break; + case AV1_OBU_METADATA: { + err = cbs_av1_write_metadata_obu(ctx, pbc, &obu->obu.metadata); + if(err < 0) + return err; + } break; + case AV1_OBU_PADDING: { + err = cbs_av1_write_padding_obu(ctx, pbc, &obu->obu.padding); + if(err < 0) + return err; + } break; + default: + return AVERROR(ENOSYS); + } + + end_pos = put_bits_count(pbc); + header_size = (end_pos - start_pos + 7) / 8; + if(td) { + obu->obu_size = header_size + td->data_size; + } + else if(header_size > 0) { + // Add trailing bits and recalculate. + err = cbs_av1_write_trailing_bits(ctx, pbc, 8 - end_pos % 8); + if(err < 0) + return err; + end_pos = put_bits_count(pbc); + obu->obu_size = header_size = (end_pos - start_pos + 7) / 8; + } + else { + // Empty OBU. + obu->obu_size = 0; + } + + end_pos = put_bits_count(pbc); + // Must now be byte-aligned. + av_assert0(end_pos % 8 == 0); + flush_put_bits(pbc); + start_pos /= 8; + end_pos /= 8; + + *pbc = pbc_tmp; + err = cbs_av1_write_leb128(ctx, pbc, "obu_size", obu->obu_size); + if(err < 0) + return err; + + data_pos = put_bits_count(pbc) / 8; + flush_put_bits(pbc); + av_assert0(data_pos <= start_pos); + + if(8 * obu->obu_size > put_bits_left(pbc)) + return AVERROR(ENOSPC); + + if(obu->obu_size > 0) { + memmove(pbc->buf + data_pos, + pbc->buf + start_pos, header_size); + skip_put_bytes(pbc, header_size); + + if(td) { + memcpy(pbc->buf + data_pos + header_size, + td->data, td->data_size); + skip_put_bytes(pbc, td->data_size); + } + } + + // OBU data must be byte-aligned. + av_assert0(put_bits_count(pbc) % 8 == 0); + + return 0; +} + +static int cbs_av1_assemble_fragment(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag) { + size_t size, pos; + int i; + + size = 0; + for(i = 0; i < frag->nb_units; i++) + size += frag->units[i].data_size; + + frag->data_ref = av_buffer_alloc(size + AV_INPUT_BUFFER_PADDING_SIZE); + if(!frag->data_ref) + return AVERROR(ENOMEM); + frag->data = frag->data_ref->data; + memset(frag->data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + + pos = 0; + for(i = 0; i < frag->nb_units; i++) { + memcpy(frag->data + pos, frag->units[i].data, + frag->units[i].data_size); + pos += frag->units[i].data_size; + } + av_assert0(pos == size); + frag->data_size = size; + + return 0; +} + +static void cbs_av1_flush(CodedBitstreamContext *ctx) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + + av_buffer_unref(&priv->frame_header_ref); + priv->sequence_header = NULL; + priv->frame_header = NULL; + + memset(priv->ref, 0, sizeof(priv->ref)); + priv->operating_point_idc = 0; + priv->seen_frame_header = 0; + priv->tile_num = 0; +} + +static void cbs_av1_close(CodedBitstreamContext *ctx) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + + av_buffer_unref(&priv->sequence_header_ref); + av_buffer_unref(&priv->frame_header_ref); +} + +static void cbs_av1_free_metadata(void *unit, uint8_t *content) { + AV1RawOBU *obu = (AV1RawOBU *)content; + AV1RawMetadata *md; + + av_assert0(obu->header.obu_type == AV1_OBU_METADATA); + md = &obu->obu.metadata; + + switch(md->metadata_type) { + case AV1_METADATA_TYPE_ITUT_T35: + av_buffer_unref(&md->metadata.itut_t35.payload_ref); + break; + } + av_free(content); +} + +static const CodedBitstreamUnitTypeDescriptor cbs_av1_unit_types[] = { + CBS_UNIT_TYPE_POD(AV1_OBU_SEQUENCE_HEADER, AV1RawOBU), + CBS_UNIT_TYPE_POD(AV1_OBU_TEMPORAL_DELIMITER, AV1RawOBU), + CBS_UNIT_TYPE_POD(AV1_OBU_FRAME_HEADER, AV1RawOBU), + CBS_UNIT_TYPE_POD(AV1_OBU_REDUNDANT_FRAME_HEADER, AV1RawOBU), + + CBS_UNIT_TYPE_INTERNAL_REF(AV1_OBU_TILE_GROUP, AV1RawOBU, + obu.tile_group.tile_data.data), + CBS_UNIT_TYPE_INTERNAL_REF(AV1_OBU_FRAME, AV1RawOBU, + obu.frame.tile_group.tile_data.data), + CBS_UNIT_TYPE_INTERNAL_REF(AV1_OBU_TILE_LIST, AV1RawOBU, + obu.tile_list.tile_data.data), + CBS_UNIT_TYPE_INTERNAL_REF(AV1_OBU_PADDING, AV1RawOBU, + obu.padding.payload), + + CBS_UNIT_TYPE_COMPLEX(AV1_OBU_METADATA, AV1RawOBU, + &cbs_av1_free_metadata), + + CBS_UNIT_TYPE_END_OF_LIST +}; + +#define OFFSET(x) offsetof(CodedBitstreamAV1Context, x) +static const AVOption cbs_av1_options[] = { + { "operating_point", "Set operating point to select layers to parse from a scalable bitstream", + OFFSET(operating_point), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, AV1_MAX_OPERATING_POINTS - 1, 0 }, + { NULL } +}; + +static const AVClass cbs_av1_class = { + .class_name = "cbs_av1", + .item_name = av_default_item_name, + .option = cbs_av1_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const CodedBitstreamType ff_cbs_type_av1 = { + .codec_id = AV_CODEC_ID_AV1, + + .priv_class = &cbs_av1_class, + .priv_data_size = sizeof(CodedBitstreamAV1Context), + + .unit_types = cbs_av1_unit_types, + + .split_fragment = &cbs_av1_split_fragment, + .read_unit = &cbs_av1_read_unit, + .write_unit = &cbs_av1_write_obu, + .assemble_fragment = &cbs_av1_assemble_fragment, + + .flush = &cbs_av1_flush, + .close = &cbs_av1_close, +}; diff --git a/third-party/cbs/cbs_av1_syntax_template.c b/third-party/cbs/cbs_av1_syntax_template.c new file mode 100644 index 00000000..1768a6d2 --- /dev/null +++ b/third-party/cbs/cbs_av1_syntax_template.c @@ -0,0 +1,2063 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +static int FUNC(obu_header)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawOBUHeader *current) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + int err; + + HEADER("OBU header"); + + fc(1, obu_forbidden_bit, 0, 0); + + fc(4, obu_type, 0, AV1_OBU_PADDING); + flag(obu_extension_flag); + flag(obu_has_size_field); + + fc(1, obu_reserved_1bit, 0, 0); + + if(current->obu_extension_flag) { + fb(3, temporal_id); + fb(2, spatial_id); + fc(3, extension_header_reserved_3bits, 0, 0); + } + else { + infer(temporal_id, 0); + infer(spatial_id, 0); + } + + priv->temporal_id = current->temporal_id; + priv->spatial_id = current->spatial_id; + + return 0; +} + +static int FUNC(trailing_bits)(CodedBitstreamContext *ctx, RWContext *rw, int nb_bits) { + int err; + + av_assert0(nb_bits > 0); + + fixed(1, trailing_one_bit, 1); + --nb_bits; + + while(nb_bits > 0) { + fixed(1, trailing_zero_bit, 0); + --nb_bits; + } + + return 0; +} + +static int FUNC(byte_alignment)(CodedBitstreamContext *ctx, RWContext *rw) { + int err; + + while(byte_alignment(rw) != 0) + fixed(1, zero_bit, 0); + + return 0; +} + +static int FUNC(color_config)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawColorConfig *current, int seq_profile) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + int err; + + flag(high_bitdepth); + + if(seq_profile == FF_PROFILE_AV1_PROFESSIONAL && + current->high_bitdepth) { + flag(twelve_bit); + priv->bit_depth = current->twelve_bit ? 12 : 10; + } + else { + priv->bit_depth = current->high_bitdepth ? 10 : 8; + } + + if(seq_profile == FF_PROFILE_AV1_HIGH) + infer(mono_chrome, 0); + else + flag(mono_chrome); + priv->num_planes = current->mono_chrome ? 1 : 3; + + flag(color_description_present_flag); + if(current->color_description_present_flag) { + fb(8, color_primaries); + fb(8, transfer_characteristics); + fb(8, matrix_coefficients); + } + else { + infer(color_primaries, AVCOL_PRI_UNSPECIFIED); + infer(transfer_characteristics, AVCOL_TRC_UNSPECIFIED); + infer(matrix_coefficients, AVCOL_SPC_UNSPECIFIED); + } + + if(current->mono_chrome) { + flag(color_range); + + infer(subsampling_x, 1); + infer(subsampling_y, 1); + infer(chroma_sample_position, AV1_CSP_UNKNOWN); + infer(separate_uv_delta_q, 0); + } + else if(current->color_primaries == AVCOL_PRI_BT709 && + current->transfer_characteristics == AVCOL_TRC_IEC61966_2_1 && + current->matrix_coefficients == AVCOL_SPC_RGB) { + infer(color_range, 1); + infer(subsampling_x, 0); + infer(subsampling_y, 0); + flag(separate_uv_delta_q); + } + else { + flag(color_range); + + if(seq_profile == FF_PROFILE_AV1_MAIN) { + infer(subsampling_x, 1); + infer(subsampling_y, 1); + } + else if(seq_profile == FF_PROFILE_AV1_HIGH) { + infer(subsampling_x, 0); + infer(subsampling_y, 0); + } + else { + if(priv->bit_depth == 12) { + fb(1, subsampling_x); + if(current->subsampling_x) + fb(1, subsampling_y); + else + infer(subsampling_y, 0); + } + else { + infer(subsampling_x, 1); + infer(subsampling_y, 0); + } + } + if(current->subsampling_x && current->subsampling_y) { + fc(2, chroma_sample_position, AV1_CSP_UNKNOWN, + AV1_CSP_COLOCATED); + } + + flag(separate_uv_delta_q); + } + + return 0; +} + +static int FUNC(timing_info)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawTimingInfo *current) { + int err; + + fc(32, num_units_in_display_tick, 1, MAX_UINT_BITS(32)); + fc(32, time_scale, 1, MAX_UINT_BITS(32)); + + flag(equal_picture_interval); + if(current->equal_picture_interval) + uvlc(num_ticks_per_picture_minus_1, 0, MAX_UINT_BITS(32) - 1); + + return 0; +} + +static int FUNC(decoder_model_info)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawDecoderModelInfo *current) { + int err; + + fb(5, buffer_delay_length_minus_1); + fb(32, num_units_in_decoding_tick); + fb(5, buffer_removal_time_length_minus_1); + fb(5, frame_presentation_time_length_minus_1); + + return 0; +} + +static int FUNC(sequence_header_obu)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawSequenceHeader *current) { + int i, err; + + HEADER("Sequence Header"); + + fc(3, seq_profile, FF_PROFILE_AV1_MAIN, + FF_PROFILE_AV1_PROFESSIONAL); + flag(still_picture); + flag(reduced_still_picture_header); + + if(current->reduced_still_picture_header) { + infer(timing_info_present_flag, 0); + infer(decoder_model_info_present_flag, 0); + infer(initial_display_delay_present_flag, 0); + infer(operating_points_cnt_minus_1, 0); + infer(operating_point_idc[0], 0); + + fb(5, seq_level_idx[0]); + + infer(seq_tier[0], 0); + infer(decoder_model_present_for_this_op[0], 0); + infer(initial_display_delay_present_for_this_op[0], 0); + } + else { + flag(timing_info_present_flag); + if(current->timing_info_present_flag) { + CHECK(FUNC(timing_info)(ctx, rw, ¤t->timing_info)); + + flag(decoder_model_info_present_flag); + if(current->decoder_model_info_present_flag) { + CHECK(FUNC(decoder_model_info)(ctx, rw, ¤t->decoder_model_info)); + } + } + else { + infer(decoder_model_info_present_flag, 0); + } + + flag(initial_display_delay_present_flag); + + fb(5, operating_points_cnt_minus_1); + for(i = 0; i <= current->operating_points_cnt_minus_1; i++) { + fbs(12, operating_point_idc[i], 1, i); + fbs(5, seq_level_idx[i], 1, i); + + if(current->seq_level_idx[i] > 7) + flags(seq_tier[i], 1, i); + else + infer(seq_tier[i], 0); + + if(current->decoder_model_info_present_flag) { + flags(decoder_model_present_for_this_op[i], 1, i); + if(current->decoder_model_present_for_this_op[i]) { + int n = current->decoder_model_info.buffer_delay_length_minus_1 + 1; + fbs(n, decoder_buffer_delay[i], 1, i); + fbs(n, encoder_buffer_delay[i], 1, i); + flags(low_delay_mode_flag[i], 1, i); + } + } + else { + infer(decoder_model_present_for_this_op[i], 0); + } + + if(current->initial_display_delay_present_flag) { + flags(initial_display_delay_present_for_this_op[i], 1, i); + if(current->initial_display_delay_present_for_this_op[i]) + fbs(4, initial_display_delay_minus_1[i], 1, i); + } + } + } + + fb(4, frame_width_bits_minus_1); + fb(4, frame_height_bits_minus_1); + + fb(current->frame_width_bits_minus_1 + 1, max_frame_width_minus_1); + fb(current->frame_height_bits_minus_1 + 1, max_frame_height_minus_1); + + if(current->reduced_still_picture_header) + infer(frame_id_numbers_present_flag, 0); + else + flag(frame_id_numbers_present_flag); + if(current->frame_id_numbers_present_flag) { + fb(4, delta_frame_id_length_minus_2); + fb(3, additional_frame_id_length_minus_1); + } + + flag(use_128x128_superblock); + flag(enable_filter_intra); + flag(enable_intra_edge_filter); + + if(current->reduced_still_picture_header) { + infer(enable_interintra_compound, 0); + infer(enable_masked_compound, 0); + infer(enable_warped_motion, 0); + infer(enable_dual_filter, 0); + infer(enable_order_hint, 0); + infer(enable_jnt_comp, 0); + infer(enable_ref_frame_mvs, 0); + + infer(seq_force_screen_content_tools, + AV1_SELECT_SCREEN_CONTENT_TOOLS); + infer(seq_force_integer_mv, + AV1_SELECT_INTEGER_MV); + } + else { + flag(enable_interintra_compound); + flag(enable_masked_compound); + flag(enable_warped_motion); + flag(enable_dual_filter); + + flag(enable_order_hint); + if(current->enable_order_hint) { + flag(enable_jnt_comp); + flag(enable_ref_frame_mvs); + } + else { + infer(enable_jnt_comp, 0); + infer(enable_ref_frame_mvs, 0); + } + + flag(seq_choose_screen_content_tools); + if(current->seq_choose_screen_content_tools) + infer(seq_force_screen_content_tools, + AV1_SELECT_SCREEN_CONTENT_TOOLS); + else + fb(1, seq_force_screen_content_tools); + if(current->seq_force_screen_content_tools > 0) { + flag(seq_choose_integer_mv); + if(current->seq_choose_integer_mv) + infer(seq_force_integer_mv, + AV1_SELECT_INTEGER_MV); + else + fb(1, seq_force_integer_mv); + } + else { + infer(seq_force_integer_mv, AV1_SELECT_INTEGER_MV); + } + + if(current->enable_order_hint) + fb(3, order_hint_bits_minus_1); + } + + flag(enable_superres); + flag(enable_cdef); + flag(enable_restoration); + + CHECK(FUNC(color_config)(ctx, rw, ¤t->color_config, + current->seq_profile)); + + flag(film_grain_params_present); + + return 0; +} + +static int FUNC(temporal_delimiter_obu)(CodedBitstreamContext *ctx, RWContext *rw) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + + HEADER("Temporal Delimiter"); + + priv->seen_frame_header = 0; + + return 0; +} + +static int FUNC(set_frame_refs)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq = priv->sequence_header; + static const uint8_t ref_frame_list[AV1_NUM_REF_FRAMES - 2] = { + AV1_REF_FRAME_LAST2, AV1_REF_FRAME_LAST3, AV1_REF_FRAME_BWDREF, + AV1_REF_FRAME_ALTREF2, AV1_REF_FRAME_ALTREF + }; + int8_t ref_frame_idx[AV1_REFS_PER_FRAME], used_frame[AV1_NUM_REF_FRAMES]; + int8_t shifted_order_hints[AV1_NUM_REF_FRAMES]; + int cur_frame_hint, latest_order_hint, earliest_order_hint, ref; + int i, j; + + for(i = 0; i < AV1_REFS_PER_FRAME; i++) + ref_frame_idx[i] = -1; + ref_frame_idx[AV1_REF_FRAME_LAST - AV1_REF_FRAME_LAST] = current->last_frame_idx; + ref_frame_idx[AV1_REF_FRAME_GOLDEN - AV1_REF_FRAME_LAST] = current->golden_frame_idx; + + for(i = 0; i < AV1_NUM_REF_FRAMES; i++) + used_frame[i] = 0; + used_frame[current->last_frame_idx] = 1; + used_frame[current->golden_frame_idx] = 1; + + cur_frame_hint = 1 << (seq->order_hint_bits_minus_1); + for(i = 0; i < AV1_NUM_REF_FRAMES; i++) + shifted_order_hints[i] = cur_frame_hint + + cbs_av1_get_relative_dist(seq, priv->ref[i].order_hint, + priv->order_hint); + + latest_order_hint = shifted_order_hints[current->last_frame_idx]; + earliest_order_hint = shifted_order_hints[current->golden_frame_idx]; + + ref = -1; + for(i = 0; i < AV1_NUM_REF_FRAMES; i++) { + int hint = shifted_order_hints[i]; + if(!used_frame[i] && hint >= cur_frame_hint && + (ref < 0 || hint >= latest_order_hint)) { + ref = i; + latest_order_hint = hint; + } + } + if(ref >= 0) { + ref_frame_idx[AV1_REF_FRAME_ALTREF - AV1_REF_FRAME_LAST] = ref; + used_frame[ref] = 1; + } + + ref = -1; + for(i = 0; i < AV1_NUM_REF_FRAMES; i++) { + int hint = shifted_order_hints[i]; + if(!used_frame[i] && hint >= cur_frame_hint && + (ref < 0 || hint < earliest_order_hint)) { + ref = i; + earliest_order_hint = hint; + } + } + if(ref >= 0) { + ref_frame_idx[AV1_REF_FRAME_BWDREF - AV1_REF_FRAME_LAST] = ref; + used_frame[ref] = 1; + } + + ref = -1; + for(i = 0; i < AV1_NUM_REF_FRAMES; i++) { + int hint = shifted_order_hints[i]; + if(!used_frame[i] && hint >= cur_frame_hint && + (ref < 0 || hint < earliest_order_hint)) { + ref = i; + earliest_order_hint = hint; + } + } + if(ref >= 0) { + ref_frame_idx[AV1_REF_FRAME_ALTREF2 - AV1_REF_FRAME_LAST] = ref; + used_frame[ref] = 1; + } + + for(i = 0; i < AV1_REFS_PER_FRAME - 2; i++) { + int ref_frame = ref_frame_list[i]; + if(ref_frame_idx[ref_frame - AV1_REF_FRAME_LAST] < 0) { + ref = -1; + for(j = 0; j < AV1_NUM_REF_FRAMES; j++) { + int hint = shifted_order_hints[j]; + if(!used_frame[j] && hint < cur_frame_hint && + (ref < 0 || hint >= latest_order_hint)) { + ref = j; + latest_order_hint = hint; + } + } + if(ref >= 0) { + ref_frame_idx[ref_frame - AV1_REF_FRAME_LAST] = ref; + used_frame[ref] = 1; + } + } + } + + ref = -1; + for(i = 0; i < AV1_NUM_REF_FRAMES; i++) { + int hint = shifted_order_hints[i]; + if(ref < 0 || hint < earliest_order_hint) { + ref = i; + earliest_order_hint = hint; + } + } + for(i = 0; i < AV1_REFS_PER_FRAME; i++) { + if(ref_frame_idx[i] < 0) + ref_frame_idx[i] = ref; + infer(ref_frame_idx[i], ref_frame_idx[i]); + } + + return 0; +} + +static int FUNC(superres_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq = priv->sequence_header; + int denom, err; + + if(seq->enable_superres) + flag(use_superres); + else + infer(use_superres, 0); + + if(current->use_superres) { + fb(3, coded_denom); + denom = current->coded_denom + AV1_SUPERRES_DENOM_MIN; + } + else { + denom = AV1_SUPERRES_NUM; + } + + priv->upscaled_width = priv->frame_width; + priv->frame_width = (priv->upscaled_width * AV1_SUPERRES_NUM + + denom / 2) / + denom; + + return 0; +} + +static int FUNC(frame_size)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq = priv->sequence_header; + int err; + + if(current->frame_size_override_flag) { + fb(seq->frame_width_bits_minus_1 + 1, frame_width_minus_1); + fb(seq->frame_height_bits_minus_1 + 1, frame_height_minus_1); + } + else { + infer(frame_width_minus_1, seq->max_frame_width_minus_1); + infer(frame_height_minus_1, seq->max_frame_height_minus_1); + } + + priv->frame_width = current->frame_width_minus_1 + 1; + priv->frame_height = current->frame_height_minus_1 + 1; + + CHECK(FUNC(superres_params)(ctx, rw, current)); + + return 0; +} + +static int FUNC(render_size)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + int err; + + flag(render_and_frame_size_different); + + if(current->render_and_frame_size_different) { + fb(16, render_width_minus_1); + fb(16, render_height_minus_1); + } + else { + infer(render_width_minus_1, current->frame_width_minus_1); + infer(render_height_minus_1, current->frame_height_minus_1); + } + + priv->render_width = current->render_width_minus_1 + 1; + priv->render_height = current->render_height_minus_1 + 1; + + return 0; +} + +static int FUNC(frame_size_with_refs)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + int i, err; + + for(i = 0; i < AV1_REFS_PER_FRAME; i++) { + flags(found_ref[i], 1, i); + if(current->found_ref[i]) { + AV1ReferenceFrameState *ref = + &priv->ref[current->ref_frame_idx[i]]; + + if(!ref->valid) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Missing reference frame needed for frame size " + "(ref = %d, ref_frame_idx = %d).\n", + i, current->ref_frame_idx[i]); + return AVERROR_INVALIDDATA; + } + + infer(frame_width_minus_1, ref->upscaled_width - 1); + infer(frame_height_minus_1, ref->frame_height - 1); + infer(render_width_minus_1, ref->render_width - 1); + infer(render_height_minus_1, ref->render_height - 1); + + priv->upscaled_width = ref->upscaled_width; + priv->frame_width = priv->upscaled_width; + priv->frame_height = ref->frame_height; + priv->render_width = ref->render_width; + priv->render_height = ref->render_height; + break; + } + } + + if(i >= AV1_REFS_PER_FRAME) { + CHECK(FUNC(frame_size)(ctx, rw, current)); + CHECK(FUNC(render_size)(ctx, rw, current)); + } + else { + CHECK(FUNC(superres_params)(ctx, rw, current)); + } + + return 0; +} + +static int FUNC(interpolation_filter)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) { + int err; + + flag(is_filter_switchable); + if(current->is_filter_switchable) + infer(interpolation_filter, + AV1_INTERPOLATION_FILTER_SWITCHABLE); + else + fb(2, interpolation_filter); + + return 0; +} + +static int FUNC(tile_info)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq = priv->sequence_header; + int mi_cols, mi_rows, sb_cols, sb_rows, sb_shift, sb_size; + int max_tile_width_sb, max_tile_height_sb, max_tile_area_sb; + int min_log2_tile_cols, max_log2_tile_cols, max_log2_tile_rows; + int min_log2_tiles, min_log2_tile_rows; + int i, err; + + mi_cols = 2 * ((priv->frame_width + 7) >> 3); + mi_rows = 2 * ((priv->frame_height + 7) >> 3); + + sb_cols = seq->use_128x128_superblock ? ((mi_cols + 31) >> 5) : ((mi_cols + 15) >> 4); + sb_rows = seq->use_128x128_superblock ? ((mi_rows + 31) >> 5) : ((mi_rows + 15) >> 4); + + sb_shift = seq->use_128x128_superblock ? 5 : 4; + sb_size = sb_shift + 2; + + max_tile_width_sb = AV1_MAX_TILE_WIDTH >> sb_size; + max_tile_area_sb = AV1_MAX_TILE_AREA >> (2 * sb_size); + + min_log2_tile_cols = cbs_av1_tile_log2(max_tile_width_sb, sb_cols); + max_log2_tile_cols = cbs_av1_tile_log2(1, FFMIN(sb_cols, AV1_MAX_TILE_COLS)); + max_log2_tile_rows = cbs_av1_tile_log2(1, FFMIN(sb_rows, AV1_MAX_TILE_ROWS)); + min_log2_tiles = FFMAX(min_log2_tile_cols, + cbs_av1_tile_log2(max_tile_area_sb, sb_rows * sb_cols)); + + flag(uniform_tile_spacing_flag); + + if(current->uniform_tile_spacing_flag) { + int tile_width_sb, tile_height_sb; + + increment(tile_cols_log2, min_log2_tile_cols, max_log2_tile_cols); + + tile_width_sb = (sb_cols + (1 << current->tile_cols_log2) - 1) >> + current->tile_cols_log2; + current->tile_cols = (sb_cols + tile_width_sb - 1) / tile_width_sb; + + min_log2_tile_rows = FFMAX(min_log2_tiles - current->tile_cols_log2, 0); + + increment(tile_rows_log2, min_log2_tile_rows, max_log2_tile_rows); + + tile_height_sb = (sb_rows + (1 << current->tile_rows_log2) - 1) >> + current->tile_rows_log2; + current->tile_rows = (sb_rows + tile_height_sb - 1) / tile_height_sb; + + for(i = 0; i < current->tile_cols - 1; i++) + infer(width_in_sbs_minus_1[i], tile_width_sb - 1); + infer(width_in_sbs_minus_1[i], + sb_cols - (current->tile_cols - 1) * tile_width_sb - 1); + for(i = 0; i < current->tile_rows - 1; i++) + infer(height_in_sbs_minus_1[i], tile_height_sb - 1); + infer(height_in_sbs_minus_1[i], + sb_rows - (current->tile_rows - 1) * tile_height_sb - 1); + } + else { + int widest_tile_sb, start_sb, size_sb, max_width, max_height; + + widest_tile_sb = 0; + + start_sb = 0; + for(i = 0; start_sb < sb_cols && i < AV1_MAX_TILE_COLS; i++) { + max_width = FFMIN(sb_cols - start_sb, max_tile_width_sb); + ns(max_width, width_in_sbs_minus_1[i], 1, i); + size_sb = current->width_in_sbs_minus_1[i] + 1; + widest_tile_sb = FFMAX(size_sb, widest_tile_sb); + start_sb += size_sb; + } + current->tile_cols_log2 = cbs_av1_tile_log2(1, i); + current->tile_cols = i; + + if(min_log2_tiles > 0) + max_tile_area_sb = (sb_rows * sb_cols) >> (min_log2_tiles + 1); + else + max_tile_area_sb = sb_rows * sb_cols; + max_tile_height_sb = FFMAX(max_tile_area_sb / widest_tile_sb, 1); + + start_sb = 0; + for(i = 0; start_sb < sb_rows && i < AV1_MAX_TILE_ROWS; i++) { + max_height = FFMIN(sb_rows - start_sb, max_tile_height_sb); + ns(max_height, height_in_sbs_minus_1[i], 1, i); + size_sb = current->height_in_sbs_minus_1[i] + 1; + start_sb += size_sb; + } + current->tile_rows_log2 = cbs_av1_tile_log2(1, i); + current->tile_rows = i; + } + + if(current->tile_cols_log2 > 0 || + current->tile_rows_log2 > 0) { + fb(current->tile_cols_log2 + current->tile_rows_log2, + context_update_tile_id); + fb(2, tile_size_bytes_minus1); + } + else { + infer(context_update_tile_id, 0); + } + + priv->tile_cols = current->tile_cols; + priv->tile_rows = current->tile_rows; + + return 0; +} + +static int FUNC(quantization_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq = priv->sequence_header; + int err; + + fb(8, base_q_idx); + + delta_q(delta_q_y_dc); + + if(priv->num_planes > 1) { + if(seq->color_config.separate_uv_delta_q) + flag(diff_uv_delta); + else + infer(diff_uv_delta, 0); + + delta_q(delta_q_u_dc); + delta_q(delta_q_u_ac); + + if(current->diff_uv_delta) { + delta_q(delta_q_v_dc); + delta_q(delta_q_v_ac); + } + else { + infer(delta_q_v_dc, current->delta_q_u_dc); + infer(delta_q_v_ac, current->delta_q_u_ac); + } + } + else { + infer(delta_q_u_dc, 0); + infer(delta_q_u_ac, 0); + infer(delta_q_v_dc, 0); + infer(delta_q_v_ac, 0); + } + + flag(using_qmatrix); + if(current->using_qmatrix) { + fb(4, qm_y); + fb(4, qm_u); + if(seq->color_config.separate_uv_delta_q) + fb(4, qm_v); + else + infer(qm_v, current->qm_u); + } + + return 0; +} + +static int FUNC(segmentation_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + static const uint8_t bits[AV1_SEG_LVL_MAX] = { 8, 6, 6, 6, 6, 3, 0, 0 }; + static const uint8_t sign[AV1_SEG_LVL_MAX] = { 1, 1, 1, 1, 1, 0, 0, 0 }; + static const uint8_t default_feature_enabled[AV1_SEG_LVL_MAX] = { 0 }; + static const int16_t default_feature_value[AV1_SEG_LVL_MAX] = { 0 }; + int i, j, err; + + flag(segmentation_enabled); + + if(current->segmentation_enabled) { + if(current->primary_ref_frame == AV1_PRIMARY_REF_NONE) { + infer(segmentation_update_map, 1); + infer(segmentation_temporal_update, 0); + infer(segmentation_update_data, 1); + } + else { + flag(segmentation_update_map); + if(current->segmentation_update_map) + flag(segmentation_temporal_update); + else + infer(segmentation_temporal_update, 0); + flag(segmentation_update_data); + } + + for(i = 0; i < AV1_MAX_SEGMENTS; i++) { + const uint8_t *ref_feature_enabled; + const int16_t *ref_feature_value; + + if(current->primary_ref_frame == AV1_PRIMARY_REF_NONE) { + ref_feature_enabled = default_feature_enabled; + ref_feature_value = default_feature_value; + } + else { + ref_feature_enabled = + priv->ref[current->ref_frame_idx[current->primary_ref_frame]].feature_enabled[i]; + ref_feature_value = + priv->ref[current->ref_frame_idx[current->primary_ref_frame]].feature_value[i]; + } + + for(j = 0; j < AV1_SEG_LVL_MAX; j++) { + if(current->segmentation_update_data) { + flags(feature_enabled[i][j], 2, i, j); + + if(current->feature_enabled[i][j] && bits[j] > 0) { + if(sign[j]) + sus(1 + bits[j], feature_value[i][j], 2, i, j); + else + fbs(bits[j], feature_value[i][j], 2, i, j); + } + else { + infer(feature_value[i][j], 0); + } + } + else { + infer(feature_enabled[i][j], ref_feature_enabled[j]); + infer(feature_value[i][j], ref_feature_value[j]); + } + } + } + } + else { + for(i = 0; i < AV1_MAX_SEGMENTS; i++) { + for(j = 0; j < AV1_SEG_LVL_MAX; j++) { + infer(feature_enabled[i][j], 0); + infer(feature_value[i][j], 0); + } + } + } + + return 0; +} + +static int FUNC(delta_q_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) { + int err; + + if(current->base_q_idx > 0) + flag(delta_q_present); + else + infer(delta_q_present, 0); + + if(current->delta_q_present) + fb(2, delta_q_res); + + return 0; +} + +static int FUNC(delta_lf_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) { + int err; + + if(current->delta_q_present) { + if(!current->allow_intrabc) + flag(delta_lf_present); + else + infer(delta_lf_present, 0); + if(current->delta_lf_present) { + fb(2, delta_lf_res); + flag(delta_lf_multi); + } + else { + infer(delta_lf_res, 0); + infer(delta_lf_multi, 0); + } + } + else { + infer(delta_lf_present, 0); + infer(delta_lf_res, 0); + infer(delta_lf_multi, 0); + } + + return 0; +} + +static int FUNC(loop_filter_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + static const int8_t default_loop_filter_ref_deltas[AV1_TOTAL_REFS_PER_FRAME] = { 1, 0, 0, 0, -1, 0, -1, -1 }; + static const int8_t default_loop_filter_mode_deltas[2] = { 0, 0 }; + int i, err; + + if(priv->coded_lossless || current->allow_intrabc) { + infer(loop_filter_level[0], 0); + infer(loop_filter_level[1], 0); + infer(loop_filter_ref_deltas[AV1_REF_FRAME_INTRA], 1); + infer(loop_filter_ref_deltas[AV1_REF_FRAME_LAST], 0); + infer(loop_filter_ref_deltas[AV1_REF_FRAME_LAST2], 0); + infer(loop_filter_ref_deltas[AV1_REF_FRAME_LAST3], 0); + infer(loop_filter_ref_deltas[AV1_REF_FRAME_BWDREF], 0); + infer(loop_filter_ref_deltas[AV1_REF_FRAME_GOLDEN], -1); + infer(loop_filter_ref_deltas[AV1_REF_FRAME_ALTREF], -1); + infer(loop_filter_ref_deltas[AV1_REF_FRAME_ALTREF2], -1); + for(i = 0; i < 2; i++) + infer(loop_filter_mode_deltas[i], 0); + return 0; + } + + fb(6, loop_filter_level[0]); + fb(6, loop_filter_level[1]); + + if(priv->num_planes > 1) { + if(current->loop_filter_level[0] || + current->loop_filter_level[1]) { + fb(6, loop_filter_level[2]); + fb(6, loop_filter_level[3]); + } + } + + fb(3, loop_filter_sharpness); + + flag(loop_filter_delta_enabled); + if(current->loop_filter_delta_enabled) { + const int8_t *ref_loop_filter_ref_deltas, *ref_loop_filter_mode_deltas; + + if(current->primary_ref_frame == AV1_PRIMARY_REF_NONE) { + ref_loop_filter_ref_deltas = default_loop_filter_ref_deltas; + ref_loop_filter_mode_deltas = default_loop_filter_mode_deltas; + } + else { + ref_loop_filter_ref_deltas = + priv->ref[current->ref_frame_idx[current->primary_ref_frame]].loop_filter_ref_deltas; + ref_loop_filter_mode_deltas = + priv->ref[current->ref_frame_idx[current->primary_ref_frame]].loop_filter_mode_deltas; + } + + flag(loop_filter_delta_update); + for(i = 0; i < AV1_TOTAL_REFS_PER_FRAME; i++) { + if(current->loop_filter_delta_update) + flags(update_ref_delta[i], 1, i); + else + infer(update_ref_delta[i], 0); + if(current->update_ref_delta[i]) + sus(1 + 6, loop_filter_ref_deltas[i], 1, i); + else + infer(loop_filter_ref_deltas[i], ref_loop_filter_ref_deltas[i]); + } + for(i = 0; i < 2; i++) { + if(current->loop_filter_delta_update) + flags(update_mode_delta[i], 1, i); + else + infer(update_mode_delta[i], 0); + if(current->update_mode_delta[i]) + sus(1 + 6, loop_filter_mode_deltas[i], 1, i); + else + infer(loop_filter_mode_deltas[i], ref_loop_filter_mode_deltas[i]); + } + } + else { + for(i = 0; i < AV1_TOTAL_REFS_PER_FRAME; i++) + infer(loop_filter_ref_deltas[i], default_loop_filter_ref_deltas[i]); + for(i = 0; i < 2; i++) + infer(loop_filter_mode_deltas[i], default_loop_filter_mode_deltas[i]); + } + + return 0; +} + +static int FUNC(cdef_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq = priv->sequence_header; + int i, err; + + if(priv->coded_lossless || current->allow_intrabc || + !seq->enable_cdef) { + infer(cdef_damping_minus_3, 0); + infer(cdef_bits, 0); + infer(cdef_y_pri_strength[0], 0); + infer(cdef_y_sec_strength[0], 0); + infer(cdef_uv_pri_strength[0], 0); + infer(cdef_uv_sec_strength[0], 0); + + return 0; + } + + fb(2, cdef_damping_minus_3); + fb(2, cdef_bits); + + for(i = 0; i < (1 << current->cdef_bits); i++) { + fbs(4, cdef_y_pri_strength[i], 1, i); + fbs(2, cdef_y_sec_strength[i], 1, i); + + if(priv->num_planes > 1) { + fbs(4, cdef_uv_pri_strength[i], 1, i); + fbs(2, cdef_uv_sec_strength[i], 1, i); + } + } + + return 0; +} + +static int FUNC(lr_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq = priv->sequence_header; + int uses_lr, uses_chroma_lr; + int i, err; + + if(priv->all_lossless || current->allow_intrabc || + !seq->enable_restoration) { + return 0; + } + + uses_lr = uses_chroma_lr = 0; + for(i = 0; i < priv->num_planes; i++) { + fbs(2, lr_type[i], 1, i); + + if(current->lr_type[i] != AV1_RESTORE_NONE) { + uses_lr = 1; + if(i > 0) + uses_chroma_lr = 1; + } + } + + if(uses_lr) { + if(seq->use_128x128_superblock) + increment(lr_unit_shift, 1, 2); + else + increment(lr_unit_shift, 0, 2); + + if(seq->color_config.subsampling_x && + seq->color_config.subsampling_y && uses_chroma_lr) { + fb(1, lr_uv_shift); + } + else { + infer(lr_uv_shift, 0); + } + } + + return 0; +} + +static int FUNC(read_tx_mode)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + int err; + + if(priv->coded_lossless) + infer(tx_mode, 0); + else + increment(tx_mode, 1, 2); + + return 0; +} + +static int FUNC(frame_reference_mode)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) { + int err; + + if(current->frame_type == AV1_FRAME_INTRA_ONLY || + current->frame_type == AV1_FRAME_KEY) + infer(reference_select, 0); + else + flag(reference_select); + + return 0; +} + +static int FUNC(skip_mode_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq = priv->sequence_header; + int skip_mode_allowed; + int err; + + if(current->frame_type == AV1_FRAME_KEY || + current->frame_type == AV1_FRAME_INTRA_ONLY || + !current->reference_select || !seq->enable_order_hint) { + skip_mode_allowed = 0; + } + else { + int forward_idx, backward_idx; + int forward_hint, backward_hint; + int ref_hint, dist, i; + + forward_idx = -1; + backward_idx = -1; + for(i = 0; i < AV1_REFS_PER_FRAME; i++) { + ref_hint = priv->ref[current->ref_frame_idx[i]].order_hint; + dist = cbs_av1_get_relative_dist(seq, ref_hint, + priv->order_hint); + if(dist < 0) { + if(forward_idx < 0 || + cbs_av1_get_relative_dist(seq, ref_hint, + forward_hint) > 0) { + forward_idx = i; + forward_hint = ref_hint; + } + } + else if(dist > 0) { + if(backward_idx < 0 || + cbs_av1_get_relative_dist(seq, ref_hint, + backward_hint) < 0) { + backward_idx = i; + backward_hint = ref_hint; + } + } + } + + if(forward_idx < 0) { + skip_mode_allowed = 0; + } + else if(backward_idx >= 0) { + skip_mode_allowed = 1; + // Frames for skip mode are forward_idx and backward_idx. + } + else { + int second_forward_idx; + int second_forward_hint; + + second_forward_idx = -1; + for(i = 0; i < AV1_REFS_PER_FRAME; i++) { + ref_hint = priv->ref[current->ref_frame_idx[i]].order_hint; + if(cbs_av1_get_relative_dist(seq, ref_hint, + forward_hint) < 0) { + if(second_forward_idx < 0 || + cbs_av1_get_relative_dist(seq, ref_hint, + second_forward_hint) > 0) { + second_forward_idx = i; + second_forward_hint = ref_hint; + } + } + } + + if(second_forward_idx < 0) { + skip_mode_allowed = 0; + } + else { + skip_mode_allowed = 1; + // Frames for skip mode are forward_idx and second_forward_idx. + } + } + } + + if(skip_mode_allowed) + flag(skip_mode_present); + else + infer(skip_mode_present, 0); + + return 0; +} + +static int FUNC(global_motion_param)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current, + int type, int ref, int idx) { + uint32_t abs_bits, prec_bits, num_syms; + int err; + + if(idx < 2) { + if(type == AV1_WARP_MODEL_TRANSLATION) { + abs_bits = AV1_GM_ABS_TRANS_ONLY_BITS - !current->allow_high_precision_mv; + prec_bits = AV1_GM_TRANS_ONLY_PREC_BITS - !current->allow_high_precision_mv; + } + else { + abs_bits = AV1_GM_ABS_TRANS_BITS; + prec_bits = AV1_GM_TRANS_PREC_BITS; + } + } + else { + abs_bits = AV1_GM_ABS_ALPHA_BITS; + prec_bits = AV1_GM_ALPHA_PREC_BITS; + } + + num_syms = 2 * (1 << abs_bits) + 1; + subexp(gm_params[ref][idx], num_syms, 2, ref, idx); + + // Actual gm_params value is not reconstructed here. + (void)prec_bits; + + return 0; +} + +static int FUNC(global_motion_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) { + int ref, type; + int err; + + if(current->frame_type == AV1_FRAME_KEY || + current->frame_type == AV1_FRAME_INTRA_ONLY) + return 0; + + for(ref = AV1_REF_FRAME_LAST; ref <= AV1_REF_FRAME_ALTREF; ref++) { + flags(is_global[ref], 1, ref); + if(current->is_global[ref]) { + flags(is_rot_zoom[ref], 1, ref); + if(current->is_rot_zoom[ref]) { + type = AV1_WARP_MODEL_ROTZOOM; + } + else { + flags(is_translation[ref], 1, ref); + type = current->is_translation[ref] ? AV1_WARP_MODEL_TRANSLATION : AV1_WARP_MODEL_AFFINE; + } + } + else { + type = AV1_WARP_MODEL_IDENTITY; + } + + if(type >= AV1_WARP_MODEL_ROTZOOM) { + CHECK(FUNC(global_motion_param)(ctx, rw, current, type, ref, 2)); + CHECK(FUNC(global_motion_param)(ctx, rw, current, type, ref, 3)); + if(type == AV1_WARP_MODEL_AFFINE) { + CHECK(FUNC(global_motion_param)(ctx, rw, current, type, ref, 4)); + CHECK(FUNC(global_motion_param)(ctx, rw, current, type, ref, 5)); + } + else { + // gm_params[ref][4] = -gm_params[ref][3] + // gm_params[ref][5] = gm_params[ref][2] + } + } + if(type >= AV1_WARP_MODEL_TRANSLATION) { + CHECK(FUNC(global_motion_param)(ctx, rw, current, type, ref, 0)); + CHECK(FUNC(global_motion_param)(ctx, rw, current, type, ref, 1)); + } + } + + return 0; +} + +static int FUNC(film_grain_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFilmGrainParams *current, + AV1RawFrameHeader *frame_header) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq = priv->sequence_header; + int num_pos_luma, num_pos_chroma; + int i, err; + + if(!seq->film_grain_params_present || + (!frame_header->show_frame && !frame_header->showable_frame)) + return 0; + + flag(apply_grain); + + if(!current->apply_grain) + return 0; + + fb(16, grain_seed); + + if(frame_header->frame_type == AV1_FRAME_INTER) + flag(update_grain); + else + infer(update_grain, 1); + + if(!current->update_grain) { + fb(3, film_grain_params_ref_idx); + return 0; + } + + fc(4, num_y_points, 0, 14); + for(i = 0; i < current->num_y_points; i++) { + fcs(8, point_y_value[i], + i ? current->point_y_value[i - 1] + 1 : 0, + MAX_UINT_BITS(8) - (current->num_y_points - i - 1), + 1, i); + fbs(8, point_y_scaling[i], 1, i); + } + + if(seq->color_config.mono_chrome) + infer(chroma_scaling_from_luma, 0); + else + flag(chroma_scaling_from_luma); + + if(seq->color_config.mono_chrome || + current->chroma_scaling_from_luma || + (seq->color_config.subsampling_x == 1 && + seq->color_config.subsampling_y == 1 && + current->num_y_points == 0)) { + infer(num_cb_points, 0); + infer(num_cr_points, 0); + } + else { + fc(4, num_cb_points, 0, 10); + for(i = 0; i < current->num_cb_points; i++) { + fcs(8, point_cb_value[i], + i ? current->point_cb_value[i - 1] + 1 : 0, + MAX_UINT_BITS(8) - (current->num_cb_points - i - 1), + 1, i); + fbs(8, point_cb_scaling[i], 1, i); + } + fc(4, num_cr_points, 0, 10); + for(i = 0; i < current->num_cr_points; i++) { + fcs(8, point_cr_value[i], + i ? current->point_cr_value[i - 1] + 1 : 0, + MAX_UINT_BITS(8) - (current->num_cr_points - i - 1), + 1, i); + fbs(8, point_cr_scaling[i], 1, i); + } + } + + fb(2, grain_scaling_minus_8); + fb(2, ar_coeff_lag); + num_pos_luma = 2 * current->ar_coeff_lag * (current->ar_coeff_lag + 1); + if(current->num_y_points) { + num_pos_chroma = num_pos_luma + 1; + for(i = 0; i < num_pos_luma; i++) + fbs(8, ar_coeffs_y_plus_128[i], 1, i); + } + else { + num_pos_chroma = num_pos_luma; + } + if(current->chroma_scaling_from_luma || current->num_cb_points) { + for(i = 0; i < num_pos_chroma; i++) + fbs(8, ar_coeffs_cb_plus_128[i], 1, i); + } + if(current->chroma_scaling_from_luma || current->num_cr_points) { + for(i = 0; i < num_pos_chroma; i++) + fbs(8, ar_coeffs_cr_plus_128[i], 1, i); + } + fb(2, ar_coeff_shift_minus_6); + fb(2, grain_scale_shift); + if(current->num_cb_points) { + fb(8, cb_mult); + fb(8, cb_luma_mult); + fb(9, cb_offset); + } + if(current->num_cr_points) { + fb(8, cr_mult); + fb(8, cr_luma_mult); + fb(9, cr_offset); + } + + flag(overlap_flag); + flag(clip_to_restricted_range); + + return 0; +} + +static int FUNC(uncompressed_header)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq; + int id_len, diff_len, all_frames, frame_is_intra, order_hint_bits; + int i, err; + + if(!priv->sequence_header) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "No sequence header available: " + "unable to decode frame header.\n"); + return AVERROR_INVALIDDATA; + } + seq = priv->sequence_header; + + id_len = seq->additional_frame_id_length_minus_1 + + seq->delta_frame_id_length_minus_2 + 3; + all_frames = (1 << AV1_NUM_REF_FRAMES) - 1; + + if(seq->reduced_still_picture_header) { + infer(show_existing_frame, 0); + infer(frame_type, AV1_FRAME_KEY); + infer(show_frame, 1); + infer(showable_frame, 0); + frame_is_intra = 1; + } + else { + flag(show_existing_frame); + + if(current->show_existing_frame) { + AV1ReferenceFrameState *ref; + + fb(3, frame_to_show_map_idx); + ref = &priv->ref[current->frame_to_show_map_idx]; + + if(!ref->valid) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Missing reference frame needed for " + "show_existing_frame (frame_to_show_map_idx = %d).\n", + current->frame_to_show_map_idx); + return AVERROR_INVALIDDATA; + } + + if(seq->decoder_model_info_present_flag && + !seq->timing_info.equal_picture_interval) { + fb(seq->decoder_model_info.frame_presentation_time_length_minus_1 + 1, + frame_presentation_time); + } + + if(seq->frame_id_numbers_present_flag) + fb(id_len, display_frame_id); + + infer(frame_type, ref->frame_type); + if(current->frame_type == AV1_FRAME_KEY) { + infer(refresh_frame_flags, all_frames); + + // Section 7.21 + infer(current_frame_id, ref->frame_id); + priv->upscaled_width = ref->upscaled_width; + priv->frame_width = ref->frame_width; + priv->frame_height = ref->frame_height; + priv->render_width = ref->render_width; + priv->render_height = ref->render_height; + priv->bit_depth = ref->bit_depth; + priv->order_hint = ref->order_hint; + } + else + infer(refresh_frame_flags, 0); + + infer(frame_width_minus_1, ref->upscaled_width - 1); + infer(frame_height_minus_1, ref->frame_height - 1); + infer(render_width_minus_1, ref->render_width - 1); + infer(render_height_minus_1, ref->render_height - 1); + + // Section 7.20 + goto update_refs; + } + + fb(2, frame_type); + frame_is_intra = (current->frame_type == AV1_FRAME_INTRA_ONLY || + current->frame_type == AV1_FRAME_KEY); + + flag(show_frame); + if(current->show_frame && + seq->decoder_model_info_present_flag && + !seq->timing_info.equal_picture_interval) { + fb(seq->decoder_model_info.frame_presentation_time_length_minus_1 + 1, + frame_presentation_time); + } + if(current->show_frame) + infer(showable_frame, current->frame_type != AV1_FRAME_KEY); + else + flag(showable_frame); + + if(current->frame_type == AV1_FRAME_SWITCH || + (current->frame_type == AV1_FRAME_KEY && current->show_frame)) + infer(error_resilient_mode, 1); + else + flag(error_resilient_mode); + } + + if(current->frame_type == AV1_FRAME_KEY && current->show_frame) { + for(i = 0; i < AV1_NUM_REF_FRAMES; i++) { + priv->ref[i].valid = 0; + priv->ref[i].order_hint = 0; + } + } + + flag(disable_cdf_update); + + if(seq->seq_force_screen_content_tools == + AV1_SELECT_SCREEN_CONTENT_TOOLS) { + flag(allow_screen_content_tools); + } + else { + infer(allow_screen_content_tools, + seq->seq_force_screen_content_tools); + } + if(current->allow_screen_content_tools) { + if(seq->seq_force_integer_mv == AV1_SELECT_INTEGER_MV) + flag(force_integer_mv); + else + infer(force_integer_mv, seq->seq_force_integer_mv); + } + else { + infer(force_integer_mv, 0); + } + + if(seq->frame_id_numbers_present_flag) { + fb(id_len, current_frame_id); + + diff_len = seq->delta_frame_id_length_minus_2 + 2; + for(i = 0; i < AV1_NUM_REF_FRAMES; i++) { + if(current->current_frame_id > (1 << diff_len)) { + if(priv->ref[i].frame_id > current->current_frame_id || + priv->ref[i].frame_id < (current->current_frame_id - + (1 << diff_len))) + priv->ref[i].valid = 0; + } + else { + if(priv->ref[i].frame_id > current->current_frame_id && + priv->ref[i].frame_id < ((1 << id_len) + + current->current_frame_id - + (1 << diff_len))) + priv->ref[i].valid = 0; + } + } + } + else { + infer(current_frame_id, 0); + } + + if(current->frame_type == AV1_FRAME_SWITCH) + infer(frame_size_override_flag, 1); + else if(seq->reduced_still_picture_header) + infer(frame_size_override_flag, 0); + else + flag(frame_size_override_flag); + + order_hint_bits = + seq->enable_order_hint ? seq->order_hint_bits_minus_1 + 1 : 0; + if(order_hint_bits > 0) + fb(order_hint_bits, order_hint); + else + infer(order_hint, 0); + priv->order_hint = current->order_hint; + + if(frame_is_intra || current->error_resilient_mode) + infer(primary_ref_frame, AV1_PRIMARY_REF_NONE); + else + fb(3, primary_ref_frame); + + if(seq->decoder_model_info_present_flag) { + flag(buffer_removal_time_present_flag); + if(current->buffer_removal_time_present_flag) { + for(i = 0; i <= seq->operating_points_cnt_minus_1; i++) { + if(seq->decoder_model_present_for_this_op[i]) { + int op_pt_idc = seq->operating_point_idc[i]; + int in_temporal_layer = (op_pt_idc >> priv->temporal_id) & 1; + int in_spatial_layer = (op_pt_idc >> (priv->spatial_id + 8)) & 1; + if(seq->operating_point_idc[i] == 0 || + (in_temporal_layer && in_spatial_layer)) { + fbs(seq->decoder_model_info.buffer_removal_time_length_minus_1 + 1, + buffer_removal_time[i], 1, i); + } + } + } + } + } + + if(current->frame_type == AV1_FRAME_SWITCH || + (current->frame_type == AV1_FRAME_KEY && current->show_frame)) + infer(refresh_frame_flags, all_frames); + else + fb(8, refresh_frame_flags); + + if(!frame_is_intra || current->refresh_frame_flags != all_frames) { + if(seq->enable_order_hint) { + for(i = 0; i < AV1_NUM_REF_FRAMES; i++) { + if(current->error_resilient_mode) + fbs(order_hint_bits, ref_order_hint[i], 1, i); + else + infer(ref_order_hint[i], priv->ref[i].order_hint); + if(current->ref_order_hint[i] != priv->ref[i].order_hint) + priv->ref[i].valid = 0; + } + } + } + + if(current->frame_type == AV1_FRAME_KEY || + current->frame_type == AV1_FRAME_INTRA_ONLY) { + CHECK(FUNC(frame_size)(ctx, rw, current)); + CHECK(FUNC(render_size)(ctx, rw, current)); + + if(current->allow_screen_content_tools && + priv->upscaled_width == priv->frame_width) + flag(allow_intrabc); + else + infer(allow_intrabc, 0); + } + else { + if(!seq->enable_order_hint) { + infer(frame_refs_short_signaling, 0); + } + else { + flag(frame_refs_short_signaling); + if(current->frame_refs_short_signaling) { + fb(3, last_frame_idx); + fb(3, golden_frame_idx); + CHECK(FUNC(set_frame_refs)(ctx, rw, current)); + } + } + + for(i = 0; i < AV1_REFS_PER_FRAME; i++) { + if(!current->frame_refs_short_signaling) + fbs(3, ref_frame_idx[i], 1, i); + if(seq->frame_id_numbers_present_flag) { + fbs(seq->delta_frame_id_length_minus_2 + 2, + delta_frame_id_minus1[i], 1, i); + } + } + + if(current->frame_size_override_flag && + !current->error_resilient_mode) { + CHECK(FUNC(frame_size_with_refs)(ctx, rw, current)); + } + else { + CHECK(FUNC(frame_size)(ctx, rw, current)); + CHECK(FUNC(render_size)(ctx, rw, current)); + } + + if(current->force_integer_mv) + infer(allow_high_precision_mv, 0); + else + flag(allow_high_precision_mv); + + CHECK(FUNC(interpolation_filter)(ctx, rw, current)); + + flag(is_motion_mode_switchable); + + if(current->error_resilient_mode || + !seq->enable_ref_frame_mvs) + infer(use_ref_frame_mvs, 0); + else + flag(use_ref_frame_mvs); + + infer(allow_intrabc, 0); + } + + if(!frame_is_intra) { + // Derive reference frame sign biases. + } + + if(seq->reduced_still_picture_header || current->disable_cdf_update) + infer(disable_frame_end_update_cdf, 1); + else + flag(disable_frame_end_update_cdf); + + if(current->primary_ref_frame == AV1_PRIMARY_REF_NONE) { + // Init non-coeff CDFs. + // Setup past independence. + } + else { + // Load CDF tables from previous frame. + // Load params from previous frame. + } + + if(current->use_ref_frame_mvs) { + // Perform motion field estimation process. + } + + CHECK(FUNC(tile_info)(ctx, rw, current)); + + CHECK(FUNC(quantization_params)(ctx, rw, current)); + + CHECK(FUNC(segmentation_params)(ctx, rw, current)); + + CHECK(FUNC(delta_q_params)(ctx, rw, current)); + + CHECK(FUNC(delta_lf_params)(ctx, rw, current)); + + // Init coeff CDFs / load previous segments. + + priv->coded_lossless = 1; + for(i = 0; i < AV1_MAX_SEGMENTS; i++) { + int qindex; + if(current->feature_enabled[i][AV1_SEG_LVL_ALT_Q]) { + qindex = (current->base_q_idx + + current->feature_value[i][AV1_SEG_LVL_ALT_Q]); + } + else { + qindex = current->base_q_idx; + } + qindex = av_clip_uintp2(qindex, 8); + + if(qindex || current->delta_q_y_dc || + current->delta_q_u_ac || current->delta_q_u_dc || + current->delta_q_v_ac || current->delta_q_v_dc) { + priv->coded_lossless = 0; + } + } + priv->all_lossless = priv->coded_lossless && + priv->frame_width == priv->upscaled_width; + + CHECK(FUNC(loop_filter_params)(ctx, rw, current)); + + CHECK(FUNC(cdef_params)(ctx, rw, current)); + + CHECK(FUNC(lr_params)(ctx, rw, current)); + + CHECK(FUNC(read_tx_mode)(ctx, rw, current)); + + CHECK(FUNC(frame_reference_mode)(ctx, rw, current)); + + CHECK(FUNC(skip_mode_params)(ctx, rw, current)); + + if(frame_is_intra || current->error_resilient_mode || + !seq->enable_warped_motion) + infer(allow_warped_motion, 0); + else + flag(allow_warped_motion); + + flag(reduced_tx_set); + + CHECK(FUNC(global_motion_params)(ctx, rw, current)); + + CHECK(FUNC(film_grain_params)(ctx, rw, ¤t->film_grain, current)); + + av_log(ctx->log_ctx, AV_LOG_DEBUG, "Frame %d: size %dx%d " + "upscaled %d render %dx%d subsample %dx%d " + "bitdepth %d tiles %dx%d.\n", + priv->order_hint, + priv->frame_width, priv->frame_height, priv->upscaled_width, + priv->render_width, priv->render_height, + seq->color_config.subsampling_x + 1, + seq->color_config.subsampling_y + 1, priv->bit_depth, + priv->tile_rows, priv->tile_cols); + +update_refs: + for(i = 0; i < AV1_NUM_REF_FRAMES; i++) { + if(current->refresh_frame_flags & (1 << i)) { + priv->ref[i] = (AV1ReferenceFrameState) { + .valid = 1, + .frame_id = current->current_frame_id, + .upscaled_width = priv->upscaled_width, + .frame_width = priv->frame_width, + .frame_height = priv->frame_height, + .render_width = priv->render_width, + .render_height = priv->render_height, + .frame_type = current->frame_type, + .subsampling_x = seq->color_config.subsampling_x, + .subsampling_y = seq->color_config.subsampling_y, + .bit_depth = priv->bit_depth, + .order_hint = priv->order_hint, + }; + memcpy(priv->ref[i].loop_filter_ref_deltas, current->loop_filter_ref_deltas, + sizeof(current->loop_filter_ref_deltas)); + memcpy(priv->ref[i].loop_filter_mode_deltas, current->loop_filter_mode_deltas, + sizeof(current->loop_filter_mode_deltas)); + memcpy(priv->ref[i].feature_enabled, current->feature_enabled, + sizeof(current->feature_enabled)); + memcpy(priv->ref[i].feature_value, current->feature_value, + sizeof(current->feature_value)); + } + } + + return 0; +} + +static int FUNC(frame_header_obu)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current, int redundant, + AVBufferRef *rw_buffer_ref) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + int start_pos, fh_bits, fh_bytes, err; + uint8_t *fh_start; + + if(priv->seen_frame_header) { + if(!redundant) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid repeated " + "frame header OBU.\n"); + return AVERROR_INVALIDDATA; + } + else { + GetBitContext fh; + size_t i, b; + uint32_t val; + + HEADER("Redundant Frame Header"); + + av_assert0(priv->frame_header_ref && priv->frame_header); + + init_get_bits(&fh, priv->frame_header, + priv->frame_header_size); + for(i = 0; i < priv->frame_header_size; i += 8) { + b = FFMIN(priv->frame_header_size - i, 8); + val = get_bits(&fh, b); + xf(b, frame_header_copy[i], + val, val, val, 1, i / 8); + } + } + } + else { + if(redundant) + HEADER("Redundant Frame Header (used as Frame Header)"); + else + HEADER("Frame Header"); + +#ifdef READ + start_pos = get_bits_count(rw); +#else + start_pos = put_bits_count(rw); +#endif + + CHECK(FUNC(uncompressed_header)(ctx, rw, current)); + + priv->tile_num = 0; + + if(current->show_existing_frame) { + priv->seen_frame_header = 0; + } + else { + priv->seen_frame_header = 1; + + av_buffer_unref(&priv->frame_header_ref); + +#ifdef READ + fh_bits = get_bits_count(rw) - start_pos; + fh_start = (uint8_t *)rw->buffer + start_pos / 8; +#else + // Need to flush the bitwriter so that we can copy its output, + // but use a copy so we don't affect the caller's structure. + { + PutBitContext tmp = *rw; + flush_put_bits(&tmp); + } + + fh_bits = put_bits_count(rw) - start_pos; + fh_start = rw->buf + start_pos / 8; +#endif + fh_bytes = (fh_bits + 7) / 8; + + priv->frame_header_size = fh_bits; + + if(rw_buffer_ref) { + priv->frame_header_ref = av_buffer_ref(rw_buffer_ref); + if(!priv->frame_header_ref) + return AVERROR(ENOMEM); + priv->frame_header = fh_start; + } + else { + priv->frame_header_ref = + av_buffer_alloc(fh_bytes + AV_INPUT_BUFFER_PADDING_SIZE); + if(!priv->frame_header_ref) + return AVERROR(ENOMEM); + priv->frame_header = priv->frame_header_ref->data; + memcpy(priv->frame_header, fh_start, fh_bytes); + } + } + } + + return 0; +} + +static int FUNC(tile_group_obu)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawTileGroup *current) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + int num_tiles, tile_bits; + int err; + + HEADER("Tile Group"); + + num_tiles = priv->tile_cols * priv->tile_rows; + if(num_tiles > 1) + flag(tile_start_and_end_present_flag); + else + infer(tile_start_and_end_present_flag, 0); + + if(num_tiles == 1 || !current->tile_start_and_end_present_flag) { + infer(tg_start, 0); + infer(tg_end, num_tiles - 1); + } + else { + tile_bits = cbs_av1_tile_log2(1, priv->tile_cols) + + cbs_av1_tile_log2(1, priv->tile_rows); + fc(tile_bits, tg_start, priv->tile_num, num_tiles - 1); + fc(tile_bits, tg_end, current->tg_start, num_tiles - 1); + } + + priv->tile_num = current->tg_end + 1; + + CHECK(FUNC(byte_alignment)(ctx, rw)); + + // Reset header for next frame. + if(current->tg_end == num_tiles - 1) + priv->seen_frame_header = 0; + + // Tile data follows. + + return 0; +} + +static int FUNC(frame_obu)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrame *current, + AVBufferRef *rw_buffer_ref) { + int err; + + CHECK(FUNC(frame_header_obu)(ctx, rw, ¤t->header, + 0, rw_buffer_ref)); + + CHECK(FUNC(byte_alignment)(ctx, rw)); + + CHECK(FUNC(tile_group_obu)(ctx, rw, ¤t->tile_group)); + + return 0; +} + +static int FUNC(tile_list_obu)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawTileList *current) { + int err; + + fb(8, output_frame_width_in_tiles_minus_1); + fb(8, output_frame_height_in_tiles_minus_1); + + fb(16, tile_count_minus_1); + + // Tile data follows. + + return 0; +} + +static int FUNC(metadata_hdr_cll)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawMetadataHDRCLL *current) { + int err; + + fb(16, max_cll); + fb(16, max_fall); + + return 0; +} + +static int FUNC(metadata_hdr_mdcv)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawMetadataHDRMDCV *current) { + int err, i; + + for(i = 0; i < 3; i++) { + fbs(16, primary_chromaticity_x[i], 1, i); + fbs(16, primary_chromaticity_y[i], 1, i); + } + + fb(16, white_point_chromaticity_x); + fb(16, white_point_chromaticity_y); + + fc(32, luminance_max, 1, MAX_UINT_BITS(32)); + // luminance_min must be lower than luminance_max. Convert luminance_max from + // 24.8 fixed point to 18.14 fixed point in order to compare them. + fc(32, luminance_min, 0, FFMIN(((uint64_t)current->luminance_max << 6) - 1, MAX_UINT_BITS(32))); + + return 0; +} + +static int FUNC(scalability_structure)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawMetadataScalability *current) { + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq; + int err, i, j; + + if(!priv->sequence_header) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "No sequence header available: " + "unable to parse scalability metadata.\n"); + return AVERROR_INVALIDDATA; + } + seq = priv->sequence_header; + + fb(2, spatial_layers_cnt_minus_1); + flag(spatial_layer_dimensions_present_flag); + flag(spatial_layer_description_present_flag); + flag(temporal_group_description_present_flag); + fc(3, scalability_structure_reserved_3bits, 0, 0); + if(current->spatial_layer_dimensions_present_flag) { + for(i = 0; i <= current->spatial_layers_cnt_minus_1; i++) { + fcs(16, spatial_layer_max_width[i], + 0, seq->max_frame_width_minus_1 + 1, 1, i); + fcs(16, spatial_layer_max_height[i], + 0, seq->max_frame_height_minus_1 + 1, 1, i); + } + } + if(current->spatial_layer_description_present_flag) { + for(i = 0; i <= current->spatial_layers_cnt_minus_1; i++) + fbs(8, spatial_layer_ref_id[i], 1, i); + } + if(current->temporal_group_description_present_flag) { + fb(8, temporal_group_size); + for(i = 0; i < current->temporal_group_size; i++) { + fbs(3, temporal_group_temporal_id[i], 1, i); + flags(temporal_group_temporal_switching_up_point_flag[i], 1, i); + flags(temporal_group_spatial_switching_up_point_flag[i], 1, i); + fbs(3, temporal_group_ref_cnt[i], 1, i); + for(j = 0; j < current->temporal_group_ref_cnt[i]; j++) { + fbs(8, temporal_group_ref_pic_diff[i][j], 2, i, j); + } + } + } + + return 0; +} + +static int FUNC(metadata_scalability)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawMetadataScalability *current) { + int err; + + fb(8, scalability_mode_idc); + + if(current->scalability_mode_idc == AV1_SCALABILITY_SS) + CHECK(FUNC(scalability_structure)(ctx, rw, current)); + + return 0; +} + +static int FUNC(metadata_itut_t35)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawMetadataITUTT35 *current) { + int err; + size_t i; + + fb(8, itu_t_t35_country_code); + if(current->itu_t_t35_country_code == 0xff) + fb(8, itu_t_t35_country_code_extension_byte); + +#ifdef READ + // The payload runs up to the start of the trailing bits, but there might + // be arbitrarily many trailing zeroes so we need to read through twice. + current->payload_size = cbs_av1_get_payload_bytes_left(rw); + + current->payload_ref = av_buffer_alloc(current->payload_size); + if(!current->payload_ref) + return AVERROR(ENOMEM); + current->payload = current->payload_ref->data; +#endif + + for(i = 0; i < current->payload_size; i++) + xf(8, itu_t_t35_payload_bytes[i], current->payload[i], + 0x00, 0xff, 1, i); + + return 0; +} + +static int FUNC(metadata_timecode)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawMetadataTimecode *current) { + int err; + + fb(5, counting_type); + flag(full_timestamp_flag); + flag(discontinuity_flag); + flag(cnt_dropped_flag); + fb(9, n_frames); + + if(current->full_timestamp_flag) { + fc(6, seconds_value, 0, 59); + fc(6, minutes_value, 0, 59); + fc(5, hours_value, 0, 23); + } + else { + flag(seconds_flag); + if(current->seconds_flag) { + fc(6, seconds_value, 0, 59); + flag(minutes_flag); + if(current->minutes_flag) { + fc(6, minutes_value, 0, 59); + flag(hours_flag); + if(current->hours_flag) + fc(5, hours_value, 0, 23); + } + } + } + + fb(5, time_offset_length); + if(current->time_offset_length > 0) + fb(current->time_offset_length, time_offset_value); + else + infer(time_offset_length, 0); + + return 0; +} + +static int FUNC(metadata_obu)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawMetadata *current) { + int err; + + leb128(metadata_type); + + switch(current->metadata_type) { + case AV1_METADATA_TYPE_HDR_CLL: + CHECK(FUNC(metadata_hdr_cll)(ctx, rw, ¤t->metadata.hdr_cll)); + break; + case AV1_METADATA_TYPE_HDR_MDCV: + CHECK(FUNC(metadata_hdr_mdcv)(ctx, rw, ¤t->metadata.hdr_mdcv)); + break; + case AV1_METADATA_TYPE_SCALABILITY: + CHECK(FUNC(metadata_scalability)(ctx, rw, ¤t->metadata.scalability)); + break; + case AV1_METADATA_TYPE_ITUT_T35: + CHECK(FUNC(metadata_itut_t35)(ctx, rw, ¤t->metadata.itut_t35)); + break; + case AV1_METADATA_TYPE_TIMECODE: + CHECK(FUNC(metadata_timecode)(ctx, rw, ¤t->metadata.timecode)); + break; + default: + // Unknown metadata type. + return AVERROR_PATCHWELCOME; + } + + return 0; +} + +static int FUNC(padding_obu)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawPadding *current) { + int i, err; + + HEADER("Padding"); + +#ifdef READ + // The payload runs up to the start of the trailing bits, but there might + // be arbitrarily many trailing zeroes so we need to read through twice. + current->payload_size = cbs_av1_get_payload_bytes_left(rw); + + current->payload_ref = av_buffer_alloc(current->payload_size); + if(!current->payload_ref) + return AVERROR(ENOMEM); + current->payload = current->payload_ref->data; +#endif + + for(i = 0; i < current->payload_size; i++) + xf(8, obu_padding_byte[i], current->payload[i], 0x00, 0xff, 1, i); + + return 0; +} diff --git a/third-party/cbs/cbs_h2645.c b/third-party/cbs/cbs_h2645.c new file mode 100644 index 00000000..4a60072d --- /dev/null +++ b/third-party/cbs/cbs_h2645.c @@ -0,0 +1,1632 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include "cbs/cbs.h" +#include "cbs/cbs_h264.h" +#include "cbs/cbs_h265.h" +#include "cbs/h264.h" +#include "cbs/h2645_parse.h" +#include "cbs/hevc.h" + +#include "bytestream.h" +#include "cbs_internal.h" +#include "h264_sei.h" +#include "hevc_sei.h" +#include "intmath.h" + + +static int cbs_read_ue_golomb(CodedBitstreamContext *ctx, GetBitContext *gbc, + const char *name, const int *subscripts, + uint32_t *write_to, + uint32_t range_min, uint32_t range_max) { + uint32_t value; + int position, i, j; + unsigned int k; + char bits[65]; + + position = get_bits_count(gbc); + + for(i = 0; i < 32; i++) { + if(get_bits_left(gbc) < i + 1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid ue-golomb code at " + "%s: bitstream ended.\n", + name); + return AVERROR_INVALIDDATA; + } + k = get_bits1(gbc); + bits[i] = k ? '1' : '0'; + if(k) + break; + } + if(i >= 32) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid ue-golomb code at " + "%s: more than 31 zeroes.\n", + name); + return AVERROR_INVALIDDATA; + } + value = 1; + for(j = 0; j < i; j++) { + k = get_bits1(gbc); + bits[i + j + 1] = k ? '1' : '0'; + value = value << 1 | k; + } + bits[i + j + 1] = 0; + --value; + + if(ctx->trace_enable) + ff_cbs_trace_syntax_element(ctx, position, name, subscripts, + bits, value); + + if(value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%" PRIu32 ", but must be in [%" PRIu32 ",%" PRIu32 "].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + + *write_to = value; + return 0; +} + +static int cbs_read_se_golomb(CodedBitstreamContext *ctx, GetBitContext *gbc, + const char *name, const int *subscripts, + int32_t *write_to, + int32_t range_min, int32_t range_max) { + int32_t value; + int position, i, j; + unsigned int k; + uint32_t v; + char bits[65]; + + position = get_bits_count(gbc); + + for(i = 0; i < 32; i++) { + if(get_bits_left(gbc) < i + 1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid se-golomb code at " + "%s: bitstream ended.\n", + name); + return AVERROR_INVALIDDATA; + } + k = get_bits1(gbc); + bits[i] = k ? '1' : '0'; + if(k) + break; + } + if(i >= 32) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid se-golomb code at " + "%s: more than 31 zeroes.\n", + name); + return AVERROR_INVALIDDATA; + } + v = 1; + for(j = 0; j < i; j++) { + k = get_bits1(gbc); + bits[i + j + 1] = k ? '1' : '0'; + v = v << 1 | k; + } + bits[i + j + 1] = 0; + if(v & 1) + value = -(int32_t)(v / 2); + else + value = v / 2; + + if(ctx->trace_enable) + ff_cbs_trace_syntax_element(ctx, position, name, subscripts, + bits, value); + + if(value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%" PRId32 ", but must be in [%" PRId32 ",%" PRId32 "].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + + *write_to = value; + return 0; +} + +static int cbs_write_ue_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc, + const char *name, const int *subscripts, + uint32_t value, + uint32_t range_min, uint32_t range_max) { + int len; + + if(value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%" PRIu32 ", but must be in [%" PRIu32 ",%" PRIu32 "].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + av_assert0(value != UINT32_MAX); + + len = av_log2(value + 1); + if(put_bits_left(pbc) < 2 * len + 1) + return AVERROR(ENOSPC); + + if(ctx->trace_enable) { + char bits[65]; + int i; + + for(i = 0; i < len; i++) + bits[i] = '0'; + bits[len] = '1'; + for(i = 0; i < len; i++) + bits[len + i + 1] = (value + 1) >> (len - i - 1) & 1 ? '1' : '0'; + bits[len + len + 1] = 0; + + ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), + name, subscripts, bits, value); + } + + put_bits(pbc, len, 0); + if(len + 1 < 32) + put_bits(pbc, len + 1, value + 1); + else + put_bits32(pbc, value + 1); + + return 0; +} + +static int cbs_write_se_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc, + const char *name, const int *subscripts, + int32_t value, + int32_t range_min, int32_t range_max) { + int len; + uint32_t uvalue; + + if(value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%" PRId32 ", but must be in [%" PRId32 ",%" PRId32 "].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + av_assert0(value != INT32_MIN); + + if(value == 0) + uvalue = 0; + else if(value > 0) + uvalue = 2 * (uint32_t)value - 1; + else + uvalue = 2 * (uint32_t)-value; + + len = av_log2(uvalue + 1); + if(put_bits_left(pbc) < 2 * len + 1) + return AVERROR(ENOSPC); + + if(ctx->trace_enable) { + char bits[65]; + int i; + + for(i = 0; i < len; i++) + bits[i] = '0'; + bits[len] = '1'; + for(i = 0; i < len; i++) + bits[len + i + 1] = (uvalue + 1) >> (len - i - 1) & 1 ? '1' : '0'; + bits[len + len + 1] = 0; + + ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), + name, subscripts, bits, value); + } + + put_bits(pbc, len, 0); + if(len + 1 < 32) + put_bits(pbc, len + 1, uvalue + 1); + else + put_bits32(pbc, uvalue + 1); + + return 0; +} + +// payload_extension_present() - true if we are before the last 1-bit +// in the payload structure, which must be in the last byte. +static int cbs_h265_payload_extension_present(GetBitContext *gbc, uint32_t payload_size, + int cur_pos) { + int bits_left = payload_size * 8 - cur_pos; + return (bits_left > 0 && + (bits_left > 7 || show_bits(gbc, bits_left) & MAX_UINT_BITS(bits_left - 1))); +} + +#define HEADER(name) \ + do { \ + ff_cbs_trace_header(ctx, name); \ + } while(0) + +#define CHECK(call) \ + do { \ + err = (call); \ + if(err < 0) \ + return err; \ + } while(0) + +#define FUNC_NAME2(rw, codec, name) cbs_##codec##_##rw##_##name +#define FUNC_NAME1(rw, codec, name) FUNC_NAME2(rw, codec, name) +#define FUNC_H264(name) FUNC_NAME1(READWRITE, h264, name) +#define FUNC_H265(name) FUNC_NAME1(READWRITE, h265, name) +#define FUNC_SEI(name) FUNC_NAME1(READWRITE, sei, name) + +#define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]) { subs, __VA_ARGS__ }) : NULL) + +#define u(width, name, range_min, range_max) \ + xu(width, name, current->name, range_min, range_max, 0, ) +#define ub(width, name) \ + xu(width, name, current->name, 0, MAX_UINT_BITS(width), 0, ) +#define flag(name) ub(1, name) +#define ue(name, range_min, range_max) \ + xue(name, current->name, range_min, range_max, 0, ) +#define i(width, name, range_min, range_max) \ + xi(width, name, current->name, range_min, range_max, 0, ) +#define ib(width, name) \ + xi(width, name, current->name, MIN_INT_BITS(width), MAX_INT_BITS(width), 0, ) +#define se(name, range_min, range_max) \ + xse(name, current->name, range_min, range_max, 0, ) + +#define us(width, name, range_min, range_max, subs, ...) \ + xu(width, name, current->name, range_min, range_max, subs, __VA_ARGS__) +#define ubs(width, name, subs, ...) \ + xu(width, name, current->name, 0, MAX_UINT_BITS(width), subs, __VA_ARGS__) +#define flags(name, subs, ...) \ + xu(1, name, current->name, 0, 1, subs, __VA_ARGS__) +#define ues(name, range_min, range_max, subs, ...) \ + xue(name, current->name, range_min, range_max, subs, __VA_ARGS__) +#define is(width, name, range_min, range_max, subs, ...) \ + xi(width, name, current->name, range_min, range_max, subs, __VA_ARGS__) +#define ibs(width, name, subs, ...) \ + xi(width, name, current->name, MIN_INT_BITS(width), MAX_INT_BITS(width), subs, __VA_ARGS__) +#define ses(name, range_min, range_max, subs, ...) \ + xse(name, current->name, range_min, range_max, subs, __VA_ARGS__) + +#define fixed(width, name, value) \ + do { \ + av_unused uint32_t fixed_value = value; \ + xu(width, name, fixed_value, value, value, 0, ); \ + } while(0) + + +#define READ +#define READWRITE read +#define RWContext GetBitContext + +#define xu(width, name, var, range_min, range_max, subs, ...) \ + do { \ + uint32_t value; \ + CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + &value, range_min, range_max)); \ + var = value; \ + } while(0) +#define xue(name, var, range_min, range_max, subs, ...) \ + do { \ + uint32_t value; \ + CHECK(cbs_read_ue_golomb(ctx, rw, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + &value, range_min, range_max)); \ + var = value; \ + } while(0) +#define xi(width, name, var, range_min, range_max, subs, ...) \ + do { \ + int32_t value; \ + CHECK(ff_cbs_read_signed(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + &value, range_min, range_max)); \ + var = value; \ + } while(0) +#define xse(name, var, range_min, range_max, subs, ...) \ + do { \ + int32_t value; \ + CHECK(cbs_read_se_golomb(ctx, rw, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + &value, range_min, range_max)); \ + var = value; \ + } while(0) + + +#define infer(name, value) \ + do { \ + current->name = value; \ + } while(0) + +static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc) { + int bits_left = get_bits_left(gbc); + if(bits_left > 8) + return 1; + if(bits_left == 0) + return 0; + if(show_bits(gbc, bits_left) & MAX_UINT_BITS(bits_left - 1)) + return 1; + return 0; +} + +#define more_rbsp_data(var) ((var) = cbs_h2645_read_more_rbsp_data(rw)) + +#define bit_position(rw) (get_bits_count(rw)) +#define byte_alignment(rw) (get_bits_count(rw) % 8) + +#define allocate(name, size) \ + do { \ + name##_ref = av_buffer_allocz(size + \ + AV_INPUT_BUFFER_PADDING_SIZE); \ + if(!name##_ref) \ + return AVERROR(ENOMEM); \ + name = name##_ref->data; \ + } while(0) + +#define FUNC(name) FUNC_SEI(name) +#include "cbs_sei_syntax_template.c" +#undef FUNC + +#define FUNC(name) FUNC_H264(name) +#include "cbs_h264_syntax_template.c" +#undef FUNC + +#define FUNC(name) FUNC_H265(name) +#include "cbs_h265_syntax_template.c" +#undef FUNC + +#undef READ +#undef READWRITE +#undef RWContext +#undef xu +#undef xi +#undef xue +#undef xse +#undef infer +#undef more_rbsp_data +#undef bit_position +#undef byte_alignment +#undef allocate + + +#define WRITE +#define READWRITE write +#define RWContext PutBitContext + +#define xu(width, name, var, range_min, range_max, subs, ...) \ + do { \ + uint32_t value = var; \ + CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + value, range_min, range_max)); \ + } while(0) +#define xue(name, var, range_min, range_max, subs, ...) \ + do { \ + uint32_t value = var; \ + CHECK(cbs_write_ue_golomb(ctx, rw, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + value, range_min, range_max)); \ + } while(0) +#define xi(width, name, var, range_min, range_max, subs, ...) \ + do { \ + int32_t value = var; \ + CHECK(ff_cbs_write_signed(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + value, range_min, range_max)); \ + } while(0) +#define xse(name, var, range_min, range_max, subs, ...) \ + do { \ + int32_t value = var; \ + CHECK(cbs_write_se_golomb(ctx, rw, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + value, range_min, range_max)); \ + } while(0) + +#define infer(name, value) \ + do { \ + if(current->name != (value)) { \ + av_log(ctx->log_ctx, AV_LOG_ERROR, \ + "%s does not match inferred value: " \ + "%" PRId64 ", but should be %" PRId64 ".\n", \ + #name, (int64_t)current->name, (int64_t)(value)); \ + return AVERROR_INVALIDDATA; \ + } \ + } while(0) + +#define more_rbsp_data(var) (var) + +#define bit_position(rw) (put_bits_count(rw)) +#define byte_alignment(rw) (put_bits_count(rw) % 8) + +#define allocate(name, size) \ + do { \ + if(!name) { \ + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s must be set " \ + "for writing.\n", \ + #name); \ + return AVERROR_INVALIDDATA; \ + } \ + } while(0) + +#define FUNC(name) FUNC_SEI(name) +#include "cbs_sei_syntax_template.c" +#undef FUNC + +#define FUNC(name) FUNC_H264(name) +#include "cbs_h264_syntax_template.c" +#undef FUNC + +#define FUNC(name) FUNC_H265(name) +#include "cbs_h265_syntax_template.c" +#undef FUNC + +#undef WRITE +#undef READWRITE +#undef RWContext +#undef xu +#undef xi +#undef xue +#undef xse +#undef u +#undef i +#undef flag +#undef ue +#undef se +#undef infer +#undef more_rbsp_data +#undef bit_position +#undef byte_alignment +#undef allocate + + +static int cbs_h2645_fragment_add_nals(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + const H2645Packet *packet) { + int err, i; + + for(i = 0; i < packet->nb_nals; i++) { + const H2645NAL *nal = &packet->nals[i]; + AVBufferRef *ref; + size_t size = nal->size; + + if(nal->nuh_layer_id > 0) + continue; + + // Remove trailing zeroes. + while(size > 0 && nal->data[size - 1] == 0) + --size; + if(size == 0) { + av_log(ctx->log_ctx, AV_LOG_VERBOSE, "Discarding empty 0 NAL unit\n"); + continue; + } + + ref = (nal->data == nal->raw_data) ? frag->data_ref : packet->rbsp.rbsp_buffer_ref; + + err = ff_cbs_insert_unit_data(frag, -1, nal->type, + (uint8_t *)nal->data, size, ref); + if(err < 0) + return err; + } + + return 0; +} + +static int cbs_h2645_split_fragment(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + int header) { + enum AVCodecID codec_id = ctx->codec->codec_id; + CodedBitstreamH2645Context *priv = ctx->priv_data; + GetByteContext gbc; + int err; + + av_assert0(frag->data && frag->nb_units == 0); + if(frag->data_size == 0) + return 0; + + if(header && frag->data[0] && codec_id == AV_CODEC_ID_H264) { + // AVCC header. + size_t size, start, end; + int i, count, version; + + priv->mp4 = 1; + + bytestream2_init(&gbc, frag->data, frag->data_size); + + if(bytestream2_get_bytes_left(&gbc) < 6) + return AVERROR_INVALIDDATA; + + version = bytestream2_get_byte(&gbc); + if(version != 1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid AVCC header: " + "first byte %u.\n", + version); + return AVERROR_INVALIDDATA; + } + + bytestream2_skip(&gbc, 3); + priv->nal_length_size = (bytestream2_get_byte(&gbc) & 3) + 1; + + // SPS array. + count = bytestream2_get_byte(&gbc) & 0x1f; + start = bytestream2_tell(&gbc); + for(i = 0; i < count; i++) { + if(bytestream2_get_bytes_left(&gbc) < 2 * (count - i)) + return AVERROR_INVALIDDATA; + size = bytestream2_get_be16(&gbc); + if(bytestream2_get_bytes_left(&gbc) < size) + return AVERROR_INVALIDDATA; + bytestream2_skip(&gbc, size); + } + end = bytestream2_tell(&gbc); + + err = ff_h2645_packet_split(&priv->read_packet, + frag->data + start, end - start, + ctx->log_ctx, 1, 2, AV_CODEC_ID_H264, 1, 1); + if(err < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to split AVCC SPS array.\n"); + return err; + } + err = cbs_h2645_fragment_add_nals(ctx, frag, &priv->read_packet); + if(err < 0) + return err; + + // PPS array. + count = bytestream2_get_byte(&gbc); + start = bytestream2_tell(&gbc); + for(i = 0; i < count; i++) { + if(bytestream2_get_bytes_left(&gbc) < 2 * (count - i)) + return AVERROR_INVALIDDATA; + size = bytestream2_get_be16(&gbc); + if(bytestream2_get_bytes_left(&gbc) < size) + return AVERROR_INVALIDDATA; + bytestream2_skip(&gbc, size); + } + end = bytestream2_tell(&gbc); + + err = ff_h2645_packet_split(&priv->read_packet, + frag->data + start, end - start, + ctx->log_ctx, 1, 2, AV_CODEC_ID_H264, 1, 1); + if(err < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to split AVCC PPS array.\n"); + return err; + } + err = cbs_h2645_fragment_add_nals(ctx, frag, &priv->read_packet); + if(err < 0) + return err; + + if(bytestream2_get_bytes_left(&gbc) > 0) { + av_log(ctx->log_ctx, AV_LOG_WARNING, "%u bytes left at end of AVCC " + "header.\n", + bytestream2_get_bytes_left(&gbc)); + } + } + else if(header && frag->data[0] && codec_id == AV_CODEC_ID_HEVC) { + // HVCC header. + size_t size, start, end; + int i, j, nb_arrays, nal_unit_type, nb_nals, version; + + priv->mp4 = 1; + + bytestream2_init(&gbc, frag->data, frag->data_size); + + if(bytestream2_get_bytes_left(&gbc) < 23) + return AVERROR_INVALIDDATA; + + version = bytestream2_get_byte(&gbc); + if(version != 1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid HVCC header: " + "first byte %u.\n", + version); + return AVERROR_INVALIDDATA; + } + + bytestream2_skip(&gbc, 20); + priv->nal_length_size = (bytestream2_get_byte(&gbc) & 3) + 1; + + nb_arrays = bytestream2_get_byte(&gbc); + for(i = 0; i < nb_arrays; i++) { + nal_unit_type = bytestream2_get_byte(&gbc) & 0x3f; + nb_nals = bytestream2_get_be16(&gbc); + + start = bytestream2_tell(&gbc); + for(j = 0; j < nb_nals; j++) { + if(bytestream2_get_bytes_left(&gbc) < 2) + return AVERROR_INVALIDDATA; + size = bytestream2_get_be16(&gbc); + if(bytestream2_get_bytes_left(&gbc) < size) + return AVERROR_INVALIDDATA; + bytestream2_skip(&gbc, size); + } + end = bytestream2_tell(&gbc); + + err = ff_h2645_packet_split(&priv->read_packet, + frag->data + start, end - start, + ctx->log_ctx, 1, 2, AV_CODEC_ID_HEVC, 1, 1); + if(err < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to split " + "HVCC array %d (%d NAL units of type %d).\n", + i, nb_nals, nal_unit_type); + return err; + } + err = cbs_h2645_fragment_add_nals(ctx, frag, &priv->read_packet); + if(err < 0) + return err; + } + } + else { + // Annex B, or later MP4 with already-known parameters. + + err = ff_h2645_packet_split(&priv->read_packet, + frag->data, frag->data_size, + ctx->log_ctx, + priv->mp4, priv->nal_length_size, + codec_id, 1, 1); + if(err < 0) + return err; + + err = cbs_h2645_fragment_add_nals(ctx, frag, &priv->read_packet); + if(err < 0) + return err; + } + + return 0; +} + +#define cbs_h2645_replace_ps(h26n, ps_name, ps_var, id_element) \ + static int cbs_h26##h26n##_replace_##ps_var(CodedBitstreamContext *ctx, \ + CodedBitstreamUnit *unit) { \ + CodedBitstreamH26##h26n##Context *priv = ctx->priv_data; \ + H26##h26n##Raw##ps_name *ps_var = unit->content; \ + unsigned int id = ps_var->id_element; \ + int err; \ + if(id >= FF_ARRAY_ELEMS(priv->ps_var)) { \ + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid " #ps_name " id : %d.\n", id); \ + return AVERROR_INVALIDDATA; \ + } \ + err = ff_cbs_make_unit_refcounted(ctx, unit); \ + if(err < 0) \ + return err; \ + if(priv->ps_var[id] == priv->active_##ps_var) \ + priv->active_##ps_var = NULL; \ + av_buffer_unref(&priv->ps_var##_ref[id]); \ + av_assert0(unit->content_ref); \ + priv->ps_var##_ref[id] = av_buffer_ref(unit->content_ref); \ + if(!priv->ps_var##_ref[id]) \ + return AVERROR(ENOMEM); \ + priv->ps_var[id] = (H26##h26n##Raw##ps_name *)priv->ps_var##_ref[id]->data; \ + return 0; \ + } + +cbs_h2645_replace_ps(4, SPS, sps, seq_parameter_set_id) + cbs_h2645_replace_ps(4, PPS, pps, pic_parameter_set_id) + cbs_h2645_replace_ps(5, VPS, vps, vps_video_parameter_set_id) + cbs_h2645_replace_ps(5, SPS, sps, sps_seq_parameter_set_id) + cbs_h2645_replace_ps(5, PPS, pps, pps_pic_parameter_set_id) + + static int cbs_h264_read_nal_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) { + GetBitContext gbc; + int err; + + err = init_get_bits(&gbc, unit->data, 8 * unit->data_size); + if(err < 0) + return err; + + err = ff_cbs_alloc_unit_content2(ctx, unit); + if(err < 0) + return err; + + switch(unit->type) { + case H264_NAL_SPS: { + H264RawSPS *sps = unit->content; + + err = cbs_h264_read_sps(ctx, &gbc, sps); + if(err < 0) + return err; + + err = cbs_h264_replace_sps(ctx, unit); + if(err < 0) + return err; + } break; + + case H264_NAL_SPS_EXT: { + err = cbs_h264_read_sps_extension(ctx, &gbc, unit->content); + if(err < 0) + return err; + } break; + + case H264_NAL_PPS: { + H264RawPPS *pps = unit->content; + + err = cbs_h264_read_pps(ctx, &gbc, pps); + if(err < 0) + return err; + + err = cbs_h264_replace_pps(ctx, unit); + if(err < 0) + return err; + } break; + + case H264_NAL_SLICE: + case H264_NAL_IDR_SLICE: + case H264_NAL_AUXILIARY_SLICE: { + H264RawSlice *slice = unit->content; + int pos, len; + + err = cbs_h264_read_slice_header(ctx, &gbc, &slice->header); + if(err < 0) + return err; + + if(!cbs_h2645_read_more_rbsp_data(&gbc)) + return AVERROR_INVALIDDATA; + + pos = get_bits_count(&gbc); + len = unit->data_size; + + slice->data_size = len - pos / 8; + slice->data_ref = av_buffer_ref(unit->data_ref); + if(!slice->data_ref) + return AVERROR(ENOMEM); + slice->data = unit->data + pos / 8; + slice->data_bit_start = pos % 8; + } break; + + case H264_NAL_AUD: { + err = cbs_h264_read_aud(ctx, &gbc, unit->content); + if(err < 0) + return err; + } break; + + case H264_NAL_SEI: { + err = cbs_h264_read_sei(ctx, &gbc, unit->content); + if(err < 0) + return err; + } break; + + case H264_NAL_FILLER_DATA: { + err = cbs_h264_read_filler(ctx, &gbc, unit->content); + if(err < 0) + return err; + } break; + + case H264_NAL_END_SEQUENCE: + case H264_NAL_END_STREAM: { + err = (unit->type == H264_NAL_END_SEQUENCE ? + cbs_h264_read_end_of_sequence : + cbs_h264_read_end_of_stream)(ctx, &gbc, unit->content); + if(err < 0) + return err; + } break; + + default: + return AVERROR(ENOSYS); + } + + return 0; +} + +static int cbs_h265_read_nal_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) { + GetBitContext gbc; + int err; + + err = init_get_bits(&gbc, unit->data, 8 * unit->data_size); + if(err < 0) + return err; + + err = ff_cbs_alloc_unit_content2(ctx, unit); + if(err < 0) + return err; + + switch(unit->type) { + case HEVC_NAL_VPS: { + H265RawVPS *vps = unit->content; + + err = cbs_h265_read_vps(ctx, &gbc, vps); + if(err < 0) + return err; + + err = cbs_h265_replace_vps(ctx, unit); + if(err < 0) + return err; + } break; + case HEVC_NAL_SPS: { + H265RawSPS *sps = unit->content; + + err = cbs_h265_read_sps(ctx, &gbc, sps); + if(err < 0) + return err; + + err = cbs_h265_replace_sps(ctx, unit); + if(err < 0) + return err; + } break; + + case HEVC_NAL_PPS: { + H265RawPPS *pps = unit->content; + + err = cbs_h265_read_pps(ctx, &gbc, pps); + if(err < 0) + return err; + + err = cbs_h265_replace_pps(ctx, unit); + if(err < 0) + return err; + } break; + + case HEVC_NAL_TRAIL_N: + case HEVC_NAL_TRAIL_R: + case HEVC_NAL_TSA_N: + case HEVC_NAL_TSA_R: + case HEVC_NAL_STSA_N: + case HEVC_NAL_STSA_R: + case HEVC_NAL_RADL_N: + case HEVC_NAL_RADL_R: + case HEVC_NAL_RASL_N: + case HEVC_NAL_RASL_R: + case HEVC_NAL_BLA_W_LP: + case HEVC_NAL_BLA_W_RADL: + case HEVC_NAL_BLA_N_LP: + case HEVC_NAL_IDR_W_RADL: + case HEVC_NAL_IDR_N_LP: + case HEVC_NAL_CRA_NUT: { + H265RawSlice *slice = unit->content; + int pos, len; + + err = cbs_h265_read_slice_segment_header(ctx, &gbc, &slice->header); + if(err < 0) + return err; + + if(!cbs_h2645_read_more_rbsp_data(&gbc)) + return AVERROR_INVALIDDATA; + + pos = get_bits_count(&gbc); + len = unit->data_size; + + slice->data_size = len - pos / 8; + slice->data_ref = av_buffer_ref(unit->data_ref); + if(!slice->data_ref) + return AVERROR(ENOMEM); + slice->data = unit->data + pos / 8; + slice->data_bit_start = pos % 8; + } break; + + case HEVC_NAL_AUD: { + err = cbs_h265_read_aud(ctx, &gbc, unit->content); + if(err < 0) + return err; + } break; + + case HEVC_NAL_SEI_PREFIX: + case HEVC_NAL_SEI_SUFFIX: { + err = cbs_h265_read_sei(ctx, &gbc, unit->content, + unit->type == HEVC_NAL_SEI_PREFIX); + + if(err < 0) + return err; + } break; + + default: + return AVERROR(ENOSYS); + } + + return 0; +} + +static int cbs_h2645_write_slice_data(CodedBitstreamContext *ctx, + PutBitContext *pbc, const uint8_t *data, + size_t data_size, int data_bit_start) { + size_t rest = data_size - (data_bit_start + 7) / 8; + const uint8_t *pos = data + data_bit_start / 8; + + av_assert0(data_bit_start >= 0 && + data_size > data_bit_start / 8); + + if(data_size * 8 + 8 > put_bits_left(pbc)) + return AVERROR(ENOSPC); + + if(!rest) + goto rbsp_stop_one_bit; + + // First copy the remaining bits of the first byte + // The above check ensures that we do not accidentally + // copy beyond the rbsp_stop_one_bit. + if(data_bit_start % 8) + put_bits(pbc, 8 - data_bit_start % 8, + *pos++ & MAX_UINT_BITS(8 - data_bit_start % 8)); + + if(put_bits_count(pbc) % 8 == 0) { + // If the writer is aligned at this point, + // memcpy can be used to improve performance. + // This happens normally for CABAC. + flush_put_bits(pbc); + memcpy(put_bits_ptr(pbc), pos, rest); + skip_put_bytes(pbc, rest); + } + else { + // If not, we have to copy manually. + // rbsp_stop_one_bit forces us to special-case + // the last byte. + uint8_t temp; + int i; + + for(; rest > 4; rest -= 4, pos += 4) + put_bits32(pbc, AV_RB32(pos)); + + for(; rest > 1; rest--, pos++) + put_bits(pbc, 8, *pos); + + rbsp_stop_one_bit: + temp = rest ? *pos : *pos & MAX_UINT_BITS(8 - data_bit_start % 8); + + av_assert0(temp); + i = ff_ctz(*pos); + temp = temp >> i; + i = rest ? (8 - i) : (8 - i - data_bit_start % 8); + put_bits(pbc, i, temp); + if(put_bits_count(pbc) % 8) + put_bits(pbc, 8 - put_bits_count(pbc) % 8, 0); + } + + return 0; +} + +static int cbs_h264_write_nal_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + PutBitContext *pbc) { + int err; + + switch(unit->type) { + case H264_NAL_SPS: { + H264RawSPS *sps = unit->content; + + err = cbs_h264_write_sps(ctx, pbc, sps); + if(err < 0) + return err; + + err = cbs_h264_replace_sps(ctx, unit); + if(err < 0) + return err; + } break; + + case H264_NAL_SPS_EXT: { + H264RawSPSExtension *sps_ext = unit->content; + + err = cbs_h264_write_sps_extension(ctx, pbc, sps_ext); + if(err < 0) + return err; + } break; + + case H264_NAL_PPS: { + H264RawPPS *pps = unit->content; + + err = cbs_h264_write_pps(ctx, pbc, pps); + if(err < 0) + return err; + + err = cbs_h264_replace_pps(ctx, unit); + if(err < 0) + return err; + } break; + + case H264_NAL_SLICE: + case H264_NAL_IDR_SLICE: + case H264_NAL_AUXILIARY_SLICE: { + H264RawSlice *slice = unit->content; + + err = cbs_h264_write_slice_header(ctx, pbc, &slice->header); + if(err < 0) + return err; + + if(slice->data) { + err = cbs_h2645_write_slice_data(ctx, pbc, slice->data, + slice->data_size, + slice->data_bit_start); + if(err < 0) + return err; + } + else { + // No slice data - that was just the header. + // (Bitstream may be unaligned!) + } + } break; + + case H264_NAL_AUD: { + err = cbs_h264_write_aud(ctx, pbc, unit->content); + if(err < 0) + return err; + } break; + + case H264_NAL_SEI: { + err = cbs_h264_write_sei(ctx, pbc, unit->content); + if(err < 0) + return err; + } break; + + case H264_NAL_FILLER_DATA: { + err = cbs_h264_write_filler(ctx, pbc, unit->content); + if(err < 0) + return err; + } break; + + case H264_NAL_END_SEQUENCE: { + err = cbs_h264_write_end_of_sequence(ctx, pbc, unit->content); + if(err < 0) + return err; + } break; + + case H264_NAL_END_STREAM: { + err = cbs_h264_write_end_of_stream(ctx, pbc, unit->content); + if(err < 0) + return err; + } break; + + default: + av_log(ctx->log_ctx, AV_LOG_ERROR, "Write unimplemented for " + "NAL unit type %" PRIu32 ".\n", + unit->type); + return AVERROR_PATCHWELCOME; + } + + return 0; +} + +static int cbs_h265_write_nal_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + PutBitContext *pbc) { + int err; + + switch(unit->type) { + case HEVC_NAL_VPS: { + H265RawVPS *vps = unit->content; + + err = cbs_h265_write_vps(ctx, pbc, vps); + if(err < 0) + return err; + + err = cbs_h265_replace_vps(ctx, unit); + if(err < 0) + return err; + } break; + + case HEVC_NAL_SPS: { + H265RawSPS *sps = unit->content; + + err = cbs_h265_write_sps(ctx, pbc, sps); + if(err < 0) + return err; + + err = cbs_h265_replace_sps(ctx, unit); + if(err < 0) + return err; + } break; + + case HEVC_NAL_PPS: { + H265RawPPS *pps = unit->content; + + err = cbs_h265_write_pps(ctx, pbc, pps); + if(err < 0) + return err; + + err = cbs_h265_replace_pps(ctx, unit); + if(err < 0) + return err; + } break; + + case HEVC_NAL_TRAIL_N: + case HEVC_NAL_TRAIL_R: + case HEVC_NAL_TSA_N: + case HEVC_NAL_TSA_R: + case HEVC_NAL_STSA_N: + case HEVC_NAL_STSA_R: + case HEVC_NAL_RADL_N: + case HEVC_NAL_RADL_R: + case HEVC_NAL_RASL_N: + case HEVC_NAL_RASL_R: + case HEVC_NAL_BLA_W_LP: + case HEVC_NAL_BLA_W_RADL: + case HEVC_NAL_BLA_N_LP: + case HEVC_NAL_IDR_W_RADL: + case HEVC_NAL_IDR_N_LP: + case HEVC_NAL_CRA_NUT: { + H265RawSlice *slice = unit->content; + + err = cbs_h265_write_slice_segment_header(ctx, pbc, &slice->header); + if(err < 0) + return err; + + if(slice->data) { + err = cbs_h2645_write_slice_data(ctx, pbc, slice->data, + slice->data_size, + slice->data_bit_start); + if(err < 0) + return err; + } + else { + // No slice data - that was just the header. + } + } break; + + case HEVC_NAL_AUD: { + err = cbs_h265_write_aud(ctx, pbc, unit->content); + if(err < 0) + return err; + } break; + + case HEVC_NAL_SEI_PREFIX: + case HEVC_NAL_SEI_SUFFIX: { + err = cbs_h265_write_sei(ctx, pbc, unit->content, + unit->type == HEVC_NAL_SEI_PREFIX); + + if(err < 0) + return err; + } break; + + default: + av_log(ctx->log_ctx, AV_LOG_ERROR, "Write unimplemented for " + "NAL unit type %" PRIu32 ".\n", + unit->type); + return AVERROR_PATCHWELCOME; + } + + return 0; +} + +static int cbs_h2645_unit_requires_zero_byte(enum AVCodecID codec_id, + CodedBitstreamUnitType type, + int nal_unit_index) { + // Section B.1.2 in H.264, section B.2.2 in H.265. + if(nal_unit_index == 0) { + // Assume that this is the first NAL unit in an access unit. + return 1; + } + if(codec_id == AV_CODEC_ID_H264) + return type == H264_NAL_SPS || type == H264_NAL_PPS; + if(codec_id == AV_CODEC_ID_HEVC) + return type == HEVC_NAL_VPS || type == HEVC_NAL_SPS || type == HEVC_NAL_PPS; + return 0; +} + +static int cbs_h2645_assemble_fragment(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag) { + uint8_t *data; + size_t max_size, dp, sp; + int err, i, zero_run; + + for(i = 0; i < frag->nb_units; i++) { + // Data should already all have been written when we get here. + av_assert0(frag->units[i].data); + } + + max_size = 0; + for(i = 0; i < frag->nb_units; i++) { + // Start code + content with worst-case emulation prevention. + max_size += 4 + frag->units[i].data_size * 3 / 2; + } + + data = av_realloc(NULL, max_size + AV_INPUT_BUFFER_PADDING_SIZE); + if(!data) + return AVERROR(ENOMEM); + + dp = 0; + for(i = 0; i < frag->nb_units; i++) { + CodedBitstreamUnit *unit = &frag->units[i]; + + if(unit->data_bit_padding > 0) { + if(i < frag->nb_units - 1) + av_log(ctx->log_ctx, AV_LOG_WARNING, "Probably invalid " + "unaligned padding on non-final NAL unit.\n"); + else + frag->data_bit_padding = unit->data_bit_padding; + } + + if(cbs_h2645_unit_requires_zero_byte(ctx->codec->codec_id, unit->type, i)) { + // zero_byte + data[dp++] = 0; + } + // start_code_prefix_one_3bytes + data[dp++] = 0; + data[dp++] = 0; + data[dp++] = 1; + + zero_run = 0; + for(sp = 0; sp < unit->data_size; sp++) { + if(zero_run < 2) { + if(unit->data[sp] == 0) + ++zero_run; + else + zero_run = 0; + } + else { + if((unit->data[sp] & ~3) == 0) { + // emulation_prevention_three_byte + data[dp++] = 3; + } + zero_run = unit->data[sp] == 0; + } + data[dp++] = unit->data[sp]; + } + } + + av_assert0(dp <= max_size); + err = av_reallocp(&data, dp + AV_INPUT_BUFFER_PADDING_SIZE); + if(err) + return err; + memset(data + dp, 0, AV_INPUT_BUFFER_PADDING_SIZE); + + frag->data_ref = av_buffer_create(data, dp + AV_INPUT_BUFFER_PADDING_SIZE, + NULL, NULL, 0); + if(!frag->data_ref) { + av_freep(&data); + return AVERROR(ENOMEM); + } + + frag->data = data; + frag->data_size = dp; + + return 0; +} + +static void cbs_h264_flush(CodedBitstreamContext *ctx) { + CodedBitstreamH264Context *h264 = ctx->priv_data; + + for(int i = 0; i < FF_ARRAY_ELEMS(h264->sps); i++) { + av_buffer_unref(&h264->sps_ref[i]); + h264->sps[i] = NULL; + } + for(int i = 0; i < FF_ARRAY_ELEMS(h264->pps); i++) { + av_buffer_unref(&h264->pps_ref[i]); + h264->pps[i] = NULL; + } + + h264->active_sps = NULL; + h264->active_pps = NULL; + h264->last_slice_nal_unit_type = 0; +} + +static void cbs_h264_close(CodedBitstreamContext *ctx) { + CodedBitstreamH264Context *h264 = ctx->priv_data; + int i; + + ff_h2645_packet_uninit(&h264->common.read_packet); + + for(i = 0; i < FF_ARRAY_ELEMS(h264->sps); i++) + av_buffer_unref(&h264->sps_ref[i]); + for(i = 0; i < FF_ARRAY_ELEMS(h264->pps); i++) + av_buffer_unref(&h264->pps_ref[i]); +} + +static void cbs_h265_flush(CodedBitstreamContext *ctx) { + CodedBitstreamH265Context *h265 = ctx->priv_data; + + for(int i = 0; i < FF_ARRAY_ELEMS(h265->vps); i++) { + av_buffer_unref(&h265->vps_ref[i]); + h265->vps[i] = NULL; + } + for(int i = 0; i < FF_ARRAY_ELEMS(h265->sps); i++) { + av_buffer_unref(&h265->sps_ref[i]); + h265->sps[i] = NULL; + } + for(int i = 0; i < FF_ARRAY_ELEMS(h265->pps); i++) { + av_buffer_unref(&h265->pps_ref[i]); + h265->pps[i] = NULL; + } + + h265->active_vps = NULL; + h265->active_sps = NULL; + h265->active_pps = NULL; +} + +static void cbs_h265_close(CodedBitstreamContext *ctx) { + CodedBitstreamH265Context *h265 = ctx->priv_data; + int i; + + ff_h2645_packet_uninit(&h265->common.read_packet); + + for(i = 0; i < FF_ARRAY_ELEMS(h265->vps); i++) + av_buffer_unref(&h265->vps_ref[i]); + for(i = 0; i < FF_ARRAY_ELEMS(h265->sps); i++) + av_buffer_unref(&h265->sps_ref[i]); + for(i = 0; i < FF_ARRAY_ELEMS(h265->pps); i++) + av_buffer_unref(&h265->pps_ref[i]); +} + +static void cbs_h264_free_sei(void *opaque, uint8_t *content) { + H264RawSEI *sei = (H264RawSEI *)content; + ff_cbs_sei_free_message_list(&sei->message_list); + av_free(content); +} + +static const CodedBitstreamUnitTypeDescriptor cbs_h264_unit_types[] = { + CBS_UNIT_TYPE_POD(H264_NAL_SPS, H264RawSPS), + CBS_UNIT_TYPE_POD(H264_NAL_SPS_EXT, H264RawSPSExtension), + + CBS_UNIT_TYPE_INTERNAL_REF(H264_NAL_PPS, H264RawPPS, slice_group_id), + + { + .nb_unit_types = 3, + .unit_types = { + H264_NAL_IDR_SLICE, + H264_NAL_SLICE, + H264_NAL_AUXILIARY_SLICE, + }, + .content_type = CBS_CONTENT_TYPE_INTERNAL_REFS, + .content_size = sizeof(H264RawSlice), + .nb_ref_offsets = 1, + .ref_offsets = { offsetof(H264RawSlice, data) }, + }, + + CBS_UNIT_TYPE_POD(H264_NAL_AUD, H264RawAUD), CBS_UNIT_TYPE_POD(H264_NAL_FILLER_DATA, H264RawFiller), CBS_UNIT_TYPE_POD(H264_NAL_END_SEQUENCE, H264RawNALUnitHeader), CBS_UNIT_TYPE_POD(H264_NAL_END_STREAM, H264RawNALUnitHeader), + + CBS_UNIT_TYPE_COMPLEX(H264_NAL_SEI, H264RawSEI, &cbs_h264_free_sei), + + CBS_UNIT_TYPE_END_OF_LIST +}; + +static void cbs_h265_free_sei(void *opaque, uint8_t *content) { + H265RawSEI *sei = (H265RawSEI *)content; + ff_cbs_sei_free_message_list(&sei->message_list); + av_free(content); +} + +static const CodedBitstreamUnitTypeDescriptor cbs_h265_unit_types[] = { + CBS_UNIT_TYPE_INTERNAL_REF(HEVC_NAL_VPS, H265RawVPS, extension_data.data), + CBS_UNIT_TYPE_INTERNAL_REF(HEVC_NAL_SPS, H265RawSPS, extension_data.data), + CBS_UNIT_TYPE_INTERNAL_REF(HEVC_NAL_PPS, H265RawPPS, extension_data.data), + + CBS_UNIT_TYPE_POD(HEVC_NAL_AUD, H265RawAUD), + + { + // Slices of non-IRAP pictures. + .nb_unit_types = CBS_UNIT_TYPE_RANGE, + .unit_type_range_start = HEVC_NAL_TRAIL_N, + .unit_type_range_end = HEVC_NAL_RASL_R, + + .content_type = CBS_CONTENT_TYPE_INTERNAL_REFS, + .content_size = sizeof(H265RawSlice), + .nb_ref_offsets = 1, + .ref_offsets = { offsetof(H265RawSlice, data) }, + }, + + { + // Slices of IRAP pictures. + .nb_unit_types = CBS_UNIT_TYPE_RANGE, + .unit_type_range_start = HEVC_NAL_BLA_W_LP, + .unit_type_range_end = HEVC_NAL_CRA_NUT, + + .content_type = CBS_CONTENT_TYPE_INTERNAL_REFS, + .content_size = sizeof(H265RawSlice), + .nb_ref_offsets = 1, + .ref_offsets = { offsetof(H265RawSlice, data) }, + }, + + { + .nb_unit_types = 2, + .unit_types = { + HEVC_NAL_SEI_PREFIX, + HEVC_NAL_SEI_SUFFIX }, + .content_type = CBS_CONTENT_TYPE_COMPLEX, + .content_size = sizeof(H265RawSEI), + .content_free = &cbs_h265_free_sei, + }, + + CBS_UNIT_TYPE_END_OF_LIST +}; + +const CodedBitstreamType ff_cbs_type_h264 = { + .codec_id = AV_CODEC_ID_H264, + + .priv_data_size = sizeof(CodedBitstreamH264Context), + + .unit_types = cbs_h264_unit_types, + + .split_fragment = &cbs_h2645_split_fragment, + .read_unit = &cbs_h264_read_nal_unit, + .write_unit = &cbs_h264_write_nal_unit, + .assemble_fragment = &cbs_h2645_assemble_fragment, + + .flush = &cbs_h264_flush, + .close = &cbs_h264_close, +}; + +const CodedBitstreamType ff_cbs_type_h265 = { + .codec_id = AV_CODEC_ID_HEVC, + + .priv_data_size = sizeof(CodedBitstreamH265Context), + + .unit_types = cbs_h265_unit_types, + + .split_fragment = &cbs_h2645_split_fragment, + .read_unit = &cbs_h265_read_nal_unit, + .write_unit = &cbs_h265_write_nal_unit, + .assemble_fragment = &cbs_h2645_assemble_fragment, + + .flush = &cbs_h265_flush, + .close = &cbs_h265_close, +}; + +static const SEIMessageTypeDescriptor cbs_sei_common_types[] = { + { + SEI_TYPE_FILLER_PAYLOAD, + 1, + 1, + sizeof(SEIRawFillerPayload), + SEI_MESSAGE_RW(sei, filler_payload), + }, + { + SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35, + 1, + 1, + sizeof(SEIRawUserDataRegistered), + SEI_MESSAGE_RW(sei, user_data_registered), + }, + { + SEI_TYPE_USER_DATA_UNREGISTERED, + 1, + 1, + sizeof(SEIRawUserDataUnregistered), + SEI_MESSAGE_RW(sei, user_data_unregistered), + }, + { + SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME, + 1, + 0, + sizeof(SEIRawMasteringDisplayColourVolume), + SEI_MESSAGE_RW(sei, mastering_display_colour_volume), + }, + { + SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO, + 1, + 0, + sizeof(SEIRawContentLightLevelInfo), + SEI_MESSAGE_RW(sei, content_light_level_info), + }, + { + SEI_TYPE_ALTERNATIVE_TRANSFER_CHARACTERISTICS, + 1, + 0, + sizeof(SEIRawAlternativeTransferCharacteristics), + SEI_MESSAGE_RW(sei, alternative_transfer_characteristics), + }, + SEI_MESSAGE_TYPE_END, +}; + +static const SEIMessageTypeDescriptor cbs_sei_h264_types[] = { + { + SEI_TYPE_BUFFERING_PERIOD, + 1, + 0, + sizeof(H264RawSEIBufferingPeriod), + SEI_MESSAGE_RW(h264, sei_buffering_period), + }, + { + SEI_TYPE_PIC_TIMING, + 1, + 0, + sizeof(H264RawSEIPicTiming), + SEI_MESSAGE_RW(h264, sei_pic_timing), + }, + { + SEI_TYPE_PAN_SCAN_RECT, + 1, + 0, + sizeof(H264RawSEIPanScanRect), + SEI_MESSAGE_RW(h264, sei_pan_scan_rect), + }, + { + SEI_TYPE_RECOVERY_POINT, + 1, + 0, + sizeof(H264RawSEIRecoveryPoint), + SEI_MESSAGE_RW(h264, sei_recovery_point), + }, + { + SEI_TYPE_DISPLAY_ORIENTATION, + 1, + 0, + sizeof(H264RawSEIDisplayOrientation), + SEI_MESSAGE_RW(h264, sei_display_orientation), + }, + SEI_MESSAGE_TYPE_END +}; + +static const SEIMessageTypeDescriptor cbs_sei_h265_types[] = { + { + SEI_TYPE_BUFFERING_PERIOD, + 1, + 0, + sizeof(H265RawSEIBufferingPeriod), + SEI_MESSAGE_RW(h265, sei_buffering_period), + }, + { + SEI_TYPE_PIC_TIMING, + 1, + 0, + sizeof(H265RawSEIPicTiming), + SEI_MESSAGE_RW(h265, sei_pic_timing), + }, + { + SEI_TYPE_PAN_SCAN_RECT, + 1, + 0, + sizeof(H265RawSEIPanScanRect), + SEI_MESSAGE_RW(h265, sei_pan_scan_rect), + }, + { + SEI_TYPE_RECOVERY_POINT, + 1, + 0, + sizeof(H265RawSEIRecoveryPoint), + SEI_MESSAGE_RW(h265, sei_recovery_point), + }, + { + SEI_TYPE_DISPLAY_ORIENTATION, + 1, + 0, + sizeof(H265RawSEIDisplayOrientation), + SEI_MESSAGE_RW(h265, sei_display_orientation), + }, + { + SEI_TYPE_ACTIVE_PARAMETER_SETS, + 1, + 0, + sizeof(H265RawSEIActiveParameterSets), + SEI_MESSAGE_RW(h265, sei_active_parameter_sets), + }, + { + SEI_TYPE_DECODED_PICTURE_HASH, + 0, + 1, + sizeof(H265RawSEIDecodedPictureHash), + SEI_MESSAGE_RW(h265, sei_decoded_picture_hash), + }, + { + SEI_TYPE_TIME_CODE, + 1, + 0, + sizeof(H265RawSEITimeCode), + SEI_MESSAGE_RW(h265, sei_time_code), + }, + { + SEI_TYPE_ALPHA_CHANNEL_INFO, + 1, + 0, + sizeof(H265RawSEIAlphaChannelInfo), + SEI_MESSAGE_RW(h265, sei_alpha_channel_info), + }, + SEI_MESSAGE_TYPE_END +}; + +const SEIMessageTypeDescriptor *ff_cbs_sei_find_type(CodedBitstreamContext *ctx, + int payload_type) { + const SEIMessageTypeDescriptor *codec_list; + int i; + + for(i = 0; cbs_sei_common_types[i].type >= 0; i++) { + if(cbs_sei_common_types[i].type == payload_type) + return &cbs_sei_common_types[i]; + } + + switch(ctx->codec->codec_id) { + case AV_CODEC_ID_H264: + codec_list = cbs_sei_h264_types; + break; + case AV_CODEC_ID_H265: + codec_list = cbs_sei_h265_types; + break; + default: + return NULL; + } + + for(i = 0; codec_list[i].type >= 0; i++) { + if(codec_list[i].type == payload_type) + return &codec_list[i]; + } + + return NULL; +} diff --git a/third-party/cbs/cbs_h264_syntax_template.c b/third-party/cbs/cbs_h264_syntax_template.c new file mode 100644 index 00000000..a2d3735b --- /dev/null +++ b/third-party/cbs/cbs_h264_syntax_template.c @@ -0,0 +1,1175 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +static int FUNC(rbsp_trailing_bits)(CodedBitstreamContext *ctx, RWContext *rw) { + int err; + + fixed(1, rbsp_stop_one_bit, 1); + while(byte_alignment(rw) != 0) + fixed(1, rbsp_alignment_zero_bit, 0); + + return 0; +} + +static int FUNC(nal_unit_header)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawNALUnitHeader *current, + uint32_t valid_type_mask) { + int err; + + fixed(1, forbidden_zero_bit, 0); + ub(2, nal_ref_idc); + ub(5, nal_unit_type); + + if(!(1 << current->nal_unit_type & valid_type_mask)) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid NAL unit type %d.\n", + current->nal_unit_type); + return AVERROR_INVALIDDATA; + } + + if(current->nal_unit_type == 14 || + current->nal_unit_type == 20 || + current->nal_unit_type == 21) { + if(current->nal_unit_type != 21) + flag(svc_extension_flag); + else + flag(avc_3d_extension_flag); + + if(current->svc_extension_flag) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SVC not supported.\n"); + return AVERROR_PATCHWELCOME; + } + else if(current->avc_3d_extension_flag) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "3DAVC not supported.\n"); + return AVERROR_PATCHWELCOME; + } + else { + av_log(ctx->log_ctx, AV_LOG_ERROR, "MVC not supported.\n"); + return AVERROR_PATCHWELCOME; + } + } + + return 0; +} + +static int FUNC(scaling_list)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawScalingList *current, + int size_of_scaling_list) { + int err, i, scale; + + scale = 8; + for(i = 0; i < size_of_scaling_list; i++) { + ses(delta_scale[i], -128, +127, 1, i); + scale = (scale + current->delta_scale[i] + 256) % 256; + if(scale == 0) + break; + } + + return 0; +} + +static int FUNC(hrd_parameters)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawHRD *current) { + int err, i; + + ue(cpb_cnt_minus1, 0, 31); + ub(4, bit_rate_scale); + ub(4, cpb_size_scale); + + for(i = 0; i <= current->cpb_cnt_minus1; i++) { + ues(bit_rate_value_minus1[i], 0, UINT32_MAX - 1, 1, i); + ues(cpb_size_value_minus1[i], 0, UINT32_MAX - 1, 1, i); + flags(cbr_flag[i], 1, i); + } + + ub(5, initial_cpb_removal_delay_length_minus1); + ub(5, cpb_removal_delay_length_minus1); + ub(5, dpb_output_delay_length_minus1); + ub(5, time_offset_length); + + return 0; +} + +static int FUNC(vui_parameters)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawVUI *current, H264RawSPS *sps) { + int err; + + flag(aspect_ratio_info_present_flag); + if(current->aspect_ratio_info_present_flag) { + ub(8, aspect_ratio_idc); + if(current->aspect_ratio_idc == 255) { + ub(16, sar_width); + ub(16, sar_height); + } + } + else { + infer(aspect_ratio_idc, 0); + } + + flag(overscan_info_present_flag); + if(current->overscan_info_present_flag) + flag(overscan_appropriate_flag); + + flag(video_signal_type_present_flag); + if(current->video_signal_type_present_flag) { + ub(3, video_format); + flag(video_full_range_flag); + flag(colour_description_present_flag); + if(current->colour_description_present_flag) { + ub(8, colour_primaries); + ub(8, transfer_characteristics); + ub(8, matrix_coefficients); + } + else { + infer(colour_primaries, 2); + infer(transfer_characteristics, 2); + infer(matrix_coefficients, 2); + } + } + else { + infer(video_format, 5); + infer(video_full_range_flag, 0); + infer(colour_primaries, 2); + infer(transfer_characteristics, 2); + infer(matrix_coefficients, 2); + } + + flag(chroma_loc_info_present_flag); + if(current->chroma_loc_info_present_flag) { + ue(chroma_sample_loc_type_top_field, 0, 5); + ue(chroma_sample_loc_type_bottom_field, 0, 5); + } + else { + infer(chroma_sample_loc_type_top_field, 0); + infer(chroma_sample_loc_type_bottom_field, 0); + } + + flag(timing_info_present_flag); + if(current->timing_info_present_flag) { + u(32, num_units_in_tick, 1, UINT32_MAX); + u(32, time_scale, 1, UINT32_MAX); + flag(fixed_frame_rate_flag); + } + else { + infer(fixed_frame_rate_flag, 0); + } + + flag(nal_hrd_parameters_present_flag); + if(current->nal_hrd_parameters_present_flag) + CHECK(FUNC(hrd_parameters)(ctx, rw, ¤t->nal_hrd_parameters)); + + flag(vcl_hrd_parameters_present_flag); + if(current->vcl_hrd_parameters_present_flag) + CHECK(FUNC(hrd_parameters)(ctx, rw, ¤t->vcl_hrd_parameters)); + + if(current->nal_hrd_parameters_present_flag || + current->vcl_hrd_parameters_present_flag) + flag(low_delay_hrd_flag); + else + infer(low_delay_hrd_flag, 1 - current->fixed_frame_rate_flag); + + flag(pic_struct_present_flag); + + flag(bitstream_restriction_flag); + if(current->bitstream_restriction_flag) { + flag(motion_vectors_over_pic_boundaries_flag); + ue(max_bytes_per_pic_denom, 0, 16); + ue(max_bits_per_mb_denom, 0, 16); + // The current version of the standard constrains this to be in + // [0,15], but older versions allow 16. + ue(log2_max_mv_length_horizontal, 0, 16); + ue(log2_max_mv_length_vertical, 0, 16); + ue(max_num_reorder_frames, 0, H264_MAX_DPB_FRAMES); + ue(max_dec_frame_buffering, 0, H264_MAX_DPB_FRAMES); + } + else { + infer(motion_vectors_over_pic_boundaries_flag, 1); + infer(max_bytes_per_pic_denom, 2); + infer(max_bits_per_mb_denom, 1); + infer(log2_max_mv_length_horizontal, 15); + infer(log2_max_mv_length_vertical, 15); + + if((sps->profile_idc == 44 || sps->profile_idc == 86 || + sps->profile_idc == 100 || sps->profile_idc == 110 || + sps->profile_idc == 122 || sps->profile_idc == 244) && + sps->constraint_set3_flag) { + infer(max_num_reorder_frames, 0); + infer(max_dec_frame_buffering, 0); + } + else { + infer(max_num_reorder_frames, H264_MAX_DPB_FRAMES); + infer(max_dec_frame_buffering, H264_MAX_DPB_FRAMES); + } + } + + return 0; +} + +static int FUNC(vui_parameters_default)(CodedBitstreamContext *ctx, + RWContext *rw, H264RawVUI *current, + H264RawSPS *sps) { + infer(aspect_ratio_idc, 0); + + infer(video_format, 5); + infer(video_full_range_flag, 0); + infer(colour_primaries, 2); + infer(transfer_characteristics, 2); + infer(matrix_coefficients, 2); + + infer(chroma_sample_loc_type_top_field, 0); + infer(chroma_sample_loc_type_bottom_field, 0); + + infer(fixed_frame_rate_flag, 0); + infer(low_delay_hrd_flag, 1); + + infer(pic_struct_present_flag, 0); + + infer(motion_vectors_over_pic_boundaries_flag, 1); + infer(max_bytes_per_pic_denom, 2); + infer(max_bits_per_mb_denom, 1); + infer(log2_max_mv_length_horizontal, 15); + infer(log2_max_mv_length_vertical, 15); + + if((sps->profile_idc == 44 || sps->profile_idc == 86 || + sps->profile_idc == 100 || sps->profile_idc == 110 || + sps->profile_idc == 122 || sps->profile_idc == 244) && + sps->constraint_set3_flag) { + infer(max_num_reorder_frames, 0); + infer(max_dec_frame_buffering, 0); + } + else { + infer(max_num_reorder_frames, H264_MAX_DPB_FRAMES); + infer(max_dec_frame_buffering, H264_MAX_DPB_FRAMES); + } + + return 0; +} + +static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSPS *current) { + int err, i; + + HEADER("Sequence Parameter Set"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, + 1 << H264_NAL_SPS)); + + ub(8, profile_idc); + + flag(constraint_set0_flag); + flag(constraint_set1_flag); + flag(constraint_set2_flag); + flag(constraint_set3_flag); + flag(constraint_set4_flag); + flag(constraint_set5_flag); + + u(2, reserved_zero_2bits, 0, 0); + + ub(8, level_idc); + + ue(seq_parameter_set_id, 0, 31); + + if(current->profile_idc == 100 || current->profile_idc == 110 || + current->profile_idc == 122 || current->profile_idc == 244 || + current->profile_idc == 44 || current->profile_idc == 83 || + current->profile_idc == 86 || current->profile_idc == 118 || + current->profile_idc == 128 || current->profile_idc == 138) { + ue(chroma_format_idc, 0, 3); + + if(current->chroma_format_idc == 3) + flag(separate_colour_plane_flag); + else + infer(separate_colour_plane_flag, 0); + + ue(bit_depth_luma_minus8, 0, 6); + ue(bit_depth_chroma_minus8, 0, 6); + + flag(qpprime_y_zero_transform_bypass_flag); + + flag(seq_scaling_matrix_present_flag); + if(current->seq_scaling_matrix_present_flag) { + for(i = 0; i < ((current->chroma_format_idc != 3) ? 8 : 12); i++) { + flags(seq_scaling_list_present_flag[i], 1, i); + if(current->seq_scaling_list_present_flag[i]) { + if(i < 6) + CHECK(FUNC(scaling_list)(ctx, rw, + ¤t->scaling_list_4x4[i], + 16)); + else + CHECK(FUNC(scaling_list)(ctx, rw, + ¤t->scaling_list_8x8[i - 6], + 64)); + } + } + } + } + else { + infer(chroma_format_idc, current->profile_idc == 183 ? 0 : 1); + + infer(separate_colour_plane_flag, 0); + infer(bit_depth_luma_minus8, 0); + infer(bit_depth_chroma_minus8, 0); + } + + ue(log2_max_frame_num_minus4, 0, 12); + ue(pic_order_cnt_type, 0, 2); + + if(current->pic_order_cnt_type == 0) { + ue(log2_max_pic_order_cnt_lsb_minus4, 0, 12); + } + else if(current->pic_order_cnt_type == 1) { + flag(delta_pic_order_always_zero_flag); + se(offset_for_non_ref_pic, INT32_MIN + 1, INT32_MAX); + se(offset_for_top_to_bottom_field, INT32_MIN + 1, INT32_MAX); + ue(num_ref_frames_in_pic_order_cnt_cycle, 0, 255); + + for(i = 0; i < current->num_ref_frames_in_pic_order_cnt_cycle; i++) + ses(offset_for_ref_frame[i], INT32_MIN + 1, INT32_MAX, 1, i); + } + + ue(max_num_ref_frames, 0, H264_MAX_DPB_FRAMES); + flag(gaps_in_frame_num_allowed_flag); + + ue(pic_width_in_mbs_minus1, 0, H264_MAX_MB_WIDTH); + ue(pic_height_in_map_units_minus1, 0, H264_MAX_MB_HEIGHT); + + flag(frame_mbs_only_flag); + if(!current->frame_mbs_only_flag) + flag(mb_adaptive_frame_field_flag); + + flag(direct_8x8_inference_flag); + + flag(frame_cropping_flag); + if(current->frame_cropping_flag) { + ue(frame_crop_left_offset, 0, H264_MAX_WIDTH); + ue(frame_crop_right_offset, 0, H264_MAX_WIDTH); + ue(frame_crop_top_offset, 0, H264_MAX_HEIGHT); + ue(frame_crop_bottom_offset, 0, H264_MAX_HEIGHT); + } + + flag(vui_parameters_present_flag); + if(current->vui_parameters_present_flag) + CHECK(FUNC(vui_parameters)(ctx, rw, ¤t->vui, current)); + else + CHECK(FUNC(vui_parameters_default)(ctx, rw, ¤t->vui, current)); + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(sps_extension)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSPSExtension *current) { + int err; + + HEADER("Sequence Parameter Set Extension"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, + 1 << H264_NAL_SPS_EXT)); + + ue(seq_parameter_set_id, 0, 31); + + ue(aux_format_idc, 0, 3); + + if(current->aux_format_idc != 0) { + int bits; + + ue(bit_depth_aux_minus8, 0, 4); + flag(alpha_incr_flag); + + bits = current->bit_depth_aux_minus8 + 9; + ub(bits, alpha_opaque_value); + ub(bits, alpha_transparent_value); + } + + flag(additional_extension_flag); + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(pps)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawPPS *current) { + CodedBitstreamH264Context *h264 = ctx->priv_data; + const H264RawSPS *sps; + int err, i; + + HEADER("Picture Parameter Set"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, + 1 << H264_NAL_PPS)); + + ue(pic_parameter_set_id, 0, 255); + ue(seq_parameter_set_id, 0, 31); + + sps = h264->sps[current->seq_parameter_set_id]; + if(!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n", + current->seq_parameter_set_id); + return AVERROR_INVALIDDATA; + } + + flag(entropy_coding_mode_flag); + flag(bottom_field_pic_order_in_frame_present_flag); + + ue(num_slice_groups_minus1, 0, 7); + if(current->num_slice_groups_minus1 > 0) { + unsigned int pic_size; + int iGroup; + + pic_size = (sps->pic_width_in_mbs_minus1 + 1) * + (sps->pic_height_in_map_units_minus1 + 1); + + ue(slice_group_map_type, 0, 6); + + if(current->slice_group_map_type == 0) { + for(iGroup = 0; iGroup <= current->num_slice_groups_minus1; iGroup++) + ues(run_length_minus1[iGroup], 0, pic_size - 1, 1, iGroup); + } + else if(current->slice_group_map_type == 2) { + for(iGroup = 0; iGroup < current->num_slice_groups_minus1; iGroup++) { + ues(top_left[iGroup], 0, pic_size - 1, 1, iGroup); + ues(bottom_right[iGroup], + current->top_left[iGroup], pic_size - 1, 1, iGroup); + } + } + else if(current->slice_group_map_type == 3 || + current->slice_group_map_type == 4 || + current->slice_group_map_type == 5) { + flag(slice_group_change_direction_flag); + ue(slice_group_change_rate_minus1, 0, pic_size - 1); + } + else if(current->slice_group_map_type == 6) { + ue(pic_size_in_map_units_minus1, pic_size - 1, pic_size - 1); + + allocate(current->slice_group_id, + current->pic_size_in_map_units_minus1 + 1); + for(i = 0; i <= current->pic_size_in_map_units_minus1; i++) + us(av_log2(2 * current->num_slice_groups_minus1 + 1), + slice_group_id[i], 0, current->num_slice_groups_minus1, 1, i); + } + } + + ue(num_ref_idx_l0_default_active_minus1, 0, 31); + ue(num_ref_idx_l1_default_active_minus1, 0, 31); + + flag(weighted_pred_flag); + u(2, weighted_bipred_idc, 0, 2); + + se(pic_init_qp_minus26, -26 - 6 * sps->bit_depth_luma_minus8, +25); + se(pic_init_qs_minus26, -26, +25); + se(chroma_qp_index_offset, -12, +12); + + flag(deblocking_filter_control_present_flag); + flag(constrained_intra_pred_flag); + flag(redundant_pic_cnt_present_flag); + + if(more_rbsp_data(current->more_rbsp_data)) { + flag(transform_8x8_mode_flag); + + flag(pic_scaling_matrix_present_flag); + if(current->pic_scaling_matrix_present_flag) { + for(i = 0; i < 6 + (((sps->chroma_format_idc != 3) ? 2 : 6) * + current->transform_8x8_mode_flag); + i++) { + flags(pic_scaling_list_present_flag[i], 1, i); + if(current->pic_scaling_list_present_flag[i]) { + if(i < 6) + CHECK(FUNC(scaling_list)(ctx, rw, + ¤t->scaling_list_4x4[i], + 16)); + else + CHECK(FUNC(scaling_list)(ctx, rw, + ¤t->scaling_list_8x8[i - 6], + 64)); + } + } + } + + se(second_chroma_qp_index_offset, -12, +12); + } + else { + infer(transform_8x8_mode_flag, 0); + infer(pic_scaling_matrix_present_flag, 0); + infer(second_chroma_qp_index_offset, current->chroma_qp_index_offset); + } + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(sei_buffering_period)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEIBufferingPeriod *current, + SEIMessageState *sei) { + CodedBitstreamH264Context *h264 = ctx->priv_data; + const H264RawSPS *sps; + int err, i, length; + + HEADER("Buffering Period"); + + ue(seq_parameter_set_id, 0, 31); + + sps = h264->sps[current->seq_parameter_set_id]; + if(!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n", + current->seq_parameter_set_id); + return AVERROR_INVALIDDATA; + } + h264->active_sps = sps; + + if(sps->vui.nal_hrd_parameters_present_flag) { + for(i = 0; i <= sps->vui.nal_hrd_parameters.cpb_cnt_minus1; i++) { + length = sps->vui.nal_hrd_parameters.initial_cpb_removal_delay_length_minus1 + 1; + xu(length, initial_cpb_removal_delay[SchedSelIdx], + current->nal.initial_cpb_removal_delay[i], + 1, MAX_UINT_BITS(length), 1, i); + xu(length, initial_cpb_removal_delay_offset[SchedSelIdx], + current->nal.initial_cpb_removal_delay_offset[i], + 0, MAX_UINT_BITS(length), 1, i); + } + } + + if(sps->vui.vcl_hrd_parameters_present_flag) { + for(i = 0; i <= sps->vui.vcl_hrd_parameters.cpb_cnt_minus1; i++) { + length = sps->vui.vcl_hrd_parameters.initial_cpb_removal_delay_length_minus1 + 1; + xu(length, initial_cpb_removal_delay[SchedSelIdx], + current->vcl.initial_cpb_removal_delay[i], + 1, MAX_UINT_BITS(length), 1, i); + xu(length, initial_cpb_removal_delay_offset[SchedSelIdx], + current->vcl.initial_cpb_removal_delay_offset[i], + 0, MAX_UINT_BITS(length), 1, i); + } + } + + return 0; +} + +static int FUNC(sei_pic_timestamp)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEIPicTimestamp *current, + const H264RawSPS *sps) { + uint8_t time_offset_length; + int err; + + u(2, ct_type, 0, 2); + flag(nuit_field_based_flag); + u(5, counting_type, 0, 6); + flag(full_timestamp_flag); + flag(discontinuity_flag); + flag(cnt_dropped_flag); + ub(8, n_frames); + if(current->full_timestamp_flag) { + u(6, seconds_value, 0, 59); + u(6, minutes_value, 0, 59); + u(5, hours_value, 0, 23); + } + else { + flag(seconds_flag); + if(current->seconds_flag) { + u(6, seconds_value, 0, 59); + flag(minutes_flag); + if(current->minutes_flag) { + u(6, minutes_value, 0, 59); + flag(hours_flag); + if(current->hours_flag) + u(5, hours_value, 0, 23); + } + } + } + + if(sps->vui.nal_hrd_parameters_present_flag) + time_offset_length = sps->vui.nal_hrd_parameters.time_offset_length; + else if(sps->vui.vcl_hrd_parameters_present_flag) + time_offset_length = sps->vui.vcl_hrd_parameters.time_offset_length; + else + time_offset_length = 24; + + if(time_offset_length > 0) + ib(time_offset_length, time_offset); + else + infer(time_offset, 0); + + return 0; +} + +static int FUNC(sei_pic_timing)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEIPicTiming *current, + SEIMessageState *sei) { + CodedBitstreamH264Context *h264 = ctx->priv_data; + const H264RawSPS *sps; + int err; + + HEADER("Picture Timing"); + + sps = h264->active_sps; + if(!sps) { + // If there is exactly one possible SPS but it is not yet active + // then just assume that it should be the active one. + int i, k = -1; + for(i = 0; i < H264_MAX_SPS_COUNT; i++) { + if(h264->sps[i]) { + if(k >= 0) { + k = -1; + break; + } + k = i; + } + } + if(k >= 0) + sps = h264->sps[k]; + } + if(!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "No active SPS for pic_timing.\n"); + return AVERROR_INVALIDDATA; + } + + if(sps->vui.nal_hrd_parameters_present_flag || + sps->vui.vcl_hrd_parameters_present_flag) { + const H264RawHRD *hrd; + + if(sps->vui.nal_hrd_parameters_present_flag) + hrd = &sps->vui.nal_hrd_parameters; + else if(sps->vui.vcl_hrd_parameters_present_flag) + hrd = &sps->vui.vcl_hrd_parameters; + else { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "No HRD parameters for pic_timing.\n"); + return AVERROR_INVALIDDATA; + } + + ub(hrd->cpb_removal_delay_length_minus1 + 1, cpb_removal_delay); + ub(hrd->dpb_output_delay_length_minus1 + 1, dpb_output_delay); + } + + if(sps->vui.pic_struct_present_flag) { + static const uint8_t num_clock_ts[9] = { + 1, 1, 1, 2, 2, 3, 3, 2, 3 + }; + int i; + + u(4, pic_struct, 0, 8); + if(current->pic_struct > 8) + return AVERROR_INVALIDDATA; + + for(i = 0; i < num_clock_ts[current->pic_struct]; i++) { + flags(clock_timestamp_flag[i], 1, i); + if(current->clock_timestamp_flag[i]) + CHECK(FUNC(sei_pic_timestamp)(ctx, rw, + ¤t->timestamp[i], sps)); + } + } + + return 0; +} + +static int FUNC(sei_pan_scan_rect)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEIPanScanRect *current, + SEIMessageState *sei) { + int err, i; + + HEADER("Pan-Scan Rectangle"); + + ue(pan_scan_rect_id, 0, UINT32_MAX - 1); + flag(pan_scan_rect_cancel_flag); + + if(!current->pan_scan_rect_cancel_flag) { + ue(pan_scan_cnt_minus1, 0, 2); + + for(i = 0; i <= current->pan_scan_cnt_minus1; i++) { + ses(pan_scan_rect_left_offset[i], INT32_MIN + 1, INT32_MAX, 1, i); + ses(pan_scan_rect_right_offset[i], INT32_MIN + 1, INT32_MAX, 1, i); + ses(pan_scan_rect_top_offset[i], INT32_MIN + 1, INT32_MAX, 1, i); + ses(pan_scan_rect_bottom_offset[i], INT32_MIN + 1, INT32_MAX, 1, i); + } + + ue(pan_scan_rect_repetition_period, 0, 16384); + } + + return 0; +} + +static int FUNC(sei_recovery_point)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEIRecoveryPoint *current, + SEIMessageState *sei) { + int err; + + HEADER("Recovery Point"); + + ue(recovery_frame_cnt, 0, 65535); + flag(exact_match_flag); + flag(broken_link_flag); + u(2, changing_slice_group_idc, 0, 2); + + return 0; +} + +static int FUNC(sei_display_orientation)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEIDisplayOrientation *current, + SEIMessageState *sei) { + int err; + + HEADER("Display Orientation"); + + flag(display_orientation_cancel_flag); + if(!current->display_orientation_cancel_flag) { + flag(hor_flip); + flag(ver_flip); + ub(16, anticlockwise_rotation); + ue(display_orientation_repetition_period, 0, 16384); + flag(display_orientation_extension_flag); + } + + return 0; +} + +static int FUNC(sei)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEI *current) { + int err; + + HEADER("Supplemental Enhancement Information"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, + 1 << H264_NAL_SEI)); + + CHECK(FUNC_SEI(message_list)(ctx, rw, ¤t->message_list, 1)); + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(aud)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawAUD *current) { + int err; + + HEADER("Access Unit Delimiter"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, + 1 << H264_NAL_AUD)); + + ub(3, primary_pic_type); + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(ref_pic_list_modification)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSliceHeader *current) { + CodedBitstreamH264Context *h264 = ctx->priv_data; + const H264RawSPS *sps = h264->active_sps; + int err, i, mopn; + + if(current->slice_type % 5 != 2 && + current->slice_type % 5 != 4) { + flag(ref_pic_list_modification_flag_l0); + if(current->ref_pic_list_modification_flag_l0) { + for(i = 0; i < H264_MAX_RPLM_COUNT; i++) { + xue(modification_of_pic_nums_idc, + current->rplm_l0[i].modification_of_pic_nums_idc, 0, 3, 0); + + mopn = current->rplm_l0[i].modification_of_pic_nums_idc; + if(mopn == 3) + break; + + if(mopn == 0 || mopn == 1) + xue(abs_diff_pic_num_minus1, + current->rplm_l0[i].abs_diff_pic_num_minus1, + 0, (1 + current->field_pic_flag) * (1 << (sps->log2_max_frame_num_minus4 + 4)), 0); + else if(mopn == 2) + xue(long_term_pic_num, + current->rplm_l0[i].long_term_pic_num, + 0, sps->max_num_ref_frames - 1, 0); + } + } + } + + if(current->slice_type % 5 == 1) { + flag(ref_pic_list_modification_flag_l1); + if(current->ref_pic_list_modification_flag_l1) { + for(i = 0; i < H264_MAX_RPLM_COUNT; i++) { + xue(modification_of_pic_nums_idc, + current->rplm_l1[i].modification_of_pic_nums_idc, 0, 3, 0); + + mopn = current->rplm_l1[i].modification_of_pic_nums_idc; + if(mopn == 3) + break; + + if(mopn == 0 || mopn == 1) + xue(abs_diff_pic_num_minus1, + current->rplm_l1[i].abs_diff_pic_num_minus1, + 0, (1 + current->field_pic_flag) * (1 << (sps->log2_max_frame_num_minus4 + 4)), 0); + else if(mopn == 2) + xue(long_term_pic_num, + current->rplm_l1[i].long_term_pic_num, + 0, sps->max_num_ref_frames - 1, 0); + } + } + } + + return 0; +} + +static int FUNC(pred_weight_table)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSliceHeader *current) { + CodedBitstreamH264Context *h264 = ctx->priv_data; + const H264RawSPS *sps = h264->active_sps; + int chroma; + int err, i, j; + + ue(luma_log2_weight_denom, 0, 7); + + chroma = !sps->separate_colour_plane_flag && sps->chroma_format_idc != 0; + if(chroma) + ue(chroma_log2_weight_denom, 0, 7); + + for(i = 0; i <= current->num_ref_idx_l0_active_minus1; i++) { + flags(luma_weight_l0_flag[i], 1, i); + if(current->luma_weight_l0_flag[i]) { + ses(luma_weight_l0[i], -128, +127, 1, i); + ses(luma_offset_l0[i], -128, +127, 1, i); + } + if(chroma) { + flags(chroma_weight_l0_flag[i], 1, i); + if(current->chroma_weight_l0_flag[i]) { + for(j = 0; j < 2; j++) { + ses(chroma_weight_l0[i][j], -128, +127, 2, i, j); + ses(chroma_offset_l0[i][j], -128, +127, 2, i, j); + } + } + } + } + + if(current->slice_type % 5 == 1) { + for(i = 0; i <= current->num_ref_idx_l1_active_minus1; i++) { + flags(luma_weight_l1_flag[i], 1, i); + if(current->luma_weight_l1_flag[i]) { + ses(luma_weight_l1[i], -128, +127, 1, i); + ses(luma_offset_l1[i], -128, +127, 1, i); + } + if(chroma) { + flags(chroma_weight_l1_flag[i], 1, i); + if(current->chroma_weight_l1_flag[i]) { + for(j = 0; j < 2; j++) { + ses(chroma_weight_l1[i][j], -128, +127, 2, i, j); + ses(chroma_offset_l1[i][j], -128, +127, 2, i, j); + } + } + } + } + } + + return 0; +} + +static int FUNC(dec_ref_pic_marking)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSliceHeader *current, int idr_pic_flag) { + CodedBitstreamH264Context *h264 = ctx->priv_data; + const H264RawSPS *sps = h264->active_sps; + int err, i; + uint32_t mmco; + + if(idr_pic_flag) { + flag(no_output_of_prior_pics_flag); + flag(long_term_reference_flag); + } + else { + flag(adaptive_ref_pic_marking_mode_flag); + if(current->adaptive_ref_pic_marking_mode_flag) { + for(i = 0; i < H264_MAX_MMCO_COUNT; i++) { + xue(memory_management_control_operation, + current->mmco[i].memory_management_control_operation, + 0, 6, 0); + + mmco = current->mmco[i].memory_management_control_operation; + if(mmco == 0) + break; + + if(mmco == 1 || mmco == 3) + xue(difference_of_pic_nums_minus1, + current->mmco[i].difference_of_pic_nums_minus1, + 0, INT32_MAX, 0); + if(mmco == 2) + xue(long_term_pic_num, + current->mmco[i].long_term_pic_num, + 0, sps->max_num_ref_frames - 1, 0); + if(mmco == 3 || mmco == 6) + xue(long_term_frame_idx, + current->mmco[i].long_term_frame_idx, + 0, sps->max_num_ref_frames - 1, 0); + if(mmco == 4) + xue(max_long_term_frame_idx_plus1, + current->mmco[i].max_long_term_frame_idx_plus1, + 0, sps->max_num_ref_frames, 0); + } + if(i == H264_MAX_MMCO_COUNT) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Too many " + "memory management control operations.\n"); + return AVERROR_INVALIDDATA; + } + } + } + + return 0; +} + +static int FUNC(slice_header)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSliceHeader *current) { + CodedBitstreamH264Context *h264 = ctx->priv_data; + const H264RawSPS *sps; + const H264RawPPS *pps; + int err; + int idr_pic_flag; + int slice_type_i, slice_type_p, slice_type_b; + int slice_type_si, slice_type_sp; + + HEADER("Slice Header"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, + 1 << H264_NAL_SLICE | + 1 << H264_NAL_IDR_SLICE | + 1 << H264_NAL_AUXILIARY_SLICE)); + + if(current->nal_unit_header.nal_unit_type == H264_NAL_AUXILIARY_SLICE) { + if(!h264->last_slice_nal_unit_type) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Auxiliary slice " + "is not decodable without the main picture " + "in the same access unit.\n"); + return AVERROR_INVALIDDATA; + } + idr_pic_flag = h264->last_slice_nal_unit_type == H264_NAL_IDR_SLICE; + } + else { + idr_pic_flag = current->nal_unit_header.nal_unit_type == H264_NAL_IDR_SLICE; + } + + ue(first_mb_in_slice, 0, H264_MAX_MB_PIC_SIZE - 1); + ue(slice_type, 0, 9); + + slice_type_i = current->slice_type % 5 == 2; + slice_type_p = current->slice_type % 5 == 0; + slice_type_b = current->slice_type % 5 == 1; + slice_type_si = current->slice_type % 5 == 4; + slice_type_sp = current->slice_type % 5 == 3; + + if(idr_pic_flag && !(slice_type_i || slice_type_si)) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid slice type %d " + "for IDR picture.\n", + current->slice_type); + return AVERROR_INVALIDDATA; + } + + ue(pic_parameter_set_id, 0, 255); + + pps = h264->pps[current->pic_parameter_set_id]; + if(!pps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "PPS id %d not available.\n", + current->pic_parameter_set_id); + return AVERROR_INVALIDDATA; + } + h264->active_pps = pps; + + sps = h264->sps[pps->seq_parameter_set_id]; + if(!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n", + pps->seq_parameter_set_id); + return AVERROR_INVALIDDATA; + } + h264->active_sps = sps; + + if(sps->separate_colour_plane_flag) + u(2, colour_plane_id, 0, 2); + + ub(sps->log2_max_frame_num_minus4 + 4, frame_num); + + if(!sps->frame_mbs_only_flag) { + flag(field_pic_flag); + if(current->field_pic_flag) + flag(bottom_field_flag); + else + infer(bottom_field_flag, 0); + } + else { + infer(field_pic_flag, 0); + infer(bottom_field_flag, 0); + } + + if(idr_pic_flag) + ue(idr_pic_id, 0, 65535); + + if(sps->pic_order_cnt_type == 0) { + ub(sps->log2_max_pic_order_cnt_lsb_minus4 + 4, pic_order_cnt_lsb); + if(pps->bottom_field_pic_order_in_frame_present_flag && + !current->field_pic_flag) + se(delta_pic_order_cnt_bottom, INT32_MIN + 1, INT32_MAX); + } + else if(sps->pic_order_cnt_type == 1) { + if(!sps->delta_pic_order_always_zero_flag) { + se(delta_pic_order_cnt[0], INT32_MIN + 1, INT32_MAX); + if(pps->bottom_field_pic_order_in_frame_present_flag && + !current->field_pic_flag) + se(delta_pic_order_cnt[1], INT32_MIN + 1, INT32_MAX); + else + infer(delta_pic_order_cnt[1], 0); + } + else { + infer(delta_pic_order_cnt[0], 0); + infer(delta_pic_order_cnt[1], 0); + } + } + + if(pps->redundant_pic_cnt_present_flag) + ue(redundant_pic_cnt, 0, 127); + else + infer(redundant_pic_cnt, 0); + + if(current->nal_unit_header.nal_unit_type != H264_NAL_AUXILIARY_SLICE && !current->redundant_pic_cnt) + h264->last_slice_nal_unit_type = + current->nal_unit_header.nal_unit_type; + + if(slice_type_b) + flag(direct_spatial_mv_pred_flag); + + if(slice_type_p || slice_type_sp || slice_type_b) { + flag(num_ref_idx_active_override_flag); + if(current->num_ref_idx_active_override_flag) { + ue(num_ref_idx_l0_active_minus1, 0, 31); + if(slice_type_b) + ue(num_ref_idx_l1_active_minus1, 0, 31); + } + else { + infer(num_ref_idx_l0_active_minus1, + pps->num_ref_idx_l0_default_active_minus1); + infer(num_ref_idx_l1_active_minus1, + pps->num_ref_idx_l1_default_active_minus1); + } + } + + if(current->nal_unit_header.nal_unit_type == 20 || + current->nal_unit_header.nal_unit_type == 21) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "MVC / 3DAVC not supported.\n"); + return AVERROR_PATCHWELCOME; + } + else { + CHECK(FUNC(ref_pic_list_modification)(ctx, rw, current)); + } + + if((pps->weighted_pred_flag && (slice_type_p || slice_type_sp)) || + (pps->weighted_bipred_idc == 1 && slice_type_b)) { + CHECK(FUNC(pred_weight_table)(ctx, rw, current)); + } + + if(current->nal_unit_header.nal_ref_idc != 0) { + CHECK(FUNC(dec_ref_pic_marking)(ctx, rw, current, idr_pic_flag)); + } + + if(pps->entropy_coding_mode_flag && + !slice_type_i && !slice_type_si) { + ue(cabac_init_idc, 0, 2); + } + + se(slice_qp_delta, -51 - 6 * sps->bit_depth_luma_minus8, + +51 + 6 * sps->bit_depth_luma_minus8); + if(slice_type_sp || slice_type_si) { + if(slice_type_sp) + flag(sp_for_switch_flag); + se(slice_qs_delta, -51, +51); + } + + if(pps->deblocking_filter_control_present_flag) { + ue(disable_deblocking_filter_idc, 0, 2); + if(current->disable_deblocking_filter_idc != 1) { + se(slice_alpha_c0_offset_div2, -6, +6); + se(slice_beta_offset_div2, -6, +6); + } + else { + infer(slice_alpha_c0_offset_div2, 0); + infer(slice_beta_offset_div2, 0); + } + } + else { + infer(disable_deblocking_filter_idc, 0); + infer(slice_alpha_c0_offset_div2, 0); + infer(slice_beta_offset_div2, 0); + } + + if(pps->num_slice_groups_minus1 > 0 && + pps->slice_group_map_type >= 3 && + pps->slice_group_map_type <= 5) { + unsigned int pic_size, max, bits; + + pic_size = (sps->pic_width_in_mbs_minus1 + 1) * + (sps->pic_height_in_map_units_minus1 + 1); + max = (pic_size + pps->slice_group_change_rate_minus1) / + (pps->slice_group_change_rate_minus1 + 1); + bits = av_ceil_log2(max + 1); + + u(bits, slice_group_change_cycle, 0, max); + } + + if(pps->entropy_coding_mode_flag) { + while(byte_alignment(rw)) + fixed(1, cabac_alignment_one_bit, 1); + } + + return 0; +} + +static int FUNC(filler)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawFiller *current) { + int err; + + HEADER("Filler Data"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, + 1 << H264_NAL_FILLER_DATA)); + +#ifdef READ + while(show_bits(rw, 8) == 0xff) { + fixed(8, ff_byte, 0xff); + ++current->filler_size; + } +#else + { + uint32_t i; + for(i = 0; i < current->filler_size; i++) + fixed(8, ff_byte, 0xff); + } +#endif + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(end_of_sequence)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawNALUnitHeader *current) { + HEADER("End of Sequence"); + + return FUNC(nal_unit_header)(ctx, rw, current, + 1 << H264_NAL_END_SEQUENCE); +} + +static int FUNC(end_of_stream)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawNALUnitHeader *current) { + HEADER("End of Stream"); + + return FUNC(nal_unit_header)(ctx, rw, current, + 1 << H264_NAL_END_STREAM); +} diff --git a/third-party/cbs/cbs_h265_syntax_template.c b/third-party/cbs/cbs_h265_syntax_template.c new file mode 100644 index 00000000..7ceefc6f --- /dev/null +++ b/third-party/cbs/cbs_h265_syntax_template.c @@ -0,0 +1,2038 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +static int FUNC(rbsp_trailing_bits)(CodedBitstreamContext *ctx, RWContext *rw) { + int err; + + fixed(1, rbsp_stop_one_bit, 1); + while(byte_alignment(rw) != 0) + fixed(1, rbsp_alignment_zero_bit, 0); + + return 0; +} + +static int FUNC(nal_unit_header)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawNALUnitHeader *current, + int expected_nal_unit_type) { + int err; + + fixed(1, forbidden_zero_bit, 0); + + if(expected_nal_unit_type >= 0) + u(6, nal_unit_type, expected_nal_unit_type, + expected_nal_unit_type); + else + ub(6, nal_unit_type); + + u(6, nuh_layer_id, 0, 62); + u(3, nuh_temporal_id_plus1, 1, 7); + + return 0; +} + +static int FUNC(byte_alignment)(CodedBitstreamContext *ctx, RWContext *rw) { + int err; + + fixed(1, alignment_bit_equal_to_one, 1); + while(byte_alignment(rw) != 0) + fixed(1, alignment_bit_equal_to_zero, 0); + + return 0; +} + +static int FUNC(extension_data)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawExtensionData *current) { + int err; + size_t k; +#ifdef READ + GetBitContext start; + uint8_t bit; + start = *rw; + for(k = 0; cbs_h2645_read_more_rbsp_data(rw); k++) + skip_bits(rw, 1); + current->bit_length = k; + if(k > 0) { + *rw = start; + allocate(current->data, (current->bit_length + 7) / 8); + for(k = 0; k < current->bit_length; k++) { + xu(1, extension_data, bit, 0, 1, 0); + current->data[k / 8] |= bit << (7 - k % 8); + } + } +#else + for(k = 0; k < current->bit_length; k++) + xu(1, extension_data, current->data[k / 8] >> (7 - k % 8) & 1, 0, 1, 0); +#endif + return 0; +} + +static int FUNC(profile_tier_level)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawProfileTierLevel *current, + int profile_present_flag, + int max_num_sub_layers_minus1) { + int err, i, j; + + if(profile_present_flag) { + u(2, general_profile_space, 0, 0); + flag(general_tier_flag); + ub(5, general_profile_idc); + + for(j = 0; j < 32; j++) + flags(general_profile_compatibility_flag[j], 1, j); + + flag(general_progressive_source_flag); + flag(general_interlaced_source_flag); + flag(general_non_packed_constraint_flag); + flag(general_frame_only_constraint_flag); + +#define profile_compatible(x) (current->general_profile_idc == (x) || \ + current->general_profile_compatibility_flag[x]) + if(profile_compatible(4) || profile_compatible(5) || + profile_compatible(6) || profile_compatible(7) || + profile_compatible(8) || profile_compatible(9) || + profile_compatible(10)) { + flag(general_max_12bit_constraint_flag); + flag(general_max_10bit_constraint_flag); + flag(general_max_8bit_constraint_flag); + flag(general_max_422chroma_constraint_flag); + flag(general_max_420chroma_constraint_flag); + flag(general_max_monochrome_constraint_flag); + flag(general_intra_constraint_flag); + flag(general_one_picture_only_constraint_flag); + flag(general_lower_bit_rate_constraint_flag); + + if(profile_compatible(5) || profile_compatible(9) || + profile_compatible(10)) { + flag(general_max_14bit_constraint_flag); + fixed(24, general_reserved_zero_33bits, 0); + fixed(9, general_reserved_zero_33bits, 0); + } + else { + fixed(24, general_reserved_zero_34bits, 0); + fixed(10, general_reserved_zero_34bits, 0); + } + } + else if(profile_compatible(2)) { + fixed(7, general_reserved_zero_7bits, 0); + flag(general_one_picture_only_constraint_flag); + fixed(24, general_reserved_zero_35bits, 0); + fixed(11, general_reserved_zero_35bits, 0); + } + else { + fixed(24, general_reserved_zero_43bits, 0); + fixed(19, general_reserved_zero_43bits, 0); + } + + if(profile_compatible(1) || profile_compatible(2) || + profile_compatible(3) || profile_compatible(4) || + profile_compatible(5) || profile_compatible(9)) { + flag(general_inbld_flag); + } + else { + fixed(1, general_reserved_zero_bit, 0); + } +#undef profile_compatible + } + + ub(8, general_level_idc); + + for(i = 0; i < max_num_sub_layers_minus1; i++) { + flags(sub_layer_profile_present_flag[i], 1, i); + flags(sub_layer_level_present_flag[i], 1, i); + } + + if(max_num_sub_layers_minus1 > 0) { + for(i = max_num_sub_layers_minus1; i < 8; i++) + fixed(2, reserved_zero_2bits, 0); + } + + for(i = 0; i < max_num_sub_layers_minus1; i++) { + if(current->sub_layer_profile_present_flag[i]) { + us(2, sub_layer_profile_space[i], 0, 0, 1, i); + flags(sub_layer_tier_flag[i], 1, i); + ubs(5, sub_layer_profile_idc[i], 1, i); + + for(j = 0; j < 32; j++) + flags(sub_layer_profile_compatibility_flag[i][j], 2, i, j); + + flags(sub_layer_progressive_source_flag[i], 1, i); + flags(sub_layer_interlaced_source_flag[i], 1, i); + flags(sub_layer_non_packed_constraint_flag[i], 1, i); + flags(sub_layer_frame_only_constraint_flag[i], 1, i); + +#define profile_compatible(x) (current->sub_layer_profile_idc[i] == (x) || \ + current->sub_layer_profile_compatibility_flag[i][x]) + if(profile_compatible(4) || profile_compatible(5) || + profile_compatible(6) || profile_compatible(7) || + profile_compatible(8) || profile_compatible(9) || + profile_compatible(10)) { + flags(sub_layer_max_12bit_constraint_flag[i], 1, i); + flags(sub_layer_max_10bit_constraint_flag[i], 1, i); + flags(sub_layer_max_8bit_constraint_flag[i], 1, i); + flags(sub_layer_max_422chroma_constraint_flag[i], 1, i); + flags(sub_layer_max_420chroma_constraint_flag[i], 1, i); + flags(sub_layer_max_monochrome_constraint_flag[i], 1, i); + flags(sub_layer_intra_constraint_flag[i], 1, i); + flags(sub_layer_one_picture_only_constraint_flag[i], 1, i); + flags(sub_layer_lower_bit_rate_constraint_flag[i], 1, i); + + if(profile_compatible(5)) { + flags(sub_layer_max_14bit_constraint_flag[i], 1, i); + fixed(24, sub_layer_reserved_zero_33bits, 0); + fixed(9, sub_layer_reserved_zero_33bits, 0); + } + else { + fixed(24, sub_layer_reserved_zero_34bits, 0); + fixed(10, sub_layer_reserved_zero_34bits, 0); + } + } + else if(profile_compatible(2)) { + fixed(7, sub_layer_reserved_zero_7bits, 0); + flags(sub_layer_one_picture_only_constraint_flag[i], 1, i); + fixed(24, sub_layer_reserved_zero_43bits, 0); + fixed(11, sub_layer_reserved_zero_43bits, 0); + } + else { + fixed(24, sub_layer_reserved_zero_43bits, 0); + fixed(19, sub_layer_reserved_zero_43bits, 0); + } + + if(profile_compatible(1) || profile_compatible(2) || + profile_compatible(3) || profile_compatible(4) || + profile_compatible(5) || profile_compatible(9)) { + flags(sub_layer_inbld_flag[i], 1, i); + } + else { + fixed(1, sub_layer_reserved_zero_bit, 0); + } +#undef profile_compatible + } + if(current->sub_layer_level_present_flag[i]) + ubs(8, sub_layer_level_idc[i], 1, i); + } + + return 0; +} + +static int FUNC(sub_layer_hrd_parameters)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawHRDParameters *hrd, + int nal, int sub_layer_id) { + H265RawSubLayerHRDParameters *current; + int err, i; + + if(nal) + current = &hrd->nal_sub_layer_hrd_parameters[sub_layer_id]; + else + current = &hrd->vcl_sub_layer_hrd_parameters[sub_layer_id]; + + for(i = 0; i <= hrd->cpb_cnt_minus1[sub_layer_id]; i++) { + ues(bit_rate_value_minus1[i], 0, UINT32_MAX - 1, 1, i); + ues(cpb_size_value_minus1[i], 0, UINT32_MAX - 1, 1, i); + if(hrd->sub_pic_hrd_params_present_flag) { + ues(cpb_size_du_value_minus1[i], 0, UINT32_MAX - 1, 1, i); + ues(bit_rate_du_value_minus1[i], 0, UINT32_MAX - 1, 1, i); + } + flags(cbr_flag[i], 1, i); + } + + return 0; +} + +static int FUNC(hrd_parameters)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawHRDParameters *current, int common_inf_present_flag, + int max_num_sub_layers_minus1) { + int err, i; + + if(common_inf_present_flag) { + flag(nal_hrd_parameters_present_flag); + flag(vcl_hrd_parameters_present_flag); + + if(current->nal_hrd_parameters_present_flag || + current->vcl_hrd_parameters_present_flag) { + flag(sub_pic_hrd_params_present_flag); + if(current->sub_pic_hrd_params_present_flag) { + ub(8, tick_divisor_minus2); + ub(5, du_cpb_removal_delay_increment_length_minus1); + flag(sub_pic_cpb_params_in_pic_timing_sei_flag); + ub(5, dpb_output_delay_du_length_minus1); + } + + ub(4, bit_rate_scale); + ub(4, cpb_size_scale); + if(current->sub_pic_hrd_params_present_flag) + ub(4, cpb_size_du_scale); + + ub(5, initial_cpb_removal_delay_length_minus1); + ub(5, au_cpb_removal_delay_length_minus1); + ub(5, dpb_output_delay_length_minus1); + } + else { + infer(sub_pic_hrd_params_present_flag, 0); + + infer(initial_cpb_removal_delay_length_minus1, 23); + infer(au_cpb_removal_delay_length_minus1, 23); + infer(dpb_output_delay_length_minus1, 23); + } + } + + for(i = 0; i <= max_num_sub_layers_minus1; i++) { + flags(fixed_pic_rate_general_flag[i], 1, i); + + if(!current->fixed_pic_rate_general_flag[i]) + flags(fixed_pic_rate_within_cvs_flag[i], 1, i); + else + infer(fixed_pic_rate_within_cvs_flag[i], 1); + + if(current->fixed_pic_rate_within_cvs_flag[i]) { + ues(elemental_duration_in_tc_minus1[i], 0, 2047, 1, i); + infer(low_delay_hrd_flag[i], 0); + } + else + flags(low_delay_hrd_flag[i], 1, i); + + if(!current->low_delay_hrd_flag[i]) + ues(cpb_cnt_minus1[i], 0, 31, 1, i); + else + infer(cpb_cnt_minus1[i], 0); + + if(current->nal_hrd_parameters_present_flag) + CHECK(FUNC(sub_layer_hrd_parameters)(ctx, rw, current, 0, i)); + if(current->vcl_hrd_parameters_present_flag) + CHECK(FUNC(sub_layer_hrd_parameters)(ctx, rw, current, 1, i)); + } + + return 0; +} + +static int FUNC(vui_parameters)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawVUI *current, const H265RawSPS *sps) { + int err; + + flag(aspect_ratio_info_present_flag); + if(current->aspect_ratio_info_present_flag) { + ub(8, aspect_ratio_idc); + if(current->aspect_ratio_idc == 255) { + ub(16, sar_width); + ub(16, sar_height); + } + } + else { + infer(aspect_ratio_idc, 0); + } + + flag(overscan_info_present_flag); + if(current->overscan_info_present_flag) + flag(overscan_appropriate_flag); + + flag(video_signal_type_present_flag); + if(current->video_signal_type_present_flag) { + ub(3, video_format); + flag(video_full_range_flag); + flag(colour_description_present_flag); + if(current->colour_description_present_flag) { + ub(8, colour_primaries); + ub(8, transfer_characteristics); + ub(8, matrix_coefficients); + } + else { + infer(colour_primaries, 2); + infer(transfer_characteristics, 2); + infer(matrix_coefficients, 2); + } + } + else { + infer(video_format, 5); + infer(video_full_range_flag, 0); + infer(colour_primaries, 2); + infer(transfer_characteristics, 2); + infer(matrix_coefficients, 2); + } + + flag(chroma_loc_info_present_flag); + if(current->chroma_loc_info_present_flag) { + ue(chroma_sample_loc_type_top_field, 0, 5); + ue(chroma_sample_loc_type_bottom_field, 0, 5); + } + else { + infer(chroma_sample_loc_type_top_field, 0); + infer(chroma_sample_loc_type_bottom_field, 0); + } + + flag(neutral_chroma_indication_flag); + flag(field_seq_flag); + flag(frame_field_info_present_flag); + + flag(default_display_window_flag); + if(current->default_display_window_flag) { + ue(def_disp_win_left_offset, 0, 16384); + ue(def_disp_win_right_offset, 0, 16384); + ue(def_disp_win_top_offset, 0, 16384); + ue(def_disp_win_bottom_offset, 0, 16384); + } + + flag(vui_timing_info_present_flag); + if(current->vui_timing_info_present_flag) { + u(32, vui_num_units_in_tick, 1, UINT32_MAX); + u(32, vui_time_scale, 1, UINT32_MAX); + flag(vui_poc_proportional_to_timing_flag); + if(current->vui_poc_proportional_to_timing_flag) + ue(vui_num_ticks_poc_diff_one_minus1, 0, UINT32_MAX - 1); + + flag(vui_hrd_parameters_present_flag); + if(current->vui_hrd_parameters_present_flag) { + CHECK(FUNC(hrd_parameters)(ctx, rw, ¤t->hrd_parameters, + 1, sps->sps_max_sub_layers_minus1)); + } + } + + flag(bitstream_restriction_flag); + if(current->bitstream_restriction_flag) { + flag(tiles_fixed_structure_flag); + flag(motion_vectors_over_pic_boundaries_flag); + flag(restricted_ref_pic_lists_flag); + ue(min_spatial_segmentation_idc, 0, 4095); + ue(max_bytes_per_pic_denom, 0, 16); + ue(max_bits_per_min_cu_denom, 0, 16); + ue(log2_max_mv_length_horizontal, 0, 16); + ue(log2_max_mv_length_vertical, 0, 16); + } + else { + infer(tiles_fixed_structure_flag, 0); + infer(motion_vectors_over_pic_boundaries_flag, 1); + infer(min_spatial_segmentation_idc, 0); + infer(max_bytes_per_pic_denom, 2); + infer(max_bits_per_min_cu_denom, 1); + infer(log2_max_mv_length_horizontal, 15); + infer(log2_max_mv_length_vertical, 15); + } + + return 0; +} + +static int FUNC(vps)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawVPS *current) { + int err, i, j; + + HEADER("Video Parameter Set"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, HEVC_NAL_VPS)); + + ub(4, vps_video_parameter_set_id); + + flag(vps_base_layer_internal_flag); + flag(vps_base_layer_available_flag); + u(6, vps_max_layers_minus1, 0, HEVC_MAX_LAYERS - 1); + u(3, vps_max_sub_layers_minus1, 0, HEVC_MAX_SUB_LAYERS - 1); + flag(vps_temporal_id_nesting_flag); + + if(current->vps_max_sub_layers_minus1 == 0 && + current->vps_temporal_id_nesting_flag != 1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid stream: " + "vps_temporal_id_nesting_flag must be 1 if " + "vps_max_sub_layers_minus1 is 0.\n"); + return AVERROR_INVALIDDATA; + } + + fixed(16, vps_reserved_0xffff_16bits, 0xffff); + + CHECK(FUNC(profile_tier_level)(ctx, rw, ¤t->profile_tier_level, + 1, current->vps_max_sub_layers_minus1)); + + flag(vps_sub_layer_ordering_info_present_flag); + for(i = (current->vps_sub_layer_ordering_info_present_flag ? + 0 : + current->vps_max_sub_layers_minus1); + i <= current->vps_max_sub_layers_minus1; i++) { + ues(vps_max_dec_pic_buffering_minus1[i], + 0, HEVC_MAX_DPB_SIZE - 1, 1, i); + ues(vps_max_num_reorder_pics[i], + 0, current->vps_max_dec_pic_buffering_minus1[i], 1, i); + ues(vps_max_latency_increase_plus1[i], + 0, UINT32_MAX - 1, 1, i); + } + if(!current->vps_sub_layer_ordering_info_present_flag) { + for(i = 0; i < current->vps_max_sub_layers_minus1; i++) { + infer(vps_max_dec_pic_buffering_minus1[i], + current->vps_max_dec_pic_buffering_minus1[current->vps_max_sub_layers_minus1]); + infer(vps_max_num_reorder_pics[i], + current->vps_max_num_reorder_pics[current->vps_max_sub_layers_minus1]); + infer(vps_max_latency_increase_plus1[i], + current->vps_max_latency_increase_plus1[current->vps_max_sub_layers_minus1]); + } + } + + u(6, vps_max_layer_id, 0, HEVC_MAX_LAYERS - 1); + ue(vps_num_layer_sets_minus1, 0, HEVC_MAX_LAYER_SETS - 1); + for(i = 1; i <= current->vps_num_layer_sets_minus1; i++) { + for(j = 0; j <= current->vps_max_layer_id; j++) + flags(layer_id_included_flag[i][j], 2, i, j); + } + for(j = 0; j <= current->vps_max_layer_id; j++) + infer(layer_id_included_flag[0][j], j == 0); + + flag(vps_timing_info_present_flag); + if(current->vps_timing_info_present_flag) { + u(32, vps_num_units_in_tick, 1, UINT32_MAX); + u(32, vps_time_scale, 1, UINT32_MAX); + flag(vps_poc_proportional_to_timing_flag); + if(current->vps_poc_proportional_to_timing_flag) + ue(vps_num_ticks_poc_diff_one_minus1, 0, UINT32_MAX - 1); + ue(vps_num_hrd_parameters, 0, current->vps_num_layer_sets_minus1 + 1); + for(i = 0; i < current->vps_num_hrd_parameters; i++) { + ues(hrd_layer_set_idx[i], + current->vps_base_layer_internal_flag ? 0 : 1, + current->vps_num_layer_sets_minus1, 1, i); + if(i > 0) + flags(cprms_present_flag[i], 1, i); + else + infer(cprms_present_flag[0], 1); + + CHECK(FUNC(hrd_parameters)(ctx, rw, ¤t->hrd_parameters[i], + current->cprms_present_flag[i], + current->vps_max_sub_layers_minus1)); + } + } + + flag(vps_extension_flag); + if(current->vps_extension_flag) + CHECK(FUNC(extension_data)(ctx, rw, ¤t->extension_data)); + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(st_ref_pic_set)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSTRefPicSet *current, int st_rps_idx, + const H265RawSPS *sps) { + int err, i, j; + + if(st_rps_idx != 0) + flag(inter_ref_pic_set_prediction_flag); + else + infer(inter_ref_pic_set_prediction_flag, 0); + + if(current->inter_ref_pic_set_prediction_flag) { + unsigned int ref_rps_idx, num_delta_pocs, num_ref_pics; + const H265RawSTRefPicSet *ref; + int delta_rps, d_poc; + int ref_delta_poc_s0[HEVC_MAX_REFS], ref_delta_poc_s1[HEVC_MAX_REFS]; + int delta_poc_s0[HEVC_MAX_REFS], delta_poc_s1[HEVC_MAX_REFS]; + uint8_t used_by_curr_pic_s0[HEVC_MAX_REFS], + used_by_curr_pic_s1[HEVC_MAX_REFS]; + + if(st_rps_idx == sps->num_short_term_ref_pic_sets) + ue(delta_idx_minus1, 0, st_rps_idx - 1); + else + infer(delta_idx_minus1, 0); + + ref_rps_idx = st_rps_idx - (current->delta_idx_minus1 + 1); + ref = &sps->st_ref_pic_set[ref_rps_idx]; + num_delta_pocs = ref->num_negative_pics + ref->num_positive_pics; + av_assert0(num_delta_pocs < HEVC_MAX_DPB_SIZE); + + flag(delta_rps_sign); + ue(abs_delta_rps_minus1, 0, INT16_MAX); + delta_rps = (1 - 2 * current->delta_rps_sign) * + (current->abs_delta_rps_minus1 + 1); + + num_ref_pics = 0; + for(j = 0; j <= num_delta_pocs; j++) { + flags(used_by_curr_pic_flag[j], 1, j); + if(!current->used_by_curr_pic_flag[j]) + flags(use_delta_flag[j], 1, j); + else + infer(use_delta_flag[j], 1); + if(current->use_delta_flag[j]) + ++num_ref_pics; + } + if(num_ref_pics >= HEVC_MAX_DPB_SIZE) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid stream: " + "short-term ref pic set %d " + "contains too many pictures.\n", + st_rps_idx); + return AVERROR_INVALIDDATA; + } + + // Since the stored form of an RPS here is actually the delta-step + // form used when inter_ref_pic_set_prediction_flag is not set, we + // need to reconstruct that here in order to be able to refer to + // the RPS later (which is required for parsing, because we don't + // even know what syntax elements appear without it). Therefore, + // this code takes the delta-step form of the reference set, turns + // it into the delta-array form, applies the prediction process of + // 7.4.8, converts the result back to the delta-step form, and + // stores that as the current set for future use. Note that the + // inferences here mean that writers using prediction will need + // to fill in the delta-step values correctly as well - since the + // whole RPS prediction process is somewhat overly sophisticated, + // this hopefully forms a useful check for them to ensure their + // predicted form actually matches what was intended rather than + // an onerous additional requirement. + + d_poc = 0; + for(i = 0; i < ref->num_negative_pics; i++) { + d_poc -= ref->delta_poc_s0_minus1[i] + 1; + ref_delta_poc_s0[i] = d_poc; + } + d_poc = 0; + for(i = 0; i < ref->num_positive_pics; i++) { + d_poc += ref->delta_poc_s1_minus1[i] + 1; + ref_delta_poc_s1[i] = d_poc; + } + + i = 0; + for(j = ref->num_positive_pics - 1; j >= 0; j--) { + d_poc = ref_delta_poc_s1[j] + delta_rps; + if(d_poc < 0 && current->use_delta_flag[ref->num_negative_pics + j]) { + delta_poc_s0[i] = d_poc; + used_by_curr_pic_s0[i++] = + current->used_by_curr_pic_flag[ref->num_negative_pics + j]; + } + } + if(delta_rps < 0 && current->use_delta_flag[num_delta_pocs]) { + delta_poc_s0[i] = delta_rps; + used_by_curr_pic_s0[i++] = + current->used_by_curr_pic_flag[num_delta_pocs]; + } + for(j = 0; j < ref->num_negative_pics; j++) { + d_poc = ref_delta_poc_s0[j] + delta_rps; + if(d_poc < 0 && current->use_delta_flag[j]) { + delta_poc_s0[i] = d_poc; + used_by_curr_pic_s0[i++] = current->used_by_curr_pic_flag[j]; + } + } + + infer(num_negative_pics, i); + for(i = 0; i < current->num_negative_pics; i++) { + infer(delta_poc_s0_minus1[i], + -(delta_poc_s0[i] - (i == 0 ? 0 : delta_poc_s0[i - 1])) - 1); + infer(used_by_curr_pic_s0_flag[i], used_by_curr_pic_s0[i]); + } + + i = 0; + for(j = ref->num_negative_pics - 1; j >= 0; j--) { + d_poc = ref_delta_poc_s0[j] + delta_rps; + if(d_poc > 0 && current->use_delta_flag[j]) { + delta_poc_s1[i] = d_poc; + used_by_curr_pic_s1[i++] = current->used_by_curr_pic_flag[j]; + } + } + if(delta_rps > 0 && current->use_delta_flag[num_delta_pocs]) { + delta_poc_s1[i] = delta_rps; + used_by_curr_pic_s1[i++] = + current->used_by_curr_pic_flag[num_delta_pocs]; + } + for(j = 0; j < ref->num_positive_pics; j++) { + d_poc = ref_delta_poc_s1[j] + delta_rps; + if(d_poc > 0 && current->use_delta_flag[ref->num_negative_pics + j]) { + delta_poc_s1[i] = d_poc; + used_by_curr_pic_s1[i++] = + current->used_by_curr_pic_flag[ref->num_negative_pics + j]; + } + } + + infer(num_positive_pics, i); + for(i = 0; i < current->num_positive_pics; i++) { + infer(delta_poc_s1_minus1[i], + delta_poc_s1[i] - (i == 0 ? 0 : delta_poc_s1[i - 1]) - 1); + infer(used_by_curr_pic_s1_flag[i], used_by_curr_pic_s1[i]); + } + } + else { + ue(num_negative_pics, 0, 15); + ue(num_positive_pics, 0, 15 - current->num_negative_pics); + + for(i = 0; i < current->num_negative_pics; i++) { + ues(delta_poc_s0_minus1[i], 0, INT16_MAX, 1, i); + flags(used_by_curr_pic_s0_flag[i], 1, i); + } + + for(i = 0; i < current->num_positive_pics; i++) { + ues(delta_poc_s1_minus1[i], 0, INT16_MAX, 1, i); + flags(used_by_curr_pic_s1_flag[i], 1, i); + } + } + + return 0; +} + +static int FUNC(scaling_list_data)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawScalingList *current) { + int sizeId, matrixId; + int err, n, i; + + for(sizeId = 0; sizeId < 4; sizeId++) { + for(matrixId = 0; matrixId < 6; matrixId += (sizeId == 3 ? 3 : 1)) { + flags(scaling_list_pred_mode_flag[sizeId][matrixId], + 2, sizeId, matrixId); + if(!current->scaling_list_pred_mode_flag[sizeId][matrixId]) { + ues(scaling_list_pred_matrix_id_delta[sizeId][matrixId], + 0, sizeId == 3 ? matrixId / 3 : matrixId, + 2, sizeId, matrixId); + } + else { + n = FFMIN(64, 1 << (4 + (sizeId << 1))); + if(sizeId > 1) { + ses(scaling_list_dc_coef_minus8[sizeId - 2][matrixId], -7, +247, + 2, sizeId - 2, matrixId); + } + for(i = 0; i < n; i++) { + ses(scaling_list_delta_coeff[sizeId][matrixId][i], + -128, +127, 3, sizeId, matrixId, i); + } + } + } + } + + return 0; +} + +static int FUNC(sps_range_extension)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSPS *current) { + int err; + + flag(transform_skip_rotation_enabled_flag); + flag(transform_skip_context_enabled_flag); + flag(implicit_rdpcm_enabled_flag); + flag(explicit_rdpcm_enabled_flag); + flag(extended_precision_processing_flag); + flag(intra_smoothing_disabled_flag); + flag(high_precision_offsets_enabled_flag); + flag(persistent_rice_adaptation_enabled_flag); + flag(cabac_bypass_alignment_enabled_flag); + + return 0; +} + +static int FUNC(sps_scc_extension)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSPS *current) { + int err, comp, i; + + flag(sps_curr_pic_ref_enabled_flag); + + flag(palette_mode_enabled_flag); + if(current->palette_mode_enabled_flag) { + ue(palette_max_size, 0, 64); + ue(delta_palette_max_predictor_size, 0, 128); + + flag(sps_palette_predictor_initializer_present_flag); + if(current->sps_palette_predictor_initializer_present_flag) { + ue(sps_num_palette_predictor_initializer_minus1, 0, 128); + for(comp = 0; comp < (current->chroma_format_idc ? 3 : 1); comp++) { + int bit_depth = comp == 0 ? current->bit_depth_luma_minus8 + 8 : current->bit_depth_chroma_minus8 + 8; + for(i = 0; i <= current->sps_num_palette_predictor_initializer_minus1; i++) + ubs(bit_depth, sps_palette_predictor_initializers[comp][i], 2, comp, i); + } + } + } + + u(2, motion_vector_resolution_control_idc, 0, 2); + flag(intra_boundary_filtering_disable_flag); + + return 0; +} + +static int FUNC(vui_parameters_default)(CodedBitstreamContext *ctx, + RWContext *rw, H265RawVUI *current, + H265RawSPS *sps) { + infer(aspect_ratio_idc, 0); + + infer(video_format, 5); + infer(video_full_range_flag, 0); + infer(colour_primaries, 2); + infer(transfer_characteristics, 2); + infer(matrix_coefficients, 2); + + infer(chroma_sample_loc_type_top_field, 0); + infer(chroma_sample_loc_type_bottom_field, 0); + + infer(tiles_fixed_structure_flag, 0); + infer(motion_vectors_over_pic_boundaries_flag, 1); + infer(min_spatial_segmentation_idc, 0); + infer(max_bytes_per_pic_denom, 2); + infer(max_bits_per_min_cu_denom, 1); + infer(log2_max_mv_length_horizontal, 15); + infer(log2_max_mv_length_vertical, 15); + + return 0; +} + +static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSPS *current) { + CodedBitstreamH265Context *h265 = ctx->priv_data; + const H265RawVPS *vps; + int err, i; + unsigned int min_cb_log2_size_y, ctb_log2_size_y, + min_cb_size_y, min_tb_log2_size_y; + + HEADER("Sequence Parameter Set"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, HEVC_NAL_SPS)); + + ub(4, sps_video_parameter_set_id); + h265->active_vps = vps = h265->vps[current->sps_video_parameter_set_id]; + + u(3, sps_max_sub_layers_minus1, 0, HEVC_MAX_SUB_LAYERS - 1); + flag(sps_temporal_id_nesting_flag); + if(vps) { + if(vps->vps_max_sub_layers_minus1 > current->sps_max_sub_layers_minus1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid stream: " + "sps_max_sub_layers_minus1 (%d) must be less than or equal to " + "vps_max_sub_layers_minus1 (%d).\n", + vps->vps_max_sub_layers_minus1, + current->sps_max_sub_layers_minus1); + return AVERROR_INVALIDDATA; + } + if(vps->vps_temporal_id_nesting_flag && + !current->sps_temporal_id_nesting_flag) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid stream: " + "sps_temporal_id_nesting_flag must be 1 if " + "vps_temporal_id_nesting_flag is 1.\n"); + return AVERROR_INVALIDDATA; + } + } + + CHECK(FUNC(profile_tier_level)(ctx, rw, ¤t->profile_tier_level, + 1, current->sps_max_sub_layers_minus1)); + + ue(sps_seq_parameter_set_id, 0, 15); + + ue(chroma_format_idc, 0, 3); + if(current->chroma_format_idc == 3) + flag(separate_colour_plane_flag); + else + infer(separate_colour_plane_flag, 0); + + ue(pic_width_in_luma_samples, 1, HEVC_MAX_WIDTH); + ue(pic_height_in_luma_samples, 1, HEVC_MAX_HEIGHT); + + flag(conformance_window_flag); + if(current->conformance_window_flag) { + ue(conf_win_left_offset, 0, current->pic_width_in_luma_samples); + ue(conf_win_right_offset, 0, current->pic_width_in_luma_samples); + ue(conf_win_top_offset, 0, current->pic_height_in_luma_samples); + ue(conf_win_bottom_offset, 0, current->pic_height_in_luma_samples); + } + else { + infer(conf_win_left_offset, 0); + infer(conf_win_right_offset, 0); + infer(conf_win_top_offset, 0); + infer(conf_win_bottom_offset, 0); + } + + ue(bit_depth_luma_minus8, 0, 8); + ue(bit_depth_chroma_minus8, 0, 8); + + ue(log2_max_pic_order_cnt_lsb_minus4, 0, 12); + + flag(sps_sub_layer_ordering_info_present_flag); + for(i = (current->sps_sub_layer_ordering_info_present_flag ? + 0 : + current->sps_max_sub_layers_minus1); + i <= current->sps_max_sub_layers_minus1; i++) { + ues(sps_max_dec_pic_buffering_minus1[i], + 0, HEVC_MAX_DPB_SIZE - 1, 1, i); + ues(sps_max_num_reorder_pics[i], + 0, current->sps_max_dec_pic_buffering_minus1[i], 1, i); + ues(sps_max_latency_increase_plus1[i], + 0, UINT32_MAX - 1, 1, i); + } + if(!current->sps_sub_layer_ordering_info_present_flag) { + for(i = 0; i < current->sps_max_sub_layers_minus1; i++) { + infer(sps_max_dec_pic_buffering_minus1[i], + current->sps_max_dec_pic_buffering_minus1[current->sps_max_sub_layers_minus1]); + infer(sps_max_num_reorder_pics[i], + current->sps_max_num_reorder_pics[current->sps_max_sub_layers_minus1]); + infer(sps_max_latency_increase_plus1[i], + current->sps_max_latency_increase_plus1[current->sps_max_sub_layers_minus1]); + } + } + + ue(log2_min_luma_coding_block_size_minus3, 0, 3); + min_cb_log2_size_y = current->log2_min_luma_coding_block_size_minus3 + 3; + + ue(log2_diff_max_min_luma_coding_block_size, 0, 3); + ctb_log2_size_y = min_cb_log2_size_y + + current->log2_diff_max_min_luma_coding_block_size; + + min_cb_size_y = 1 << min_cb_log2_size_y; + if(current->pic_width_in_luma_samples % min_cb_size_y || + current->pic_height_in_luma_samples % min_cb_size_y) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid dimensions: %ux%u not divisible " + "by MinCbSizeY = %u.\n", + current->pic_width_in_luma_samples, + current->pic_height_in_luma_samples, min_cb_size_y); + return AVERROR_INVALIDDATA; + } + + ue(log2_min_luma_transform_block_size_minus2, 0, min_cb_log2_size_y - 3); + min_tb_log2_size_y = current->log2_min_luma_transform_block_size_minus2 + 2; + + ue(log2_diff_max_min_luma_transform_block_size, + 0, FFMIN(ctb_log2_size_y, 5) - min_tb_log2_size_y); + + ue(max_transform_hierarchy_depth_inter, + 0, ctb_log2_size_y - min_tb_log2_size_y); + ue(max_transform_hierarchy_depth_intra, + 0, ctb_log2_size_y - min_tb_log2_size_y); + + flag(scaling_list_enabled_flag); + if(current->scaling_list_enabled_flag) { + flag(sps_scaling_list_data_present_flag); + if(current->sps_scaling_list_data_present_flag) + CHECK(FUNC(scaling_list_data)(ctx, rw, ¤t->scaling_list)); + } + else { + infer(sps_scaling_list_data_present_flag, 0); + } + + flag(amp_enabled_flag); + flag(sample_adaptive_offset_enabled_flag); + + flag(pcm_enabled_flag); + if(current->pcm_enabled_flag) { + u(4, pcm_sample_bit_depth_luma_minus1, + 0, current->bit_depth_luma_minus8 + 8 - 1); + u(4, pcm_sample_bit_depth_chroma_minus1, + 0, current->bit_depth_chroma_minus8 + 8 - 1); + + ue(log2_min_pcm_luma_coding_block_size_minus3, + FFMIN(min_cb_log2_size_y, 5) - 3, FFMIN(ctb_log2_size_y, 5) - 3); + ue(log2_diff_max_min_pcm_luma_coding_block_size, + 0, FFMIN(ctb_log2_size_y, 5) - (current->log2_min_pcm_luma_coding_block_size_minus3 + 3)); + + flag(pcm_loop_filter_disabled_flag); + } + + ue(num_short_term_ref_pic_sets, 0, HEVC_MAX_SHORT_TERM_REF_PIC_SETS); + for(i = 0; i < current->num_short_term_ref_pic_sets; i++) + CHECK(FUNC(st_ref_pic_set)(ctx, rw, ¤t->st_ref_pic_set[i], i, current)); + + flag(long_term_ref_pics_present_flag); + if(current->long_term_ref_pics_present_flag) { + ue(num_long_term_ref_pics_sps, 0, HEVC_MAX_LONG_TERM_REF_PICS); + for(i = 0; i < current->num_long_term_ref_pics_sps; i++) { + ubs(current->log2_max_pic_order_cnt_lsb_minus4 + 4, + lt_ref_pic_poc_lsb_sps[i], 1, i); + flags(used_by_curr_pic_lt_sps_flag[i], 1, i); + } + } + + flag(sps_temporal_mvp_enabled_flag); + flag(strong_intra_smoothing_enabled_flag); + + flag(vui_parameters_present_flag); + if(current->vui_parameters_present_flag) + CHECK(FUNC(vui_parameters)(ctx, rw, ¤t->vui, current)); + else + CHECK(FUNC(vui_parameters_default)(ctx, rw, ¤t->vui, current)); + + flag(sps_extension_present_flag); + if(current->sps_extension_present_flag) { + flag(sps_range_extension_flag); + flag(sps_multilayer_extension_flag); + flag(sps_3d_extension_flag); + flag(sps_scc_extension_flag); + ub(4, sps_extension_4bits); + } + + if(current->sps_range_extension_flag) + CHECK(FUNC(sps_range_extension)(ctx, rw, current)); + if(current->sps_multilayer_extension_flag) + return AVERROR_PATCHWELCOME; + if(current->sps_3d_extension_flag) + return AVERROR_PATCHWELCOME; + if(current->sps_scc_extension_flag) + CHECK(FUNC(sps_scc_extension)(ctx, rw, current)); + if(current->sps_extension_4bits) + CHECK(FUNC(extension_data)(ctx, rw, ¤t->extension_data)); + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(pps_range_extension)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawPPS *current) { + CodedBitstreamH265Context *h265 = ctx->priv_data; + const H265RawSPS *sps = h265->active_sps; + int err, i; + + if(current->transform_skip_enabled_flag) + ue(log2_max_transform_skip_block_size_minus2, 0, 3); + flag(cross_component_prediction_enabled_flag); + + flag(chroma_qp_offset_list_enabled_flag); + if(current->chroma_qp_offset_list_enabled_flag) { + ue(diff_cu_chroma_qp_offset_depth, + 0, sps->log2_diff_max_min_luma_coding_block_size); + ue(chroma_qp_offset_list_len_minus1, 0, 5); + for(i = 0; i <= current->chroma_qp_offset_list_len_minus1; i++) { + ses(cb_qp_offset_list[i], -12, +12, 1, i); + ses(cr_qp_offset_list[i], -12, +12, 1, i); + } + } + + ue(log2_sao_offset_scale_luma, 0, FFMAX(0, sps->bit_depth_luma_minus8 - 2)); + ue(log2_sao_offset_scale_chroma, 0, FFMAX(0, sps->bit_depth_chroma_minus8 - 2)); + + return 0; +} + +static int FUNC(pps_scc_extension)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawPPS *current) { + int err, comp, i; + + flag(pps_curr_pic_ref_enabled_flag); + + flag(residual_adaptive_colour_transform_enabled_flag); + if(current->residual_adaptive_colour_transform_enabled_flag) { + flag(pps_slice_act_qp_offsets_present_flag); + se(pps_act_y_qp_offset_plus5, -7, +17); + se(pps_act_cb_qp_offset_plus5, -7, +17); + se(pps_act_cr_qp_offset_plus3, -9, +15); + } + else { + infer(pps_slice_act_qp_offsets_present_flag, 0); + infer(pps_act_y_qp_offset_plus5, 0); + infer(pps_act_cb_qp_offset_plus5, 0); + infer(pps_act_cr_qp_offset_plus3, 0); + } + + flag(pps_palette_predictor_initializer_present_flag); + if(current->pps_palette_predictor_initializer_present_flag) { + ue(pps_num_palette_predictor_initializer, 0, 128); + if(current->pps_num_palette_predictor_initializer > 0) { + flag(monochrome_palette_flag); + ue(luma_bit_depth_entry_minus8, 0, 8); + if(!current->monochrome_palette_flag) + ue(chroma_bit_depth_entry_minus8, 0, 8); + for(comp = 0; comp < (current->monochrome_palette_flag ? 1 : 3); comp++) { + int bit_depth = comp == 0 ? current->luma_bit_depth_entry_minus8 + 8 : current->chroma_bit_depth_entry_minus8 + 8; + for(i = 0; i < current->pps_num_palette_predictor_initializer; i++) + ubs(bit_depth, pps_palette_predictor_initializers[comp][i], 2, comp, i); + } + } + } + + return 0; +} + +static int FUNC(pps)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawPPS *current) { + CodedBitstreamH265Context *h265 = ctx->priv_data; + const H265RawSPS *sps; + int err, i; + + HEADER("Picture Parameter Set"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, HEVC_NAL_PPS)); + + ue(pps_pic_parameter_set_id, 0, 63); + ue(pps_seq_parameter_set_id, 0, 15); + sps = h265->sps[current->pps_seq_parameter_set_id]; + if(!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n", + current->pps_seq_parameter_set_id); + return AVERROR_INVALIDDATA; + } + h265->active_sps = sps; + + flag(dependent_slice_segments_enabled_flag); + flag(output_flag_present_flag); + ub(3, num_extra_slice_header_bits); + flag(sign_data_hiding_enabled_flag); + flag(cabac_init_present_flag); + + ue(num_ref_idx_l0_default_active_minus1, 0, 14); + ue(num_ref_idx_l1_default_active_minus1, 0, 14); + + se(init_qp_minus26, -(26 + 6 * sps->bit_depth_luma_minus8), +25); + + flag(constrained_intra_pred_flag); + flag(transform_skip_enabled_flag); + flag(cu_qp_delta_enabled_flag); + if(current->cu_qp_delta_enabled_flag) + ue(diff_cu_qp_delta_depth, + 0, sps->log2_diff_max_min_luma_coding_block_size); + else + infer(diff_cu_qp_delta_depth, 0); + + se(pps_cb_qp_offset, -12, +12); + se(pps_cr_qp_offset, -12, +12); + flag(pps_slice_chroma_qp_offsets_present_flag); + + flag(weighted_pred_flag); + flag(weighted_bipred_flag); + + flag(transquant_bypass_enabled_flag); + flag(tiles_enabled_flag); + flag(entropy_coding_sync_enabled_flag); + + if(current->tiles_enabled_flag) { + ue(num_tile_columns_minus1, 0, HEVC_MAX_TILE_COLUMNS); + ue(num_tile_rows_minus1, 0, HEVC_MAX_TILE_ROWS); + flag(uniform_spacing_flag); + if(!current->uniform_spacing_flag) { + for(i = 0; i < current->num_tile_columns_minus1; i++) + ues(column_width_minus1[i], 0, sps->pic_width_in_luma_samples, 1, i); + for(i = 0; i < current->num_tile_rows_minus1; i++) + ues(row_height_minus1[i], 0, sps->pic_height_in_luma_samples, 1, i); + } + flag(loop_filter_across_tiles_enabled_flag); + } + else { + infer(num_tile_columns_minus1, 0); + infer(num_tile_rows_minus1, 0); + } + + flag(pps_loop_filter_across_slices_enabled_flag); + flag(deblocking_filter_control_present_flag); + if(current->deblocking_filter_control_present_flag) { + flag(deblocking_filter_override_enabled_flag); + flag(pps_deblocking_filter_disabled_flag); + if(!current->pps_deblocking_filter_disabled_flag) { + se(pps_beta_offset_div2, -6, +6); + se(pps_tc_offset_div2, -6, +6); + } + else { + infer(pps_beta_offset_div2, 0); + infer(pps_tc_offset_div2, 0); + } + } + else { + infer(deblocking_filter_override_enabled_flag, 0); + infer(pps_deblocking_filter_disabled_flag, 0); + infer(pps_beta_offset_div2, 0); + infer(pps_tc_offset_div2, 0); + } + + flag(pps_scaling_list_data_present_flag); + if(current->pps_scaling_list_data_present_flag) + CHECK(FUNC(scaling_list_data)(ctx, rw, ¤t->scaling_list)); + + flag(lists_modification_present_flag); + + ue(log2_parallel_merge_level_minus2, + 0, (sps->log2_min_luma_coding_block_size_minus3 + 3 + sps->log2_diff_max_min_luma_coding_block_size - 2)); + + flag(slice_segment_header_extension_present_flag); + + flag(pps_extension_present_flag); + if(current->pps_extension_present_flag) { + flag(pps_range_extension_flag); + flag(pps_multilayer_extension_flag); + flag(pps_3d_extension_flag); + flag(pps_scc_extension_flag); + ub(4, pps_extension_4bits); + } + if(current->pps_range_extension_flag) + CHECK(FUNC(pps_range_extension)(ctx, rw, current)); + if(current->pps_multilayer_extension_flag) + return AVERROR_PATCHWELCOME; + if(current->pps_3d_extension_flag) + return AVERROR_PATCHWELCOME; + if(current->pps_scc_extension_flag) + CHECK(FUNC(pps_scc_extension)(ctx, rw, current)); + if(current->pps_extension_4bits) + CHECK(FUNC(extension_data)(ctx, rw, ¤t->extension_data)); + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(aud)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawAUD *current) { + int err; + + HEADER("Access Unit Delimiter"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, HEVC_NAL_AUD)); + + u(3, pic_type, 0, 2); + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(ref_pic_lists_modification)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSliceHeader *current, + unsigned int num_pic_total_curr) { + unsigned int entry_size; + int err, i; + + entry_size = av_log2(num_pic_total_curr - 1) + 1; + + flag(ref_pic_list_modification_flag_l0); + if(current->ref_pic_list_modification_flag_l0) { + for(i = 0; i <= current->num_ref_idx_l0_active_minus1; i++) + us(entry_size, list_entry_l0[i], 0, num_pic_total_curr - 1, 1, i); + } + + if(current->slice_type == HEVC_SLICE_B) { + flag(ref_pic_list_modification_flag_l1); + if(current->ref_pic_list_modification_flag_l1) { + for(i = 0; i <= current->num_ref_idx_l1_active_minus1; i++) + us(entry_size, list_entry_l1[i], 0, num_pic_total_curr - 1, 1, i); + } + } + + return 0; +} + +static int FUNC(pred_weight_table)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSliceHeader *current) { + CodedBitstreamH265Context *h265 = ctx->priv_data; + const H265RawSPS *sps = h265->active_sps; + int err, i, j; + int chroma = !sps->separate_colour_plane_flag && + sps->chroma_format_idc != 0; + + ue(luma_log2_weight_denom, 0, 7); + if(chroma) + se(delta_chroma_log2_weight_denom, -7, 7); + else + infer(delta_chroma_log2_weight_denom, 0); + + for(i = 0; i <= current->num_ref_idx_l0_active_minus1; i++) { + if(1 /* is not same POC and same layer_id */) + flags(luma_weight_l0_flag[i], 1, i); + else + infer(luma_weight_l0_flag[i], 0); + } + if(chroma) { + for(i = 0; i <= current->num_ref_idx_l0_active_minus1; i++) { + if(1 /* is not same POC and same layer_id */) + flags(chroma_weight_l0_flag[i], 1, i); + else + infer(chroma_weight_l0_flag[i], 0); + } + } + + for(i = 0; i <= current->num_ref_idx_l0_active_minus1; i++) { + if(current->luma_weight_l0_flag[i]) { + ses(delta_luma_weight_l0[i], -128, +127, 1, i); + ses(luma_offset_l0[i], + -(1 << (sps->bit_depth_luma_minus8 + 8 - 1)), + ((1 << (sps->bit_depth_luma_minus8 + 8 - 1)) - 1), 1, i); + } + else { + infer(delta_luma_weight_l0[i], 0); + infer(luma_offset_l0[i], 0); + } + if(current->chroma_weight_l0_flag[i]) { + for(j = 0; j < 2; j++) { + ses(delta_chroma_weight_l0[i][j], -128, +127, 2, i, j); + ses(chroma_offset_l0[i][j], + -(4 << (sps->bit_depth_chroma_minus8 + 8 - 1)), + ((4 << (sps->bit_depth_chroma_minus8 + 8 - 1)) - 1), 2, i, j); + } + } + else { + for(j = 0; j < 2; j++) { + infer(delta_chroma_weight_l0[i][j], 0); + infer(chroma_offset_l0[i][j], 0); + } + } + } + + if(current->slice_type == HEVC_SLICE_B) { + for(i = 0; i <= current->num_ref_idx_l1_active_minus1; i++) { + if(1 /* RefPicList1[i] is not CurrPic, nor is it in a different layer */) + flags(luma_weight_l1_flag[i], 1, i); + else + infer(luma_weight_l1_flag[i], 0); + } + if(chroma) { + for(i = 0; i <= current->num_ref_idx_l1_active_minus1; i++) { + if(1 /* RefPicList1[i] is not CurrPic, nor is it in a different layer */) + flags(chroma_weight_l1_flag[i], 1, i); + else + infer(chroma_weight_l1_flag[i], 0); + } + } + + for(i = 0; i <= current->num_ref_idx_l1_active_minus1; i++) { + if(current->luma_weight_l1_flag[i]) { + ses(delta_luma_weight_l1[i], -128, +127, 1, i); + ses(luma_offset_l1[i], + -(1 << (sps->bit_depth_luma_minus8 + 8 - 1)), + ((1 << (sps->bit_depth_luma_minus8 + 8 - 1)) - 1), 1, i); + } + else { + infer(delta_luma_weight_l1[i], 0); + infer(luma_offset_l1[i], 0); + } + if(current->chroma_weight_l1_flag[i]) { + for(j = 0; j < 2; j++) { + ses(delta_chroma_weight_l1[i][j], -128, +127, 2, i, j); + ses(chroma_offset_l1[i][j], + -(4 << (sps->bit_depth_chroma_minus8 + 8 - 1)), + ((4 << (sps->bit_depth_chroma_minus8 + 8 - 1)) - 1), 2, i, j); + } + } + else { + for(j = 0; j < 2; j++) { + infer(delta_chroma_weight_l1[i][j], 0); + infer(chroma_offset_l1[i][j], 0); + } + } + } + } + + return 0; +} + +static int FUNC(slice_segment_header)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSliceHeader *current) { + CodedBitstreamH265Context *h265 = ctx->priv_data; + const H265RawSPS *sps; + const H265RawPPS *pps; + unsigned int min_cb_log2_size_y, ctb_log2_size_y, ctb_size_y; + unsigned int pic_width_in_ctbs_y, pic_height_in_ctbs_y, pic_size_in_ctbs_y; + unsigned int num_pic_total_curr = 0; + int err, i; + + HEADER("Slice Segment Header"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, -1)); + + flag(first_slice_segment_in_pic_flag); + + if(current->nal_unit_header.nal_unit_type >= HEVC_NAL_BLA_W_LP && + current->nal_unit_header.nal_unit_type <= HEVC_NAL_RSV_IRAP_VCL23) + flag(no_output_of_prior_pics_flag); + + ue(slice_pic_parameter_set_id, 0, 63); + + pps = h265->pps[current->slice_pic_parameter_set_id]; + if(!pps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "PPS id %d not available.\n", + current->slice_pic_parameter_set_id); + return AVERROR_INVALIDDATA; + } + h265->active_pps = pps; + + sps = h265->sps[pps->pps_seq_parameter_set_id]; + if(!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n", + pps->pps_seq_parameter_set_id); + return AVERROR_INVALIDDATA; + } + h265->active_sps = sps; + + min_cb_log2_size_y = sps->log2_min_luma_coding_block_size_minus3 + 3; + ctb_log2_size_y = min_cb_log2_size_y + sps->log2_diff_max_min_luma_coding_block_size; + ctb_size_y = 1 << ctb_log2_size_y; + pic_width_in_ctbs_y = + (sps->pic_width_in_luma_samples + ctb_size_y - 1) / ctb_size_y; + pic_height_in_ctbs_y = + (sps->pic_height_in_luma_samples + ctb_size_y - 1) / ctb_size_y; + pic_size_in_ctbs_y = pic_width_in_ctbs_y * pic_height_in_ctbs_y; + + if(!current->first_slice_segment_in_pic_flag) { + unsigned int address_size = av_log2(pic_size_in_ctbs_y - 1) + 1; + if(pps->dependent_slice_segments_enabled_flag) + flag(dependent_slice_segment_flag); + else + infer(dependent_slice_segment_flag, 0); + u(address_size, slice_segment_address, 0, pic_size_in_ctbs_y - 1); + } + else { + infer(dependent_slice_segment_flag, 0); + } + + if(!current->dependent_slice_segment_flag) { + for(i = 0; i < pps->num_extra_slice_header_bits; i++) + flags(slice_reserved_flag[i], 1, i); + + ue(slice_type, 0, 2); + + if(pps->output_flag_present_flag) + flag(pic_output_flag); + + if(sps->separate_colour_plane_flag) + u(2, colour_plane_id, 0, 2); + + if(current->nal_unit_header.nal_unit_type != HEVC_NAL_IDR_W_RADL && + current->nal_unit_header.nal_unit_type != HEVC_NAL_IDR_N_LP) { + const H265RawSTRefPicSet *rps; + int dpb_slots_remaining; + + ub(sps->log2_max_pic_order_cnt_lsb_minus4 + 4, slice_pic_order_cnt_lsb); + + flag(short_term_ref_pic_set_sps_flag); + if(!current->short_term_ref_pic_set_sps_flag) { + CHECK(FUNC(st_ref_pic_set)(ctx, rw, ¤t->short_term_ref_pic_set, + sps->num_short_term_ref_pic_sets, sps)); + rps = ¤t->short_term_ref_pic_set; + } + else if(sps->num_short_term_ref_pic_sets > 1) { + unsigned int idx_size = av_log2(sps->num_short_term_ref_pic_sets - 1) + 1; + u(idx_size, short_term_ref_pic_set_idx, + 0, sps->num_short_term_ref_pic_sets - 1); + rps = &sps->st_ref_pic_set[current->short_term_ref_pic_set_idx]; + } + else { + infer(short_term_ref_pic_set_idx, 0); + rps = &sps->st_ref_pic_set[0]; + } + + dpb_slots_remaining = HEVC_MAX_DPB_SIZE - 1 - + rps->num_negative_pics - rps->num_positive_pics; + if(pps->pps_curr_pic_ref_enabled_flag && + (sps->sample_adaptive_offset_enabled_flag || + !pps->pps_deblocking_filter_disabled_flag || + pps->deblocking_filter_override_enabled_flag)) { + // This picture will occupy two DPB slots. + if(dpb_slots_remaining == 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid stream: " + "short-term ref pic set contains too many pictures " + "to use with current picture reference enabled.\n"); + return AVERROR_INVALIDDATA; + } + --dpb_slots_remaining; + } + + num_pic_total_curr = 0; + for(i = 0; i < rps->num_negative_pics; i++) + if(rps->used_by_curr_pic_s0_flag[i]) + ++num_pic_total_curr; + for(i = 0; i < rps->num_positive_pics; i++) + if(rps->used_by_curr_pic_s1_flag[i]) + ++num_pic_total_curr; + + if(sps->long_term_ref_pics_present_flag) { + unsigned int idx_size; + + if(sps->num_long_term_ref_pics_sps > 0) { + ue(num_long_term_sps, 0, FFMIN(sps->num_long_term_ref_pics_sps, dpb_slots_remaining)); + idx_size = av_log2(sps->num_long_term_ref_pics_sps - 1) + 1; + dpb_slots_remaining -= current->num_long_term_sps; + } + else { + infer(num_long_term_sps, 0); + idx_size = 0; + } + ue(num_long_term_pics, 0, dpb_slots_remaining); + + for(i = 0; i < current->num_long_term_sps + + current->num_long_term_pics; + i++) { + if(i < current->num_long_term_sps) { + if(sps->num_long_term_ref_pics_sps > 1) + us(idx_size, lt_idx_sps[i], + 0, sps->num_long_term_ref_pics_sps - 1, 1, i); + if(sps->used_by_curr_pic_lt_sps_flag[current->lt_idx_sps[i]]) + ++num_pic_total_curr; + } + else { + ubs(sps->log2_max_pic_order_cnt_lsb_minus4 + 4, poc_lsb_lt[i], 1, i); + flags(used_by_curr_pic_lt_flag[i], 1, i); + if(current->used_by_curr_pic_lt_flag[i]) + ++num_pic_total_curr; + } + flags(delta_poc_msb_present_flag[i], 1, i); + if(current->delta_poc_msb_present_flag[i]) + ues(delta_poc_msb_cycle_lt[i], 0, UINT32_MAX - 1, 1, i); + else + infer(delta_poc_msb_cycle_lt[i], 0); + } + } + + if(sps->sps_temporal_mvp_enabled_flag) + flag(slice_temporal_mvp_enabled_flag); + else + infer(slice_temporal_mvp_enabled_flag, 0); + + if(pps->pps_curr_pic_ref_enabled_flag) + ++num_pic_total_curr; + } + + if(sps->sample_adaptive_offset_enabled_flag) { + flag(slice_sao_luma_flag); + if(!sps->separate_colour_plane_flag && sps->chroma_format_idc != 0) + flag(slice_sao_chroma_flag); + else + infer(slice_sao_chroma_flag, 0); + } + else { + infer(slice_sao_luma_flag, 0); + infer(slice_sao_chroma_flag, 0); + } + + if(current->slice_type == HEVC_SLICE_P || + current->slice_type == HEVC_SLICE_B) { + flag(num_ref_idx_active_override_flag); + if(current->num_ref_idx_active_override_flag) { + ue(num_ref_idx_l0_active_minus1, 0, 14); + if(current->slice_type == HEVC_SLICE_B) + ue(num_ref_idx_l1_active_minus1, 0, 14); + else + infer(num_ref_idx_l1_active_minus1, pps->num_ref_idx_l1_default_active_minus1); + } + else { + infer(num_ref_idx_l0_active_minus1, pps->num_ref_idx_l0_default_active_minus1); + infer(num_ref_idx_l1_active_minus1, pps->num_ref_idx_l1_default_active_minus1); + } + + if(pps->lists_modification_present_flag && num_pic_total_curr > 1) + CHECK(FUNC(ref_pic_lists_modification)(ctx, rw, current, + num_pic_total_curr)); + + if(current->slice_type == HEVC_SLICE_B) + flag(mvd_l1_zero_flag); + if(pps->cabac_init_present_flag) + flag(cabac_init_flag); + else + infer(cabac_init_flag, 0); + if(current->slice_temporal_mvp_enabled_flag) { + if(current->slice_type == HEVC_SLICE_B) + flag(collocated_from_l0_flag); + else + infer(collocated_from_l0_flag, 1); + if(current->collocated_from_l0_flag) { + if(current->num_ref_idx_l0_active_minus1 > 0) + ue(collocated_ref_idx, 0, current->num_ref_idx_l0_active_minus1); + else + infer(collocated_ref_idx, 0); + } + else { + if(current->num_ref_idx_l1_active_minus1 > 0) + ue(collocated_ref_idx, 0, current->num_ref_idx_l1_active_minus1); + else + infer(collocated_ref_idx, 0); + } + } + + if((pps->weighted_pred_flag && current->slice_type == HEVC_SLICE_P) || + (pps->weighted_bipred_flag && current->slice_type == HEVC_SLICE_B)) + CHECK(FUNC(pred_weight_table)(ctx, rw, current)); + + ue(five_minus_max_num_merge_cand, 0, 4); + if(sps->motion_vector_resolution_control_idc == 2) + flag(use_integer_mv_flag); + else + infer(use_integer_mv_flag, sps->motion_vector_resolution_control_idc); + } + + se(slice_qp_delta, + -6 * sps->bit_depth_luma_minus8 - (pps->init_qp_minus26 + 26), + +51 - (pps->init_qp_minus26 + 26)); + if(pps->pps_slice_chroma_qp_offsets_present_flag) { + se(slice_cb_qp_offset, -12, +12); + se(slice_cr_qp_offset, -12, +12); + } + else { + infer(slice_cb_qp_offset, 0); + infer(slice_cr_qp_offset, 0); + } + if(pps->pps_slice_act_qp_offsets_present_flag) { + se(slice_act_y_qp_offset, + -12 - (pps->pps_act_y_qp_offset_plus5 - 5), + +12 - (pps->pps_act_y_qp_offset_plus5 - 5)); + se(slice_act_cb_qp_offset, + -12 - (pps->pps_act_cb_qp_offset_plus5 - 5), + +12 - (pps->pps_act_cb_qp_offset_plus5 - 5)); + se(slice_act_cr_qp_offset, + -12 - (pps->pps_act_cr_qp_offset_plus3 - 3), + +12 - (pps->pps_act_cr_qp_offset_plus3 - 3)); + } + else { + infer(slice_act_y_qp_offset, 0); + infer(slice_act_cb_qp_offset, 0); + infer(slice_act_cr_qp_offset, 0); + } + if(pps->chroma_qp_offset_list_enabled_flag) + flag(cu_chroma_qp_offset_enabled_flag); + else + infer(cu_chroma_qp_offset_enabled_flag, 0); + + if(pps->deblocking_filter_override_enabled_flag) + flag(deblocking_filter_override_flag); + else + infer(deblocking_filter_override_flag, 0); + if(current->deblocking_filter_override_flag) { + flag(slice_deblocking_filter_disabled_flag); + if(!current->slice_deblocking_filter_disabled_flag) { + se(slice_beta_offset_div2, -6, +6); + se(slice_tc_offset_div2, -6, +6); + } + else { + infer(slice_beta_offset_div2, pps->pps_beta_offset_div2); + infer(slice_tc_offset_div2, pps->pps_tc_offset_div2); + } + } + else { + infer(slice_deblocking_filter_disabled_flag, + pps->pps_deblocking_filter_disabled_flag); + infer(slice_beta_offset_div2, pps->pps_beta_offset_div2); + infer(slice_tc_offset_div2, pps->pps_tc_offset_div2); + } + if(pps->pps_loop_filter_across_slices_enabled_flag && + (current->slice_sao_luma_flag || current->slice_sao_chroma_flag || + !current->slice_deblocking_filter_disabled_flag)) + flag(slice_loop_filter_across_slices_enabled_flag); + else + infer(slice_loop_filter_across_slices_enabled_flag, + pps->pps_loop_filter_across_slices_enabled_flag); + } + + if(pps->tiles_enabled_flag || pps->entropy_coding_sync_enabled_flag) { + unsigned int num_entry_point_offsets_limit; + if(!pps->tiles_enabled_flag && pps->entropy_coding_sync_enabled_flag) + num_entry_point_offsets_limit = pic_height_in_ctbs_y - 1; + else if(pps->tiles_enabled_flag && !pps->entropy_coding_sync_enabled_flag) + num_entry_point_offsets_limit = + (pps->num_tile_columns_minus1 + 1) * (pps->num_tile_rows_minus1 + 1); + else + num_entry_point_offsets_limit = + (pps->num_tile_columns_minus1 + 1) * pic_height_in_ctbs_y - 1; + ue(num_entry_point_offsets, 0, num_entry_point_offsets_limit); + + if(current->num_entry_point_offsets > HEVC_MAX_ENTRY_POINT_OFFSETS) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Too many entry points: " + "%" PRIu16 ".\n", + current->num_entry_point_offsets); + return AVERROR_PATCHWELCOME; + } + + if(current->num_entry_point_offsets > 0) { + ue(offset_len_minus1, 0, 31); + for(i = 0; i < current->num_entry_point_offsets; i++) + ubs(current->offset_len_minus1 + 1, entry_point_offset_minus1[i], 1, i); + } + } + + if(pps->slice_segment_header_extension_present_flag) { + ue(slice_segment_header_extension_length, 0, 256); + for(i = 0; i < current->slice_segment_header_extension_length; i++) + us(8, slice_segment_header_extension_data_byte[i], 0x00, 0xff, 1, i); + } + + CHECK(FUNC(byte_alignment)(ctx, rw)); + + return 0; +} + +static int FUNC(sei_buffering_period)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIBufferingPeriod *current, SEIMessageState *sei) { + CodedBitstreamH265Context *h265 = ctx->priv_data; + const H265RawSPS *sps; + const H265RawHRDParameters *hrd; + int err, i, length; + +#ifdef READ + int start_pos, end_pos; + start_pos = get_bits_count(rw); +#endif + + HEADER("Buffering Period"); + + ue(bp_seq_parameter_set_id, 0, HEVC_MAX_SPS_COUNT - 1); + + sps = h265->sps[current->bp_seq_parameter_set_id]; + if(!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n", + current->bp_seq_parameter_set_id); + return AVERROR_INVALIDDATA; + } + h265->active_sps = sps; + + if(!sps->vui_parameters_present_flag || + !sps->vui.vui_hrd_parameters_present_flag) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Buffering period SEI requires " + "HRD parameters to be present in SPS.\n"); + return AVERROR_INVALIDDATA; + } + hrd = &sps->vui.hrd_parameters; + if(!hrd->nal_hrd_parameters_present_flag && + !hrd->vcl_hrd_parameters_present_flag) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Buffering period SEI requires " + "NAL or VCL HRD parameters to be present.\n"); + return AVERROR_INVALIDDATA; + } + + if(!hrd->sub_pic_hrd_params_present_flag) + flag(irap_cpb_params_present_flag); + else + infer(irap_cpb_params_present_flag, 0); + if(current->irap_cpb_params_present_flag) { + length = hrd->au_cpb_removal_delay_length_minus1 + 1; + ub(length, cpb_delay_offset); + length = hrd->dpb_output_delay_length_minus1 + 1; + ub(length, dpb_delay_offset); + } + else { + infer(cpb_delay_offset, 0); + infer(dpb_delay_offset, 0); + } + + flag(concatenation_flag); + + length = hrd->au_cpb_removal_delay_length_minus1 + 1; + ub(length, au_cpb_removal_delay_delta_minus1); + + if(hrd->nal_hrd_parameters_present_flag) { + for(i = 0; i <= hrd->cpb_cnt_minus1[0]; i++) { + length = hrd->initial_cpb_removal_delay_length_minus1 + 1; + + ubs(length, nal_initial_cpb_removal_delay[i], 1, i); + ubs(length, nal_initial_cpb_removal_offset[i], 1, i); + + if(hrd->sub_pic_hrd_params_present_flag || + current->irap_cpb_params_present_flag) { + ubs(length, nal_initial_alt_cpb_removal_delay[i], 1, i); + ubs(length, nal_initial_alt_cpb_removal_offset[i], 1, i); + } + } + } + if(hrd->vcl_hrd_parameters_present_flag) { + for(i = 0; i <= hrd->cpb_cnt_minus1[0]; i++) { + length = hrd->initial_cpb_removal_delay_length_minus1 + 1; + + ubs(length, vcl_initial_cpb_removal_delay[i], 1, i); + ubs(length, vcl_initial_cpb_removal_offset[i], 1, i); + + if(hrd->sub_pic_hrd_params_present_flag || + current->irap_cpb_params_present_flag) { + ubs(length, vcl_initial_alt_cpb_removal_delay[i], 1, i); + ubs(length, vcl_initial_alt_cpb_removal_offset[i], 1, i); + } + } + } + +#ifdef READ + end_pos = get_bits_count(rw); + if(cbs_h265_payload_extension_present(rw, sei->payload_size, + end_pos - start_pos)) + flag(use_alt_cpb_params_flag); + else + infer(use_alt_cpb_params_flag, 0); +#else + // If unknown extension data exists, then use_alt_cpb_params_flag is + // coded in the bitstream and must be written even if it's 0. + if(current->use_alt_cpb_params_flag || sei->extension_present) { + flag(use_alt_cpb_params_flag); + // Ensure this bit is not the last in the payload by making the + // more_data_in_payload() check evaluate to true, so it may not + // be mistaken as something else by decoders. + sei->extension_present = 1; + } +#endif + + return 0; +} + +static int FUNC(sei_pic_timing)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIPicTiming *current, SEIMessageState *sei) { + CodedBitstreamH265Context *h265 = ctx->priv_data; + const H265RawSPS *sps; + const H265RawHRDParameters *hrd; + int err, expected_source_scan_type, i, length; + + HEADER("Picture Timing"); + + sps = h265->active_sps; + if(!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "No active SPS for pic_timing.\n"); + return AVERROR_INVALIDDATA; + } + + expected_source_scan_type = 2 - + 2 * sps->profile_tier_level.general_interlaced_source_flag - + sps->profile_tier_level.general_progressive_source_flag; + + if(sps->vui.frame_field_info_present_flag) { + u(4, pic_struct, 0, 12); + u(2, source_scan_type, + expected_source_scan_type >= 0 ? expected_source_scan_type : 0, + expected_source_scan_type >= 0 ? expected_source_scan_type : 2); + flag(duplicate_flag); + } + else { + infer(pic_struct, 0); + infer(source_scan_type, + expected_source_scan_type >= 0 ? expected_source_scan_type : 2); + infer(duplicate_flag, 0); + } + + if(sps->vui_parameters_present_flag && + sps->vui.vui_hrd_parameters_present_flag) + hrd = &sps->vui.hrd_parameters; + else + hrd = NULL; + if(hrd && (hrd->nal_hrd_parameters_present_flag || + hrd->vcl_hrd_parameters_present_flag)) { + length = hrd->au_cpb_removal_delay_length_minus1 + 1; + ub(length, au_cpb_removal_delay_minus1); + + length = hrd->dpb_output_delay_length_minus1 + 1; + ub(length, pic_dpb_output_delay); + + if(hrd->sub_pic_hrd_params_present_flag) { + length = hrd->dpb_output_delay_du_length_minus1 + 1; + ub(length, pic_dpb_output_du_delay); + } + + if(hrd->sub_pic_hrd_params_present_flag && + hrd->sub_pic_cpb_params_in_pic_timing_sei_flag) { + // Each decoding unit must contain at least one slice segment. + ue(num_decoding_units_minus1, 0, HEVC_MAX_SLICE_SEGMENTS); + flag(du_common_cpb_removal_delay_flag); + + length = hrd->du_cpb_removal_delay_increment_length_minus1 + 1; + if(current->du_common_cpb_removal_delay_flag) + ub(length, du_common_cpb_removal_delay_increment_minus1); + + for(i = 0; i <= current->num_decoding_units_minus1; i++) { + ues(num_nalus_in_du_minus1[i], + 0, HEVC_MAX_SLICE_SEGMENTS, 1, i); + if(!current->du_common_cpb_removal_delay_flag && + i < current->num_decoding_units_minus1) + ubs(length, du_cpb_removal_delay_increment_minus1[i], 1, i); + } + } + } + + return 0; +} + +static int FUNC(sei_pan_scan_rect)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIPanScanRect *current, SEIMessageState *sei) { + int err, i; + + HEADER("Pan-Scan Rectangle"); + + ue(pan_scan_rect_id, 0, UINT32_MAX - 1); + flag(pan_scan_rect_cancel_flag); + + if(!current->pan_scan_rect_cancel_flag) { + ue(pan_scan_cnt_minus1, 0, 2); + + for(i = 0; i <= current->pan_scan_cnt_minus1; i++) { + ses(pan_scan_rect_left_offset[i], INT32_MIN + 1, INT32_MAX, 1, i); + ses(pan_scan_rect_right_offset[i], INT32_MIN + 1, INT32_MAX, 1, i); + ses(pan_scan_rect_top_offset[i], INT32_MIN + 1, INT32_MAX, 1, i); + ses(pan_scan_rect_bottom_offset[i], INT32_MIN + 1, INT32_MAX, 1, i); + } + + flag(pan_scan_rect_persistence_flag); + } + + return 0; +} + +static int FUNC(sei_recovery_point)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIRecoveryPoint *current, SEIMessageState *sei) { + int err; + + HEADER("Recovery Point"); + + se(recovery_poc_cnt, -32768, 32767); + + flag(exact_match_flag); + flag(broken_link_flag); + + return 0; +} + +static int FUNC(sei_display_orientation)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIDisplayOrientation *current, SEIMessageState *sei) { + int err; + + HEADER("Display Orientation"); + + flag(display_orientation_cancel_flag); + if(!current->display_orientation_cancel_flag) { + flag(hor_flip); + flag(ver_flip); + ub(16, anticlockwise_rotation); + flag(display_orientation_persistence_flag); + } + + return 0; +} + +static int FUNC(sei_active_parameter_sets)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIActiveParameterSets *current, SEIMessageState *sei) { + CodedBitstreamH265Context *h265 = ctx->priv_data; + const H265RawVPS *vps; + int err, i; + + HEADER("Active Parameter Sets"); + + u(4, active_video_parameter_set_id, 0, HEVC_MAX_VPS_COUNT); + vps = h265->vps[current->active_video_parameter_set_id]; + if(!vps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "VPS id %d not available for active " + "parameter sets.\n", + current->active_video_parameter_set_id); + return AVERROR_INVALIDDATA; + } + h265->active_vps = vps; + + flag(self_contained_cvs_flag); + flag(no_parameter_set_update_flag); + + ue(num_sps_ids_minus1, 0, HEVC_MAX_SPS_COUNT - 1); + for(i = 0; i <= current->num_sps_ids_minus1; i++) + ues(active_seq_parameter_set_id[i], 0, HEVC_MAX_SPS_COUNT - 1, 1, i); + + for(i = vps->vps_base_layer_internal_flag; + i <= FFMIN(62, vps->vps_max_layers_minus1); i++) { + ues(layer_sps_idx[i], 0, current->num_sps_ids_minus1, 1, i); + + if(i == 0) + h265->active_sps = h265->sps[current->active_seq_parameter_set_id[current->layer_sps_idx[0]]]; + } + + return 0; +} + +static int FUNC(sei_decoded_picture_hash)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIDecodedPictureHash *current, SEIMessageState *sei) { + CodedBitstreamH265Context *h265 = ctx->priv_data; + const H265RawSPS *sps = h265->active_sps; + int err, c, i; + + HEADER("Decoded Picture Hash"); + + if(!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "No active SPS for decoded picture hash.\n"); + return AVERROR_INVALIDDATA; + } + + u(8, hash_type, 0, 2); + + for(c = 0; c < (sps->chroma_format_idc == 0 ? 1 : 3); c++) { + if(current->hash_type == 0) { + for(i = 0; i < 16; i++) + us(8, picture_md5[c][i], 0x00, 0xff, 2, c, i); + } + else if(current->hash_type == 1) { + us(16, picture_crc[c], 0x0000, 0xffff, 1, c); + } + else if(current->hash_type == 2) { + us(32, picture_checksum[c], 0x00000000, 0xffffffff, 1, c); + } + } + + return 0; +} + +static int FUNC(sei_time_code)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEITimeCode *current, SEIMessageState *sei) { + int err, i; + + HEADER("Time Code"); + + u(2, num_clock_ts, 1, 3); + + for(i = 0; i < current->num_clock_ts; i++) { + flags(clock_timestamp_flag[i], 1, i); + + if(current->clock_timestamp_flag[i]) { + flags(units_field_based_flag[i], 1, i); + us(5, counting_type[i], 0, 6, 1, i); + flags(full_timestamp_flag[i], 1, i); + flags(discontinuity_flag[i], 1, i); + flags(cnt_dropped_flag[i], 1, i); + + ubs(9, n_frames[i], 1, i); + + if(current->full_timestamp_flag[i]) { + us(6, seconds_value[i], 0, 59, 1, i); + us(6, minutes_value[i], 0, 59, 1, i); + us(5, hours_value[i], 0, 23, 1, i); + } + else { + flags(seconds_flag[i], 1, i); + if(current->seconds_flag[i]) { + us(6, seconds_value[i], 0, 59, 1, i); + flags(minutes_flag[i], 1, i); + if(current->minutes_flag[i]) { + us(6, minutes_value[i], 0, 59, 1, i); + flags(hours_flag[i], 1, i); + if(current->hours_flag[i]) + us(5, hours_value[i], 0, 23, 1, i); + } + } + } + + ubs(5, time_offset_length[i], 1, i); + if(current->time_offset_length[i] > 0) + ibs(current->time_offset_length[i], time_offset_value[i], 1, i); + else + infer(time_offset_value[i], 0); + } + } + + return 0; +} + +static int FUNC(sei_alpha_channel_info)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIAlphaChannelInfo *current, SEIMessageState *sei) { + int err, length; + + HEADER("Alpha Channel Information"); + + flag(alpha_channel_cancel_flag); + if(!current->alpha_channel_cancel_flag) { + ub(3, alpha_channel_use_idc); + ub(3, alpha_channel_bit_depth_minus8); + length = current->alpha_channel_bit_depth_minus8 + 9; + ub(length, alpha_transparent_value); + ub(length, alpha_opaque_value); + flag(alpha_channel_incr_flag); + flag(alpha_channel_clip_flag); + if(current->alpha_channel_clip_flag) + flag(alpha_channel_clip_type_flag); + } + else { + infer(alpha_channel_use_idc, 2); + infer(alpha_channel_incr_flag, 0); + infer(alpha_channel_clip_flag, 0); + } + + return 0; +} + +static int FUNC(sei)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEI *current, int prefix) { + int err; + + if(prefix) + HEADER("Prefix Supplemental Enhancement Information"); + else + HEADER("Suffix Supplemental Enhancement Information"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, + prefix ? HEVC_NAL_SEI_PREFIX : HEVC_NAL_SEI_SUFFIX)); + + CHECK(FUNC_SEI(message_list)(ctx, rw, ¤t->message_list, prefix)); + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} diff --git a/third-party/cbs/cbs_internal.h b/third-party/cbs/cbs_internal.h new file mode 100644 index 00000000..4fb6e7c4 --- /dev/null +++ b/third-party/cbs/cbs_internal.h @@ -0,0 +1,220 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CBS_INTERNAL_H +#define AVCODEC_CBS_INTERNAL_H + +#include + +#include +#include + +#include "cbs/cbs.h" +#include "get_bits.h" +#include "put_bits.h" + + +enum CBSContentType { + // Unit content is a simple structure. + CBS_CONTENT_TYPE_POD, + // Unit content contains some references to other structures, but all + // managed via buffer reference counting. The descriptor defines the + // structure offsets of every buffer reference. + CBS_CONTENT_TYPE_INTERNAL_REFS, + // Unit content is something more complex. The descriptor defines + // special functions to manage the content. + CBS_CONTENT_TYPE_COMPLEX, +}; + +enum { + // Maximum number of unit types described by the same unit type + // descriptor. + CBS_MAX_UNIT_TYPES = 3, + // Maximum number of reference buffer offsets in any one unit. + CBS_MAX_REF_OFFSETS = 2, + // Special value used in a unit type descriptor to indicate that it + // applies to a large range of types rather than a set of discrete + // values. + CBS_UNIT_TYPE_RANGE = -1, +}; + +typedef const struct CodedBitstreamUnitTypeDescriptor { + // Number of entries in the unit_types array, or the special value + // CBS_UNIT_TYPE_RANGE to indicate that the range fields should be + // used instead. + int nb_unit_types; + + // Array of unit types that this entry describes. + const CodedBitstreamUnitType unit_types[CBS_MAX_UNIT_TYPES]; + + // Start and end of unit type range, used if nb_unit_types is + // CBS_UNIT_TYPE_RANGE. + const CodedBitstreamUnitType unit_type_range_start; + const CodedBitstreamUnitType unit_type_range_end; + + // The type of content described. + enum CBSContentType content_type; + // The size of the structure which should be allocated to contain + // the decomposed content of this type of unit. + size_t content_size; + + // Number of entries in the ref_offsets array. Only used if the + // content_type is CBS_CONTENT_TYPE_INTERNAL_REFS. + int nb_ref_offsets; + // The structure must contain two adjacent elements: + // type *field; + // AVBufferRef *field_ref; + // where field points to something in the buffer referred to by + // field_ref. This offset is then set to offsetof(struct, field). + size_t ref_offsets[CBS_MAX_REF_OFFSETS]; + + void (*content_free)(void *opaque, uint8_t *data); + int (*content_clone)(AVBufferRef **ref, CodedBitstreamUnit *unit); +} CodedBitstreamUnitTypeDescriptor; + +typedef struct CodedBitstreamType { + enum AVCodecID codec_id; + + // A class for the private data, used to declare private AVOptions. + // This field is NULL for types that do not declare any options. + // If this field is non-NULL, the first member of the filter private data + // must be a pointer to AVClass. + const AVClass *priv_class; + + size_t priv_data_size; + + // List of unit type descriptors for this codec. + // Terminated by a descriptor with nb_unit_types equal to zero. + const CodedBitstreamUnitTypeDescriptor *unit_types; + + // Split frag->data into coded bitstream units, creating the + // frag->units array. Fill data but not content on each unit. + // The header argument should be set if the fragment came from + // a header block, which may require different parsing for some + // codecs (e.g. the AVCC header in H.264). + int (*split_fragment)(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + int header); + + // Read the unit->data bitstream and decompose it, creating + // unit->content. + int (*read_unit)(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit); + + // Write the data bitstream from unit->content into pbc. + // Return value AVERROR(ENOSPC) indicates that pbc was too small. + int (*write_unit)(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + PutBitContext *pbc); + + // Read the data from all of frag->units and assemble it into + // a bitstream for the whole fragment. + int (*assemble_fragment)(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag); + + // Reset the codec internal state. + void (*flush)(CodedBitstreamContext *ctx); + + // Free the codec internal state. + void (*close)(CodedBitstreamContext *ctx); +} CodedBitstreamType; + + +// Helper functions for trace output. + +void ff_cbs_trace_header(CodedBitstreamContext *ctx, + const char *name); + +void ff_cbs_trace_syntax_element(CodedBitstreamContext *ctx, int position, + const char *name, const int *subscripts, + const char *bitstring, int64_t value); + + +// Helper functions for read/write of common bitstream elements, including +// generation of trace output. + +int ff_cbs_read_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc, + int width, const char *name, + const int *subscripts, uint32_t *write_to, + uint32_t range_min, uint32_t range_max); + +int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc, + int width, const char *name, + const int *subscripts, uint32_t value, + uint32_t range_min, uint32_t range_max); + +int ff_cbs_read_signed(CodedBitstreamContext *ctx, GetBitContext *gbc, + int width, const char *name, + const int *subscripts, int32_t *write_to, + int32_t range_min, int32_t range_max); + +int ff_cbs_write_signed(CodedBitstreamContext *ctx, PutBitContext *pbc, + int width, const char *name, + const int *subscripts, int32_t value, + int32_t range_min, int32_t range_max); + +// The largest unsigned value representable in N bits, suitable for use as +// range_max in the above functions. +#define MAX_UINT_BITS(length) ((UINT64_C(1) << (length)) - 1) + +// The largest signed value representable in N bits, suitable for use as +// range_max in the above functions. +#define MAX_INT_BITS(length) ((INT64_C(1) << ((length)-1)) - 1) + +// The smallest signed value representable in N bits, suitable for use as +// range_min in the above functions. +#define MIN_INT_BITS(length) (-(INT64_C(1) << ((length)-1))) + + +#define CBS_UNIT_TYPE_POD(type, structure) \ + { \ + .nb_unit_types = 1, \ + .unit_types = { type }, \ + .content_type = CBS_CONTENT_TYPE_POD, \ + .content_size = sizeof(structure), \ + } +#define CBS_UNIT_TYPE_INTERNAL_REF(type, structure, ref_field) \ + { \ + .nb_unit_types = 1, \ + .unit_types = { type }, \ + .content_type = CBS_CONTENT_TYPE_INTERNAL_REFS, \ + .content_size = sizeof(structure), \ + .nb_ref_offsets = 1, \ + .ref_offsets = { offsetof(structure, ref_field) }, \ + } +#define CBS_UNIT_TYPE_COMPLEX(type, structure, free_func) \ + { \ + .nb_unit_types = 1, \ + .unit_types = { type }, \ + .content_type = CBS_CONTENT_TYPE_COMPLEX, \ + .content_size = sizeof(structure), \ + .content_free = free_func, \ + } +#define CBS_UNIT_TYPE_END_OF_LIST \ + { .nb_unit_types = 0 } + + +extern const CodedBitstreamType ff_cbs_type_av1; +extern const CodedBitstreamType ff_cbs_type_h264; +extern const CodedBitstreamType ff_cbs_type_h265; +extern const CodedBitstreamType ff_cbs_type_jpeg; +extern const CodedBitstreamType ff_cbs_type_mpeg2; +extern const CodedBitstreamType ff_cbs_type_vp9; + + +#endif /* AVCODEC_CBS_INTERNAL_H */ diff --git a/third-party/cbs/cbs_jpeg.c b/third-party/cbs/cbs_jpeg.c new file mode 100644 index 00000000..bb4f08a1 --- /dev/null +++ b/third-party/cbs/cbs_jpeg.c @@ -0,0 +1,482 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "cbs/cbs_jpeg.h" +#include "cbs/cbs.h" + +#include "cbs_internal.h" + + +#define HEADER(name) \ + do { \ + ff_cbs_trace_header(ctx, name); \ + } while(0) + +#define CHECK(call) \ + do { \ + err = (call); \ + if(err < 0) \ + return err; \ + } while(0) + +#define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]) { subs, __VA_ARGS__ }) : NULL) + +#define u(width, name, range_min, range_max) \ + xu(width, name, range_min, range_max, 0, ) +#define us(width, name, sub, range_min, range_max) \ + xu(width, name, range_min, range_max, 1, sub) + + +#define READ +#define READWRITE read +#define RWContext GetBitContext +#define FUNC(name) cbs_jpeg_read_##name + +#define xu(width, name, range_min, range_max, subs, ...) \ + do { \ + uint32_t value; \ + CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + &value, range_min, range_max)); \ + current->name = value; \ + } while(0) + +#include "cbs_jpeg_syntax_template.c" + +#undef READ +#undef READWRITE +#undef RWContext +#undef FUNC +#undef xu + +#define WRITE +#define READWRITE write +#define RWContext PutBitContext +#define FUNC(name) cbs_jpeg_write_##name + +#define xu(width, name, range_min, range_max, subs, ...) \ + do { \ + uint32_t value = current->name; \ + CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + value, range_min, range_max)); \ + } while(0) + + +#include "cbs_jpeg_syntax_template.c" + +#undef WRITE +#undef READWRITE +#undef RWContext +#undef FUNC +#undef xu + + +static void cbs_jpeg_free_application_data(void *opaque, uint8_t *content) { + JPEGRawApplicationData *ad = (JPEGRawApplicationData *)content; + av_buffer_unref(&ad->Ap_ref); + av_freep(&content); +} + +static void cbs_jpeg_free_comment(void *opaque, uint8_t *content) { + JPEGRawComment *comment = (JPEGRawComment *)content; + av_buffer_unref(&comment->Cm_ref); + av_freep(&content); +} + +static void cbs_jpeg_free_scan(void *opaque, uint8_t *content) { + JPEGRawScan *scan = (JPEGRawScan *)content; + av_buffer_unref(&scan->data_ref); + av_freep(&content); +} + +static int cbs_jpeg_split_fragment(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + int header) { + AVBufferRef *data_ref; + uint8_t *data; + size_t data_size; + int unit, start, end, marker, next_start, next_marker; + int err, i, j, length; + + if(frag->data_size < 4) { + // Definitely too short to be meaningful. + return AVERROR_INVALIDDATA; + } + + for(i = 0; i + 1 < frag->data_size && frag->data[i] != 0xff; i++) + ; + if(i > 0) { + av_log(ctx->log_ctx, AV_LOG_WARNING, "Discarding %d bytes at " + "beginning of image.\n", + i); + } + for(++i; i + 1 < frag->data_size && frag->data[i] == 0xff; i++) + ; + if(i + 1 >= frag->data_size && frag->data[i]) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: " + "no SOI marker found.\n"); + return AVERROR_INVALIDDATA; + } + marker = frag->data[i]; + if(marker != JPEG_MARKER_SOI) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: first " + "marker is %02x, should be SOI.\n", + marker); + return AVERROR_INVALIDDATA; + } + for(++i; i + 1 < frag->data_size && frag->data[i] == 0xff; i++) + ; + if(i + 1 >= frag->data_size) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: " + "no image content found.\n"); + return AVERROR_INVALIDDATA; + } + marker = frag->data[i]; + start = i + 1; + + for(unit = 0;; unit++) { + if(marker == JPEG_MARKER_EOI) { + break; + } + else if(marker == JPEG_MARKER_SOS) { + next_marker = -1; + end = start; + for(i = start; i + 1 < frag->data_size; i++) { + if(frag->data[i] != 0xff) + continue; + end = i; + for(++i; i + 1 < frag->data_size && + frag->data[i] == 0xff; + i++) + ; + if(i + 1 < frag->data_size) { + if(frag->data[i] == 0x00) + continue; + next_marker = frag->data[i]; + next_start = i + 1; + } + break; + } + } + else { + i = start; + if(i + 2 > frag->data_size) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: " + "truncated at %02x marker.\n", + marker); + return AVERROR_INVALIDDATA; + } + length = AV_RB16(frag->data + i); + if(i + length > frag->data_size) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: " + "truncated at %02x marker segment.\n", + marker); + return AVERROR_INVALIDDATA; + } + end = start + length; + + i = end; + if(frag->data[i] != 0xff) { + next_marker = -1; + } + else { + for(++i; i + 1 < frag->data_size && + frag->data[i] == 0xff; + i++) + ; + if(i + 1 >= frag->data_size) { + next_marker = -1; + } + else { + next_marker = frag->data[i]; + next_start = i + 1; + } + } + } + + if(marker == JPEG_MARKER_SOS) { + length = AV_RB16(frag->data + start); + + if(length > end - start) + return AVERROR_INVALIDDATA; + + data_ref = NULL; + data = av_malloc(end - start + + AV_INPUT_BUFFER_PADDING_SIZE); + if(!data) + return AVERROR(ENOMEM); + + memcpy(data, frag->data + start, length); + for(i = start + length, j = length; i < end; i++, j++) { + if(frag->data[i] == 0xff) { + while(frag->data[i] == 0xff) + ++i; + data[j] = 0xff; + } + else { + data[j] = frag->data[i]; + } + } + data_size = j; + + memset(data + data_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + } + else { + data = frag->data + start; + data_size = end - start; + data_ref = frag->data_ref; + } + + err = ff_cbs_insert_unit_data(frag, unit, marker, + data, data_size, data_ref); + if(err < 0) + return err; + + if(next_marker == -1) + break; + marker = next_marker; + start = next_start; + } + + return 0; +} + +static int cbs_jpeg_read_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) { + GetBitContext gbc; + int err; + + err = init_get_bits(&gbc, unit->data, 8 * unit->data_size); + if(err < 0) + return err; + + if(unit->type >= JPEG_MARKER_SOF0 && + unit->type <= JPEG_MARKER_SOF3) { + err = ff_cbs_alloc_unit_content(unit, + sizeof(JPEGRawFrameHeader), + NULL); + if(err < 0) + return err; + + err = cbs_jpeg_read_frame_header(ctx, &gbc, unit->content); + if(err < 0) + return err; + } + else if(unit->type >= JPEG_MARKER_APPN && + unit->type <= JPEG_MARKER_APPN + 15) { + err = ff_cbs_alloc_unit_content(unit, + sizeof(JPEGRawApplicationData), + &cbs_jpeg_free_application_data); + if(err < 0) + return err; + + err = cbs_jpeg_read_application_data(ctx, &gbc, unit->content); + if(err < 0) + return err; + } + else if(unit->type == JPEG_MARKER_SOS) { + JPEGRawScan *scan; + int pos; + + err = ff_cbs_alloc_unit_content(unit, + sizeof(JPEGRawScan), + &cbs_jpeg_free_scan); + if(err < 0) + return err; + scan = unit->content; + + err = cbs_jpeg_read_scan_header(ctx, &gbc, &scan->header); + if(err < 0) + return err; + + pos = get_bits_count(&gbc); + av_assert0(pos % 8 == 0); + if(pos > 0) { + scan->data_size = unit->data_size - pos / 8; + scan->data_ref = av_buffer_ref(unit->data_ref); + if(!scan->data_ref) + return AVERROR(ENOMEM); + scan->data = unit->data + pos / 8; + } + } + else { + switch(unit->type) { +#define SEGMENT(marker, type, func, free) \ + case JPEG_MARKER_##marker: { \ + err = ff_cbs_alloc_unit_content(unit, \ + sizeof(type), free); \ + if(err < 0) \ + return err; \ + err = cbs_jpeg_read_##func(ctx, &gbc, unit->content); \ + if(err < 0) \ + return err; \ + } break + SEGMENT(DQT, JPEGRawQuantisationTableSpecification, dqt, NULL); + SEGMENT(DHT, JPEGRawHuffmanTableSpecification, dht, NULL); + SEGMENT(COM, JPEGRawComment, comment, &cbs_jpeg_free_comment); +#undef SEGMENT + default: + return AVERROR(ENOSYS); + } + } + + return 0; +} + +static int cbs_jpeg_write_scan(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + PutBitContext *pbc) { + JPEGRawScan *scan = unit->content; + int err; + + err = cbs_jpeg_write_scan_header(ctx, pbc, &scan->header); + if(err < 0) + return err; + + if(scan->data) { + if(scan->data_size * 8 > put_bits_left(pbc)) + return AVERROR(ENOSPC); + + av_assert0(put_bits_count(pbc) % 8 == 0); + + flush_put_bits(pbc); + + memcpy(put_bits_ptr(pbc), scan->data, scan->data_size); + skip_put_bytes(pbc, scan->data_size); + } + + return 0; +} + +static int cbs_jpeg_write_segment(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + PutBitContext *pbc) { + int err; + + if(unit->type >= JPEG_MARKER_SOF0 && + unit->type <= JPEG_MARKER_SOF3) { + err = cbs_jpeg_write_frame_header(ctx, pbc, unit->content); + } + else if(unit->type >= JPEG_MARKER_APPN && + unit->type <= JPEG_MARKER_APPN + 15) { + err = cbs_jpeg_write_application_data(ctx, pbc, unit->content); + } + else { + switch(unit->type) { +#define SEGMENT(marker, func) \ + case JPEG_MARKER_##marker: \ + err = cbs_jpeg_write_##func(ctx, pbc, unit->content); \ + break; + SEGMENT(DQT, dqt); + SEGMENT(DHT, dht); + SEGMENT(COM, comment); + default: + return AVERROR_PATCHWELCOME; + } + } + + return err; +} + +static int cbs_jpeg_write_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + PutBitContext *pbc) { + if(unit->type == JPEG_MARKER_SOS) + return cbs_jpeg_write_scan(ctx, unit, pbc); + else + return cbs_jpeg_write_segment(ctx, unit, pbc); +} + +static int cbs_jpeg_assemble_fragment(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag) { + const CodedBitstreamUnit *unit; + uint8_t *data; + size_t size, dp, sp; + int i; + + size = 4; // SOI + EOI. + for(i = 0; i < frag->nb_units; i++) { + unit = &frag->units[i]; + size += 2 + unit->data_size; + if(unit->type == JPEG_MARKER_SOS) { + for(sp = 0; sp < unit->data_size; sp++) { + if(unit->data[sp] == 0xff) + ++size; + } + } + } + + frag->data_ref = av_buffer_alloc(size + AV_INPUT_BUFFER_PADDING_SIZE); + if(!frag->data_ref) + return AVERROR(ENOMEM); + data = frag->data_ref->data; + + dp = 0; + + data[dp++] = 0xff; + data[dp++] = JPEG_MARKER_SOI; + + for(i = 0; i < frag->nb_units; i++) { + unit = &frag->units[i]; + + data[dp++] = 0xff; + data[dp++] = unit->type; + + if(unit->type != JPEG_MARKER_SOS) { + memcpy(data + dp, unit->data, unit->data_size); + dp += unit->data_size; + } + else { + sp = AV_RB16(unit->data); + av_assert0(sp <= unit->data_size); + memcpy(data + dp, unit->data, sp); + dp += sp; + + for(; sp < unit->data_size; sp++) { + if(unit->data[sp] == 0xff) { + data[dp++] = 0xff; + data[dp++] = 0x00; + } + else { + data[dp++] = unit->data[sp]; + } + } + } + } + + data[dp++] = 0xff; + data[dp++] = JPEG_MARKER_EOI; + + av_assert0(dp == size); + + memset(data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + frag->data = data; + frag->data_size = size; + + return 0; +} + +const CodedBitstreamType ff_cbs_type_jpeg = { + .codec_id = AV_CODEC_ID_MJPEG, + + .split_fragment = &cbs_jpeg_split_fragment, + .read_unit = &cbs_jpeg_read_unit, + .write_unit = &cbs_jpeg_write_unit, + .assemble_fragment = &cbs_jpeg_assemble_fragment, +}; diff --git a/third-party/cbs/cbs_jpeg_syntax_template.c b/third-party/cbs/cbs_jpeg_syntax_template.c new file mode 100644 index 00000000..613a6835 --- /dev/null +++ b/third-party/cbs/cbs_jpeg_syntax_template.c @@ -0,0 +1,189 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +static int FUNC(frame_header)(CodedBitstreamContext *ctx, RWContext *rw, + JPEGRawFrameHeader *current) { + int err, i; + + HEADER("Frame Header"); + + u(16, Lf, 8, 8 + 3 * JPEG_MAX_COMPONENTS); + + u(8, P, 2, 16); + u(16, Y, 0, JPEG_MAX_HEIGHT); + u(16, X, 1, JPEG_MAX_WIDTH); + u(8, Nf, 1, JPEG_MAX_COMPONENTS); + + for(i = 0; i < current->Nf; i++) { + us(8, C[i], i, 0, JPEG_MAX_COMPONENTS); + us(4, H[i], i, 1, 4); + us(4, V[i], i, 1, 4); + us(8, Tq[i], i, 0, 3); + } + + return 0; +} + +static int FUNC(quantisation_table)(CodedBitstreamContext *ctx, RWContext *rw, + JPEGRawQuantisationTable *current) { + int err, i; + + u(4, Pq, 0, 1); + u(4, Tq, 0, 3); + + if(current->Pq) { + for(i = 0; i < 64; i++) + us(16, Q[i], i, 1, 255); + } + else { + for(i = 0; i < 64; i++) + us(8, Q[i], i, 1, 255); + } + + return 0; +} + +static int FUNC(dqt)(CodedBitstreamContext *ctx, RWContext *rw, + JPEGRawQuantisationTableSpecification *current) { + int err, i, n; + + HEADER("Quantisation Tables"); + + u(16, Lq, 2, 2 + 4 * 65); + n = current->Lq / 65; + + for(i = 0; i < n; i++) + CHECK(FUNC(quantisation_table)(ctx, rw, ¤t->table[i])); + + return 0; +} + +static int FUNC(huffman_table)(CodedBitstreamContext *ctx, RWContext *rw, + JPEGRawHuffmanTable *current) { + int err, i, j, ij; + + u(4, Tc, 0, 1); + u(4, Th, 0, 3); + + for(i = 0; i < 16; i++) + us(8, L[i], i, 0, 224); + + ij = 0; + for(i = 0; i < 16; i++) { + for(j = 0; j < current->L[i]; j++) { + if(ij >= 224) + return AVERROR_INVALIDDATA; + us(8, V[ij], ij, 0, 255); + ++ij; + } + } + + return 0; +} + +static int FUNC(dht)(CodedBitstreamContext *ctx, RWContext *rw, + JPEGRawHuffmanTableSpecification *current) { + int err, i, j, n; + + HEADER("Huffman Tables"); + + u(16, Lh, 2, 2 + 8 * (1 + 16 + 256)); + + n = 2; + for(i = 0; n < current->Lh; i++) { + if(i >= 8) + return AVERROR_INVALIDDATA; + + CHECK(FUNC(huffman_table)(ctx, rw, ¤t->table[i])); + + ++n; + for(j = 0; j < 16; j++) + n += 1 + current->table[i].L[j]; + } + + return 0; +} + +static int FUNC(scan_header)(CodedBitstreamContext *ctx, RWContext *rw, + JPEGRawScanHeader *current) { + int err, j; + + HEADER("Scan"); + + u(16, Ls, 6, 6 + 2 * JPEG_MAX_COMPONENTS); + + u(8, Ns, 1, 4); + for(j = 0; j < current->Ns; j++) { + us(8, Cs[j], j, 0, JPEG_MAX_COMPONENTS); + us(4, Td[j], j, 0, 3); + us(4, Ta[j], j, 0, 3); + } + + u(8, Ss, 0, 63); + u(8, Se, 0, 63); + u(4, Ah, 0, 13); + u(4, Al, 0, 15); + + return 0; +} + +static int FUNC(application_data)(CodedBitstreamContext *ctx, RWContext *rw, + JPEGRawApplicationData *current) { + int err, i; + + HEADER("Application Data"); + + u(16, Lp, 2, 65535); + + if(current->Lp > 2) { +#ifdef READ + current->Ap_ref = av_buffer_alloc(current->Lp - 2); + if(!current->Ap_ref) + return AVERROR(ENOMEM); + current->Ap = current->Ap_ref->data; +#endif + + for(i = 0; i < current->Lp - 2; i++) + us(8, Ap[i], i, 0, 255); + } + + return 0; +} + +static int FUNC(comment)(CodedBitstreamContext *ctx, RWContext *rw, + JPEGRawComment *current) { + int err, i; + + HEADER("Comment"); + + u(16, Lc, 2, 65535); + + if(current->Lc > 2) { +#ifdef READ + current->Cm_ref = av_buffer_alloc(current->Lc - 2); + if(!current->Cm_ref) + return AVERROR(ENOMEM); + current->Cm = current->Cm_ref->data; +#endif + + for(i = 0; i < current->Lc - 2; i++) + us(8, Cm[i], i, 0, 255); + } + + return 0; +} diff --git a/third-party/cbs/cbs_mpeg2.c b/third-party/cbs/cbs_mpeg2.c new file mode 100644 index 00000000..98a975f8 --- /dev/null +++ b/third-party/cbs/cbs_mpeg2.c @@ -0,0 +1,469 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "cbs/cbs.h" +#include "cbs/cbs_mpeg2.h" + +#include "cbs_internal.h" + + +#define HEADER(name) \ + do { \ + ff_cbs_trace_header(ctx, name); \ + } while(0) + +#define CHECK(call) \ + do { \ + err = (call); \ + if(err < 0) \ + return err; \ + } while(0) + +#define FUNC_NAME(rw, codec, name) cbs_##codec##_##rw##_##name +#define FUNC_MPEG2(rw, name) FUNC_NAME(rw, mpeg2, name) +#define FUNC(name) FUNC_MPEG2(READWRITE, name) + +#define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]) { subs, __VA_ARGS__ }) : NULL) + +#define ui(width, name) \ + xui(width, name, current->name, 0, MAX_UINT_BITS(width), 0, ) +#define uir(width, name) \ + xui(width, name, current->name, 1, MAX_UINT_BITS(width), 0, ) +#define uis(width, name, subs, ...) \ + xui(width, name, current->name, 0, MAX_UINT_BITS(width), subs, __VA_ARGS__) +#define uirs(width, name, subs, ...) \ + xui(width, name, current->name, 1, MAX_UINT_BITS(width), subs, __VA_ARGS__) +#define xui(width, name, var, range_min, range_max, subs, ...) \ + xuia(width, #name, var, range_min, range_max, subs, __VA_ARGS__) +#define sis(width, name, subs, ...) \ + xsi(width, name, current->name, subs, __VA_ARGS__) + +#define marker_bit() \ + bit("marker_bit", 1) +#define bit(string, value) \ + do { \ + av_unused uint32_t bit = value; \ + xuia(1, string, bit, value, value, 0, ); \ + } while(0) + + +#define READ +#define READWRITE read +#define RWContext GetBitContext + +#define xuia(width, string, var, range_min, range_max, subs, ...) \ + do { \ + uint32_t value; \ + CHECK(ff_cbs_read_unsigned(ctx, rw, width, string, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + &value, range_min, range_max)); \ + var = value; \ + } while(0) + +#define xsi(width, name, var, subs, ...) \ + do { \ + int32_t value; \ + CHECK(ff_cbs_read_signed(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), &value, \ + MIN_INT_BITS(width), \ + MAX_INT_BITS(width))); \ + var = value; \ + } while(0) + +#define nextbits(width, compare, var) \ + (get_bits_left(rw) >= width && \ + (var = show_bits(rw, width)) == (compare)) + +#define infer(name, value) \ + do { \ + current->name = value; \ + } while(0) + +#include "cbs_mpeg2_syntax_template.c" + +#undef READ +#undef READWRITE +#undef RWContext +#undef xuia +#undef xsi +#undef nextbits +#undef infer + + +#define WRITE +#define READWRITE write +#define RWContext PutBitContext + +#define xuia(width, string, var, range_min, range_max, subs, ...) \ + do { \ + CHECK(ff_cbs_write_unsigned(ctx, rw, width, string, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + var, range_min, range_max)); \ + } while(0) + +#define xsi(width, name, var, subs, ...) \ + do { \ + CHECK(ff_cbs_write_signed(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), var, \ + MIN_INT_BITS(width), \ + MAX_INT_BITS(width))); \ + } while(0) + +#define nextbits(width, compare, var) (var) + +#define infer(name, value) \ + do { \ + if(current->name != (value)) { \ + av_log(ctx->log_ctx, AV_LOG_WARNING, "Warning: " \ + "%s does not match inferred value: " \ + "%" PRId64 ", but should be %" PRId64 ".\n", \ + #name, (int64_t)current->name, (int64_t)(value)); \ + } \ + } while(0) + +#include "cbs_mpeg2_syntax_template.c" + +#undef WRITE +#undef READWRITE +#undef RWContext +#undef xuia +#undef xsi +#undef nextbits +#undef infer + +static const uint8_t *avpriv_find_start_code(const uint8_t *restrict p, + const uint8_t *end, + uint32_t *restrict state) { + int i; + + av_assert0(p <= end); + if(p >= end) + return end; + + for(i = 0; i < 3; i++) { + uint32_t tmp = *state << 8; + *state = tmp + *(p++); + if(tmp == 0x100 || p == end) + return p; + } + + while(p < end) { + if(p[-1] > 1) p += 3; + else if(p[-2]) + p += 2; + else if(p[-3] | (p[-1] - 1)) + p++; + else { + p++; + break; + } + } + + p = FFMIN(p, end) - 4; + *state = AV_RB32(p); + + return p + 4; +} + +static int cbs_mpeg2_split_fragment(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + int header) { + const uint8_t *start, *end; + CodedBitstreamUnitType unit_type; + uint32_t start_code = -1; + size_t unit_size; + int err, i, final = 0; + + start = avpriv_find_start_code(frag->data, frag->data + frag->data_size, + &start_code); + if(start_code >> 8 != 0x000001) { + // No start code found. + return AVERROR_INVALIDDATA; + } + + for(i = 0;; i++) { + unit_type = start_code & 0xff; + + if(start == frag->data + frag->data_size) { + // The last four bytes form a start code which constitutes + // a unit of its own. In this situation avpriv_find_start_code + // won't modify start_code at all so modify start_code so that + // the next unit will be treated as the last unit. + start_code = 0; + } + + end = avpriv_find_start_code(start--, frag->data + frag->data_size, + &start_code); + + // start points to the byte containing the start_code_identifier + // (may be the last byte of fragment->data); end points to the byte + // following the byte containing the start code identifier (or to + // the end of fragment->data). + if(start_code >> 8 == 0x000001) { + // Unit runs from start to the beginning of the start code + // pointed to by end (including any padding zeroes). + unit_size = (end - 4) - start; + } + else { + // We didn't find a start code, so this is the final unit. + unit_size = end - start; + final = 1; + } + + err = ff_cbs_insert_unit_data(frag, i, unit_type, (uint8_t *)start, + unit_size, frag->data_ref); + if(err < 0) + return err; + + if(final) + break; + + start = end; + } + + return 0; +} + +static int cbs_mpeg2_read_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) { + GetBitContext gbc; + int err; + + err = init_get_bits(&gbc, unit->data, 8 * unit->data_size); + if(err < 0) + return err; + + err = ff_cbs_alloc_unit_content2(ctx, unit); + if(err < 0) + return err; + + if(MPEG2_START_IS_SLICE(unit->type)) { + MPEG2RawSlice *slice = unit->content; + int pos, len; + + err = cbs_mpeg2_read_slice_header(ctx, &gbc, &slice->header); + if(err < 0) + return err; + + if(!get_bits_left(&gbc)) + return AVERROR_INVALIDDATA; + + pos = get_bits_count(&gbc); + len = unit->data_size; + + slice->data_size = len - pos / 8; + slice->data_ref = av_buffer_ref(unit->data_ref); + if(!slice->data_ref) + return AVERROR(ENOMEM); + slice->data = unit->data + pos / 8; + + slice->data_bit_start = pos % 8; + } + else { + switch(unit->type) { +#define START(start_code, type, read_func, free_func) \ + case start_code: { \ + type *header = unit->content; \ + err = cbs_mpeg2_read_##read_func(ctx, &gbc, header); \ + if(err < 0) \ + return err; \ + } break; + START(MPEG2_START_PICTURE, MPEG2RawPictureHeader, + picture_header, &cbs_mpeg2_free_picture_header); + START(MPEG2_START_USER_DATA, MPEG2RawUserData, + user_data, &cbs_mpeg2_free_user_data); + START(MPEG2_START_SEQUENCE_HEADER, MPEG2RawSequenceHeader, + sequence_header, NULL); + START(MPEG2_START_EXTENSION, MPEG2RawExtensionData, + extension_data, NULL); + START(MPEG2_START_GROUP, MPEG2RawGroupOfPicturesHeader, + group_of_pictures_header, NULL); + START(MPEG2_START_SEQUENCE_END, MPEG2RawSequenceEnd, + sequence_end, NULL); +#undef START + default: + return AVERROR(ENOSYS); + } + } + + return 0; +} + +static int cbs_mpeg2_write_header(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + PutBitContext *pbc) { + int err; + + switch(unit->type) { +#define START(start_code, type, func) \ + case start_code: \ + err = cbs_mpeg2_write_##func(ctx, pbc, unit->content); \ + break; + START(MPEG2_START_PICTURE, MPEG2RawPictureHeader, picture_header); + START(MPEG2_START_USER_DATA, MPEG2RawUserData, user_data); + START(MPEG2_START_SEQUENCE_HEADER, MPEG2RawSequenceHeader, sequence_header); + START(MPEG2_START_EXTENSION, MPEG2RawExtensionData, extension_data); + START(MPEG2_START_GROUP, MPEG2RawGroupOfPicturesHeader, + group_of_pictures_header); + START(MPEG2_START_SEQUENCE_END, MPEG2RawSequenceEnd, sequence_end); +#undef START + default: + av_log(ctx->log_ctx, AV_LOG_ERROR, "Write unimplemented for start " + "code %02" PRIx32 ".\n", + unit->type); + return AVERROR_PATCHWELCOME; + } + + return err; +} + +static int cbs_mpeg2_write_slice(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + PutBitContext *pbc) { + MPEG2RawSlice *slice = unit->content; + int err; + + err = cbs_mpeg2_write_slice_header(ctx, pbc, &slice->header); + if(err < 0) + return err; + + if(slice->data) { + size_t rest = slice->data_size - (slice->data_bit_start + 7) / 8; + uint8_t *pos = slice->data + slice->data_bit_start / 8; + + av_assert0(slice->data_bit_start >= 0 && + slice->data_size > slice->data_bit_start / 8); + + if(slice->data_size * 8 + 8 > put_bits_left(pbc)) + return AVERROR(ENOSPC); + + // First copy the remaining bits of the first byte + if(slice->data_bit_start % 8) + put_bits(pbc, 8 - slice->data_bit_start % 8, + *pos++ & MAX_UINT_BITS(8 - slice->data_bit_start % 8)); + + if(put_bits_count(pbc) % 8 == 0) { + // If the writer is aligned at this point, + // memcpy can be used to improve performance. + // This is the normal case. + flush_put_bits(pbc); + memcpy(put_bits_ptr(pbc), pos, rest); + skip_put_bytes(pbc, rest); + } + else { + // If not, we have to copy manually: + for(; rest > 3; rest -= 4, pos += 4) + put_bits32(pbc, AV_RB32(pos)); + + for(; rest; rest--, pos++) + put_bits(pbc, 8, *pos); + + // Align with zeros + put_bits(pbc, 8 - put_bits_count(pbc) % 8, 0); + } + } + + return 0; +} + +static int cbs_mpeg2_write_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + PutBitContext *pbc) { + if(MPEG2_START_IS_SLICE(unit->type)) + return cbs_mpeg2_write_slice(ctx, unit, pbc); + else + return cbs_mpeg2_write_header(ctx, unit, pbc); +} + +static int cbs_mpeg2_assemble_fragment(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag) { + uint8_t *data; + size_t size, dp; + int i; + + size = 0; + for(i = 0; i < frag->nb_units; i++) + size += 3 + frag->units[i].data_size; + + frag->data_ref = av_buffer_alloc(size + AV_INPUT_BUFFER_PADDING_SIZE); + if(!frag->data_ref) + return AVERROR(ENOMEM); + data = frag->data_ref->data; + + dp = 0; + for(i = 0; i < frag->nb_units; i++) { + CodedBitstreamUnit *unit = &frag->units[i]; + + data[dp++] = 0; + data[dp++] = 0; + data[dp++] = 1; + + memcpy(data + dp, unit->data, unit->data_size); + dp += unit->data_size; + } + + av_assert0(dp == size); + + memset(data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + frag->data = data; + frag->data_size = size; + + return 0; +} + +static const CodedBitstreamUnitTypeDescriptor cbs_mpeg2_unit_types[] = { + CBS_UNIT_TYPE_INTERNAL_REF(MPEG2_START_PICTURE, MPEG2RawPictureHeader, + extra_information_picture.extra_information), + + { + .nb_unit_types = CBS_UNIT_TYPE_RANGE, + .unit_type_range_start = 0x01, + .unit_type_range_end = 0xaf, + + .content_type = CBS_CONTENT_TYPE_INTERNAL_REFS, + .content_size = sizeof(MPEG2RawSlice), + .nb_ref_offsets = 2, + .ref_offsets = { offsetof(MPEG2RawSlice, header.extra_information_slice.extra_information), + offsetof(MPEG2RawSlice, data) }, + }, + + CBS_UNIT_TYPE_INTERNAL_REF(MPEG2_START_USER_DATA, MPEG2RawUserData, + user_data), + + CBS_UNIT_TYPE_POD(MPEG2_START_SEQUENCE_HEADER, MPEG2RawSequenceHeader), + CBS_UNIT_TYPE_POD(MPEG2_START_EXTENSION, MPEG2RawExtensionData), + CBS_UNIT_TYPE_POD(MPEG2_START_SEQUENCE_END, MPEG2RawSequenceEnd), + CBS_UNIT_TYPE_POD(MPEG2_START_GROUP, MPEG2RawGroupOfPicturesHeader), + + CBS_UNIT_TYPE_END_OF_LIST +}; + +const CodedBitstreamType ff_cbs_type_mpeg2 = { + .codec_id = AV_CODEC_ID_MPEG2VIDEO, + + .priv_data_size = sizeof(CodedBitstreamMPEG2Context), + + .unit_types = cbs_mpeg2_unit_types, + + .split_fragment = &cbs_mpeg2_split_fragment, + .read_unit = &cbs_mpeg2_read_unit, + .write_unit = &cbs_mpeg2_write_unit, + .assemble_fragment = &cbs_mpeg2_assemble_fragment, +}; diff --git a/third-party/cbs/cbs_mpeg2_syntax_template.c b/third-party/cbs/cbs_mpeg2_syntax_template.c new file mode 100644 index 00000000..7fc1e80a --- /dev/null +++ b/third-party/cbs/cbs_mpeg2_syntax_template.c @@ -0,0 +1,413 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +static int FUNC(sequence_header)(CodedBitstreamContext *ctx, RWContext *rw, + MPEG2RawSequenceHeader *current) { + CodedBitstreamMPEG2Context *mpeg2 = ctx->priv_data; + int err, i; + + HEADER("Sequence Header"); + + ui(8, sequence_header_code); + + uir(12, horizontal_size_value); + uir(12, vertical_size_value); + + mpeg2->horizontal_size = current->horizontal_size_value; + mpeg2->vertical_size = current->vertical_size_value; + + uir(4, aspect_ratio_information); + uir(4, frame_rate_code); + ui(18, bit_rate_value); + + marker_bit(); + + ui(10, vbv_buffer_size_value); + ui(1, constrained_parameters_flag); + + ui(1, load_intra_quantiser_matrix); + if(current->load_intra_quantiser_matrix) { + for(i = 0; i < 64; i++) + uirs(8, intra_quantiser_matrix[i], 1, i); + } + + ui(1, load_non_intra_quantiser_matrix); + if(current->load_non_intra_quantiser_matrix) { + for(i = 0; i < 64; i++) + uirs(8, non_intra_quantiser_matrix[i], 1, i); + } + + return 0; +} + +static int FUNC(user_data)(CodedBitstreamContext *ctx, RWContext *rw, + MPEG2RawUserData *current) { + size_t k; + int err; + + HEADER("User Data"); + + ui(8, user_data_start_code); + +#ifdef READ + k = get_bits_left(rw); + av_assert0(k % 8 == 0); + current->user_data_length = k /= 8; + if(k > 0) { + current->user_data_ref = av_buffer_allocz(k + AV_INPUT_BUFFER_PADDING_SIZE); + if(!current->user_data_ref) + return AVERROR(ENOMEM); + current->user_data = current->user_data_ref->data; + } +#endif + + for(k = 0; k < current->user_data_length; k++) + uis(8, user_data[k], 1, k); + + return 0; +} + +static int FUNC(sequence_extension)(CodedBitstreamContext *ctx, RWContext *rw, + MPEG2RawSequenceExtension *current) { + CodedBitstreamMPEG2Context *mpeg2 = ctx->priv_data; + int err; + + HEADER("Sequence Extension"); + + ui(8, profile_and_level_indication); + ui(1, progressive_sequence); + ui(2, chroma_format); + ui(2, horizontal_size_extension); + ui(2, vertical_size_extension); + + mpeg2->horizontal_size = (mpeg2->horizontal_size & 0xfff) | + current->horizontal_size_extension << 12; + mpeg2->vertical_size = (mpeg2->vertical_size & 0xfff) | + current->vertical_size_extension << 12; + mpeg2->progressive_sequence = current->progressive_sequence; + + ui(12, bit_rate_extension); + marker_bit(); + ui(8, vbv_buffer_size_extension); + ui(1, low_delay); + ui(2, frame_rate_extension_n); + ui(5, frame_rate_extension_d); + + return 0; +} + +static int FUNC(sequence_display_extension)(CodedBitstreamContext *ctx, RWContext *rw, + MPEG2RawSequenceDisplayExtension *current) { + int err; + + HEADER("Sequence Display Extension"); + + ui(3, video_format); + + ui(1, colour_description); + if(current->colour_description) { +#ifdef READ +#define READ_AND_PATCH(name) \ + do { \ + ui(8, name); \ + if(current->name == 0) { \ + current->name = 2; \ + av_log(ctx->log_ctx, AV_LOG_WARNING, "%s in a sequence display " \ + "extension had the invalid value 0. Setting it to 2 " \ + "(meaning unknown) instead.\n", \ + #name); \ + } \ + } while(0) + READ_AND_PATCH(colour_primaries); + READ_AND_PATCH(transfer_characteristics); + READ_AND_PATCH(matrix_coefficients); +#undef READ_AND_PATCH +#else + uir(8, colour_primaries); + uir(8, transfer_characteristics); + uir(8, matrix_coefficients); +#endif + } + else { + infer(colour_primaries, 2); + infer(transfer_characteristics, 2); + infer(matrix_coefficients, 2); + } + + ui(14, display_horizontal_size); + marker_bit(); + ui(14, display_vertical_size); + + return 0; +} + +static int FUNC(group_of_pictures_header)(CodedBitstreamContext *ctx, RWContext *rw, + MPEG2RawGroupOfPicturesHeader *current) { + int err; + + HEADER("Group of Pictures Header"); + + ui(8, group_start_code); + + ui(25, time_code); + ui(1, closed_gop); + ui(1, broken_link); + + return 0; +} + +static int FUNC(extra_information)(CodedBitstreamContext *ctx, RWContext *rw, + MPEG2RawExtraInformation *current, + const char *element_name, const char *marker_name) { + int err; + size_t k; +#ifdef READ + GetBitContext start = *rw; + uint8_t bit; + + for(k = 0; nextbits(1, 1, bit); k++) + skip_bits(rw, 1 + 8); + current->extra_information_length = k; + if(k > 0) { + *rw = start; + current->extra_information_ref = + av_buffer_allocz(k + AV_INPUT_BUFFER_PADDING_SIZE); + if(!current->extra_information_ref) + return AVERROR(ENOMEM); + current->extra_information = current->extra_information_ref->data; + } +#endif + + for(k = 0; k < current->extra_information_length; k++) { + bit(marker_name, 1); + xuia(8, element_name, + current->extra_information[k], 0, 255, 1, k); + } + + bit(marker_name, 0); + + return 0; +} + +static int FUNC(picture_header)(CodedBitstreamContext *ctx, RWContext *rw, + MPEG2RawPictureHeader *current) { + int err; + + HEADER("Picture Header"); + + ui(8, picture_start_code); + + ui(10, temporal_reference); + uir(3, picture_coding_type); + ui(16, vbv_delay); + + if(current->picture_coding_type == 2 || + current->picture_coding_type == 3) { + ui(1, full_pel_forward_vector); + ui(3, forward_f_code); + } + + if(current->picture_coding_type == 3) { + ui(1, full_pel_backward_vector); + ui(3, backward_f_code); + } + + CHECK(FUNC(extra_information)(ctx, rw, ¤t->extra_information_picture, + "extra_information_picture[k]", "extra_bit_picture")); + + return 0; +} + +static int FUNC(picture_coding_extension)(CodedBitstreamContext *ctx, RWContext *rw, + MPEG2RawPictureCodingExtension *current) { + CodedBitstreamMPEG2Context *mpeg2 = ctx->priv_data; + int err; + + HEADER("Picture Coding Extension"); + + uir(4, f_code[0][0]); + uir(4, f_code[0][1]); + uir(4, f_code[1][0]); + uir(4, f_code[1][1]); + + ui(2, intra_dc_precision); + ui(2, picture_structure); + ui(1, top_field_first); + ui(1, frame_pred_frame_dct); + ui(1, concealment_motion_vectors); + ui(1, q_scale_type); + ui(1, intra_vlc_format); + ui(1, alternate_scan); + ui(1, repeat_first_field); + ui(1, chroma_420_type); + ui(1, progressive_frame); + + if(mpeg2->progressive_sequence) { + if(current->repeat_first_field) { + if(current->top_field_first) + mpeg2->number_of_frame_centre_offsets = 3; + else + mpeg2->number_of_frame_centre_offsets = 2; + } + else { + mpeg2->number_of_frame_centre_offsets = 1; + } + } + else { + if(current->picture_structure == 1 || // Top field. + current->picture_structure == 2) { // Bottom field. + mpeg2->number_of_frame_centre_offsets = 1; + } + else { + if(current->repeat_first_field) + mpeg2->number_of_frame_centre_offsets = 3; + else + mpeg2->number_of_frame_centre_offsets = 2; + } + } + + ui(1, composite_display_flag); + if(current->composite_display_flag) { + ui(1, v_axis); + ui(3, field_sequence); + ui(1, sub_carrier); + ui(7, burst_amplitude); + ui(8, sub_carrier_phase); + } + + return 0; +} + +static int FUNC(quant_matrix_extension)(CodedBitstreamContext *ctx, RWContext *rw, + MPEG2RawQuantMatrixExtension *current) { + int err, i; + + HEADER("Quant Matrix Extension"); + + ui(1, load_intra_quantiser_matrix); + if(current->load_intra_quantiser_matrix) { + for(i = 0; i < 64; i++) + uirs(8, intra_quantiser_matrix[i], 1, i); + } + + ui(1, load_non_intra_quantiser_matrix); + if(current->load_non_intra_quantiser_matrix) { + for(i = 0; i < 64; i++) + uirs(8, non_intra_quantiser_matrix[i], 1, i); + } + + ui(1, load_chroma_intra_quantiser_matrix); + if(current->load_chroma_intra_quantiser_matrix) { + for(i = 0; i < 64; i++) + uirs(8, intra_quantiser_matrix[i], 1, i); + } + + ui(1, load_chroma_non_intra_quantiser_matrix); + if(current->load_chroma_non_intra_quantiser_matrix) { + for(i = 0; i < 64; i++) + uirs(8, chroma_non_intra_quantiser_matrix[i], 1, i); + } + + return 0; +} + +static int FUNC(picture_display_extension)(CodedBitstreamContext *ctx, RWContext *rw, + MPEG2RawPictureDisplayExtension *current) { + CodedBitstreamMPEG2Context *mpeg2 = ctx->priv_data; + int err, i; + + HEADER("Picture Display Extension"); + + for(i = 0; i < mpeg2->number_of_frame_centre_offsets; i++) { + sis(16, frame_centre_horizontal_offset[i], 1, i); + marker_bit(); + sis(16, frame_centre_vertical_offset[i], 1, i); + marker_bit(); + } + + return 0; +} + +static int FUNC(extension_data)(CodedBitstreamContext *ctx, RWContext *rw, + MPEG2RawExtensionData *current) { + int err; + + HEADER("Extension Data"); + + ui(8, extension_start_code); + ui(4, extension_start_code_identifier); + + switch(current->extension_start_code_identifier) { + case MPEG2_EXTENSION_SEQUENCE: + return FUNC(sequence_extension)(ctx, rw, ¤t->data.sequence); + case MPEG2_EXTENSION_SEQUENCE_DISPLAY: + return FUNC(sequence_display_extension)(ctx, rw, ¤t->data.sequence_display); + case MPEG2_EXTENSION_QUANT_MATRIX: + return FUNC(quant_matrix_extension)(ctx, rw, ¤t->data.quant_matrix); + case MPEG2_EXTENSION_PICTURE_DISPLAY: + return FUNC(picture_display_extension)(ctx, rw, ¤t->data.picture_display); + case MPEG2_EXTENSION_PICTURE_CODING: + return FUNC(picture_coding_extension)(ctx, rw, ¤t->data.picture_coding); + default: + av_log(ctx->log_ctx, AV_LOG_ERROR, "Extension ID %d not supported.\n", + current->extension_start_code_identifier); + return AVERROR_PATCHWELCOME; + } +} + +static int FUNC(slice_header)(CodedBitstreamContext *ctx, RWContext *rw, + MPEG2RawSliceHeader *current) { + CodedBitstreamMPEG2Context *mpeg2 = ctx->priv_data; + int err; + + HEADER("Slice Header"); + + ui(8, slice_vertical_position); + + if(mpeg2->vertical_size > 2800) + ui(3, slice_vertical_position_extension); + if(mpeg2->scalable) { + if(mpeg2->scalable_mode == 0) + ui(7, priority_breakpoint); + } + + uir(5, quantiser_scale_code); + + if(nextbits(1, 1, current->slice_extension_flag)) { + ui(1, slice_extension_flag); + ui(1, intra_slice); + ui(1, slice_picture_id_enable); + ui(6, slice_picture_id); + } + + CHECK(FUNC(extra_information)(ctx, rw, ¤t->extra_information_slice, + "extra_information_slice[k]", "extra_bit_slice")); + + return 0; +} + +static int FUNC(sequence_end)(CodedBitstreamContext *ctx, RWContext *rw, + MPEG2RawSequenceEnd *current) { + int err; + + HEADER("Sequence End"); + + ui(8, sequence_end_code); + + return 0; +} diff --git a/third-party/cbs/cbs_sei.c b/third-party/cbs/cbs_sei.c new file mode 100644 index 00000000..c184d67d --- /dev/null +++ b/third-party/cbs/cbs_sei.c @@ -0,0 +1,355 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "cbs/cbs_sei.h" +#include "cbs/cbs.h" +#include "cbs/cbs_h264.h" +#include "cbs/cbs_h265.h" + +#include "cbs_internal.h" + +static void cbs_free_user_data_registered(void *opaque, uint8_t *data) { + SEIRawUserDataRegistered *udr = (SEIRawUserDataRegistered *)data; + av_buffer_unref(&udr->data_ref); + av_free(udr); +} + +static void cbs_free_user_data_unregistered(void *opaque, uint8_t *data) { + SEIRawUserDataUnregistered *udu = (SEIRawUserDataUnregistered *)data; + av_buffer_unref(&udu->data_ref); + av_free(udu); +} + +int ff_cbs_sei_alloc_message_payload(SEIRawMessage *message, + const SEIMessageTypeDescriptor *desc) { + void (*free_func)(void *, uint8_t *); + + av_assert0(message->payload == NULL && + message->payload_ref == NULL); + message->payload_type = desc->type; + + if(desc->type == SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35) + free_func = &cbs_free_user_data_registered; + else if(desc->type == SEI_TYPE_USER_DATA_UNREGISTERED) + free_func = &cbs_free_user_data_unregistered; + else + free_func = NULL; + + if(free_func) { + message->payload = av_mallocz(desc->size); + if(!message->payload) + return AVERROR(ENOMEM); + message->payload_ref = + av_buffer_create(message->payload, desc->size, + free_func, NULL, 0); + } + else { + message->payload_ref = av_buffer_alloc(desc->size); + } + if(!message->payload_ref) { + av_freep(&message->payload); + return AVERROR(ENOMEM); + } + message->payload = message->payload_ref->data; + + return 0; +} + +int ff_cbs_sei_list_add(SEIRawMessageList *list) { + void *ptr; + int old_count = list->nb_messages_allocated; + + av_assert0(list->nb_messages <= old_count); + if(list->nb_messages + 1 > old_count) { + int new_count = 2 * old_count + 1; + + ptr = av_realloc_array(list->messages, + new_count, sizeof(*list->messages)); + if(!ptr) + return AVERROR(ENOMEM); + + list->messages = ptr; + list->nb_messages_allocated = new_count; + + // Zero the newly-added entries. + memset(list->messages + old_count, 0, + (new_count - old_count) * sizeof(*list->messages)); + } + ++list->nb_messages; + return 0; +} + +void ff_cbs_sei_free_message_list(SEIRawMessageList *list) { + for(int i = 0; i < list->nb_messages; i++) { + SEIRawMessage *message = &list->messages[i]; + av_buffer_unref(&message->payload_ref); + av_buffer_unref(&message->extension_data_ref); + } + av_free(list->messages); +} + +static int cbs_sei_get_unit(CodedBitstreamContext *ctx, + CodedBitstreamFragment *au, + int prefix, + CodedBitstreamUnit **sei_unit) { + CodedBitstreamUnit *unit; + int sei_type, highest_vcl_type, err, i, position; + + switch(ctx->codec->codec_id) { + case AV_CODEC_ID_H264: + // (We can ignore auxiliary slices because we only have prefix + // SEI in H.264 and an auxiliary picture must always follow a + // primary picture.) + highest_vcl_type = H264_NAL_IDR_SLICE; + if(prefix) + sei_type = H264_NAL_SEI; + else + return AVERROR(EINVAL); + break; + case AV_CODEC_ID_H265: + highest_vcl_type = HEVC_NAL_RSV_VCL31; + if(prefix) + sei_type = HEVC_NAL_SEI_PREFIX; + else + sei_type = HEVC_NAL_SEI_SUFFIX; + break; + default: + return AVERROR(EINVAL); + } + + // Find an existing SEI NAL unit of the right type. + unit = NULL; + for(i = 0; i < au->nb_units; i++) { + if(au->units[i].type == sei_type) { + unit = &au->units[i]; + break; + } + } + + if(unit) { + *sei_unit = unit; + return 0; + } + + // Need to add a new SEI NAL unit ... + if(prefix) { + // ... before the first VCL NAL unit. + for(i = 0; i < au->nb_units; i++) { + if(au->units[i].type < highest_vcl_type) + break; + } + position = i; + } + else { + // ... after the last VCL NAL unit. + for(i = au->nb_units - 1; i >= 0; i--) { + if(au->units[i].type < highest_vcl_type) + break; + } + if(i < 0) { + // No VCL units; just put it at the end. + position = au->nb_units; + } + else { + position = i + 1; + } + } + + err = ff_cbs_insert_unit_content(au, position, sei_type, + NULL, NULL); + if(err < 0) + return err; + unit = &au->units[position]; + unit->type = sei_type; + + err = ff_cbs_alloc_unit_content2(ctx, unit); + if(err < 0) + return err; + + switch(ctx->codec->codec_id) { + case AV_CODEC_ID_H264: { + H264RawSEI sei = { + .nal_unit_header = { + .nal_ref_idc = 0, + .nal_unit_type = sei_type, + }, + }; + memcpy(unit->content, &sei, sizeof(sei)); + } break; + case AV_CODEC_ID_H265: { + H265RawSEI sei = { + .nal_unit_header = { + .nal_unit_type = sei_type, + .nuh_layer_id = 0, + .nuh_temporal_id_plus1 = 1, + }, + }; + memcpy(unit->content, &sei, sizeof(sei)); + } break; + default: + av_assert0(0); + } + + *sei_unit = unit; + return 0; +} + +static int cbs_sei_get_message_list(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + SEIRawMessageList **list) { + switch(ctx->codec->codec_id) { + case AV_CODEC_ID_H264: { + H264RawSEI *sei = unit->content; + if(unit->type != H264_NAL_SEI) + return AVERROR(EINVAL); + *list = &sei->message_list; + } break; + case AV_CODEC_ID_H265: { + H265RawSEI *sei = unit->content; + if(unit->type != HEVC_NAL_SEI_PREFIX && + unit->type != HEVC_NAL_SEI_SUFFIX) + return AVERROR(EINVAL); + *list = &sei->message_list; + } break; + default: + return AVERROR(EINVAL); + } + + return 0; +} + +int ff_cbs_sei_add_message(CodedBitstreamContext *ctx, + CodedBitstreamFragment *au, + int prefix, + uint32_t payload_type, + void *payload_data, + AVBufferRef *payload_buf) { + const SEIMessageTypeDescriptor *desc; + CodedBitstreamUnit *unit; + SEIRawMessageList *list; + SEIRawMessage *message; + AVBufferRef *payload_ref; + int err; + + desc = ff_cbs_sei_find_type(ctx, payload_type); + if(!desc) + return AVERROR(EINVAL); + + // Find an existing SEI unit or make a new one to add to. + err = cbs_sei_get_unit(ctx, au, prefix, &unit); + if(err < 0) + return err; + + // Find the message list inside the codec-dependent unit. + err = cbs_sei_get_message_list(ctx, unit, &list); + if(err < 0) + return err; + + // Add a new message to the message list. + err = ff_cbs_sei_list_add(list); + if(err < 0) + return err; + + if(payload_buf) { + payload_ref = av_buffer_ref(payload_buf); + if(!payload_ref) + return AVERROR(ENOMEM); + } + else { + payload_ref = NULL; + } + + message = &list->messages[list->nb_messages - 1]; + + message->payload_type = payload_type; + message->payload = payload_data; + message->payload_ref = payload_ref; + + return 0; +} + +int ff_cbs_sei_find_message(CodedBitstreamContext *ctx, + CodedBitstreamFragment *au, + uint32_t payload_type, + SEIRawMessage **iter) { + int err, i, j, found; + + found = 0; + for(i = 0; i < au->nb_units; i++) { + CodedBitstreamUnit *unit = &au->units[i]; + SEIRawMessageList *list; + + err = cbs_sei_get_message_list(ctx, unit, &list); + if(err < 0) + continue; + + for(j = 0; j < list->nb_messages; j++) { + SEIRawMessage *message = &list->messages[j]; + + if(message->payload_type == payload_type) { + if(!*iter || found) { + *iter = message; + return 0; + } + if(message == *iter) + found = 1; + } + } + } + + return AVERROR(ENOENT); +} + +static void cbs_sei_delete_message(SEIRawMessageList *list, + int position) { + SEIRawMessage *message; + + av_assert0(0 <= position && position < list->nb_messages); + + message = &list->messages[position]; + av_buffer_unref(&message->payload_ref); + av_buffer_unref(&message->extension_data_ref); + + --list->nb_messages; + + if(list->nb_messages > 0) { + memmove(list->messages + position, + list->messages + position + 1, + (list->nb_messages - position) * sizeof(*list->messages)); + } +} + +void ff_cbs_sei_delete_message_type(CodedBitstreamContext *ctx, + CodedBitstreamFragment *au, + uint32_t payload_type) { + int err, i, j; + + for(i = 0; i < au->nb_units; i++) { + CodedBitstreamUnit *unit = &au->units[i]; + SEIRawMessageList *list; + + err = cbs_sei_get_message_list(ctx, unit, &list); + if(err < 0) + continue; + + for(j = list->nb_messages - 1; j >= 0; j--) { + if(list->messages[j].payload_type == payload_type) + cbs_sei_delete_message(list, j); + } + } +} diff --git a/third-party/cbs/cbs_sei_syntax_template.c b/third-party/cbs/cbs_sei_syntax_template.c new file mode 100644 index 00000000..e9688013 --- /dev/null +++ b/third-party/cbs/cbs_sei_syntax_template.c @@ -0,0 +1,310 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +static int FUNC(filler_payload)(CodedBitstreamContext *ctx, RWContext *rw, + SEIRawFillerPayload *current, SEIMessageState *state) { + int err, i; + + HEADER("Filler Payload"); + +#ifdef READ + current->payload_size = state->payload_size; +#endif + + for(i = 0; i < current->payload_size; i++) + fixed(8, ff_byte, 0xff); + + return 0; +} + +static int FUNC(user_data_registered)(CodedBitstreamContext *ctx, RWContext *rw, + SEIRawUserDataRegistered *current, SEIMessageState *state) { + int err, i, j; + + HEADER("User Data Registered ITU-T T.35"); + + u(8, itu_t_t35_country_code, 0x00, 0xff); + if(current->itu_t_t35_country_code != 0xff) + i = 1; + else { + u(8, itu_t_t35_country_code_extension_byte, 0x00, 0xff); + i = 2; + } + +#ifdef READ + if(state->payload_size < i) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Invalid SEI user data registered payload.\n"); + return AVERROR_INVALIDDATA; + } + current->data_length = state->payload_size - i; +#endif + + allocate(current->data, current->data_length); + for(j = 0; j < current->data_length; j++) + xu(8, itu_t_t35_payload_byte[], current->data[j], 0x00, 0xff, 1, i + j); + + return 0; +} + +static int FUNC(user_data_unregistered)(CodedBitstreamContext *ctx, RWContext *rw, + SEIRawUserDataUnregistered *current, SEIMessageState *state) { + int err, i; + + HEADER("User Data Unregistered"); + +#ifdef READ + if(state->payload_size < 16) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Invalid SEI user data unregistered payload.\n"); + return AVERROR_INVALIDDATA; + } + current->data_length = state->payload_size - 16; +#endif + + for(i = 0; i < 16; i++) + us(8, uuid_iso_iec_11578[i], 0x00, 0xff, 1, i); + + allocate(current->data, current->data_length); + + for(i = 0; i < current->data_length; i++) + xu(8, user_data_payload_byte[i], current->data[i], 0x00, 0xff, 1, i); + + return 0; +} + +static int FUNC(mastering_display_colour_volume)(CodedBitstreamContext *ctx, RWContext *rw, + SEIRawMasteringDisplayColourVolume *current, SEIMessageState *state) { + int err, c; + + HEADER("Mastering Display Colour Volume"); + + for(c = 0; c < 3; c++) { + ubs(16, display_primaries_x[c], 1, c); + ubs(16, display_primaries_y[c], 1, c); + } + + ub(16, white_point_x); + ub(16, white_point_y); + + ub(32, max_display_mastering_luminance); + ub(32, min_display_mastering_luminance); + + return 0; +} + +static int FUNC(content_light_level_info)(CodedBitstreamContext *ctx, RWContext *rw, + SEIRawContentLightLevelInfo *current, SEIMessageState *state) { + int err; + + HEADER("Content Light Level Information"); + + ub(16, max_content_light_level); + ub(16, max_pic_average_light_level); + + return 0; +} + +static int FUNC(alternative_transfer_characteristics)(CodedBitstreamContext *ctx, RWContext *rw, + SEIRawAlternativeTransferCharacteristics *current, + SEIMessageState *state) { + int err; + + HEADER("Alternative Transfer Characteristics"); + + ub(8, preferred_transfer_characteristics); + + return 0; +} + +static int FUNC(message)(CodedBitstreamContext *ctx, RWContext *rw, + SEIRawMessage *current) { + const SEIMessageTypeDescriptor *desc; + int err, i; + + desc = ff_cbs_sei_find_type(ctx, current->payload_type); + if(desc) { + SEIMessageState state = { + .payload_type = current->payload_type, + .payload_size = current->payload_size, + .extension_present = current->extension_bit_length > 0, + }; + int start_position, current_position, bits_written; + +#ifdef READ + CHECK(ff_cbs_sei_alloc_message_payload(current, desc)); +#endif + + start_position = bit_position(rw); + + CHECK(desc->READWRITE(ctx, rw, current->payload, &state)); + + current_position = bit_position(rw); + bits_written = current_position - start_position; + + if(byte_alignment(rw) || state.extension_present || + bits_written < 8 * current->payload_size) { + size_t bits_left; + +#ifdef READ + GetBitContext tmp = *rw; + int trailing_bits, trailing_zero_bits; + + bits_left = 8 * current->payload_size - bits_written; + if(bits_left > 8) + skip_bits_long(&tmp, bits_left - 8); + trailing_bits = get_bits(&tmp, FFMIN(bits_left, 8)); + if(trailing_bits == 0) { + // The trailing bits must contain a bit_equal_to_one, so + // they can't all be zero. + return AVERROR_INVALIDDATA; + } + trailing_zero_bits = ff_ctz(trailing_bits); + current->extension_bit_length = + bits_left - 1 - trailing_zero_bits; +#endif + + if(current->extension_bit_length > 0) { + allocate(current->extension_data, + (current->extension_bit_length + 7) / 8); + + bits_left = current->extension_bit_length; + for(i = 0; bits_left > 0; i++) { + int length = FFMIN(bits_left, 8); + xu(length, reserved_payload_extension_data, + current->extension_data[i], + 0, MAX_UINT_BITS(length), 0); + bits_left -= length; + } + } + + fixed(1, bit_equal_to_one, 1); + while(byte_alignment(rw)) + fixed(1, bit_equal_to_zero, 0); + } + +#ifdef WRITE + current->payload_size = (put_bits_count(rw) - start_position) / 8; +#endif + } + else { + uint8_t *data; + + allocate(current->payload, current->payload_size); + data = current->payload; + + for(i = 0; i < current->payload_size; i++) + xu(8, payload_byte[i], data[i], 0, 255, 1, i); + } + + return 0; +} + +static int FUNC(message_list)(CodedBitstreamContext *ctx, RWContext *rw, + SEIRawMessageList *current, int prefix) { + SEIRawMessage *message; + int err, k; + +#ifdef READ + for(k = 0;; k++) { + uint32_t payload_type = 0; + uint32_t payload_size = 0; + uint32_t tmp; + GetBitContext payload_gbc; + + while(show_bits(rw, 8) == 0xff) { + fixed(8, ff_byte, 0xff); + payload_type += 255; + } + xu(8, last_payload_type_byte, tmp, 0, 254, 0); + payload_type += tmp; + + while(show_bits(rw, 8) == 0xff) { + fixed(8, ff_byte, 0xff); + payload_size += 255; + } + xu(8, last_payload_size_byte, tmp, 0, 254, 0); + payload_size += tmp; + + // There must be space remaining for both the payload and + // the trailing bits on the SEI NAL unit. + if(payload_size + 1 > get_bits_left(rw) / 8) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Invalid SEI message: payload_size too large " + "(%" PRIu32 " bytes).\n", + payload_size); + return AVERROR_INVALIDDATA; + } + CHECK(init_get_bits(&payload_gbc, rw->buffer, + get_bits_count(rw) + 8 * payload_size)); + skip_bits_long(&payload_gbc, get_bits_count(rw)); + + CHECK(ff_cbs_sei_list_add(current)); + message = ¤t->messages[k]; + + message->payload_type = payload_type; + message->payload_size = payload_size; + + CHECK(FUNC(message)(ctx, &payload_gbc, message)); + + skip_bits_long(rw, 8 * payload_size); + + if(!cbs_h2645_read_more_rbsp_data(rw)) + break; + } +#else + for(k = 0; k < current->nb_messages; k++) { + PutBitContext start_state; + uint32_t tmp; + int trace, i; + + message = ¤t->messages[k]; + + // We write the payload twice in order to find the size. Trace + // output is switched off for the first write. + trace = ctx->trace_enable; + ctx->trace_enable = 0; + + start_state = *rw; + for(i = 0; i < 2; i++) { + *rw = start_state; + + tmp = message->payload_type; + while(tmp >= 255) { + fixed(8, ff_byte, 0xff); + tmp -= 255; + } + xu(8, last_payload_type_byte, tmp, 0, 254, 0); + + tmp = message->payload_size; + while(tmp >= 255) { + fixed(8, ff_byte, 0xff); + tmp -= 255; + } + xu(8, last_payload_size_byte, tmp, 0, 254, 0); + + err = FUNC(message)(ctx, rw, message); + ctx->trace_enable = trace; + if(err < 0) + return err; + } + } +#endif + + return 0; +} diff --git a/third-party/cbs/cbs_vp9.c b/third-party/cbs/cbs_vp9.c new file mode 100644 index 00000000..74da5b03 --- /dev/null +++ b/third-party/cbs/cbs_vp9.c @@ -0,0 +1,675 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "cbs/cbs.h" +#include "cbs/cbs_vp9.h" +#include "cbs_internal.h" + + +static int cbs_vp9_read_s(CodedBitstreamContext *ctx, GetBitContext *gbc, + int width, const char *name, + const int *subscripts, int32_t *write_to) { + uint32_t magnitude; + int position, sign; + int32_t value; + + if(ctx->trace_enable) + position = get_bits_count(gbc); + + if(get_bits_left(gbc) < width + 1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid signed value at " + "%s: bitstream ended.\n", + name); + return AVERROR_INVALIDDATA; + } + + magnitude = get_bits(gbc, width); + sign = get_bits1(gbc); + value = sign ? -(int32_t)magnitude : magnitude; + + if(ctx->trace_enable) { + char bits[33]; + int i; + for(i = 0; i < width; i++) + bits[i] = magnitude >> (width - i - 1) & 1 ? '1' : '0'; + bits[i] = sign ? '1' : '0'; + bits[i + 1] = 0; + + ff_cbs_trace_syntax_element(ctx, position, name, subscripts, + bits, value); + } + + *write_to = value; + return 0; +} + +static int cbs_vp9_write_s(CodedBitstreamContext *ctx, PutBitContext *pbc, + int width, const char *name, + const int *subscripts, int32_t value) { + uint32_t magnitude; + int sign; + + if(put_bits_left(pbc) < width + 1) + return AVERROR(ENOSPC); + + sign = value < 0; + magnitude = sign ? -value : value; + + if(ctx->trace_enable) { + char bits[33]; + int i; + for(i = 0; i < width; i++) + bits[i] = magnitude >> (width - i - 1) & 1 ? '1' : '0'; + bits[i] = sign ? '1' : '0'; + bits[i + 1] = 0; + + ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), + name, subscripts, bits, value); + } + + put_bits(pbc, width, magnitude); + put_bits(pbc, 1, sign); + + return 0; +} + +static int cbs_vp9_read_increment(CodedBitstreamContext *ctx, GetBitContext *gbc, + uint32_t range_min, uint32_t range_max, + const char *name, uint32_t *write_to) { + uint32_t value; + int position, i; + char bits[8]; + + av_assert0(range_min <= range_max && range_max - range_min < sizeof(bits) - 1); + if(ctx->trace_enable) + position = get_bits_count(gbc); + + for(i = 0, value = range_min; value < range_max;) { + if(get_bits_left(gbc) < 1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid increment value at " + "%s: bitstream ended.\n", + name); + return AVERROR_INVALIDDATA; + } + if(get_bits1(gbc)) { + bits[i++] = '1'; + ++value; + } + else { + bits[i++] = '0'; + break; + } + } + + if(ctx->trace_enable) { + bits[i] = 0; + ff_cbs_trace_syntax_element(ctx, position, name, NULL, bits, value); + } + + *write_to = value; + return 0; +} + +static int cbs_vp9_write_increment(CodedBitstreamContext *ctx, PutBitContext *pbc, + uint32_t range_min, uint32_t range_max, + const char *name, uint32_t value) { + int len; + + av_assert0(range_min <= range_max && range_max - range_min < 8); + if(value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%" PRIu32 ", but must be in [%" PRIu32 ",%" PRIu32 "].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + + if(value == range_max) + len = range_max - range_min; + else + len = value - range_min + 1; + if(put_bits_left(pbc) < len) + return AVERROR(ENOSPC); + + if(ctx->trace_enable) { + char bits[8]; + int i; + for(i = 0; i < len; i++) { + if(range_min + i == value) + bits[i] = '0'; + else + bits[i] = '1'; + } + bits[i] = 0; + ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), + name, NULL, bits, value); + } + + if(len > 0) + put_bits(pbc, len, (1 << len) - 1 - (value != range_max)); + + return 0; +} + +static int cbs_vp9_read_le(CodedBitstreamContext *ctx, GetBitContext *gbc, + int width, const char *name, + const int *subscripts, uint32_t *write_to) { + uint32_t value; + int position, b; + + av_assert0(width % 8 == 0); + + if(ctx->trace_enable) + position = get_bits_count(gbc); + + if(get_bits_left(gbc) < width) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid le value at " + "%s: bitstream ended.\n", + name); + return AVERROR_INVALIDDATA; + } + + value = 0; + for(b = 0; b < width; b += 8) + value |= get_bits(gbc, 8) << b; + + if(ctx->trace_enable) { + char bits[33]; + int i; + for(b = 0; b < width; b += 8) + for(i = 0; i < 8; i++) + bits[b + i] = value >> (b + i) & 1 ? '1' : '0'; + bits[b] = 0; + + ff_cbs_trace_syntax_element(ctx, position, name, subscripts, + bits, value); + } + + *write_to = value; + return 0; +} + +static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc, + int width, const char *name, + const int *subscripts, uint32_t value) { + int b; + + av_assert0(width % 8 == 0); + + if(put_bits_left(pbc) < width) + return AVERROR(ENOSPC); + + if(ctx->trace_enable) { + char bits[33]; + int i; + for(b = 0; b < width; b += 8) + for(i = 0; i < 8; i++) + bits[b + i] = value >> (b + i) & 1 ? '1' : '0'; + bits[b] = 0; + + ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), + name, subscripts, bits, value); + } + + for(b = 0; b < width; b += 8) + put_bits(pbc, 8, value >> b & 0xff); + + return 0; +} + +#define HEADER(name) \ + do { \ + ff_cbs_trace_header(ctx, name); \ + } while(0) + +#define CHECK(call) \ + do { \ + err = (call); \ + if(err < 0) \ + return err; \ + } while(0) + +#define FUNC_NAME(rw, codec, name) cbs_##codec##_##rw##_##name +#define FUNC_VP9(rw, name) FUNC_NAME(rw, vp9, name) +#define FUNC(name) FUNC_VP9(READWRITE, name) + +#define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]) { subs, __VA_ARGS__ }) : NULL) + +#define f(width, name) \ + xf(width, name, current->name, 0, ) +#define s(width, name) \ + xs(width, name, current->name, 0, ) +#define fs(width, name, subs, ...) \ + xf(width, name, current->name, subs, __VA_ARGS__) +#define ss(width, name, subs, ...) \ + xs(width, name, current->name, subs, __VA_ARGS__) + +#define READ +#define READWRITE read +#define RWContext GetBitContext + +#define xf(width, name, var, subs, ...) \ + do { \ + uint32_t value; \ + CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + &value, 0, (1 << width) - 1)); \ + var = value; \ + } while(0) +#define xs(width, name, var, subs, ...) \ + do { \ + int32_t value; \ + CHECK(cbs_vp9_read_s(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), &value)); \ + var = value; \ + } while(0) + + +#define increment(name, min, max) \ + do { \ + uint32_t value; \ + CHECK(cbs_vp9_read_increment(ctx, rw, min, max, #name, &value)); \ + current->name = value; \ + } while(0) + +#define fle(width, name, subs, ...) \ + do { \ + CHECK(cbs_vp9_read_le(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), ¤t->name)); \ + } while(0) + +#define delta_q(name) \ + do { \ + uint8_t delta_coded; \ + int8_t delta_q; \ + xf(1, name.delta_coded, delta_coded, 0, ); \ + if(delta_coded) \ + xs(4, name.delta_q, delta_q, 0, ); \ + else \ + delta_q = 0; \ + current->name = delta_q; \ + } while(0) + +#define prob(name, subs, ...) \ + do { \ + uint8_t prob_coded; \ + uint8_t prob; \ + xf(1, name.prob_coded, prob_coded, subs, __VA_ARGS__); \ + if(prob_coded) \ + xf(8, name.prob, prob, subs, __VA_ARGS__); \ + else \ + prob = 255; \ + current->name = prob; \ + } while(0) + +#define fixed(width, name, value) \ + do { \ + av_unused uint32_t fixed_value; \ + CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ + 0, &fixed_value, value, value)); \ + } while(0) + +#define infer(name, value) \ + do { \ + current->name = value; \ + } while(0) + +#define byte_alignment(rw) (get_bits_count(rw) % 8) + +#include "cbs_vp9_syntax_template.c" + +#undef READ +#undef READWRITE +#undef RWContext +#undef xf +#undef xs +#undef increment +#undef fle +#undef delta_q +#undef prob +#undef fixed +#undef infer +#undef byte_alignment + + +#define WRITE +#define READWRITE write +#define RWContext PutBitContext + +#define xf(width, name, var, subs, ...) \ + do { \ + CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + var, 0, (1 << width) - 1)); \ + } while(0) +#define xs(width, name, var, subs, ...) \ + do { \ + CHECK(cbs_vp9_write_s(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), var)); \ + } while(0) + +#define increment(name, min, max) \ + do { \ + CHECK(cbs_vp9_write_increment(ctx, rw, min, max, #name, current->name)); \ + } while(0) + +#define fle(width, name, subs, ...) \ + do { \ + CHECK(cbs_vp9_write_le(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), current->name)); \ + } while(0) + +#define delta_q(name) \ + do { \ + xf(1, name.delta_coded, !!current->name, 0, ); \ + if(current->name) \ + xs(4, name.delta_q, current->name, 0, ); \ + } while(0) + +#define prob(name, subs, ...) \ + do { \ + xf(1, name.prob_coded, current->name != 255, subs, __VA_ARGS__); \ + if(current->name != 255) \ + xf(8, name.prob, current->name, subs, __VA_ARGS__); \ + } while(0) + +#define fixed(width, name, value) \ + do { \ + CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \ + 0, value, value, value)); \ + } while(0) + +#define infer(name, value) \ + do { \ + if(current->name != (value)) { \ + av_log(ctx->log_ctx, AV_LOG_WARNING, "Warning: " \ + "%s does not match inferred value: " \ + "%" PRId64 ", but should be %" PRId64 ".\n", \ + #name, (int64_t)current->name, (int64_t)(value)); \ + } \ + } while(0) + +#define byte_alignment(rw) (put_bits_count(rw) % 8) + +#include "cbs_vp9_syntax_template.c" + +#undef WRITE +#undef READWRITE +#undef RWContext +#undef xf +#undef xs +#undef increment +#undef fle +#undef delta_q +#undef prob +#undef fixed +#undef infer +#undef byte_alignment + + +static int cbs_vp9_split_fragment(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + int header) { + uint8_t superframe_header; + int err; + + if(frag->data_size == 0) + return AVERROR_INVALIDDATA; + + // Last byte in the packet. + superframe_header = frag->data[frag->data_size - 1]; + + if((superframe_header & 0xe0) == 0xc0) { + VP9RawSuperframeIndex sfi; + GetBitContext gbc; + size_t index_size, pos; + int i; + + index_size = 2 + (((superframe_header & 0x18) >> 3) + 1) * + ((superframe_header & 0x07) + 1); + + if(index_size > frag->data_size) + return AVERROR_INVALIDDATA; + + err = init_get_bits(&gbc, frag->data + frag->data_size - index_size, + 8 * index_size); + if(err < 0) + return err; + + err = cbs_vp9_read_superframe_index(ctx, &gbc, &sfi); + if(err < 0) + return err; + + pos = 0; + for(i = 0; i <= sfi.frames_in_superframe_minus_1; i++) { + if(pos + sfi.frame_sizes[i] + index_size > frag->data_size) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Frame %d too large " + "in superframe: %" PRIu32 " bytes.\n", + i, sfi.frame_sizes[i]); + return AVERROR_INVALIDDATA; + } + + err = ff_cbs_insert_unit_data(frag, -1, 0, + frag->data + pos, + sfi.frame_sizes[i], + frag->data_ref); + if(err < 0) + return err; + + pos += sfi.frame_sizes[i]; + } + if(pos + index_size != frag->data_size) { + av_log(ctx->log_ctx, AV_LOG_WARNING, "Extra padding at " + "end of superframe: %zu bytes.\n", + frag->data_size - (pos + index_size)); + } + + return 0; + } + else { + err = ff_cbs_insert_unit_data(frag, -1, 0, + frag->data, frag->data_size, + frag->data_ref); + if(err < 0) + return err; + } + + return 0; +} + +static int cbs_vp9_read_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) { + VP9RawFrame *frame; + GetBitContext gbc; + int err, pos; + + err = init_get_bits(&gbc, unit->data, 8 * unit->data_size); + if(err < 0) + return err; + + err = ff_cbs_alloc_unit_content2(ctx, unit); + if(err < 0) + return err; + frame = unit->content; + + err = cbs_vp9_read_frame(ctx, &gbc, frame); + if(err < 0) + return err; + + pos = get_bits_count(&gbc); + av_assert0(pos % 8 == 0); + pos /= 8; + av_assert0(pos <= unit->data_size); + + if(pos == unit->data_size) { + // No data (e.g. a show-existing-frame frame). + } + else { + frame->data_ref = av_buffer_ref(unit->data_ref); + if(!frame->data_ref) + return AVERROR(ENOMEM); + + frame->data = unit->data + pos; + frame->data_size = unit->data_size - pos; + } + + return 0; +} + +static int cbs_vp9_write_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + PutBitContext *pbc) { + VP9RawFrame *frame = unit->content; + int err; + + err = cbs_vp9_write_frame(ctx, pbc, frame); + if(err < 0) + return err; + + // Frame must be byte-aligned. + av_assert0(put_bits_count(pbc) % 8 == 0); + + if(frame->data) { + if(frame->data_size > put_bits_left(pbc) / 8) + return AVERROR(ENOSPC); + + flush_put_bits(pbc); + memcpy(put_bits_ptr(pbc), frame->data, frame->data_size); + skip_put_bytes(pbc, frame->data_size); + } + + return 0; +} + +static int cbs_vp9_assemble_fragment(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag) { + int err; + + if(frag->nb_units == 1) { + // Output is just the content of the single frame. + + CodedBitstreamUnit *frame = &frag->units[0]; + + frag->data_ref = av_buffer_ref(frame->data_ref); + if(!frag->data_ref) + return AVERROR(ENOMEM); + + frag->data = frame->data; + frag->data_size = frame->data_size; + } + else { + // Build superframe out of frames. + + VP9RawSuperframeIndex sfi; + PutBitContext pbc; + AVBufferRef *ref; + uint8_t *data; + size_t size, max, pos; + int i, size_len; + + if(frag->nb_units > 8) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Too many frames to " + "make superframe: %d.\n", + frag->nb_units); + return AVERROR(EINVAL); + } + + max = 0; + for(i = 0; i < frag->nb_units; i++) + if(max < frag->units[i].data_size) + max = frag->units[i].data_size; + + if(max < 2) + size_len = 1; + else + size_len = av_log2(max) / 8 + 1; + av_assert0(size_len <= 4); + + sfi.superframe_marker = VP9_SUPERFRAME_MARKER; + sfi.bytes_per_framesize_minus_1 = size_len - 1; + sfi.frames_in_superframe_minus_1 = frag->nb_units - 1; + + size = 2; + for(i = 0; i < frag->nb_units; i++) { + size += size_len + frag->units[i].data_size; + sfi.frame_sizes[i] = frag->units[i].data_size; + } + + ref = av_buffer_alloc(size + AV_INPUT_BUFFER_PADDING_SIZE); + if(!ref) + return AVERROR(ENOMEM); + data = ref->data; + memset(data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + + pos = 0; + for(i = 0; i < frag->nb_units; i++) { + av_assert0(size - pos > frag->units[i].data_size); + memcpy(data + pos, frag->units[i].data, + frag->units[i].data_size); + pos += frag->units[i].data_size; + } + av_assert0(size - pos == 2 + frag->nb_units * size_len); + + init_put_bits(&pbc, data + pos, size - pos); + + err = cbs_vp9_write_superframe_index(ctx, &pbc, &sfi); + if(err < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to write " + "superframe index.\n"); + av_buffer_unref(&ref); + return err; + } + + av_assert0(put_bits_left(&pbc) == 0); + flush_put_bits(&pbc); + + frag->data_ref = ref; + frag->data = data; + frag->data_size = size; + } + + return 0; +} + +static void cbs_vp9_flush(CodedBitstreamContext *ctx) { + CodedBitstreamVP9Context *vp9 = ctx->priv_data; + + memset(vp9->ref, 0, sizeof(vp9->ref)); +} + +static const CodedBitstreamUnitTypeDescriptor cbs_vp9_unit_types[] = { + CBS_UNIT_TYPE_INTERNAL_REF(0, VP9RawFrame, data), + CBS_UNIT_TYPE_END_OF_LIST +}; + +const CodedBitstreamType ff_cbs_type_vp9 = { + .codec_id = AV_CODEC_ID_VP9, + + .priv_data_size = sizeof(CodedBitstreamVP9Context), + + .unit_types = cbs_vp9_unit_types, + + .split_fragment = &cbs_vp9_split_fragment, + .read_unit = &cbs_vp9_read_unit, + .write_unit = &cbs_vp9_write_unit, + + .flush = &cbs_vp9_flush, + + .assemble_fragment = &cbs_vp9_assemble_fragment, +}; diff --git a/third-party/cbs/cbs_vp9_syntax_template.c b/third-party/cbs/cbs_vp9_syntax_template.c new file mode 100644 index 00000000..a0e15c53 --- /dev/null +++ b/third-party/cbs/cbs_vp9_syntax_template.c @@ -0,0 +1,422 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +static int FUNC(frame_sync_code)(CodedBitstreamContext *ctx, RWContext *rw, + VP9RawFrameHeader *current) { + int err; + + fixed(8, frame_sync_byte_0, VP9_FRAME_SYNC_0); + fixed(8, frame_sync_byte_1, VP9_FRAME_SYNC_1); + fixed(8, frame_sync_byte_2, VP9_FRAME_SYNC_2); + + return 0; +} + +static int FUNC(color_config)(CodedBitstreamContext *ctx, RWContext *rw, + VP9RawFrameHeader *current, int profile) { + CodedBitstreamVP9Context *vp9 = ctx->priv_data; + int err; + + if(profile >= 2) { + f(1, ten_or_twelve_bit); + vp9->bit_depth = current->ten_or_twelve_bit ? 12 : 10; + } + else + vp9->bit_depth = 8; + + f(3, color_space); + + if(current->color_space != VP9_CS_RGB) { + f(1, color_range); + if(profile == 1 || profile == 3) { + f(1, subsampling_x); + f(1, subsampling_y); + fixed(1, reserved_zero, 0); + } + else { + infer(subsampling_x, 1); + infer(subsampling_y, 1); + } + } + else { + infer(color_range, 1); + if(profile == 1 || profile == 3) { + infer(subsampling_x, 0); + infer(subsampling_y, 0); + fixed(1, reserved_zero, 0); + } + } + + vp9->subsampling_x = current->subsampling_x; + vp9->subsampling_y = current->subsampling_y; + + return 0; +} + +static int FUNC(frame_size)(CodedBitstreamContext *ctx, RWContext *rw, + VP9RawFrameHeader *current) { + CodedBitstreamVP9Context *vp9 = ctx->priv_data; + int err; + + f(16, frame_width_minus_1); + f(16, frame_height_minus_1); + + vp9->frame_width = current->frame_width_minus_1 + 1; + vp9->frame_height = current->frame_height_minus_1 + 1; + + vp9->mi_cols = (vp9->frame_width + 7) >> 3; + vp9->mi_rows = (vp9->frame_height + 7) >> 3; + vp9->sb64_cols = (vp9->mi_cols + 7) >> 3; + vp9->sb64_rows = (vp9->mi_rows + 7) >> 3; + + return 0; +} + +static int FUNC(render_size)(CodedBitstreamContext *ctx, RWContext *rw, + VP9RawFrameHeader *current) { + int err; + + f(1, render_and_frame_size_different); + + if(current->render_and_frame_size_different) { + f(16, render_width_minus_1); + f(16, render_height_minus_1); + } + + return 0; +} + +static int FUNC(frame_size_with_refs)(CodedBitstreamContext *ctx, RWContext *rw, + VP9RawFrameHeader *current) { + CodedBitstreamVP9Context *vp9 = ctx->priv_data; + int err, i; + + for(i = 0; i < VP9_REFS_PER_FRAME; i++) { + fs(1, found_ref[i], 1, i); + if(current->found_ref[i]) { + VP9ReferenceFrameState *ref = + &vp9->ref[current->ref_frame_idx[i]]; + + vp9->frame_width = ref->frame_width; + vp9->frame_height = ref->frame_height; + + vp9->subsampling_x = ref->subsampling_x; + vp9->subsampling_y = ref->subsampling_y; + vp9->bit_depth = ref->bit_depth; + + break; + } + } + if(i >= VP9_REFS_PER_FRAME) + CHECK(FUNC(frame_size)(ctx, rw, current)); + else { + vp9->mi_cols = (vp9->frame_width + 7) >> 3; + vp9->mi_rows = (vp9->frame_height + 7) >> 3; + vp9->sb64_cols = (vp9->mi_cols + 7) >> 3; + vp9->sb64_rows = (vp9->mi_rows + 7) >> 3; + } + CHECK(FUNC(render_size)(ctx, rw, current)); + + return 0; +} + +static int FUNC(interpolation_filter)(CodedBitstreamContext *ctx, RWContext *rw, + VP9RawFrameHeader *current) { + int err; + + f(1, is_filter_switchable); + if(!current->is_filter_switchable) + f(2, raw_interpolation_filter_type); + + return 0; +} + +static int FUNC(loop_filter_params)(CodedBitstreamContext *ctx, RWContext *rw, + VP9RawFrameHeader *current) { + int err, i; + + f(6, loop_filter_level); + f(3, loop_filter_sharpness); + + f(1, loop_filter_delta_enabled); + if(current->loop_filter_delta_enabled) { + f(1, loop_filter_delta_update); + if(current->loop_filter_delta_update) { + for(i = 0; i < VP9_MAX_REF_FRAMES; i++) { + fs(1, update_ref_delta[i], 1, i); + if(current->update_ref_delta[i]) + ss(6, loop_filter_ref_deltas[i], 1, i); + } + for(i = 0; i < 2; i++) { + fs(1, update_mode_delta[i], 1, i); + if(current->update_mode_delta[i]) + ss(6, loop_filter_mode_deltas[i], 1, i); + } + } + } + + return 0; +} + +static int FUNC(quantization_params)(CodedBitstreamContext *ctx, RWContext *rw, + VP9RawFrameHeader *current) { + int err; + + f(8, base_q_idx); + + delta_q(delta_q_y_dc); + delta_q(delta_q_uv_dc); + delta_q(delta_q_uv_ac); + + return 0; +} + +static int FUNC(segmentation_params)(CodedBitstreamContext *ctx, RWContext *rw, + VP9RawFrameHeader *current) { + static const uint8_t segmentation_feature_bits[VP9_SEG_LVL_MAX] = { 8, 6, 2, 0 }; + static const uint8_t segmentation_feature_signed[VP9_SEG_LVL_MAX] = { 1, 1, 0, 0 }; + + int err, i, j; + + f(1, segmentation_enabled); + + if(current->segmentation_enabled) { + f(1, segmentation_update_map); + if(current->segmentation_update_map) { + for(i = 0; i < 7; i++) + prob(segmentation_tree_probs[i], 1, i); + f(1, segmentation_temporal_update); + for(i = 0; i < 3; i++) { + if(current->segmentation_temporal_update) + prob(segmentation_pred_prob[i], 1, i); + else + infer(segmentation_pred_prob[i], 255); + } + } + + f(1, segmentation_update_data); + if(current->segmentation_update_data) { + f(1, segmentation_abs_or_delta_update); + for(i = 0; i < VP9_MAX_SEGMENTS; i++) { + for(j = 0; j < VP9_SEG_LVL_MAX; j++) { + fs(1, feature_enabled[i][j], 2, i, j); + if(current->feature_enabled[i][j] && + segmentation_feature_bits[j]) { + fs(segmentation_feature_bits[j], + feature_value[i][j], 2, i, j); + if(segmentation_feature_signed[j]) + fs(1, feature_sign[i][j], 2, i, j); + else + infer(feature_sign[i][j], 0); + } + else { + infer(feature_value[i][j], 0); + infer(feature_sign[i][j], 0); + } + } + } + } + } + + return 0; +} + +static int FUNC(tile_info)(CodedBitstreamContext *ctx, RWContext *rw, + VP9RawFrameHeader *current) { + CodedBitstreamVP9Context *vp9 = ctx->priv_data; + int min_log2_tile_cols, max_log2_tile_cols; + int err; + + min_log2_tile_cols = 0; + while((VP9_MAX_TILE_WIDTH_B64 << min_log2_tile_cols) < vp9->sb64_cols) + ++min_log2_tile_cols; + max_log2_tile_cols = 0; + while((vp9->sb64_cols >> (max_log2_tile_cols + 1)) >= VP9_MIN_TILE_WIDTH_B64) + ++max_log2_tile_cols; + + increment(tile_cols_log2, min_log2_tile_cols, max_log2_tile_cols); + + increment(tile_rows_log2, 0, 2); + + return 0; +} + +static int FUNC(uncompressed_header)(CodedBitstreamContext *ctx, RWContext *rw, + VP9RawFrameHeader *current) { + CodedBitstreamVP9Context *vp9 = ctx->priv_data; + int err, i; + + f(2, frame_marker); + + f(1, profile_low_bit); + f(1, profile_high_bit); + vp9->profile = (current->profile_high_bit << 1) + current->profile_low_bit; + if(vp9->profile == 3) + fixed(1, reserved_zero, 0); + + f(1, show_existing_frame); + if(current->show_existing_frame) { + f(3, frame_to_show_map_idx); + infer(header_size_in_bytes, 0); + infer(refresh_frame_flags, 0x00); + infer(loop_filter_level, 0); + return 0; + } + + f(1, frame_type); + f(1, show_frame); + f(1, error_resilient_mode); + + if(current->frame_type == VP9_KEY_FRAME) { + CHECK(FUNC(frame_sync_code)(ctx, rw, current)); + CHECK(FUNC(color_config)(ctx, rw, current, vp9->profile)); + CHECK(FUNC(frame_size)(ctx, rw, current)); + CHECK(FUNC(render_size)(ctx, rw, current)); + + infer(refresh_frame_flags, 0xff); + } + else { + if(current->show_frame == 0) + f(1, intra_only); + else + infer(intra_only, 0); + + if(current->error_resilient_mode == 0) + f(2, reset_frame_context); + else + infer(reset_frame_context, 0); + + if(current->intra_only == 1) { + CHECK(FUNC(frame_sync_code)(ctx, rw, current)); + + if(vp9->profile > 0) { + CHECK(FUNC(color_config)(ctx, rw, current, vp9->profile)); + } + else { + infer(color_space, 1); + infer(subsampling_x, 1); + infer(subsampling_y, 1); + vp9->bit_depth = 8; + + vp9->subsampling_x = current->subsampling_x; + vp9->subsampling_y = current->subsampling_y; + } + + f(8, refresh_frame_flags); + + CHECK(FUNC(frame_size)(ctx, rw, current)); + CHECK(FUNC(render_size)(ctx, rw, current)); + } + else { + f(8, refresh_frame_flags); + + for(i = 0; i < VP9_REFS_PER_FRAME; i++) { + fs(3, ref_frame_idx[i], 1, i); + fs(1, ref_frame_sign_bias[VP9_LAST_FRAME + i], + 1, VP9_LAST_FRAME + i); + } + + CHECK(FUNC(frame_size_with_refs)(ctx, rw, current)); + f(1, allow_high_precision_mv); + CHECK(FUNC(interpolation_filter)(ctx, rw, current)); + } + } + + if(current->error_resilient_mode == 0) { + f(1, refresh_frame_context); + f(1, frame_parallel_decoding_mode); + } + else { + infer(refresh_frame_context, 0); + infer(frame_parallel_decoding_mode, 1); + } + + f(2, frame_context_idx); + + CHECK(FUNC(loop_filter_params)(ctx, rw, current)); + CHECK(FUNC(quantization_params)(ctx, rw, current)); + CHECK(FUNC(segmentation_params)(ctx, rw, current)); + CHECK(FUNC(tile_info)(ctx, rw, current)); + + f(16, header_size_in_bytes); + + for(i = 0; i < VP9_NUM_REF_FRAMES; i++) { + if(current->refresh_frame_flags & (1 << i)) { + vp9->ref[i] = (VP9ReferenceFrameState) { + .frame_width = vp9->frame_width, + .frame_height = vp9->frame_height, + .subsampling_x = vp9->subsampling_x, + .subsampling_y = vp9->subsampling_y, + .bit_depth = vp9->bit_depth, + }; + } + } + + av_log(ctx->log_ctx, AV_LOG_DEBUG, "Frame: size %dx%d " + "subsample %dx%d bit_depth %d tiles %dx%d.\n", + vp9->frame_width, vp9->frame_height, + vp9->subsampling_x, vp9->subsampling_y, + vp9->bit_depth, 1 << current->tile_cols_log2, + 1 << current->tile_rows_log2); + + return 0; +} + +static int FUNC(trailing_bits)(CodedBitstreamContext *ctx, RWContext *rw) { + int err; + while(byte_alignment(rw) != 0) + fixed(1, zero_bit, 0); + + return 0; +} + +static int FUNC(frame)(CodedBitstreamContext *ctx, RWContext *rw, + VP9RawFrame *current) { + int err; + + HEADER("Frame"); + + CHECK(FUNC(uncompressed_header)(ctx, rw, ¤t->header)); + + CHECK(FUNC(trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(superframe_index)(CodedBitstreamContext *ctx, RWContext *rw, + VP9RawSuperframeIndex *current) { + int err, i; + + HEADER("Superframe Index"); + + f(3, superframe_marker); + f(2, bytes_per_framesize_minus_1); + f(3, frames_in_superframe_minus_1); + + for(i = 0; i <= current->frames_in_superframe_minus_1; i++) { + // Surprise little-endian! + fle(8 * (current->bytes_per_framesize_minus_1 + 1), + frame_sizes[i], 1, i); + } + + f(3, superframe_marker); + f(2, bytes_per_framesize_minus_1); + f(3, frames_in_superframe_minus_1); + + return 0; +} diff --git a/third-party/cbs/config.h b/third-party/cbs/config.h new file mode 100644 index 00000000..b33923f3 --- /dev/null +++ b/third-party/cbs/config.h @@ -0,0 +1,27 @@ +#ifndef CBS_CONFIG_H +#define CBS_CONFIG_H + +#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \ + defined(__BIG_ENDIAN__) || \ + defined(__ARMEB__) || \ + defined(__THUMBEB__) || \ + defined(__AARCH64EB__) || \ + defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) +// It's a big-endian target architecture +#define AV_HAVE_BIGENDIAN 1 + +#elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || \ + defined(__LITTLE_ENDIAN__) || \ + defined(__ARMEL__) || \ + defined(__THUMBEL__) || \ + defined(__AARCH64EL__) || \ + defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || \ + defined(_WIN32) +// It's a little-endian target architecture +#define AV_HAVE_BIGENDIAN 0 + +#else +#error "Unknown Endianness" +#endif + +#endif \ No newline at end of file diff --git a/third-party/cbs/defs.h b/third-party/cbs/defs.h new file mode 100644 index 00000000..1c5f0ce8 --- /dev/null +++ b/third-party/cbs/defs.h @@ -0,0 +1,51 @@ +/* + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_DEFS_H +#define AVCODEC_DEFS_H + +/** + * @file + * @ingroup libavc + * Misc types and constants that do not belong anywhere else. + */ + +#include +#include + +/** + * @ingroup lavc_decoding + * Required number of additionally allocated bytes at the end of the input bitstream for decoding. + * This is mainly needed because some optimized bitstream readers read + * 32 or 64 bit at once and could read over the end.
+ * Note: If the first 23 bits of the additional bytes are not 0, then damaged + * MPEG bitstreams could cause overread and segfault. + */ +#define AV_INPUT_BUFFER_PADDING_SIZE 64 + +/** + * Encode extradata length to a buffer. Used by xiph codecs. + * + * @param s buffer to write to; must be at least (v/255+1) bytes long + * @param v size of extradata in bytes + * @return number of bytes written to the buffer. + */ +unsigned int av_xiphlacing(unsigned char *s, unsigned int v); + +#endif // AVCODEC_DEFS_H diff --git a/third-party/cbs/get_bits.h b/third-party/cbs/get_bits.h new file mode 100644 index 00000000..3c34b040 --- /dev/null +++ b/third-party/cbs/get_bits.h @@ -0,0 +1,831 @@ +/* + * Copyright (c) 2004 Michael Niedermayer + * Copyright (c) 2016 Alexandra Hájková + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * bitstream reader API header. + */ + +#ifndef AVCODEC_GET_BITS_H +#define AVCODEC_GET_BITS_H + +#include + +#include +#include +#include +#include + +#include "defs.h" +#include "mathops.h" +#include "vlc.h" + +/* + * Safe bitstream reading: + * optionally, the get_bits API can check to ensure that we + * don't read past input buffer boundaries. This is protected + * with CONFIG_SAFE_BITSTREAM_READER at the global level, and + * then below that with UNCHECKED_BITSTREAM_READER at the per- + * decoder level. This means that decoders that check internally + * can "#define UNCHECKED_BITSTREAM_READER 1" to disable + * overread checks. + * Boundary checking causes a minor performance penalty so for + * applications that won't want/need this, it can be disabled + * globally using "#define CONFIG_SAFE_BITSTREAM_READER 0". + */ +#ifndef UNCHECKED_BITSTREAM_READER +#define UNCHECKED_BITSTREAM_READER 0 +#endif + +#ifndef CACHED_BITSTREAM_READER +#define CACHED_BITSTREAM_READER 0 +#endif + +#include "cbs/h2645_parse.h" + +static inline unsigned int get_bits(GetBitContext *s, int n); +static inline void skip_bits(GetBitContext *s, int n); +static inline unsigned int show_bits(GetBitContext *s, int n); + +/* Bitstream reader API docs: + * name + * arbitrary name which is used as prefix for the internal variables + * + * gb + * getbitcontext + * + * OPEN_READER(name, gb) + * load gb into local variables + * + * CLOSE_READER(name, gb) + * store local vars in gb + * + * UPDATE_CACHE(name, gb) + * Refill the internal cache from the bitstream. + * After this call at least MIN_CACHE_BITS will be available. + * + * GET_CACHE(name, gb) + * Will output the contents of the internal cache, + * next bit is MSB of 32 or 64 bits (FIXME 64 bits). + * + * SHOW_UBITS(name, gb, num) + * Will return the next num bits. + * + * SHOW_SBITS(name, gb, num) + * Will return the next num bits and do sign extension. + * + * SKIP_BITS(name, gb, num) + * Will skip over the next num bits. + * Note, this is equivalent to SKIP_CACHE; SKIP_COUNTER. + * + * SKIP_CACHE(name, gb, num) + * Will remove the next num bits from the cache (note SKIP_COUNTER + * MUST be called before UPDATE_CACHE / CLOSE_READER). + * + * SKIP_COUNTER(name, gb, num) + * Will increment the internal bit counter (see SKIP_CACHE & SKIP_BITS). + * + * LAST_SKIP_BITS(name, gb, num) + * Like SKIP_BITS, to be used if next call is UPDATE_CACHE or CLOSE_READER. + * + * BITS_LEFT(name, gb) + * Return the number of bits left + * + * For examples see get_bits, show_bits, skip_bits, get_vlc. + */ + +#if CACHED_BITSTREAM_READER +#define MIN_CACHE_BITS 64 +#elif defined LONG_BITSTREAM_READER +#define MIN_CACHE_BITS 32 +#else +#define MIN_CACHE_BITS 25 +#endif + +#if !CACHED_BITSTREAM_READER + +#define OPEN_READER_NOSIZE(name, gb) \ + unsigned int name##_index = (gb)->index; \ + unsigned int av_unused name##_cache + +#if UNCHECKED_BITSTREAM_READER +#define OPEN_READER(name, gb) OPEN_READER_NOSIZE(name, gb) + +#define BITS_AVAILABLE(name, gb) 1 +#else +#define OPEN_READER(name, gb) \ + OPEN_READER_NOSIZE(name, gb); \ + unsigned int name##_size_plus8 = (gb)->size_in_bits_plus8 + +#define BITS_AVAILABLE(name, gb) name##_index < name##_size_plus8 +#endif + +#define CLOSE_READER(name, gb) (gb)->index = name##_index + +#ifdef LONG_BITSTREAM_READER + +#define UPDATE_CACHE_LE(name, gb) name##_cache = \ + AV_RL64((gb)->buffer + (name##_index >> 3)) >> (name##_index & 7) + +#define UPDATE_CACHE_BE(name, gb) name##_cache = \ + AV_RB64((gb)->buffer + (name##_index >> 3)) >> (32 - (name##_index & 7)) + +#else + +#define UPDATE_CACHE_LE(name, gb) name##_cache = \ + AV_RL32((gb)->buffer + (name##_index >> 3)) >> (name##_index & 7) + +#define UPDATE_CACHE_BE(name, gb) name##_cache = \ + AV_RB32((gb)->buffer + (name##_index >> 3)) << (name##_index & 7) + +#endif + + +#ifdef BITSTREAM_READER_LE + +#define UPDATE_CACHE(name, gb) UPDATE_CACHE_LE(name, gb) + +#define SKIP_CACHE(name, gb, num) name##_cache >>= (num) + +#else + +#define UPDATE_CACHE(name, gb) UPDATE_CACHE_BE(name, gb) + +#define SKIP_CACHE(name, gb, num) name##_cache <<= (num) + +#endif + +#if UNCHECKED_BITSTREAM_READER +#define SKIP_COUNTER(name, gb, num) name##_index += (num) +#else +#define SKIP_COUNTER(name, gb, num) \ + name##_index = FFMIN(name##_size_plus8, name##_index + (num)) +#endif + +#define BITS_LEFT(name, gb) ((int)((gb)->size_in_bits - name##_index)) + +#define SKIP_BITS(name, gb, num) \ + do { \ + SKIP_CACHE(name, gb, num); \ + SKIP_COUNTER(name, gb, num); \ + } while(0) + +#define LAST_SKIP_BITS(name, gb, num) SKIP_COUNTER(name, gb, num) + +#define SHOW_UBITS_LE(name, gb, num) zero_extend(name##_cache, num) +#define SHOW_SBITS_LE(name, gb, num) sign_extend(name##_cache, num) + +#define SHOW_UBITS_BE(name, gb, num) NEG_USR32(name##_cache, num) +#define SHOW_SBITS_BE(name, gb, num) NEG_SSR32(name##_cache, num) + +#ifdef BITSTREAM_READER_LE +#define SHOW_UBITS(name, gb, num) SHOW_UBITS_LE(name, gb, num) +#define SHOW_SBITS(name, gb, num) SHOW_SBITS_LE(name, gb, num) +#else +#define SHOW_UBITS(name, gb, num) SHOW_UBITS_BE(name, gb, num) +#define SHOW_SBITS(name, gb, num) SHOW_SBITS_BE(name, gb, num) +#endif + +#define GET_CACHE(name, gb) ((uint32_t)name##_cache) + +#endif + +static inline int get_bits_count(const GetBitContext *s) { +#if CACHED_BITSTREAM_READER + return s->index - s->bits_left; +#else + return s->index; +#endif +} + +#if CACHED_BITSTREAM_READER +static inline void refill_32(GetBitContext *s, int is_le) { +#if !UNCHECKED_BITSTREAM_READER + if(s->index >> 3 >= s->buffer_end - s->buffer) + return; +#endif + + if(is_le) + s->cache = (uint64_t)AV_RL32(s->buffer + (s->index >> 3)) << s->bits_left | s->cache; + else + s->cache = s->cache | (uint64_t)AV_RB32(s->buffer + (s->index >> 3)) << (32 - s->bits_left); + s->index += 32; + s->bits_left += 32; +} + +static inline void refill_64(GetBitContext *s, int is_le) { +#if !UNCHECKED_BITSTREAM_READER + if(s->index >> 3 >= s->buffer_end - s->buffer) + return; +#endif + + if(is_le) + s->cache = AV_RL64(s->buffer + (s->index >> 3)); + else + s->cache = AV_RB64(s->buffer + (s->index >> 3)); + s->index += 64; + s->bits_left = 64; +} + +static inline uint64_t get_val(GetBitContext *s, unsigned n, int is_le) { + uint64_t ret; + av_assert2(n > 0 && n <= 63); + if(is_le) { + ret = s->cache & ((UINT64_C(1) << n) - 1); + s->cache >>= n; + } + else { + ret = s->cache >> (64 - n); + s->cache <<= n; + } + s->bits_left -= n; + return ret; +} + +static inline unsigned show_val(const GetBitContext *s, unsigned n) { +#ifdef BITSTREAM_READER_LE + return s->cache & ((UINT64_C(1) << n) - 1); +#else + return s->cache >> (64 - n); +#endif +} +#endif + +/** + * Skips the specified number of bits. + * @param n the number of bits to skip, + * For the UNCHECKED_BITSTREAM_READER this must not cause the distance + * from the start to overflow int32_t. Staying within the bitstream + padding + * is sufficient, too. + */ +static inline void skip_bits_long(GetBitContext *s, int n) { +#if CACHED_BITSTREAM_READER + skip_bits(s, n); +#else +#if UNCHECKED_BITSTREAM_READER + s->index += n; +#else + s->index += av_clip(n, -s->index, s->size_in_bits_plus8 - s->index); +#endif +#endif +} + +#if CACHED_BITSTREAM_READER +static inline void skip_remaining(GetBitContext *s, unsigned n) { +#ifdef BITSTREAM_READER_LE + s->cache >>= n; +#else + s->cache <<= n; +#endif + s->bits_left -= n; +} +#endif + +/** + * Read MPEG-1 dc-style VLC (sign bit + mantissa with no MSB). + * if MSB not set it is negative + * @param n length in bits + */ +static inline int get_xbits(GetBitContext *s, int n) { +#if CACHED_BITSTREAM_READER + int32_t cache = show_bits(s, 32); + int sign = ~cache >> 31; + skip_remaining(s, n); + + return ((((uint32_t)(sign ^ cache)) >> (32 - n)) ^ sign) - sign; +#else + register int sign; + register int32_t cache; + OPEN_READER(re, s); + av_assert2(n > 0 && n <= 25); + UPDATE_CACHE(re, s); + cache = GET_CACHE(re, s); + sign = ~cache >> 31; + LAST_SKIP_BITS(re, s, n); + CLOSE_READER(re, s); + return (NEG_USR32(sign ^ cache, n) ^ sign) - sign; +#endif +} + +#if !CACHED_BITSTREAM_READER +static inline int get_xbits_le(GetBitContext *s, int n) { + register int sign; + register int32_t cache; + OPEN_READER(re, s); + av_assert2(n > 0 && n <= 25); + UPDATE_CACHE_LE(re, s); + cache = GET_CACHE(re, s); + sign = sign_extend(~cache, n) >> 31; + LAST_SKIP_BITS(re, s, n); + CLOSE_READER(re, s); + return (zero_extend(sign ^ cache, n) ^ sign) - sign; +} +#endif + +static inline int get_sbits(GetBitContext *s, int n) { + register int tmp; +#if CACHED_BITSTREAM_READER + av_assert2(n > 0 && n <= 25); + tmp = sign_extend(get_bits(s, n), n); +#else + OPEN_READER(re, s); + av_assert2(n > 0 && n <= 25); + UPDATE_CACHE(re, s); + tmp = SHOW_SBITS(re, s, n); + LAST_SKIP_BITS(re, s, n); + CLOSE_READER(re, s); +#endif + return tmp; +} + +/** + * Read 1-25 bits. + */ +static inline unsigned int get_bits(GetBitContext *s, int n) { + register unsigned int tmp; +#if CACHED_BITSTREAM_READER + + av_assert2(n > 0 && n <= 32); + if(n > s->bits_left) { +#ifdef BITSTREAM_READER_LE + refill_32(s, 1); +#else + refill_32(s, 0); +#endif + if(s->bits_left < 32) + s->bits_left = n; + } + +#ifdef BITSTREAM_READER_LE + tmp = get_val(s, n, 1); +#else + tmp = get_val(s, n, 0); +#endif +#else + OPEN_READER(re, s); + av_assert2(n > 0 && n <= 25); + UPDATE_CACHE(re, s); + tmp = SHOW_UBITS(re, s, n); + LAST_SKIP_BITS(re, s, n); + CLOSE_READER(re, s); +#endif + av_assert2(tmp < UINT64_C(1) << n); + return tmp; +} + +/** + * Read 0-25 bits. + */ +static av_always_inline int get_bitsz(GetBitContext *s, int n) { + return n ? get_bits(s, n) : 0; +} + +static inline unsigned int get_bits_le(GetBitContext *s, int n) { +#if CACHED_BITSTREAM_READER + av_assert2(n > 0 && n <= 32); + if(n > s->bits_left) { + refill_32(s, 1); + if(s->bits_left < 32) + s->bits_left = n; + } + + return get_val(s, n, 1); +#else + register int tmp; + OPEN_READER(re, s); + av_assert2(n > 0 && n <= 25); + UPDATE_CACHE_LE(re, s); + tmp = SHOW_UBITS_LE(re, s, n); + LAST_SKIP_BITS(re, s, n); + CLOSE_READER(re, s); + return tmp; +#endif +} + +/** + * Show 1-25 bits. + */ +static inline unsigned int show_bits(GetBitContext *s, int n) { + register unsigned int tmp; +#if CACHED_BITSTREAM_READER + if(n > s->bits_left) +#ifdef BITSTREAM_READER_LE + refill_32(s, 1); +#else + refill_32(s, 0); +#endif + + tmp = show_val(s, n); +#else + OPEN_READER_NOSIZE(re, s); + av_assert2(n > 0 && n <= 25); + UPDATE_CACHE(re, s); + tmp = SHOW_UBITS(re, s, n); +#endif + return tmp; +} + +static inline void skip_bits(GetBitContext *s, int n) { +#if CACHED_BITSTREAM_READER + if(n < s->bits_left) + skip_remaining(s, n); + else { + n -= s->bits_left; + s->cache = 0; + s->bits_left = 0; + + if(n >= 64) { + unsigned skip = (n / 8) * 8; + + n -= skip; + s->index += skip; + } +#ifdef BITSTREAM_READER_LE + refill_64(s, 1); +#else + refill_64(s, 0); +#endif + if(n) + skip_remaining(s, n); + } +#else + OPEN_READER(re, s); + LAST_SKIP_BITS(re, s, n); + CLOSE_READER(re, s); +#endif +} + +static inline unsigned int get_bits1(GetBitContext *s) { +#if CACHED_BITSTREAM_READER + if(!s->bits_left) +#ifdef BITSTREAM_READER_LE + refill_64(s, 1); +#else + refill_64(s, 0); +#endif + +#ifdef BITSTREAM_READER_LE + return get_val(s, 1, 1); +#else + return get_val(s, 1, 0); +#endif +#else + unsigned int index = s->index; + uint8_t result = s->buffer[index >> 3]; +#ifdef BITSTREAM_READER_LE + result >>= index & 7; + result &= 1; +#else + result <<= index & 7; + result >>= 8 - 1; +#endif +#if !UNCHECKED_BITSTREAM_READER + if(s->index < s->size_in_bits_plus8) +#endif + index++; + s->index = index; + + return result; +#endif +} + +static inline unsigned int show_bits1(GetBitContext *s) { + return show_bits(s, 1); +} + +static inline void skip_bits1(GetBitContext *s) { + skip_bits(s, 1); +} + +/** + * Read 0-32 bits. + */ +static inline unsigned int get_bits_long(GetBitContext *s, int n) { + av_assert2(n >= 0 && n <= 32); + if(!n) { + return 0; +#if CACHED_BITSTREAM_READER + } + return get_bits(s, n); +#else + } + else if(n <= MIN_CACHE_BITS) { + return get_bits(s, n); + } + else { +#ifdef BITSTREAM_READER_LE + unsigned ret = get_bits(s, 16); + return ret | (get_bits(s, n - 16) << 16); +#else + unsigned ret = get_bits(s, 16) << (n - 16); + return ret | get_bits(s, n - 16); +#endif + } +#endif +} + +/** + * Read 0-64 bits. + */ +static inline uint64_t get_bits64(GetBitContext *s, int n) { + if(n <= 32) { + return get_bits_long(s, n); + } + else { +#ifdef BITSTREAM_READER_LE + uint64_t ret = get_bits_long(s, 32); + return ret | (uint64_t)get_bits_long(s, n - 32) << 32; +#else + uint64_t ret = (uint64_t)get_bits_long(s, n - 32) << 32; + return ret | get_bits_long(s, 32); +#endif + } +} + +/** + * Read 0-32 bits as a signed integer. + */ +static inline int get_sbits_long(GetBitContext *s, int n) { + // sign_extend(x, 0) is undefined + if(!n) + return 0; + + return sign_extend(get_bits_long(s, n), n); +} + +/** + * Show 0-32 bits. + */ +static inline unsigned int show_bits_long(GetBitContext *s, int n) { + if(n <= MIN_CACHE_BITS) { + return show_bits(s, n); + } + else { + GetBitContext gb = *s; + return get_bits_long(&gb, n); + } +} + +static inline int check_marker(void *logctx, GetBitContext *s, const char *msg) { + int bit = get_bits1(s); + if(!bit) + av_log(logctx, AV_LOG_INFO, "Marker bit missing at %d of %d %s\n", + get_bits_count(s) - 1, s->size_in_bits, msg); + + return bit; +} + +static inline int init_get_bits_xe(GetBitContext *s, const uint8_t *buffer, + int bit_size, int is_le) { + int buffer_size; + int ret = 0; + + if(bit_size >= INT_MAX - FFMAX(7, AV_INPUT_BUFFER_PADDING_SIZE * 8) || bit_size < 0 || !buffer) { + bit_size = 0; + buffer = NULL; + ret = AVERROR_INVALIDDATA; + } + + buffer_size = (bit_size + 7) >> 3; + + s->buffer = buffer; + s->size_in_bits = bit_size; + s->size_in_bits_plus8 = bit_size + 8; + s->buffer_end = buffer + buffer_size; + s->index = 0; + +#if CACHED_BITSTREAM_READER + s->cache = 0; + s->bits_left = 0; + refill_64(s, is_le); +#endif + + return ret; +} + +/** + * Initialize GetBitContext. + * @param buffer bitstream buffer, must be AV_INPUT_BUFFER_PADDING_SIZE bytes + * larger than the actual read bits because some optimized bitstream + * readers read 32 or 64 bit at once and could read over the end + * @param bit_size the size of the buffer in bits + * @return 0 on success, AVERROR_INVALIDDATA if the buffer_size would overflow. + */ +static inline int init_get_bits(GetBitContext *s, const uint8_t *buffer, + int bit_size) { +#ifdef BITSTREAM_READER_LE + return init_get_bits_xe(s, buffer, bit_size, 1); +#else + return init_get_bits_xe(s, buffer, bit_size, 0); +#endif +} + +/** + * Initialize GetBitContext. + * @param buffer bitstream buffer, must be AV_INPUT_BUFFER_PADDING_SIZE bytes + * larger than the actual read bits because some optimized bitstream + * readers read 32 or 64 bit at once and could read over the end + * @param byte_size the size of the buffer in bytes + * @return 0 on success, AVERROR_INVALIDDATA if the buffer_size would overflow. + */ +static inline int init_get_bits8(GetBitContext *s, const uint8_t *buffer, + int byte_size) { + if(byte_size > INT_MAX / 8 || byte_size < 0) + byte_size = -1; + return init_get_bits(s, buffer, byte_size * 8); +} + +static inline int init_get_bits8_le(GetBitContext *s, const uint8_t *buffer, + int byte_size) { + if(byte_size > INT_MAX / 8 || byte_size < 0) + byte_size = -1; + return init_get_bits_xe(s, buffer, byte_size * 8, 1); +} + +static inline const uint8_t *align_get_bits(GetBitContext *s) { + int n = -get_bits_count(s) & 7; + if(n) + skip_bits(s, n); + return s->buffer + (s->index >> 3); +} + +/** + * If the vlc code is invalid and max_depth=1, then no bits will be removed. + * If the vlc code is invalid and max_depth>1, then the number of bits removed + * is undefined. + */ +#define GET_VLC(code, name, gb, table, bits, max_depth) \ + do { \ + int n, nb_bits; \ + unsigned int index; \ + \ + index = SHOW_UBITS(name, gb, bits); \ + code = table[index][0]; \ + n = table[index][1]; \ + \ + if(max_depth > 1 && n < 0) { \ + LAST_SKIP_BITS(name, gb, bits); \ + UPDATE_CACHE(name, gb); \ + \ + nb_bits = -n; \ + \ + index = SHOW_UBITS(name, gb, nb_bits) + code; \ + code = table[index][0]; \ + n = table[index][1]; \ + if(max_depth > 2 && n < 0) { \ + LAST_SKIP_BITS(name, gb, nb_bits); \ + UPDATE_CACHE(name, gb); \ + \ + nb_bits = -n; \ + \ + index = SHOW_UBITS(name, gb, nb_bits) + code; \ + code = table[index][0]; \ + n = table[index][1]; \ + } \ + } \ + SKIP_BITS(name, gb, n); \ + } while(0) + +#define GET_RL_VLC(level, run, name, gb, table, bits, \ + max_depth, need_update) \ + do { \ + int n, nb_bits; \ + unsigned int index; \ + \ + index = SHOW_UBITS(name, gb, bits); \ + level = table[index].level; \ + n = table[index].len; \ + \ + if(max_depth > 1 && n < 0) { \ + SKIP_BITS(name, gb, bits); \ + if(need_update) { \ + UPDATE_CACHE(name, gb); \ + } \ + \ + nb_bits = -n; \ + \ + index = SHOW_UBITS(name, gb, nb_bits) + level; \ + level = table[index].level; \ + n = table[index].len; \ + if(max_depth > 2 && n < 0) { \ + LAST_SKIP_BITS(name, gb, nb_bits); \ + if(need_update) { \ + UPDATE_CACHE(name, gb); \ + } \ + nb_bits = -n; \ + \ + index = SHOW_UBITS(name, gb, nb_bits) + level; \ + level = table[index].level; \ + n = table[index].len; \ + } \ + } \ + run = table[index].run; \ + SKIP_BITS(name, gb, n); \ + } while(0) + +/* Return the LUT element for the given bitstream configuration. */ +static inline int set_idx(GetBitContext *s, int code, int *n, int *nb_bits, + VLC_TYPE (*table)[2]) { + unsigned idx; + + *nb_bits = -*n; + idx = show_bits(s, *nb_bits) + code; + *n = table[idx][1]; + + return table[idx][0]; +} + +/** + * Parse a vlc code. + * @param bits is the number of bits which will be read at once, must be + * identical to nb_bits in init_vlc() + * @param max_depth is the number of times bits bits must be read to completely + * read the longest vlc code + * = (max_vlc_length + bits - 1) / bits + * @returns the code parsed or -1 if no vlc matches + */ +static av_always_inline int get_vlc2(GetBitContext *s, VLC_TYPE (*table)[2], + int bits, int max_depth) { +#if CACHED_BITSTREAM_READER + int nb_bits; + unsigned idx = show_bits(s, bits); + int code = table[idx][0]; + int n = table[idx][1]; + + if(max_depth > 1 && n < 0) { + skip_remaining(s, bits); + code = set_idx(s, code, &n, &nb_bits, table); + if(max_depth > 2 && n < 0) { + skip_remaining(s, nb_bits); + code = set_idx(s, code, &n, &nb_bits, table); + } + } + skip_remaining(s, n); + + return code; +#else + int code; + + OPEN_READER(re, s); + UPDATE_CACHE(re, s); + + GET_VLC(code, re, s, table, bits, max_depth); + + CLOSE_READER(re, s); + + return code; +#endif +} + +static inline int decode012(GetBitContext *gb) { + int n; + n = get_bits1(gb); + if(n == 0) + return 0; + else + return get_bits1(gb) + 1; +} + +static inline int decode210(GetBitContext *gb) { + if(get_bits1(gb)) + return 0; + else + return 2 - get_bits1(gb); +} + +static inline int get_bits_left(GetBitContext *gb) { + return gb->size_in_bits - get_bits_count(gb); +} + +static inline int skip_1stop_8data_bits(GetBitContext *gb) { + if(get_bits_left(gb) <= 0) + return AVERROR_INVALIDDATA; + + while(get_bits1(gb)) { + skip_bits(gb, 8); + if(get_bits_left(gb) <= 0) + return AVERROR_INVALIDDATA; + } + + return 0; +} + +#endif /* AVCODEC_GET_BITS_H */ diff --git a/third-party/cbs/h2645_parse.c b/third-party/cbs/h2645_parse.c new file mode 100644 index 00000000..a401ca57 --- /dev/null +++ b/third-party/cbs/h2645_parse.c @@ -0,0 +1,535 @@ +/* + * H.264/HEVC common parsing code + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include + +#include "cbs/h264.h" +#include "cbs/h2645_parse.h" +#include "cbs/hevc.h" + +#include "bytestream.h" +#include "config.h" +#include "get_bits.h" +#include "intmath.h" + +int ff_h2645_extract_rbsp(const uint8_t *src, int length, + H2645RBSP *rbsp, H2645NAL *nal, int small_padding) { + int i, si, di; + uint8_t *dst; + + nal->skipped_bytes = 0; +#define STARTCODE_TEST \ + if(i + 2 < length && src[i + 1] == 0 && src[i + 2] <= 3) { \ + if(src[i + 2] != 3 && src[i + 2] != 0) { \ + /* startcode, so we must be past the end */ \ + length = i; \ + } \ + break; \ + } +#if HAVE_FAST_UNALIGNED +#define FIND_FIRST_ZERO \ + if(i > 0 && !src[i]) \ + i--; \ + while(src[i]) \ + i++ +#if HAVE_FAST_64BIT + for(i = 0; i + 1 < length; i += 9) { + if(!((~AV_RN64(src + i) & + (AV_RN64(src + i) - 0x0100010001000101ULL)) & + 0x8000800080008080ULL)) + continue; + FIND_FIRST_ZERO; + STARTCODE_TEST; + i -= 7; + } +#else + for(i = 0; i + 1 < length; i += 5) { + if(!((~AV_RN32(src + i) & + (AV_RN32(src + i) - 0x01000101U)) & + 0x80008080U)) + continue; + FIND_FIRST_ZERO; + STARTCODE_TEST; + i -= 3; + } +#endif /* HAVE_FAST_64BIT */ +#else + for(i = 0; i + 1 < length; i += 2) { + if(src[i]) + continue; + if(i > 0 && src[i - 1] == 0) + i--; + STARTCODE_TEST; + } +#endif /* HAVE_FAST_UNALIGNED */ + + if(i >= length - 1 && small_padding) { // no escaped 0 + nal->data = + nal->raw_data = src; + nal->size = + nal->raw_size = length; + return length; + } + else if(i > length) + i = length; + + nal->rbsp_buffer = &rbsp->rbsp_buffer[rbsp->rbsp_buffer_size]; + dst = nal->rbsp_buffer; + + memcpy(dst, src, i); + si = di = i; + while(si + 2 < length) { + // remove escapes (very rare 1:2^22) + if(src[si + 2] > 3) { + dst[di++] = src[si++]; + dst[di++] = src[si++]; + } + else if(src[si] == 0 && src[si + 1] == 0 && src[si + 2] != 0) { + if(src[si + 2] == 3) { // escape + dst[di++] = 0; + dst[di++] = 0; + si += 3; + + if(nal->skipped_bytes_pos) { + nal->skipped_bytes++; + if(nal->skipped_bytes_pos_size < nal->skipped_bytes) { + nal->skipped_bytes_pos_size *= 2; + av_assert0(nal->skipped_bytes_pos_size >= nal->skipped_bytes); + av_reallocp_array(&nal->skipped_bytes_pos, + nal->skipped_bytes_pos_size, + sizeof(*nal->skipped_bytes_pos)); + if(!nal->skipped_bytes_pos) { + nal->skipped_bytes_pos_size = 0; + return AVERROR(ENOMEM); + } + } + if(nal->skipped_bytes_pos) + nal->skipped_bytes_pos[nal->skipped_bytes - 1] = di - 1; + } + continue; + } + else // next start code + goto nsc; + } + + dst[di++] = src[si++]; + } + while(si < length) + dst[di++] = src[si++]; + +nsc: + memset(dst + di, 0, AV_INPUT_BUFFER_PADDING_SIZE); + + nal->data = dst; + nal->size = di; + nal->raw_data = src; + nal->raw_size = si; + rbsp->rbsp_buffer_size += si; + + return si; +} + +static const char *const hevc_nal_type_name[64] = { + "TRAIL_N", // HEVC_NAL_TRAIL_N + "TRAIL_R", // HEVC_NAL_TRAIL_R + "TSA_N", // HEVC_NAL_TSA_N + "TSA_R", // HEVC_NAL_TSA_R + "STSA_N", // HEVC_NAL_STSA_N + "STSA_R", // HEVC_NAL_STSA_R + "RADL_N", // HEVC_NAL_RADL_N + "RADL_R", // HEVC_NAL_RADL_R + "RASL_N", // HEVC_NAL_RASL_N + "RASL_R", // HEVC_NAL_RASL_R + "RSV_VCL_N10", // HEVC_NAL_VCL_N10 + "RSV_VCL_R11", // HEVC_NAL_VCL_R11 + "RSV_VCL_N12", // HEVC_NAL_VCL_N12 + "RSV_VLC_R13", // HEVC_NAL_VCL_R13 + "RSV_VCL_N14", // HEVC_NAL_VCL_N14 + "RSV_VCL_R15", // HEVC_NAL_VCL_R15 + "BLA_W_LP", // HEVC_NAL_BLA_W_LP + "BLA_W_RADL", // HEVC_NAL_BLA_W_RADL + "BLA_N_LP", // HEVC_NAL_BLA_N_LP + "IDR_W_RADL", // HEVC_NAL_IDR_W_RADL + "IDR_N_LP", // HEVC_NAL_IDR_N_LP + "CRA_NUT", // HEVC_NAL_CRA_NUT + "RSV_IRAP_VCL22", // HEVC_NAL_RSV_IRAP_VCL22 + "RSV_IRAP_VCL23", // HEVC_NAL_RSV_IRAP_VCL23 + "RSV_VCL24", // HEVC_NAL_RSV_VCL24 + "RSV_VCL25", // HEVC_NAL_RSV_VCL25 + "RSV_VCL26", // HEVC_NAL_RSV_VCL26 + "RSV_VCL27", // HEVC_NAL_RSV_VCL27 + "RSV_VCL28", // HEVC_NAL_RSV_VCL28 + "RSV_VCL29", // HEVC_NAL_RSV_VCL29 + "RSV_VCL30", // HEVC_NAL_RSV_VCL30 + "RSV_VCL31", // HEVC_NAL_RSV_VCL31 + "VPS", // HEVC_NAL_VPS + "SPS", // HEVC_NAL_SPS + "PPS", // HEVC_NAL_PPS + "AUD", // HEVC_NAL_AUD + "EOS_NUT", // HEVC_NAL_EOS_NUT + "EOB_NUT", // HEVC_NAL_EOB_NUT + "FD_NUT", // HEVC_NAL_FD_NUT + "SEI_PREFIX", // HEVC_NAL_SEI_PREFIX + "SEI_SUFFIX", // HEVC_NAL_SEI_SUFFIX + "RSV_NVCL41", // HEVC_NAL_RSV_NVCL41 + "RSV_NVCL42", // HEVC_NAL_RSV_NVCL42 + "RSV_NVCL43", // HEVC_NAL_RSV_NVCL43 + "RSV_NVCL44", // HEVC_NAL_RSV_NVCL44 + "RSV_NVCL45", // HEVC_NAL_RSV_NVCL45 + "RSV_NVCL46", // HEVC_NAL_RSV_NVCL46 + "RSV_NVCL47", // HEVC_NAL_RSV_NVCL47 + "UNSPEC48", // HEVC_NAL_UNSPEC48 + "UNSPEC49", // HEVC_NAL_UNSPEC49 + "UNSPEC50", // HEVC_NAL_UNSPEC50 + "UNSPEC51", // HEVC_NAL_UNSPEC51 + "UNSPEC52", // HEVC_NAL_UNSPEC52 + "UNSPEC53", // HEVC_NAL_UNSPEC53 + "UNSPEC54", // HEVC_NAL_UNSPEC54 + "UNSPEC55", // HEVC_NAL_UNSPEC55 + "UNSPEC56", // HEVC_NAL_UNSPEC56 + "UNSPEC57", // HEVC_NAL_UNSPEC57 + "UNSPEC58", // HEVC_NAL_UNSPEC58 + "UNSPEC59", // HEVC_NAL_UNSPEC59 + "UNSPEC60", // HEVC_NAL_UNSPEC60 + "UNSPEC61", // HEVC_NAL_UNSPEC61 + "UNSPEC62", // HEVC_NAL_UNSPEC62 + "UNSPEC63", // HEVC_NAL_UNSPEC63 +}; + +static const char *hevc_nal_unit_name(int nal_type) { + av_assert0(nal_type >= 0 && nal_type < 64); + return hevc_nal_type_name[nal_type]; +} + +static const char *const h264_nal_type_name[32] = { + "Unspecified 0", //H264_NAL_UNSPECIFIED + "Coded slice of a non-IDR picture", // H264_NAL_SLICE + "Coded slice data partition A", // H264_NAL_DPA + "Coded slice data partition B", // H264_NAL_DPB + "Coded slice data partition C", // H264_NAL_DPC + "IDR", // H264_NAL_IDR_SLICE + "SEI", // H264_NAL_SEI + "SPS", // H264_NAL_SPS + "PPS", // H264_NAL_PPS + "AUD", // H264_NAL_AUD + "End of sequence", // H264_NAL_END_SEQUENCE + "End of stream", // H264_NAL_END_STREAM + "Filler data", // H264_NAL_FILLER_DATA + "SPS extension", // H264_NAL_SPS_EXT + "Prefix", // H264_NAL_PREFIX + "Subset SPS", // H264_NAL_SUB_SPS + "Depth parameter set", // H264_NAL_DPS + "Reserved 17", // H264_NAL_RESERVED17 + "Reserved 18", // H264_NAL_RESERVED18 + "Auxiliary coded picture without partitioning", // H264_NAL_AUXILIARY_SLICE + "Slice extension", // H264_NAL_EXTEN_SLICE + "Slice extension for a depth view or a 3D-AVC texture view", // H264_NAL_DEPTH_EXTEN_SLICE + "Reserved 22", // H264_NAL_RESERVED22 + "Reserved 23", // H264_NAL_RESERVED23 + "Unspecified 24", // H264_NAL_UNSPECIFIED24 + "Unspecified 25", // H264_NAL_UNSPECIFIED25 + "Unspecified 26", // H264_NAL_UNSPECIFIED26 + "Unspecified 27", // H264_NAL_UNSPECIFIED27 + "Unspecified 28", // H264_NAL_UNSPECIFIED28 + "Unspecified 29", // H264_NAL_UNSPECIFIED29 + "Unspecified 30", // H264_NAL_UNSPECIFIED30 + "Unspecified 31", // H264_NAL_UNSPECIFIED31 +}; + +static const char *h264_nal_unit_name(int nal_type) { + av_assert0(nal_type >= 0 && nal_type < 32); + return h264_nal_type_name[nal_type]; +} + +static int get_bit_length(H2645NAL *nal, int skip_trailing_zeros) { + int size = nal->size; + int v; + + while(skip_trailing_zeros && size > 0 && nal->data[size - 1] == 0) + size--; + + if(!size) + return 0; + + v = nal->data[size - 1]; + + if(size > INT_MAX / 8) + return AVERROR(ERANGE); + size *= 8; + + /* remove the stop bit and following trailing zeros, + * or nothing for damaged bitstreams */ + if(v) + size -= ff_ctz(v) + 1; + + return size; +} + +/** + * @return AVERROR_INVALIDDATA if the packet is not a valid NAL unit, + * 0 otherwise + */ +static int hevc_parse_nal_header(H2645NAL *nal, void *logctx) { + GetBitContext *gb = &nal->gb; + + if(get_bits1(gb) != 0) + return AVERROR_INVALIDDATA; + + nal->type = get_bits(gb, 6); + + nal->nuh_layer_id = get_bits(gb, 6); + nal->temporal_id = get_bits(gb, 3) - 1; + if(nal->temporal_id < 0) + return AVERROR_INVALIDDATA; + + av_log(logctx, AV_LOG_DEBUG, + "nal_unit_type: %d(%s), nuh_layer_id: %d, temporal_id: %d\n", + nal->type, hevc_nal_unit_name(nal->type), nal->nuh_layer_id, nal->temporal_id); + + return 0; +} + +static int h264_parse_nal_header(H2645NAL *nal, void *logctx) { + GetBitContext *gb = &nal->gb; + + if(get_bits1(gb) != 0) + return AVERROR_INVALIDDATA; + + nal->ref_idc = get_bits(gb, 2); + nal->type = get_bits(gb, 5); + + av_log(logctx, AV_LOG_DEBUG, + "nal_unit_type: %d(%s), nal_ref_idc: %d\n", + nal->type, h264_nal_unit_name(nal->type), nal->ref_idc); + + return 0; +} + +static int find_next_start_code(const uint8_t *buf, const uint8_t *next_avc) { + int i = 0; + + if(buf + 3 >= next_avc) + return next_avc - buf; + + while(buf + i + 3 < next_avc) { + if(buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1) + break; + i++; + } + return i + 3; +} + +static void alloc_rbsp_buffer(H2645RBSP *rbsp, unsigned int size, int use_ref) { + int min_size = size; + + if(size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE) + goto fail; + size += AV_INPUT_BUFFER_PADDING_SIZE; + + if(rbsp->rbsp_buffer_alloc_size >= size && + (!rbsp->rbsp_buffer_ref || av_buffer_is_writable(rbsp->rbsp_buffer_ref))) { + av_assert0(rbsp->rbsp_buffer); + memset(rbsp->rbsp_buffer + min_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + return; + } + + size = FFMIN(size + size / 16 + 32, INT_MAX); + + if(rbsp->rbsp_buffer_ref) + av_buffer_unref(&rbsp->rbsp_buffer_ref); + else + av_free(rbsp->rbsp_buffer); + + rbsp->rbsp_buffer = av_mallocz(size); + if(!rbsp->rbsp_buffer) + goto fail; + rbsp->rbsp_buffer_alloc_size = size; + + if(use_ref) { + rbsp->rbsp_buffer_ref = av_buffer_create(rbsp->rbsp_buffer, size, + NULL, NULL, 0); + if(!rbsp->rbsp_buffer_ref) + goto fail; + } + + return; + +fail: + rbsp->rbsp_buffer_alloc_size = 0; + if(rbsp->rbsp_buffer_ref) { + av_buffer_unref(&rbsp->rbsp_buffer_ref); + rbsp->rbsp_buffer = NULL; + } + else + av_freep(&rbsp->rbsp_buffer); + + return; +} + +int ff_h2645_packet_split(H2645Packet *pkt, const uint8_t *buf, int length, + void *logctx, int is_nalff, int nal_length_size, + enum AVCodecID codec_id, int small_padding, int use_ref) { + GetByteContext bc; + int consumed, ret = 0; + int next_avc = is_nalff ? 0 : length; + int64_t padding = small_padding ? 0 : MAX_MBPAIR_SIZE; + + bytestream2_init(&bc, buf, length); + alloc_rbsp_buffer(&pkt->rbsp, length + padding, use_ref); + + if(!pkt->rbsp.rbsp_buffer) + return AVERROR(ENOMEM); + + pkt->rbsp.rbsp_buffer_size = 0; + pkt->nb_nals = 0; + while(bytestream2_get_bytes_left(&bc) >= 4) { + H2645NAL *nal; + int extract_length = 0; + int skip_trailing_zeros = 1; + + if(bytestream2_tell(&bc) == next_avc) { + int i = 0; + extract_length = get_nalsize(nal_length_size, + bc.buffer, bytestream2_get_bytes_left(&bc), &i, logctx); + if(extract_length < 0) + return extract_length; + + bytestream2_skip(&bc, nal_length_size); + + next_avc = bytestream2_tell(&bc) + extract_length; + } + else { + int buf_index; + + if(bytestream2_tell(&bc) > next_avc) + av_log(logctx, AV_LOG_WARNING, "Exceeded next NALFF position, re-syncing.\n"); + + /* search start code */ + buf_index = find_next_start_code(bc.buffer, buf + next_avc); + + bytestream2_skip(&bc, buf_index); + + if(!bytestream2_get_bytes_left(&bc)) { + if(pkt->nb_nals > 0) { + // No more start codes: we discarded some irrelevant + // bytes at the end of the packet. + return 0; + } + else { + av_log(logctx, AV_LOG_ERROR, "No start code is found.\n"); + return AVERROR_INVALIDDATA; + } + } + + extract_length = FFMIN(bytestream2_get_bytes_left(&bc), next_avc - bytestream2_tell(&bc)); + + if(bytestream2_tell(&bc) >= next_avc) { + /* skip to the start of the next NAL */ + bytestream2_skip(&bc, next_avc - bytestream2_tell(&bc)); + continue; + } + } + + if(pkt->nals_allocated < pkt->nb_nals + 1) { + int new_size = pkt->nals_allocated + 1; + void *tmp; + + if(new_size >= INT_MAX / sizeof(*pkt->nals)) + return AVERROR(ENOMEM); + + tmp = av_fast_realloc(pkt->nals, &pkt->nal_buffer_size, new_size * sizeof(*pkt->nals)); + if(!tmp) + return AVERROR(ENOMEM); + + pkt->nals = tmp; + memset(pkt->nals + pkt->nals_allocated, 0, sizeof(*pkt->nals)); + + nal = &pkt->nals[pkt->nb_nals]; + nal->skipped_bytes_pos_size = FFMIN(1024, extract_length / 3 + 1); // initial buffer size + nal->skipped_bytes_pos = av_malloc_array(nal->skipped_bytes_pos_size, sizeof(*nal->skipped_bytes_pos)); + if(!nal->skipped_bytes_pos) + return AVERROR(ENOMEM); + + pkt->nals_allocated = new_size; + } + nal = &pkt->nals[pkt->nb_nals]; + + consumed = ff_h2645_extract_rbsp(bc.buffer, extract_length, &pkt->rbsp, nal, small_padding); + if(consumed < 0) + return consumed; + + if(is_nalff && (extract_length != consumed) && extract_length) + av_log(logctx, AV_LOG_DEBUG, + "NALFF: Consumed only %d bytes instead of %d\n", + consumed, extract_length); + + bytestream2_skip(&bc, consumed); + + /* see commit 3566042a0 */ + if(bytestream2_get_bytes_left(&bc) >= 4 && + bytestream2_peek_be32(&bc) == 0x000001E0) + skip_trailing_zeros = 0; + + nal->size_bits = get_bit_length(nal, skip_trailing_zeros); + + if(nal->size <= 0 || nal->size_bits <= 0) + continue; + + ret = init_get_bits(&nal->gb, nal->data, nal->size_bits); + if(ret < 0) + return ret; + + /* Reset type in case it contains a stale value from a previously parsed NAL */ + nal->type = 0; + + if(codec_id == AV_CODEC_ID_HEVC) + ret = hevc_parse_nal_header(nal, logctx); + else + ret = h264_parse_nal_header(nal, logctx); + if(ret < 0) { + av_log(logctx, AV_LOG_WARNING, "Invalid NAL unit %d, skipping.\n", + nal->type); + continue; + } + + pkt->nb_nals++; + } + + return 0; +} + +void ff_h2645_packet_uninit(H2645Packet *pkt) { + int i; + for(i = 0; i < pkt->nals_allocated; i++) { + av_freep(&pkt->nals[i].skipped_bytes_pos); + } + av_freep(&pkt->nals); + pkt->nals_allocated = pkt->nal_buffer_size = 0; + if(pkt->rbsp.rbsp_buffer_ref) { + av_buffer_unref(&pkt->rbsp.rbsp_buffer_ref); + pkt->rbsp.rbsp_buffer = NULL; + } + else + av_freep(&pkt->rbsp.rbsp_buffer); + pkt->rbsp.rbsp_buffer_alloc_size = pkt->rbsp.rbsp_buffer_size = 0; +} diff --git a/third-party/cbs/h264_ps.h b/third-party/cbs/h264_ps.h new file mode 100644 index 00000000..fbd0cb7a --- /dev/null +++ b/third-party/cbs/h264_ps.h @@ -0,0 +1,173 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * H.264 parameter set handling + */ + +#ifndef AVCODEC_H264_PS_H +#define AVCODEC_H264_PS_H + +#include + +#include +#include +#include +#include + +#include "cbs/h264.h" + +#include "get_bits.h" + +#define MAX_SPS_COUNT 32 +#define MAX_PPS_COUNT 256 +#define MAX_LOG2_MAX_FRAME_NUM (12 + 4) + +/** + * Sequence parameter set + */ +typedef struct SPS { + unsigned int sps_id; + int profile_idc; + int level_idc; + int chroma_format_idc; + int transform_bypass; ///< qpprime_y_zero_transform_bypass_flag + int log2_max_frame_num; ///< log2_max_frame_num_minus4 + 4 + int poc_type; ///< pic_order_cnt_type + int log2_max_poc_lsb; ///< log2_max_pic_order_cnt_lsb_minus4 + int delta_pic_order_always_zero_flag; + int offset_for_non_ref_pic; + int offset_for_top_to_bottom_field; + int poc_cycle_length; ///< num_ref_frames_in_pic_order_cnt_cycle + int ref_frame_count; ///< num_ref_frames + int gaps_in_frame_num_allowed_flag; + int mb_width; ///< pic_width_in_mbs_minus1 + 1 + ///< (pic_height_in_map_units_minus1 + 1) * (2 - frame_mbs_only_flag) + int mb_height; + int frame_mbs_only_flag; + int mb_aff; ///< mb_adaptive_frame_field_flag + int direct_8x8_inference_flag; + int crop; ///< frame_cropping_flag + + /* those 4 are already in luma samples */ + unsigned int crop_left; ///< frame_cropping_rect_left_offset + unsigned int crop_right; ///< frame_cropping_rect_right_offset + unsigned int crop_top; ///< frame_cropping_rect_top_offset + unsigned int crop_bottom; ///< frame_cropping_rect_bottom_offset + int vui_parameters_present_flag; + AVRational sar; + int video_signal_type_present_flag; + int full_range; + int colour_description_present_flag; + enum AVColorPrimaries color_primaries; + enum AVColorTransferCharacteristic color_trc; + enum AVColorSpace colorspace; + enum AVChromaLocation chroma_location; + + int timing_info_present_flag; + uint32_t num_units_in_tick; + uint32_t time_scale; + int fixed_frame_rate_flag; + int32_t offset_for_ref_frame[256]; + int bitstream_restriction_flag; + int num_reorder_frames; + int scaling_matrix_present; + uint8_t scaling_matrix4[6][16]; + uint8_t scaling_matrix8[6][64]; + int nal_hrd_parameters_present_flag; + int vcl_hrd_parameters_present_flag; + int pic_struct_present_flag; + int time_offset_length; + int cpb_cnt; ///< See H.264 E.1.2 + int initial_cpb_removal_delay_length; ///< initial_cpb_removal_delay_length_minus1 + 1 + int cpb_removal_delay_length; ///< cpb_removal_delay_length_minus1 + 1 + int dpb_output_delay_length; ///< dpb_output_delay_length_minus1 + 1 + int bit_depth_luma; ///< bit_depth_luma_minus8 + 8 + int bit_depth_chroma; ///< bit_depth_chroma_minus8 + 8 + int residual_color_transform_flag; ///< residual_colour_transform_flag + int constraint_set_flags; ///< constraint_set[0-3]_flag + uint8_t data[4096]; + size_t data_size; +} SPS; + +/** + * Picture parameter set + */ +typedef struct PPS { + unsigned int sps_id; + int cabac; ///< entropy_coding_mode_flag + int pic_order_present; ///< pic_order_present_flag + int slice_group_count; ///< num_slice_groups_minus1 + 1 + int mb_slice_group_map_type; + unsigned int ref_count[2]; ///< num_ref_idx_l0/1_active_minus1 + 1 + int weighted_pred; ///< weighted_pred_flag + int weighted_bipred_idc; + int init_qp; ///< pic_init_qp_minus26 + 26 + int init_qs; ///< pic_init_qs_minus26 + 26 + int chroma_qp_index_offset[2]; + int deblocking_filter_parameters_present; ///< deblocking_filter_parameters_present_flag + int constrained_intra_pred; ///< constrained_intra_pred_flag + int redundant_pic_cnt_present; ///< redundant_pic_cnt_present_flag + int transform_8x8_mode; ///< transform_8x8_mode_flag + uint8_t scaling_matrix4[6][16]; + uint8_t scaling_matrix8[6][64]; + uint8_t chroma_qp_table[2][QP_MAX_NUM + 1]; ///< pre-scaled (with chroma_qp_index_offset) version of qp_table + int chroma_qp_diff; + uint8_t data[4096]; + size_t data_size; + + uint32_t dequant4_buffer[6][QP_MAX_NUM + 1][16]; + uint32_t dequant8_buffer[6][QP_MAX_NUM + 1][64]; + uint32_t (*dequant4_coeff[6])[16]; + uint32_t (*dequant8_coeff[6])[64]; + + AVBufferRef *sps_ref; + const SPS *sps; +} PPS; + +typedef struct H264ParamSets { + AVBufferRef *sps_list[MAX_SPS_COUNT]; + AVBufferRef *pps_list[MAX_PPS_COUNT]; + + AVBufferRef *pps_ref; + /* currently active parameters sets */ + const PPS *pps; + const SPS *sps; + + int overread_warning_printed[2]; +} H264ParamSets; + +/** + * Decode SPS + */ +int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx, + H264ParamSets *ps, int ignore_truncation); + +/** + * Decode PPS + */ +int ff_h264_decode_picture_parameter_set(GetBitContext *gb, AVCodecContext *avctx, + H264ParamSets *ps, int bit_length); + +/** + * Uninit H264 param sets structure. + */ +void ff_h264_ps_uninit(H264ParamSets *ps); + +#endif /* AVCODEC_H264_PS_H */ diff --git a/third-party/cbs/h264_sei.h b/third-party/cbs/h264_sei.h new file mode 100644 index 00000000..64dbb86e --- /dev/null +++ b/third-party/cbs/h264_sei.h @@ -0,0 +1,202 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_H264_SEI_H +#define AVCODEC_H264_SEI_H + +#include "cbs/sei.h" +#include "get_bits.h" +#include "h264_ps.h" + + +/** + * pic_struct in picture timing SEI message + */ +typedef enum { + H264_SEI_PIC_STRUCT_FRAME = 0, ///< 0: %frame + H264_SEI_PIC_STRUCT_TOP_FIELD = 1, ///< 1: top field + H264_SEI_PIC_STRUCT_BOTTOM_FIELD = 2, ///< 2: bottom field + H264_SEI_PIC_STRUCT_TOP_BOTTOM = 3, ///< 3: top field, bottom field, in that order + H264_SEI_PIC_STRUCT_BOTTOM_TOP = 4, ///< 4: bottom field, top field, in that order + H264_SEI_PIC_STRUCT_TOP_BOTTOM_TOP = 5, ///< 5: top field, bottom field, top field repeated, in that order + H264_SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM = 6, ///< 6: bottom field, top field, bottom field repeated, in that order + H264_SEI_PIC_STRUCT_FRAME_DOUBLING = 7, ///< 7: %frame doubling + H264_SEI_PIC_STRUCT_FRAME_TRIPLING = 8 ///< 8: %frame tripling +} H264_SEI_PicStructType; + +/** + * frame_packing_arrangement types + */ +typedef enum { + H264_SEI_FPA_TYPE_CHECKERBOARD = 0, + H264_SEI_FPA_TYPE_INTERLEAVE_COLUMN = 1, + H264_SEI_FPA_TYPE_INTERLEAVE_ROW = 2, + H264_SEI_FPA_TYPE_SIDE_BY_SIDE = 3, + H264_SEI_FPA_TYPE_TOP_BOTTOM = 4, + H264_SEI_FPA_TYPE_INTERLEAVE_TEMPORAL = 5, + H264_SEI_FPA_TYPE_2D = 6, +} H264_SEI_FpaType; + +typedef struct H264SEITimeCode { + /* When not continuously receiving full timecodes, we have to reference + the previous timecode received */ + int full; + int frame; + int seconds; + int minutes; + int hours; + int dropframe; +} H264SEITimeCode; + +typedef struct H264SEIPictureTiming { + // maximum size of pic_timing according to the spec should be 274 bits + uint8_t payload[40]; + int payload_size_bits; + + int present; + H264_SEI_PicStructType pic_struct; + + /** + * Bit set of clock types for fields/frames in picture timing SEI message. + * For each found ct_type, appropriate bit is set (e.g., bit 1 for + * interlaced). + */ + int ct_type; + + /** + * dpb_output_delay in picture timing SEI message, see H.264 C.2.2 + */ + int dpb_output_delay; + + /** + * cpb_removal_delay in picture timing SEI message, see H.264 C.1.2 + */ + int cpb_removal_delay; + + /** + * Maximum three timecodes in a pic_timing SEI. + */ + H264SEITimeCode timecode[3]; + + /** + * Number of timecode in use + */ + int timecode_cnt; +} H264SEIPictureTiming; + +typedef struct H264SEIAFD { + int present; + uint8_t active_format_description; +} H264SEIAFD; + +typedef struct H264SEIA53Caption { + AVBufferRef *buf_ref; +} H264SEIA53Caption; + +typedef struct H264SEIUnregistered { + int x264_build; + AVBufferRef **buf_ref; + int nb_buf_ref; +} H264SEIUnregistered; + +typedef struct H264SEIRecoveryPoint { + /** + * recovery_frame_cnt + * + * Set to -1 if no recovery point SEI message found or to number of frames + * before playback synchronizes. Frames having recovery point are key + * frames. + */ + int recovery_frame_cnt; +} H264SEIRecoveryPoint; + +typedef struct H264SEIBufferingPeriod { + int present; ///< Buffering period SEI flag + int initial_cpb_removal_delay[32]; ///< Initial timestamps for CPBs +} H264SEIBufferingPeriod; + +typedef struct H264SEIFramePacking { + int present; + int arrangement_id; + int arrangement_cancel_flag; ///< is previous arrangement canceled, -1 if never received + H264_SEI_FpaType arrangement_type; + int arrangement_repetition_period; + int content_interpretation_type; + int quincunx_sampling_flag; + int current_frame_is_frame0_flag; +} H264SEIFramePacking; + +typedef struct H264SEIDisplayOrientation { + int present; + int anticlockwise_rotation; + int hflip, vflip; +} H264SEIDisplayOrientation; + +typedef struct H264SEIGreenMetaData { + uint8_t green_metadata_type; + uint8_t period_type; + uint16_t num_seconds; + uint16_t num_pictures; + uint8_t percent_non_zero_macroblocks; + uint8_t percent_intra_coded_macroblocks; + uint8_t percent_six_tap_filtering; + uint8_t percent_alpha_point_deblocking_instance; + uint8_t xsd_metric_type; + uint16_t xsd_metric_value; +} H264SEIGreenMetaData; + +typedef struct H264SEIAlternativeTransfer { + int present; + int preferred_transfer_characteristics; +} H264SEIAlternativeTransfer; + +typedef struct H264SEIContext { + H264SEIPictureTiming picture_timing; + H264SEIAFD afd; + H264SEIA53Caption a53_caption; + H264SEIUnregistered unregistered; + H264SEIRecoveryPoint recovery_point; + H264SEIBufferingPeriod buffering_period; + H264SEIFramePacking frame_packing; + H264SEIDisplayOrientation display_orientation; + H264SEIGreenMetaData green_metadata; + H264SEIAlternativeTransfer alternative_transfer; +} H264SEIContext; + +struct H264ParamSets; + +int ff_h264_sei_decode(H264SEIContext *h, GetBitContext *gb, + const struct H264ParamSets *ps, void *logctx); + +/** + * Reset SEI values at the beginning of the frame. + */ +void ff_h264_sei_uninit(H264SEIContext *h); + +/** + * Get stereo_mode string from the h264 frame_packing_arrangement + */ +const char *ff_h264_sei_stereo_mode(const H264SEIFramePacking *h); + +/** + * Parse the contents of a picture timing message given an active SPS. + */ +int ff_h264_sei_process_picture_timing(H264SEIPictureTiming *h, const SPS *sps, + void *logctx); + +#endif /* AVCODEC_H264_SEI_H */ diff --git a/third-party/cbs/hevc_sei.h b/third-party/cbs/hevc_sei.h new file mode 100644 index 00000000..8dfffcc4 --- /dev/null +++ b/third-party/cbs/hevc_sei.h @@ -0,0 +1,142 @@ +/* + * HEVC Supplementary Enhancement Information messages + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_HEVC_SEI_H +#define AVCODEC_HEVC_SEI_H + +#include + +#include + +#include "cbs/sei.h" + +#include "get_bits.h" + + +typedef enum { + HEVC_SEI_PIC_STRUCT_FRAME_DOUBLING = 7, + HEVC_SEI_PIC_STRUCT_FRAME_TRIPLING = 8 +} HEVC_SEI_PicStructType; + +typedef struct HEVCSEIPictureHash { + uint8_t md5[3][16]; + uint8_t is_md5; +} HEVCSEIPictureHash; + +typedef struct HEVCSEIFramePacking { + int present; + int arrangement_type; + int content_interpretation_type; + int quincunx_subsampling; + int current_frame_is_frame0_flag; +} HEVCSEIFramePacking; + +typedef struct HEVCSEIDisplayOrientation { + int present; + int anticlockwise_rotation; + int hflip, vflip; +} HEVCSEIDisplayOrientation; + +typedef struct HEVCSEIPictureTiming { + int picture_struct; +} HEVCSEIPictureTiming; + +typedef struct HEVCSEIA53Caption { + AVBufferRef *buf_ref; +} HEVCSEIA53Caption; + +typedef struct HEVCSEIUnregistered { + AVBufferRef **buf_ref; + int nb_buf_ref; +} HEVCSEIUnregistered; + +typedef struct HEVCSEIMasteringDisplay { + int present; + uint16_t display_primaries[3][2]; + uint16_t white_point[2]; + uint32_t max_luminance; + uint32_t min_luminance; +} HEVCSEIMasteringDisplay; + +typedef struct HEVCSEIDynamicHDRPlus { + AVBufferRef *info; +} HEVCSEIDynamicHDRPlus; + +typedef struct HEVCSEIContentLight { + int present; + uint16_t max_content_light_level; + uint16_t max_pic_average_light_level; +} HEVCSEIContentLight; + +typedef struct HEVCSEIAlternativeTransfer { + int present; + int preferred_transfer_characteristics; +} HEVCSEIAlternativeTransfer; + +typedef struct HEVCSEITimeCode { + int present; + uint8_t num_clock_ts; + uint8_t clock_timestamp_flag[3]; + uint8_t units_field_based_flag[3]; + uint8_t counting_type[3]; + uint8_t full_timestamp_flag[3]; + uint8_t discontinuity_flag[3]; + uint8_t cnt_dropped_flag[3]; + uint16_t n_frames[3]; + uint8_t seconds_value[3]; + uint8_t minutes_value[3]; + uint8_t hours_value[3]; + uint8_t seconds_flag[3]; + uint8_t minutes_flag[3]; + uint8_t hours_flag[3]; + uint8_t time_offset_length[3]; + int32_t time_offset_value[3]; +} HEVCSEITimeCode; + +typedef struct HEVCSEI { + HEVCSEIPictureHash picture_hash; + HEVCSEIFramePacking frame_packing; + HEVCSEIDisplayOrientation display_orientation; + HEVCSEIPictureTiming picture_timing; + HEVCSEIA53Caption a53_caption; + HEVCSEIUnregistered unregistered; + HEVCSEIMasteringDisplay mastering_display; + HEVCSEIDynamicHDRPlus dynamic_hdr_plus; + HEVCSEIContentLight content_light; + int active_seq_parameter_set_id; + HEVCSEIAlternativeTransfer alternative_transfer; + HEVCSEITimeCode timecode; +} HEVCSEI; + +struct HEVCParamSets; + +int ff_hevc_decode_nal_sei(GetBitContext *gb, void *logctx, HEVCSEI *s, + const struct HEVCParamSets *ps, int type); + +/** + * Reset SEI values that are stored on the Context. + * e.g. Caption data that was extracted during NAL + * parsing. + * + * @param s HEVCContext. + */ +void ff_hevc_reset_sei(HEVCSEI *s); + +#endif /* AVCODEC_HEVC_SEI_H */ diff --git a/third-party/cbs/include/cbs/av1.h b/third-party/cbs/include/cbs/av1.h new file mode 100644 index 00000000..d8c2b9e7 --- /dev/null +++ b/third-party/cbs/include/cbs/av1.h @@ -0,0 +1,171 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * AV1 common definitions + */ + +#ifndef AVCODEC_AV1_H +#define AVCODEC_AV1_H + +// OBU types (section 6.2.2). +typedef enum { + // 0 reserved. + AV1_OBU_SEQUENCE_HEADER = 1, + AV1_OBU_TEMPORAL_DELIMITER = 2, + AV1_OBU_FRAME_HEADER = 3, + AV1_OBU_TILE_GROUP = 4, + AV1_OBU_METADATA = 5, + AV1_OBU_FRAME = 6, + AV1_OBU_REDUNDANT_FRAME_HEADER = 7, + AV1_OBU_TILE_LIST = 8, + // 9-14 reserved. + AV1_OBU_PADDING = 15, +} AV1_OBU_Type; + +// Metadata types (section 6.7.1). +enum { + AV1_METADATA_TYPE_HDR_CLL = 1, + AV1_METADATA_TYPE_HDR_MDCV = 2, + AV1_METADATA_TYPE_SCALABILITY = 3, + AV1_METADATA_TYPE_ITUT_T35 = 4, + AV1_METADATA_TYPE_TIMECODE = 5, +}; + +// Frame types (section 6.8.2). +enum { + AV1_FRAME_KEY = 0, + AV1_FRAME_INTER = 1, + AV1_FRAME_INTRA_ONLY = 2, + AV1_FRAME_SWITCH = 3, +}; + +// Reference frames (section 6.10.24). +enum { + AV1_REF_FRAME_INTRA = 0, + AV1_REF_FRAME_LAST = 1, + AV1_REF_FRAME_LAST2 = 2, + AV1_REF_FRAME_LAST3 = 3, + AV1_REF_FRAME_GOLDEN = 4, + AV1_REF_FRAME_BWDREF = 5, + AV1_REF_FRAME_ALTREF2 = 6, + AV1_REF_FRAME_ALTREF = 7, +}; + +// Constants (section 3). +enum { + AV1_MAX_OPERATING_POINTS = 32, + + AV1_MAX_SB_SIZE = 128, + AV1_MI_SIZE = 4, + + AV1_MAX_TILE_WIDTH = 4096, + AV1_MAX_TILE_AREA = 4096 * 2304, + AV1_MAX_TILE_ROWS = 64, + AV1_MAX_TILE_COLS = 64, + + AV1_NUM_REF_FRAMES = 8, + AV1_REFS_PER_FRAME = 7, + AV1_TOTAL_REFS_PER_FRAME = 8, + AV1_PRIMARY_REF_NONE = 7, + + AV1_MAX_SEGMENTS = 8, + AV1_SEG_LVL_MAX = 8, + + AV1_SEG_LVL_ALT_Q = 0, + AV1_SEG_LVL_ALT_LF_Y_V = 1, + AV1_SEG_LVL_REF_FRAME = 5, + AV1_SEG_LVL_SKIP = 6, + AV1_SEG_LVL_GLOBAL_MV = 7, + + AV1_SELECT_SCREEN_CONTENT_TOOLS = 2, + AV1_SELECT_INTEGER_MV = 2, + + AV1_SUPERRES_NUM = 8, + AV1_SUPERRES_DENOM_MIN = 9, + + AV1_INTERPOLATION_FILTER_SWITCHABLE = 4, + + AV1_GM_ABS_ALPHA_BITS = 12, + AV1_GM_ALPHA_PREC_BITS = 15, + AV1_GM_ABS_TRANS_ONLY_BITS = 9, + AV1_GM_TRANS_ONLY_PREC_BITS = 3, + AV1_GM_ABS_TRANS_BITS = 12, + AV1_GM_TRANS_PREC_BITS = 6, + AV1_WARPEDMODEL_PREC_BITS = 16, + + AV1_WARP_MODEL_IDENTITY = 0, + AV1_WARP_MODEL_TRANSLATION = 1, + AV1_WARP_MODEL_ROTZOOM = 2, + AV1_WARP_MODEL_AFFINE = 3, +}; + + +// The main colour configuration information uses the same ISO/IEC 23001-8 +// (H.273) enums as FFmpeg does, so separate definitions are not required. + +// Chroma sample position. +enum { + AV1_CSP_UNKNOWN = 0, + AV1_CSP_VERTICAL = 1, // -> AVCHROMA_LOC_LEFT. + AV1_CSP_COLOCATED = 2, // -> AVCHROMA_LOC_TOPLEFT. +}; + +// Scalability modes (section 6.7.5) +enum { + AV1_SCALABILITY_L1T2 = 0, + AV1_SCALABILITY_L1T3 = 1, + AV1_SCALABILITY_L2T1 = 2, + AV1_SCALABILITY_L2T2 = 3, + AV1_SCALABILITY_L2T3 = 4, + AV1_SCALABILITY_S2T1 = 5, + AV1_SCALABILITY_S2T2 = 6, + AV1_SCALABILITY_S2T3 = 7, + AV1_SCALABILITY_L2T1h = 8, + AV1_SCALABILITY_L2T2h = 9, + AV1_SCALABILITY_L2T3h = 10, + AV1_SCALABILITY_S2T1h = 11, + AV1_SCALABILITY_S2T2h = 12, + AV1_SCALABILITY_S2T3h = 13, + AV1_SCALABILITY_SS = 14, + AV1_SCALABILITY_L3T1 = 15, + AV1_SCALABILITY_L3T2 = 16, + AV1_SCALABILITY_L3T3 = 17, + AV1_SCALABILITY_S3T1 = 18, + AV1_SCALABILITY_S3T2 = 19, + AV1_SCALABILITY_S3T3 = 20, + AV1_SCALABILITY_L3T2_KEY = 21, + AV1_SCALABILITY_L3T3_KEY = 22, + AV1_SCALABILITY_L4T5_KEY = 23, + AV1_SCALABILITY_L4T7_KEY = 24, + AV1_SCALABILITY_L3T2_KEY_SHIFT = 25, + AV1_SCALABILITY_L3T3_KEY_SHIFT = 26, + AV1_SCALABILITY_L4T5_KEY_SHIFT = 27, + AV1_SCALABILITY_L4T7_KEY_SHIFT = 28, +}; + +// Frame Restoration types (section 6.10.15) +enum { + AV1_RESTORE_NONE = 0, + AV1_RESTORE_WIENER = 1, + AV1_RESTORE_SGRPROJ = 2, + AV1_RESTORE_SWITCHABLE = 3, +}; + +#endif /* AVCODEC_AV1_H */ diff --git a/third-party/cbs/include/cbs/cbs.h b/third-party/cbs/include/cbs/cbs.h new file mode 100644 index 00000000..5502a166 --- /dev/null +++ b/third-party/cbs/include/cbs/cbs.h @@ -0,0 +1,448 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CBS_H +#define AVCODEC_CBS_H + +#include +#include + +#include + +#include + + +/* + * This defines a framework for converting between a coded bitstream + * and structures defining all individual syntax elements found in + * such a stream. + * + * Conversion in both directions is possible. Given a coded bitstream + * (any meaningful fragment), it can be parsed and decomposed into + * syntax elements stored in a set of codec-specific structures. + * Similarly, given a set of those same codec-specific structures the + * syntax elements can be serialised and combined to create a coded + * bitstream. + */ + +struct CodedBitstreamType; + +/** + * The codec-specific type of a bitstream unit. + * + * AV1: obu_type + * H.264 / AVC: nal_unit_type + * H.265 / HEVC: nal_unit_type + * JPEG: marker value (without 0xff prefix) + * MPEG-2: start code value (without prefix) + * VP9: unused, set to zero (every unit is a frame) + */ +typedef uint32_t CodedBitstreamUnitType; + +/** + * Coded bitstream unit structure. + * + * A bitstream unit the smallest element of a bitstream which + * is meaningful on its own. For example, an H.264 NAL unit. + * + * See the codec-specific header for the meaning of this for any + * particular codec. + */ +typedef struct CodedBitstreamUnit { + /** + * Codec-specific type of this unit. + */ + CodedBitstreamUnitType type; + + /** + * Pointer to the directly-parsable bitstream form of this unit. + * + * May be NULL if the unit currently only exists in decomposed form. + */ + uint8_t *data; + /** + * The number of bytes in the bitstream (including any padding bits + * in the final byte). + */ + size_t data_size; + /** + * The number of bits which should be ignored in the final byte. + * + * This supports non-byte-aligned bitstreams. + */ + size_t data_bit_padding; + /** + * A reference to the buffer containing data. + * + * Must be set if data is not NULL. + */ + AVBufferRef *data_ref; + + /** + * Pointer to the decomposed form of this unit. + * + * The type of this structure depends on both the codec and the + * type of this unit. May be NULL if the unit only exists in + * bitstream form. + */ + void *content; + /** + * If content is reference counted, a reference to the buffer containing + * content. Null if content is not reference counted. + */ + AVBufferRef *content_ref; +} CodedBitstreamUnit; + +/** + * Coded bitstream fragment structure, combining one or more units. + * + * This is any sequence of units. It need not form some greater whole, + * though in many cases it will. For example, an H.264 access unit, + * which is composed of a sequence of H.264 NAL units. + */ +typedef struct CodedBitstreamFragment { + /** + * Pointer to the bitstream form of this fragment. + * + * May be NULL if the fragment only exists as component units. + */ + uint8_t *data; + /** + * The number of bytes in the bitstream. + * + * The number of bytes in the bitstream (including any padding bits + * in the final byte). + */ + size_t data_size; + /** + * The number of bits which should be ignored in the final byte. + */ + size_t data_bit_padding; + /** + * A reference to the buffer containing data. + * + * Must be set if data is not NULL. + */ + AVBufferRef *data_ref; + + /** + * Number of units in this fragment. + * + * This may be zero if the fragment only exists in bitstream form + * and has not been decomposed. + */ + int nb_units; + + /** + * Number of allocated units. + * + * Must always be >= nb_units; designed for internal use by cbs. + */ + int nb_units_allocated; + + /** + * Pointer to an array of units of length nb_units_allocated. + * Only the first nb_units are valid. + * + * Must be NULL if nb_units_allocated is zero. + */ + CodedBitstreamUnit *units; +} CodedBitstreamFragment; + +/** + * Context structure for coded bitstream operations. + */ +typedef struct CodedBitstreamContext { + /** + * Logging context to be passed to all av_log() calls associated + * with this context. + */ + void *log_ctx; + + /** + * Internal codec-specific hooks. + */ + const struct CodedBitstreamType *codec; + + /** + * Internal codec-specific data. + * + * This contains any information needed when reading/writing + * bitsteams which will not necessarily be present in a fragment. + * For example, for H.264 it contains all currently visible + * parameter sets - they are required to determine the bitstream + * syntax but need not be present in every access unit. + */ + void *priv_data; + + /** + * Array of unit types which should be decomposed when reading. + * + * Types not in this list will be available in bitstream form only. + * If NULL, all supported types will be decomposed. + */ + const CodedBitstreamUnitType *decompose_unit_types; + /** + * Length of the decompose_unit_types array. + */ + int nb_decompose_unit_types; + + /** + * Enable trace output during read/write operations. + */ + int trace_enable; + /** + * Log level to use for trace output. + * + * From AV_LOG_*; defaults to AV_LOG_TRACE. + */ + int trace_level; + + /** + * Write buffer. Used as intermediate buffer when writing units. + * For internal use of cbs only. + */ + uint8_t *write_buffer; + size_t write_buffer_size; +} CodedBitstreamContext; + + +/** + * Table of all supported codec IDs. + * + * Terminated by AV_CODEC_ID_NONE. + */ +extern const enum AVCodecID ff_cbs_all_codec_ids[]; + + +/** + * Create and initialise a new context for the given codec. + */ +int ff_cbs_init(CodedBitstreamContext **ctx, + enum AVCodecID codec_id, void *log_ctx); + +/** + * Reset all internal state in a context. + */ +void ff_cbs_flush(CodedBitstreamContext *ctx); + +/** + * Close a context and free all internal state. + */ +void ff_cbs_close(CodedBitstreamContext **ctx); + + +/** + * Read the extradata bitstream found in codec parameters into a + * fragment, then split into units and decompose. + * + * This also updates the internal state, so will need to be called for + * codecs with extradata to read parameter sets necessary for further + * parsing even if the fragment itself is not desired. + * + * The fragment must have been zeroed or reset via ff_cbs_fragment_reset + * before use. + */ +int ff_cbs_read_extradata(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + const AVCodecParameters *par); + +/** + * Read the extradata bitstream found in a codec context into a + * fragment, then split into units and decompose. + * + * This acts identical to ff_cbs_read_extradata() for the case where + * you already have a codec context. + */ +int ff_cbs_read_extradata_from_codec(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + const AVCodecContext *avctx); + +/** + * Read the data bitstream from a packet into a fragment, then + * split into units and decompose. + * + * This also updates the internal state of the coded bitstream context + * with any persistent data from the fragment which may be required to + * read following fragments (e.g. parameter sets). + * + * The fragment must have been zeroed or reset via ff_cbs_fragment_reset + * before use. + */ +int ff_cbs_read_packet(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + const AVPacket *pkt); + +/** + * Read a bitstream from a memory region into a fragment, then + * split into units and decompose. + * + * This also updates the internal state of the coded bitstream context + * with any persistent data from the fragment which may be required to + * read following fragments (e.g. parameter sets). + * + * The fragment must have been zeroed or reset via ff_cbs_fragment_reset + * before use. + */ +int ff_cbs_read(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + const uint8_t *data, size_t size); + + +/** + * Write the content of the fragment to its own internal buffer. + * + * Writes the content of all units and then assembles them into a new + * data buffer. When modifying the content of decomposed units, this + * can be used to regenerate the bitstream form of units or the whole + * fragment so that it can be extracted for other use. + * + * This also updates the internal state of the coded bitstream context + * with any persistent data from the fragment which may be required to + * write following fragments (e.g. parameter sets). + */ +int ff_cbs_write_fragment_data(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag); + +/** + * Write the bitstream of a fragment to the extradata in codec parameters. + * + * Modifies context and fragment as ff_cbs_write_fragment_data does and + * replaces any existing extradata in the structure. + */ +int ff_cbs_write_extradata(CodedBitstreamContext *ctx, + AVCodecParameters *par, + CodedBitstreamFragment *frag); + +/** + * Write the bitstream of a fragment to a packet. + * + * Modifies context and fragment as ff_cbs_write_fragment_data does. + * + * On success, the packet's buf is unreferenced and its buf, data and + * size fields are set to the corresponding values from the newly updated + * fragment; other fields are not touched. On failure, the packet is not + * touched at all. + */ +int ff_cbs_write_packet(CodedBitstreamContext *ctx, + AVPacket *pkt, + CodedBitstreamFragment *frag); + + +/** + * Free the units contained in a fragment as well as the fragment's + * own data buffer, but not the units array itself. + */ +void ff_cbs_fragment_reset(CodedBitstreamFragment *frag); + +/** + * Free the units array of a fragment in addition to what + * ff_cbs_fragment_reset does. + */ +void ff_cbs_fragment_free(CodedBitstreamFragment *frag); + +/** + * Allocate a new internal content buffer of the given size in the unit. + * + * The content will be zeroed. + */ +int ff_cbs_alloc_unit_content(CodedBitstreamUnit *unit, + size_t size, + void (*free)(void *opaque, uint8_t *content)); + +/** + * Allocate a new internal content buffer matching the type of the unit. + * + * The content will be zeroed. + */ +int ff_cbs_alloc_unit_content2(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit); + + +/** + * Allocate a new internal data buffer of the given size in the unit. + * + * The data buffer will have input padding. + */ +int ff_cbs_alloc_unit_data(CodedBitstreamUnit *unit, + size_t size); + +/** + * Insert a new unit into a fragment with the given content. + * + * The content structure continues to be owned by the caller if + * content_buf is not supplied. + */ +int ff_cbs_insert_unit_content(CodedBitstreamFragment *frag, + int position, + CodedBitstreamUnitType type, + void *content, + AVBufferRef *content_buf); + +/** + * Insert a new unit into a fragment with the given data bitstream. + * + * If data_buf is not supplied then data must have been allocated with + * av_malloc() and will on success become owned by the unit after this + * call or freed on error. + */ +int ff_cbs_insert_unit_data(CodedBitstreamFragment *frag, + int position, + CodedBitstreamUnitType type, + uint8_t *data, size_t data_size, + AVBufferRef *data_buf); + +/** + * Delete a unit from a fragment and free all memory it uses. + * + * Requires position to be >= 0 and < frag->nb_units. + */ +void ff_cbs_delete_unit(CodedBitstreamFragment *frag, + int position); + + +/** + * Make the content of a unit refcounted. + * + * If the unit is not refcounted, this will do a deep copy of the unit + * content to new refcounted buffers. + * + * It is not valid to call this function on a unit which does not have + * decomposed content. + */ +int ff_cbs_make_unit_refcounted(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit); + +/** + * Make the content of a unit writable so that internal fields can be + * modified. + * + * If it is known that there are no other references to the content of + * the unit, does nothing and returns success. Otherwise (including the + * case where the unit content is not refcounted), it does a full clone + * of the content (including any internal buffers) to make a new copy, + * and replaces the existing references inside the unit with that. + * + * It is not valid to call this function on a unit which does not have + * decomposed content. + */ +int ff_cbs_make_unit_writable(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit); + + +#endif /* AVCODEC_CBS_H */ diff --git a/third-party/cbs/include/cbs/cbs_av1.h b/third-party/cbs/include/cbs/cbs_av1.h new file mode 100644 index 00000000..760f9a5d --- /dev/null +++ b/third-party/cbs/include/cbs/cbs_av1.h @@ -0,0 +1,464 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CBS_AV1_H +#define AVCODEC_CBS_AV1_H + +#include +#include + +#include "av1.h" +#include "cbs.h" + + +typedef struct AV1RawOBUHeader { + uint8_t obu_forbidden_bit; + uint8_t obu_type; + uint8_t obu_extension_flag; + uint8_t obu_has_size_field; + uint8_t obu_reserved_1bit; + + uint8_t temporal_id; + uint8_t spatial_id; + uint8_t extension_header_reserved_3bits; +} AV1RawOBUHeader; + +typedef struct AV1RawColorConfig { + uint8_t high_bitdepth; + uint8_t twelve_bit; + uint8_t mono_chrome; + + uint8_t color_description_present_flag; + uint8_t color_primaries; + uint8_t transfer_characteristics; + uint8_t matrix_coefficients; + + uint8_t color_range; + uint8_t subsampling_x; + uint8_t subsampling_y; + uint8_t chroma_sample_position; + uint8_t separate_uv_delta_q; +} AV1RawColorConfig; + +typedef struct AV1RawTimingInfo { + uint32_t num_units_in_display_tick; + uint32_t time_scale; + + uint8_t equal_picture_interval; + uint32_t num_ticks_per_picture_minus_1; +} AV1RawTimingInfo; + +typedef struct AV1RawDecoderModelInfo { + uint8_t buffer_delay_length_minus_1; + uint32_t num_units_in_decoding_tick; + uint8_t buffer_removal_time_length_minus_1; + uint8_t frame_presentation_time_length_minus_1; +} AV1RawDecoderModelInfo; + +typedef struct AV1RawSequenceHeader { + uint8_t seq_profile; + uint8_t still_picture; + uint8_t reduced_still_picture_header; + + uint8_t timing_info_present_flag; + uint8_t decoder_model_info_present_flag; + uint8_t initial_display_delay_present_flag; + uint8_t operating_points_cnt_minus_1; + + AV1RawTimingInfo timing_info; + AV1RawDecoderModelInfo decoder_model_info; + + uint16_t operating_point_idc[AV1_MAX_OPERATING_POINTS]; + uint8_t seq_level_idx[AV1_MAX_OPERATING_POINTS]; + uint8_t seq_tier[AV1_MAX_OPERATING_POINTS]; + uint8_t decoder_model_present_for_this_op[AV1_MAX_OPERATING_POINTS]; + uint32_t decoder_buffer_delay[AV1_MAX_OPERATING_POINTS]; + uint32_t encoder_buffer_delay[AV1_MAX_OPERATING_POINTS]; + uint8_t low_delay_mode_flag[AV1_MAX_OPERATING_POINTS]; + uint8_t initial_display_delay_present_for_this_op[AV1_MAX_OPERATING_POINTS]; + uint8_t initial_display_delay_minus_1[AV1_MAX_OPERATING_POINTS]; + + uint8_t frame_width_bits_minus_1; + uint8_t frame_height_bits_minus_1; + uint16_t max_frame_width_minus_1; + uint16_t max_frame_height_minus_1; + + uint8_t frame_id_numbers_present_flag; + uint8_t delta_frame_id_length_minus_2; + uint8_t additional_frame_id_length_minus_1; + + uint8_t use_128x128_superblock; + uint8_t enable_filter_intra; + uint8_t enable_intra_edge_filter; + uint8_t enable_interintra_compound; + uint8_t enable_masked_compound; + uint8_t enable_warped_motion; + uint8_t enable_dual_filter; + + uint8_t enable_order_hint; + uint8_t enable_jnt_comp; + uint8_t enable_ref_frame_mvs; + + uint8_t seq_choose_screen_content_tools; + uint8_t seq_force_screen_content_tools; + uint8_t seq_choose_integer_mv; + uint8_t seq_force_integer_mv; + + uint8_t order_hint_bits_minus_1; + + uint8_t enable_superres; + uint8_t enable_cdef; + uint8_t enable_restoration; + + AV1RawColorConfig color_config; + + uint8_t film_grain_params_present; +} AV1RawSequenceHeader; + +typedef struct AV1RawFilmGrainParams { + uint8_t apply_grain; + uint16_t grain_seed; + uint8_t update_grain; + uint8_t film_grain_params_ref_idx; + uint8_t num_y_points; + uint8_t point_y_value[14]; + uint8_t point_y_scaling[14]; + uint8_t chroma_scaling_from_luma; + uint8_t num_cb_points; + uint8_t point_cb_value[10]; + uint8_t point_cb_scaling[10]; + uint8_t num_cr_points; + uint8_t point_cr_value[10]; + uint8_t point_cr_scaling[10]; + uint8_t grain_scaling_minus_8; + uint8_t ar_coeff_lag; + uint8_t ar_coeffs_y_plus_128[24]; + uint8_t ar_coeffs_cb_plus_128[25]; + uint8_t ar_coeffs_cr_plus_128[25]; + uint8_t ar_coeff_shift_minus_6; + uint8_t grain_scale_shift; + uint8_t cb_mult; + uint8_t cb_luma_mult; + uint16_t cb_offset; + uint8_t cr_mult; + uint8_t cr_luma_mult; + uint16_t cr_offset; + uint8_t overlap_flag; + uint8_t clip_to_restricted_range; +} AV1RawFilmGrainParams; + +typedef struct AV1RawFrameHeader { + uint8_t show_existing_frame; + uint8_t frame_to_show_map_idx; + uint32_t frame_presentation_time; + uint32_t display_frame_id; + + uint8_t frame_type; + uint8_t show_frame; + uint8_t showable_frame; + + uint8_t error_resilient_mode; + uint8_t disable_cdf_update; + uint8_t allow_screen_content_tools; + uint8_t force_integer_mv; + + uint32_t current_frame_id; + uint8_t frame_size_override_flag; + uint8_t order_hint; + + uint8_t buffer_removal_time_present_flag; + uint32_t buffer_removal_time[AV1_MAX_OPERATING_POINTS]; + + uint8_t primary_ref_frame; + uint16_t frame_width_minus_1; + uint16_t frame_height_minus_1; + uint8_t use_superres; + uint8_t coded_denom; + uint8_t render_and_frame_size_different; + uint16_t render_width_minus_1; + uint16_t render_height_minus_1; + + uint8_t found_ref[AV1_REFS_PER_FRAME]; + + uint8_t refresh_frame_flags; + uint8_t allow_intrabc; + uint8_t ref_order_hint[AV1_NUM_REF_FRAMES]; + uint8_t frame_refs_short_signaling; + uint8_t last_frame_idx; + uint8_t golden_frame_idx; + int8_t ref_frame_idx[AV1_REFS_PER_FRAME]; + uint32_t delta_frame_id_minus1[AV1_REFS_PER_FRAME]; + + uint8_t allow_high_precision_mv; + uint8_t is_filter_switchable; + uint8_t interpolation_filter; + uint8_t is_motion_mode_switchable; + uint8_t use_ref_frame_mvs; + + uint8_t disable_frame_end_update_cdf; + + uint8_t uniform_tile_spacing_flag; + uint8_t tile_cols_log2; + uint8_t tile_rows_log2; + uint8_t width_in_sbs_minus_1[AV1_MAX_TILE_COLS]; + uint8_t height_in_sbs_minus_1[AV1_MAX_TILE_ROWS]; + uint16_t context_update_tile_id; + uint8_t tile_size_bytes_minus1; + + // These are derived values, but it's very unhelpful to have to + // recalculate them all the time so we store them here. + uint16_t tile_cols; + uint16_t tile_rows; + + uint8_t base_q_idx; + int8_t delta_q_y_dc; + uint8_t diff_uv_delta; + int8_t delta_q_u_dc; + int8_t delta_q_u_ac; + int8_t delta_q_v_dc; + int8_t delta_q_v_ac; + uint8_t using_qmatrix; + uint8_t qm_y; + uint8_t qm_u; + uint8_t qm_v; + + uint8_t segmentation_enabled; + uint8_t segmentation_update_map; + uint8_t segmentation_temporal_update; + uint8_t segmentation_update_data; + uint8_t feature_enabled[AV1_MAX_SEGMENTS][AV1_SEG_LVL_MAX]; + int16_t feature_value[AV1_MAX_SEGMENTS][AV1_SEG_LVL_MAX]; + + uint8_t delta_q_present; + uint8_t delta_q_res; + uint8_t delta_lf_present; + uint8_t delta_lf_res; + uint8_t delta_lf_multi; + + uint8_t loop_filter_level[4]; + uint8_t loop_filter_sharpness; + uint8_t loop_filter_delta_enabled; + uint8_t loop_filter_delta_update; + uint8_t update_ref_delta[AV1_TOTAL_REFS_PER_FRAME]; + int8_t loop_filter_ref_deltas[AV1_TOTAL_REFS_PER_FRAME]; + uint8_t update_mode_delta[2]; + int8_t loop_filter_mode_deltas[2]; + + uint8_t cdef_damping_minus_3; + uint8_t cdef_bits; + uint8_t cdef_y_pri_strength[8]; + uint8_t cdef_y_sec_strength[8]; + uint8_t cdef_uv_pri_strength[8]; + uint8_t cdef_uv_sec_strength[8]; + + uint8_t lr_type[3]; + uint8_t lr_unit_shift; + uint8_t lr_uv_shift; + + uint8_t tx_mode; + uint8_t reference_select; + uint8_t skip_mode_present; + + uint8_t allow_warped_motion; + uint8_t reduced_tx_set; + + uint8_t is_global[AV1_TOTAL_REFS_PER_FRAME]; + uint8_t is_rot_zoom[AV1_TOTAL_REFS_PER_FRAME]; + uint8_t is_translation[AV1_TOTAL_REFS_PER_FRAME]; + //AV1RawSubexp gm_params[AV1_TOTAL_REFS_PER_FRAME][6]; + uint32_t gm_params[AV1_TOTAL_REFS_PER_FRAME][6]; + + AV1RawFilmGrainParams film_grain; +} AV1RawFrameHeader; + +typedef struct AV1RawTileData { + uint8_t *data; + AVBufferRef *data_ref; + size_t data_size; +} AV1RawTileData; + +typedef struct AV1RawTileGroup { + uint8_t tile_start_and_end_present_flag; + uint16_t tg_start; + uint16_t tg_end; + + AV1RawTileData tile_data; +} AV1RawTileGroup; + +typedef struct AV1RawFrame { + AV1RawFrameHeader header; + AV1RawTileGroup tile_group; +} AV1RawFrame; + +typedef struct AV1RawTileList { + uint8_t output_frame_width_in_tiles_minus_1; + uint8_t output_frame_height_in_tiles_minus_1; + uint16_t tile_count_minus_1; + + AV1RawTileData tile_data; +} AV1RawTileList; + +typedef struct AV1RawMetadataHDRCLL { + uint16_t max_cll; + uint16_t max_fall; +} AV1RawMetadataHDRCLL; + +typedef struct AV1RawMetadataHDRMDCV { + uint16_t primary_chromaticity_x[3]; + uint16_t primary_chromaticity_y[3]; + uint16_t white_point_chromaticity_x; + uint16_t white_point_chromaticity_y; + uint32_t luminance_max; + uint32_t luminance_min; +} AV1RawMetadataHDRMDCV; + +typedef struct AV1RawMetadataScalability { + uint8_t scalability_mode_idc; + uint8_t spatial_layers_cnt_minus_1; + uint8_t spatial_layer_dimensions_present_flag; + uint8_t spatial_layer_description_present_flag; + uint8_t temporal_group_description_present_flag; + uint8_t scalability_structure_reserved_3bits; + uint16_t spatial_layer_max_width[4]; + uint16_t spatial_layer_max_height[4]; + uint8_t spatial_layer_ref_id[4]; + uint8_t temporal_group_size; + uint8_t temporal_group_temporal_id[255]; + uint8_t temporal_group_temporal_switching_up_point_flag[255]; + uint8_t temporal_group_spatial_switching_up_point_flag[255]; + uint8_t temporal_group_ref_cnt[255]; + uint8_t temporal_group_ref_pic_diff[255][7]; +} AV1RawMetadataScalability; + +typedef struct AV1RawMetadataITUTT35 { + uint8_t itu_t_t35_country_code; + uint8_t itu_t_t35_country_code_extension_byte; + + uint8_t *payload; + AVBufferRef *payload_ref; + size_t payload_size; +} AV1RawMetadataITUTT35; + +typedef struct AV1RawMetadataTimecode { + uint8_t counting_type; + uint8_t full_timestamp_flag; + uint8_t discontinuity_flag; + uint8_t cnt_dropped_flag; + uint16_t n_frames; + uint8_t seconds_value; + uint8_t minutes_value; + uint8_t hours_value; + uint8_t seconds_flag; + uint8_t minutes_flag; + uint8_t hours_flag; + uint8_t time_offset_length; + uint32_t time_offset_value; +} AV1RawMetadataTimecode; + +typedef struct AV1RawMetadata { + uint64_t metadata_type; + union { + AV1RawMetadataHDRCLL hdr_cll; + AV1RawMetadataHDRMDCV hdr_mdcv; + AV1RawMetadataScalability scalability; + AV1RawMetadataITUTT35 itut_t35; + AV1RawMetadataTimecode timecode; + } metadata; +} AV1RawMetadata; + +typedef struct AV1RawPadding { + uint8_t *payload; + AVBufferRef *payload_ref; + size_t payload_size; +} AV1RawPadding; + + +typedef struct AV1RawOBU { + AV1RawOBUHeader header; + + size_t obu_size; + + union { + AV1RawSequenceHeader sequence_header; + AV1RawFrameHeader frame_header; + AV1RawFrame frame; + AV1RawTileGroup tile_group; + AV1RawTileList tile_list; + AV1RawMetadata metadata; + AV1RawPadding padding; + } obu; +} AV1RawOBU; + +typedef struct AV1ReferenceFrameState { + int valid; // RefValid + int frame_id; // RefFrameId + int upscaled_width; // RefUpscaledWidth + int frame_width; // RefFrameWidth + int frame_height; // RefFrameHeight + int render_width; // RefRenderWidth + int render_height; // RefRenderHeight + int frame_type; // RefFrameType + int subsampling_x; // RefSubsamplingX + int subsampling_y; // RefSubsamplingY + int bit_depth; // RefBitDepth + int order_hint; // RefOrderHint + + int8_t loop_filter_ref_deltas[AV1_TOTAL_REFS_PER_FRAME]; + int8_t loop_filter_mode_deltas[2]; + uint8_t feature_enabled[AV1_MAX_SEGMENTS][AV1_SEG_LVL_MAX]; + int16_t feature_value[AV1_MAX_SEGMENTS][AV1_SEG_LVL_MAX]; +} AV1ReferenceFrameState; + +typedef struct CodedBitstreamAV1Context { + const AVClass *class; + + AV1RawSequenceHeader *sequence_header; + AVBufferRef *sequence_header_ref; + + int seen_frame_header; + AVBufferRef *frame_header_ref; + uint8_t *frame_header; + size_t frame_header_size; + + int temporal_id; + int spatial_id; + int operating_point_idc; + + int bit_depth; + int order_hint; + int frame_width; + int frame_height; + int upscaled_width; + int render_width; + int render_height; + + int num_planes; + int coded_lossless; + int all_lossless; + int tile_cols; + int tile_rows; + int tile_num; + + AV1ReferenceFrameState ref[AV1_NUM_REF_FRAMES]; + + // AVOptions + int operating_point; +} CodedBitstreamAV1Context; + + +#endif /* AVCODEC_CBS_AV1_H */ diff --git a/third-party/cbs/include/cbs/cbs_bsf.h b/third-party/cbs/include/cbs/cbs_bsf.h new file mode 100644 index 00000000..0cfb64b4 --- /dev/null +++ b/third-party/cbs/include/cbs/cbs_bsf.h @@ -0,0 +1,131 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CBS_BSF_H +#define AVCODEC_CBS_BSF_H + +#include "cbs.h" + + +typedef struct CBSBSFType { + enum AVCodecID codec_id; + + // Name of a frame fragment in this codec (e.g. "access unit", + // "temporal unit"). + const char *fragment_name; + + // Name of a unit for this BSF, for use in error messages (e.g. + // "NAL unit", "OBU"). + const char *unit_name; + + // Update the content of a fragment with whatever metadata changes + // are desired. The associated AVPacket is provided so that any side + // data associated with the fragment can be inspected or edited. If + // pkt is NULL, then an extradata header fragment is being updated. + int (*update_fragment)(AVBSFContext *bsf, AVPacket *pkt, + CodedBitstreamFragment *frag); +} CBSBSFType; + +// Common structure for all generic CBS BSF users. An instance of this +// structure must be the first member of the BSF private context (to be +// pointed to by AVBSFContext.priv_data). +typedef struct CBSBSFContext { + const AVClass *class; + const CBSBSFType *type; + + CodedBitstreamContext *input; + CodedBitstreamContext *output; + CodedBitstreamFragment fragment; +} CBSBSFContext; + +/** + * Initialise generic CBS BSF setup. + * + * Creates the input and output CBS instances, and applies the filter to + * the extradata on the input codecpar if any is present. + * + * Since it calls the update_fragment() function immediately to deal with + * extradata, this should be called after any codec-specific setup is done + * (probably at the end of the AVBitStreamFilter.init function). + */ +int ff_cbs_bsf_generic_init(AVBSFContext *bsf, const CBSBSFType *type); + +/** + * Close a generic CBS BSF instance. + * + * If no other deinitialisation is required then this function can be used + * directly as AVBitStreamFilter.close. + */ +void ff_cbs_bsf_generic_close(AVBSFContext *bsf); + +/** + * Filter operation for CBS BSF. + * + * Reads the input packet into a CBS fragment, calls update_fragment() on + * it, then writes the result to an output packet. If the input packet + * has AV_PKT_DATA_NEW_EXTRADATA side-data associated with it then it does + * the same thing to that new extradata to form the output side-data first. + * + * If the BSF does not do anything else then this function can be used + * directly as AVBitStreamFilter.filter. + */ +int ff_cbs_bsf_generic_filter(AVBSFContext *bsf, AVPacket *pkt); + + +// Options for element manipulation. +enum { + // Pass this element through unchanged. + BSF_ELEMENT_PASS, + // Insert this element, replacing any existing instances of it. + // Associated values may be provided explicitly (as addtional options) + // or implicitly (either as side data or deduced from other parts of + // the stream). + BSF_ELEMENT_INSERT, + // Remove this element if it appears in the stream. + BSF_ELEMENT_REMOVE, + // Extract this element to side data, so that further manipulation + // can happen elsewhere. + BSF_ELEMENT_EXTRACT, +}; + +#define BSF_ELEMENT_OPTIONS_PIR(name, help, field, opt_flags) \ + { name, help, OFFSET(field), AV_OPT_TYPE_INT, \ + { .i64 = BSF_ELEMENT_PASS }, \ + BSF_ELEMENT_PASS, BSF_ELEMENT_REMOVE, opt_flags, name }, \ + { "pass", NULL, 0, AV_OPT_TYPE_CONST, \ + { .i64 = BSF_ELEMENT_PASS }, .flags = opt_flags, .unit = name }, \ + { "insert", NULL, 0, AV_OPT_TYPE_CONST, \ + { .i64 = BSF_ELEMENT_INSERT }, .flags = opt_flags, .unit = name }, \ + { "remove", NULL, 0, AV_OPT_TYPE_CONST, \ + { .i64 = BSF_ELEMENT_REMOVE }, .flags = opt_flags, .unit = name } + +#define BSF_ELEMENT_OPTIONS_PIRE(name, help, field, opt_flags) \ + { name, help, OFFSET(field), AV_OPT_TYPE_INT, \ + { .i64 = BSF_ELEMENT_PASS }, \ + BSF_ELEMENT_PASS, BSF_ELEMENT_EXTRACT, opt_flags, name }, \ + { "pass", NULL, 0, AV_OPT_TYPE_CONST, \ + { .i64 = BSF_ELEMENT_PASS }, .flags = opt_flags, .unit = name }, \ + { "insert", NULL, 0, AV_OPT_TYPE_CONST, \ + { .i64 = BSF_ELEMENT_INSERT }, .flags = opt_flags, .unit = name }, \ + { "remove", NULL, 0, AV_OPT_TYPE_CONST, \ + { .i64 = BSF_ELEMENT_REMOVE }, .flags = opt_flags, .unit = name }, \ + { "extract", NULL, 0, AV_OPT_TYPE_CONST, \ + { .i64 = BSF_ELEMENT_EXTRACT }, .flags = opt_flags, .unit = name } + + +#endif /* AVCODEC_CBS_BSF_H */ diff --git a/third-party/cbs/include/cbs/cbs_h264.h b/third-party/cbs/include/cbs/cbs_h264.h new file mode 100644 index 00000000..71397534 --- /dev/null +++ b/third-party/cbs/include/cbs/cbs_h264.h @@ -0,0 +1,406 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CBS_H264_H +#define AVCODEC_CBS_H264_H + +#include +#include + +#include "cbs.h" +#include "cbs_h2645.h" +#include "cbs_sei.h" +#include "h264.h" + + +typedef struct H264RawNALUnitHeader { + uint8_t nal_ref_idc; + uint8_t nal_unit_type; + + uint8_t svc_extension_flag; + uint8_t avc_3d_extension_flag; +} H264RawNALUnitHeader; + +typedef struct H264RawScalingList { + int8_t delta_scale[64]; +} H264RawScalingList; + +typedef struct H264RawHRD { + uint8_t cpb_cnt_minus1; + uint8_t bit_rate_scale; + uint8_t cpb_size_scale; + + uint32_t bit_rate_value_minus1[H264_MAX_CPB_CNT]; + uint32_t cpb_size_value_minus1[H264_MAX_CPB_CNT]; + uint8_t cbr_flag[H264_MAX_CPB_CNT]; + + uint8_t initial_cpb_removal_delay_length_minus1; + uint8_t cpb_removal_delay_length_minus1; + uint8_t dpb_output_delay_length_minus1; + uint8_t time_offset_length; +} H264RawHRD; + +typedef struct H264RawVUI { + uint8_t aspect_ratio_info_present_flag; + uint8_t aspect_ratio_idc; + uint16_t sar_width; + uint16_t sar_height; + + uint8_t overscan_info_present_flag; + uint8_t overscan_appropriate_flag; + + uint8_t video_signal_type_present_flag; + uint8_t video_format; + uint8_t video_full_range_flag; + uint8_t colour_description_present_flag; + uint8_t colour_primaries; + uint8_t transfer_characteristics; + uint8_t matrix_coefficients; + + uint8_t chroma_loc_info_present_flag; + uint8_t chroma_sample_loc_type_top_field; + uint8_t chroma_sample_loc_type_bottom_field; + + uint8_t timing_info_present_flag; + uint32_t num_units_in_tick; + uint32_t time_scale; + uint8_t fixed_frame_rate_flag; + + uint8_t nal_hrd_parameters_present_flag; + H264RawHRD nal_hrd_parameters; + uint8_t vcl_hrd_parameters_present_flag; + H264RawHRD vcl_hrd_parameters; + uint8_t low_delay_hrd_flag; + + uint8_t pic_struct_present_flag; + + uint8_t bitstream_restriction_flag; + uint8_t motion_vectors_over_pic_boundaries_flag; + uint8_t max_bytes_per_pic_denom; + uint8_t max_bits_per_mb_denom; + uint8_t log2_max_mv_length_horizontal; + uint8_t log2_max_mv_length_vertical; + uint8_t max_num_reorder_frames; + uint8_t max_dec_frame_buffering; +} H264RawVUI; + +typedef struct H264RawSPS { + H264RawNALUnitHeader nal_unit_header; + + uint8_t profile_idc; + uint8_t constraint_set0_flag; + uint8_t constraint_set1_flag; + uint8_t constraint_set2_flag; + uint8_t constraint_set3_flag; + uint8_t constraint_set4_flag; + uint8_t constraint_set5_flag; + uint8_t reserved_zero_2bits; + uint8_t level_idc; + + uint8_t seq_parameter_set_id; + + uint8_t chroma_format_idc; + uint8_t separate_colour_plane_flag; + uint8_t bit_depth_luma_minus8; + uint8_t bit_depth_chroma_minus8; + uint8_t qpprime_y_zero_transform_bypass_flag; + + uint8_t seq_scaling_matrix_present_flag; + uint8_t seq_scaling_list_present_flag[12]; + H264RawScalingList scaling_list_4x4[6]; + H264RawScalingList scaling_list_8x8[6]; + + uint8_t log2_max_frame_num_minus4; + uint8_t pic_order_cnt_type; + uint8_t log2_max_pic_order_cnt_lsb_minus4; + uint8_t delta_pic_order_always_zero_flag; + int32_t offset_for_non_ref_pic; + int32_t offset_for_top_to_bottom_field; + uint8_t num_ref_frames_in_pic_order_cnt_cycle; + int32_t offset_for_ref_frame[256]; + + uint8_t max_num_ref_frames; + uint8_t gaps_in_frame_num_allowed_flag; + + uint16_t pic_width_in_mbs_minus1; + uint16_t pic_height_in_map_units_minus1; + + uint8_t frame_mbs_only_flag; + uint8_t mb_adaptive_frame_field_flag; + uint8_t direct_8x8_inference_flag; + + uint8_t frame_cropping_flag; + uint16_t frame_crop_left_offset; + uint16_t frame_crop_right_offset; + uint16_t frame_crop_top_offset; + uint16_t frame_crop_bottom_offset; + + uint8_t vui_parameters_present_flag; + H264RawVUI vui; +} H264RawSPS; + +typedef struct H264RawSPSExtension { + H264RawNALUnitHeader nal_unit_header; + + uint8_t seq_parameter_set_id; + + uint8_t aux_format_idc; + uint8_t bit_depth_aux_minus8; + uint8_t alpha_incr_flag; + uint16_t alpha_opaque_value; + uint16_t alpha_transparent_value; + + uint8_t additional_extension_flag; +} H264RawSPSExtension; + +typedef struct H264RawPPS { + H264RawNALUnitHeader nal_unit_header; + + uint8_t pic_parameter_set_id; + uint8_t seq_parameter_set_id; + + uint8_t entropy_coding_mode_flag; + uint8_t bottom_field_pic_order_in_frame_present_flag; + + uint8_t num_slice_groups_minus1; + uint8_t slice_group_map_type; + uint16_t run_length_minus1[H264_MAX_SLICE_GROUPS]; + uint16_t top_left[H264_MAX_SLICE_GROUPS]; + uint16_t bottom_right[H264_MAX_SLICE_GROUPS]; + uint8_t slice_group_change_direction_flag; + uint16_t slice_group_change_rate_minus1; + uint16_t pic_size_in_map_units_minus1; + + uint8_t *slice_group_id; + AVBufferRef *slice_group_id_ref; + + uint8_t num_ref_idx_l0_default_active_minus1; + uint8_t num_ref_idx_l1_default_active_minus1; + + uint8_t weighted_pred_flag; + uint8_t weighted_bipred_idc; + + int8_t pic_init_qp_minus26; + int8_t pic_init_qs_minus26; + int8_t chroma_qp_index_offset; + + uint8_t deblocking_filter_control_present_flag; + uint8_t constrained_intra_pred_flag; + + uint8_t more_rbsp_data; + + uint8_t redundant_pic_cnt_present_flag; + uint8_t transform_8x8_mode_flag; + + uint8_t pic_scaling_matrix_present_flag; + uint8_t pic_scaling_list_present_flag[12]; + H264RawScalingList scaling_list_4x4[6]; + H264RawScalingList scaling_list_8x8[6]; + + int8_t second_chroma_qp_index_offset; +} H264RawPPS; + +typedef struct H264RawAUD { + H264RawNALUnitHeader nal_unit_header; + + uint8_t primary_pic_type; +} H264RawAUD; + +typedef struct H264RawSEIBufferingPeriod { + uint8_t seq_parameter_set_id; + struct { + uint32_t initial_cpb_removal_delay[H264_MAX_CPB_CNT]; + uint32_t initial_cpb_removal_delay_offset[H264_MAX_CPB_CNT]; + } nal, vcl; +} H264RawSEIBufferingPeriod; + +typedef struct H264RawSEIPicTimestamp { + uint8_t ct_type; + uint8_t nuit_field_based_flag; + uint8_t counting_type; + uint8_t full_timestamp_flag; + uint8_t discontinuity_flag; + uint8_t cnt_dropped_flag; + uint8_t n_frames; + uint8_t seconds_flag; + uint8_t seconds_value; + uint8_t minutes_flag; + uint8_t minutes_value; + uint8_t hours_flag; + uint8_t hours_value; + int32_t time_offset; +} H264RawSEIPicTimestamp; + +typedef struct H264RawSEIPicTiming { + uint32_t cpb_removal_delay; + uint32_t dpb_output_delay; + uint8_t pic_struct; + uint8_t clock_timestamp_flag[3]; + H264RawSEIPicTimestamp timestamp[3]; +} H264RawSEIPicTiming; + +typedef struct H264RawSEIPanScanRect { + uint32_t pan_scan_rect_id; + uint8_t pan_scan_rect_cancel_flag; + uint8_t pan_scan_cnt_minus1; + int32_t pan_scan_rect_left_offset[3]; + int32_t pan_scan_rect_right_offset[3]; + int32_t pan_scan_rect_top_offset[3]; + int32_t pan_scan_rect_bottom_offset[3]; + uint16_t pan_scan_rect_repetition_period; +} H264RawSEIPanScanRect; + +typedef struct H264RawSEIRecoveryPoint { + uint16_t recovery_frame_cnt; + uint8_t exact_match_flag; + uint8_t broken_link_flag; + uint8_t changing_slice_group_idc; +} H264RawSEIRecoveryPoint; + +typedef struct H264RawSEIDisplayOrientation { + uint8_t display_orientation_cancel_flag; + uint8_t hor_flip; + uint8_t ver_flip; + uint16_t anticlockwise_rotation; + uint16_t display_orientation_repetition_period; + uint8_t display_orientation_extension_flag; +} H264RawSEIDisplayOrientation; + +typedef struct H264RawSEI { + H264RawNALUnitHeader nal_unit_header; + SEIRawMessageList message_list; +} H264RawSEI; + +typedef struct H264RawSliceHeader { + H264RawNALUnitHeader nal_unit_header; + + uint32_t first_mb_in_slice; + uint8_t slice_type; + + uint8_t pic_parameter_set_id; + + uint8_t colour_plane_id; + + uint16_t frame_num; + uint8_t field_pic_flag; + uint8_t bottom_field_flag; + + uint16_t idr_pic_id; + + uint16_t pic_order_cnt_lsb; + int32_t delta_pic_order_cnt_bottom; + int32_t delta_pic_order_cnt[2]; + + uint8_t redundant_pic_cnt; + uint8_t direct_spatial_mv_pred_flag; + + uint8_t num_ref_idx_active_override_flag; + uint8_t num_ref_idx_l0_active_minus1; + uint8_t num_ref_idx_l1_active_minus1; + + uint8_t ref_pic_list_modification_flag_l0; + uint8_t ref_pic_list_modification_flag_l1; + struct { + uint8_t modification_of_pic_nums_idc; + int32_t abs_diff_pic_num_minus1; + uint8_t long_term_pic_num; + } rplm_l0[H264_MAX_RPLM_COUNT], rplm_l1[H264_MAX_RPLM_COUNT]; + + uint8_t luma_log2_weight_denom; + uint8_t chroma_log2_weight_denom; + + uint8_t luma_weight_l0_flag[H264_MAX_REFS]; + int8_t luma_weight_l0[H264_MAX_REFS]; + int8_t luma_offset_l0[H264_MAX_REFS]; + uint8_t chroma_weight_l0_flag[H264_MAX_REFS]; + int8_t chroma_weight_l0[H264_MAX_REFS][2]; + int8_t chroma_offset_l0[H264_MAX_REFS][2]; + + uint8_t luma_weight_l1_flag[H264_MAX_REFS]; + int8_t luma_weight_l1[H264_MAX_REFS]; + int8_t luma_offset_l1[H264_MAX_REFS]; + uint8_t chroma_weight_l1_flag[H264_MAX_REFS]; + int8_t chroma_weight_l1[H264_MAX_REFS][2]; + int8_t chroma_offset_l1[H264_MAX_REFS][2]; + + uint8_t no_output_of_prior_pics_flag; + uint8_t long_term_reference_flag; + + uint8_t adaptive_ref_pic_marking_mode_flag; + struct { + uint8_t memory_management_control_operation; + int32_t difference_of_pic_nums_minus1; + uint8_t long_term_pic_num; + uint8_t long_term_frame_idx; + uint8_t max_long_term_frame_idx_plus1; + } mmco[H264_MAX_MMCO_COUNT]; + + uint8_t cabac_init_idc; + + int8_t slice_qp_delta; + + uint8_t sp_for_switch_flag; + int8_t slice_qs_delta; + + uint8_t disable_deblocking_filter_idc; + int8_t slice_alpha_c0_offset_div2; + int8_t slice_beta_offset_div2; + + uint16_t slice_group_change_cycle; +} H264RawSliceHeader; + +typedef struct H264RawSlice { + H264RawSliceHeader header; + + uint8_t *data; + AVBufferRef *data_ref; + size_t data_size; + int data_bit_start; +} H264RawSlice; + +typedef struct H264RawFiller { + H264RawNALUnitHeader nal_unit_header; + + uint32_t filler_size; +} H264RawFiller; + + +typedef struct CodedBitstreamH264Context { + // Reader/writer context in common with the H.265 implementation. + CodedBitstreamH2645Context common; + + // All currently available parameter sets. These are updated when + // any parameter set NAL unit is read/written with this context. + AVBufferRef *sps_ref[H264_MAX_SPS_COUNT]; + AVBufferRef *pps_ref[H264_MAX_PPS_COUNT]; + H264RawSPS *sps[H264_MAX_SPS_COUNT]; + H264RawPPS *pps[H264_MAX_PPS_COUNT]; + + // The currently active parameter sets. These are updated when any + // NAL unit refers to the relevant parameter set. These pointers + // must also be present in the arrays above. + const H264RawSPS *active_sps; + const H264RawPPS *active_pps; + + // The NAL unit type of the most recent normal slice. This is required + // to be able to read/write auxiliary slices, because IdrPicFlag is + // otherwise unknown. + uint8_t last_slice_nal_unit_type; +} CodedBitstreamH264Context; + +#endif /* AVCODEC_CBS_H264_H */ diff --git a/third-party/cbs/include/cbs/cbs_h2645.h b/third-party/cbs/include/cbs/cbs_h2645.h new file mode 100644 index 00000000..03cfb7a0 --- /dev/null +++ b/third-party/cbs/include/cbs/cbs_h2645.h @@ -0,0 +1,36 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CBS_H2645_H +#define AVCODEC_CBS_H2645_H + +#include "h2645_parse.h" + + +typedef struct CodedBitstreamH2645Context { + // If set, the stream being read is in MP4 (AVCC/HVCC) format. If not + // set, the stream is assumed to be in annex B format. + int mp4; + // Size in bytes of the NAL length field for MP4 format. + int nal_length_size; + // Packet reader. + H2645Packet read_packet; +} CodedBitstreamH2645Context; + + +#endif /* AVCODEC_CBS_H2645_H */ diff --git a/third-party/cbs/include/cbs/cbs_h265.h b/third-party/cbs/include/cbs/cbs_h265.h new file mode 100644 index 00000000..73c19b2d --- /dev/null +++ b/third-party/cbs/include/cbs/cbs_h265.h @@ -0,0 +1,679 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CBS_H265_H +#define AVCODEC_CBS_H265_H + +#include +#include + +#include "cbs_h2645.h" +#include "cbs_sei.h" +#include "hevc.h" + +typedef struct H265RawNALUnitHeader { + uint8_t nal_unit_type; + uint8_t nuh_layer_id; + uint8_t nuh_temporal_id_plus1; +} H265RawNALUnitHeader; + +typedef struct H265RawProfileTierLevel { + uint8_t general_profile_space; + uint8_t general_tier_flag; + uint8_t general_profile_idc; + + uint8_t general_profile_compatibility_flag[32]; + + uint8_t general_progressive_source_flag; + uint8_t general_interlaced_source_flag; + uint8_t general_non_packed_constraint_flag; + uint8_t general_frame_only_constraint_flag; + + uint8_t general_max_12bit_constraint_flag; + uint8_t general_max_10bit_constraint_flag; + uint8_t general_max_8bit_constraint_flag; + uint8_t general_max_422chroma_constraint_flag; + uint8_t general_max_420chroma_constraint_flag; + uint8_t general_max_monochrome_constraint_flag; + uint8_t general_intra_constraint_flag; + uint8_t general_one_picture_only_constraint_flag; + uint8_t general_lower_bit_rate_constraint_flag; + uint8_t general_max_14bit_constraint_flag; + + uint8_t general_inbld_flag; + + uint8_t general_level_idc; + + uint8_t sub_layer_profile_present_flag[HEVC_MAX_SUB_LAYERS]; + uint8_t sub_layer_level_present_flag[HEVC_MAX_SUB_LAYERS]; + + uint8_t sub_layer_profile_space[HEVC_MAX_SUB_LAYERS]; + uint8_t sub_layer_tier_flag[HEVC_MAX_SUB_LAYERS]; + uint8_t sub_layer_profile_idc[HEVC_MAX_SUB_LAYERS]; + + uint8_t sub_layer_profile_compatibility_flag[HEVC_MAX_SUB_LAYERS][32]; + + uint8_t sub_layer_progressive_source_flag[HEVC_MAX_SUB_LAYERS]; + uint8_t sub_layer_interlaced_source_flag[HEVC_MAX_SUB_LAYERS]; + uint8_t sub_layer_non_packed_constraint_flag[HEVC_MAX_SUB_LAYERS]; + uint8_t sub_layer_frame_only_constraint_flag[HEVC_MAX_SUB_LAYERS]; + + uint8_t sub_layer_max_12bit_constraint_flag[HEVC_MAX_SUB_LAYERS]; + uint8_t sub_layer_max_10bit_constraint_flag[HEVC_MAX_SUB_LAYERS]; + uint8_t sub_layer_max_8bit_constraint_flag[HEVC_MAX_SUB_LAYERS]; + uint8_t sub_layer_max_422chroma_constraint_flag[HEVC_MAX_SUB_LAYERS]; + uint8_t sub_layer_max_420chroma_constraint_flag[HEVC_MAX_SUB_LAYERS]; + uint8_t sub_layer_max_monochrome_constraint_flag[HEVC_MAX_SUB_LAYERS]; + uint8_t sub_layer_intra_constraint_flag[HEVC_MAX_SUB_LAYERS]; + uint8_t sub_layer_one_picture_only_constraint_flag[HEVC_MAX_SUB_LAYERS]; + uint8_t sub_layer_lower_bit_rate_constraint_flag[HEVC_MAX_SUB_LAYERS]; + uint8_t sub_layer_max_14bit_constraint_flag[HEVC_MAX_SUB_LAYERS]; + + uint8_t sub_layer_inbld_flag[HEVC_MAX_SUB_LAYERS]; + + uint8_t sub_layer_level_idc[HEVC_MAX_SUB_LAYERS]; +} H265RawProfileTierLevel; + +typedef struct H265RawSubLayerHRDParameters { + uint32_t bit_rate_value_minus1[HEVC_MAX_CPB_CNT]; + uint32_t cpb_size_value_minus1[HEVC_MAX_CPB_CNT]; + uint32_t cpb_size_du_value_minus1[HEVC_MAX_CPB_CNT]; + uint32_t bit_rate_du_value_minus1[HEVC_MAX_CPB_CNT]; + uint8_t cbr_flag[HEVC_MAX_CPB_CNT]; +} H265RawSubLayerHRDParameters; + +typedef struct H265RawHRDParameters { + uint8_t nal_hrd_parameters_present_flag; + uint8_t vcl_hrd_parameters_present_flag; + + uint8_t sub_pic_hrd_params_present_flag; + uint8_t tick_divisor_minus2; + uint8_t du_cpb_removal_delay_increment_length_minus1; + uint8_t sub_pic_cpb_params_in_pic_timing_sei_flag; + uint8_t dpb_output_delay_du_length_minus1; + + uint8_t bit_rate_scale; + uint8_t cpb_size_scale; + uint8_t cpb_size_du_scale; + + uint8_t initial_cpb_removal_delay_length_minus1; + uint8_t au_cpb_removal_delay_length_minus1; + uint8_t dpb_output_delay_length_minus1; + + uint8_t fixed_pic_rate_general_flag[HEVC_MAX_SUB_LAYERS]; + uint8_t fixed_pic_rate_within_cvs_flag[HEVC_MAX_SUB_LAYERS]; + uint16_t elemental_duration_in_tc_minus1[HEVC_MAX_SUB_LAYERS]; + uint8_t low_delay_hrd_flag[HEVC_MAX_SUB_LAYERS]; + uint8_t cpb_cnt_minus1[HEVC_MAX_SUB_LAYERS]; + H265RawSubLayerHRDParameters nal_sub_layer_hrd_parameters[HEVC_MAX_SUB_LAYERS]; + H265RawSubLayerHRDParameters vcl_sub_layer_hrd_parameters[HEVC_MAX_SUB_LAYERS]; +} H265RawHRDParameters; + +typedef struct H265RawVUI { + uint8_t aspect_ratio_info_present_flag; + uint8_t aspect_ratio_idc; + uint16_t sar_width; + uint16_t sar_height; + + uint8_t overscan_info_present_flag; + uint8_t overscan_appropriate_flag; + + uint8_t video_signal_type_present_flag; + uint8_t video_format; + uint8_t video_full_range_flag; + uint8_t colour_description_present_flag; + uint8_t colour_primaries; + uint8_t transfer_characteristics; + uint8_t matrix_coefficients; + + uint8_t chroma_loc_info_present_flag; + uint8_t chroma_sample_loc_type_top_field; + uint8_t chroma_sample_loc_type_bottom_field; + + uint8_t neutral_chroma_indication_flag; + uint8_t field_seq_flag; + uint8_t frame_field_info_present_flag; + + uint8_t default_display_window_flag; + uint16_t def_disp_win_left_offset; + uint16_t def_disp_win_right_offset; + uint16_t def_disp_win_top_offset; + uint16_t def_disp_win_bottom_offset; + + uint8_t vui_timing_info_present_flag; + uint32_t vui_num_units_in_tick; + uint32_t vui_time_scale; + uint8_t vui_poc_proportional_to_timing_flag; + uint32_t vui_num_ticks_poc_diff_one_minus1; + uint8_t vui_hrd_parameters_present_flag; + H265RawHRDParameters hrd_parameters; + + uint8_t bitstream_restriction_flag; + uint8_t tiles_fixed_structure_flag; + uint8_t motion_vectors_over_pic_boundaries_flag; + uint8_t restricted_ref_pic_lists_flag; + uint16_t min_spatial_segmentation_idc; + uint8_t max_bytes_per_pic_denom; + uint8_t max_bits_per_min_cu_denom; + uint8_t log2_max_mv_length_horizontal; + uint8_t log2_max_mv_length_vertical; +} H265RawVUI; + +typedef struct H265RawExtensionData { + uint8_t *data; + AVBufferRef *data_ref; + size_t bit_length; +} H265RawExtensionData; + +typedef struct H265RawVPS { + H265RawNALUnitHeader nal_unit_header; + + uint8_t vps_video_parameter_set_id; + + uint8_t vps_base_layer_internal_flag; + uint8_t vps_base_layer_available_flag; + uint8_t vps_max_layers_minus1; + uint8_t vps_max_sub_layers_minus1; + uint8_t vps_temporal_id_nesting_flag; + + H265RawProfileTierLevel profile_tier_level; + + uint8_t vps_sub_layer_ordering_info_present_flag; + uint8_t vps_max_dec_pic_buffering_minus1[HEVC_MAX_SUB_LAYERS]; + uint8_t vps_max_num_reorder_pics[HEVC_MAX_SUB_LAYERS]; + uint32_t vps_max_latency_increase_plus1[HEVC_MAX_SUB_LAYERS]; + + uint8_t vps_max_layer_id; + uint16_t vps_num_layer_sets_minus1; + uint8_t layer_id_included_flag[HEVC_MAX_LAYER_SETS][HEVC_MAX_LAYERS]; + + uint8_t vps_timing_info_present_flag; + uint32_t vps_num_units_in_tick; + uint32_t vps_time_scale; + uint8_t vps_poc_proportional_to_timing_flag; + uint32_t vps_num_ticks_poc_diff_one_minus1; + uint16_t vps_num_hrd_parameters; + uint16_t hrd_layer_set_idx[HEVC_MAX_LAYER_SETS]; + uint8_t cprms_present_flag[HEVC_MAX_LAYER_SETS]; + H265RawHRDParameters hrd_parameters[HEVC_MAX_LAYER_SETS]; + + uint8_t vps_extension_flag; + H265RawExtensionData extension_data; +} H265RawVPS; + +typedef struct H265RawSTRefPicSet { + uint8_t inter_ref_pic_set_prediction_flag; + + uint8_t delta_idx_minus1; + uint8_t delta_rps_sign; + uint16_t abs_delta_rps_minus1; + + uint8_t used_by_curr_pic_flag[HEVC_MAX_REFS]; + uint8_t use_delta_flag[HEVC_MAX_REFS]; + + uint8_t num_negative_pics; + uint8_t num_positive_pics; + uint16_t delta_poc_s0_minus1[HEVC_MAX_REFS]; + uint8_t used_by_curr_pic_s0_flag[HEVC_MAX_REFS]; + uint16_t delta_poc_s1_minus1[HEVC_MAX_REFS]; + uint8_t used_by_curr_pic_s1_flag[HEVC_MAX_REFS]; +} H265RawSTRefPicSet; + +typedef struct H265RawScalingList { + uint8_t scaling_list_pred_mode_flag[4][6]; + uint8_t scaling_list_pred_matrix_id_delta[4][6]; + int16_t scaling_list_dc_coef_minus8[4][6]; + int8_t scaling_list_delta_coeff[4][6][64]; +} H265RawScalingList; + +typedef struct H265RawSPS { + H265RawNALUnitHeader nal_unit_header; + + uint8_t sps_video_parameter_set_id; + + uint8_t sps_max_sub_layers_minus1; + uint8_t sps_temporal_id_nesting_flag; + + H265RawProfileTierLevel profile_tier_level; + + uint8_t sps_seq_parameter_set_id; + + uint8_t chroma_format_idc; + uint8_t separate_colour_plane_flag; + + uint16_t pic_width_in_luma_samples; + uint16_t pic_height_in_luma_samples; + + uint8_t conformance_window_flag; + uint16_t conf_win_left_offset; + uint16_t conf_win_right_offset; + uint16_t conf_win_top_offset; + uint16_t conf_win_bottom_offset; + + uint8_t bit_depth_luma_minus8; + uint8_t bit_depth_chroma_minus8; + + uint8_t log2_max_pic_order_cnt_lsb_minus4; + + uint8_t sps_sub_layer_ordering_info_present_flag; + uint8_t sps_max_dec_pic_buffering_minus1[HEVC_MAX_SUB_LAYERS]; + uint8_t sps_max_num_reorder_pics[HEVC_MAX_SUB_LAYERS]; + uint32_t sps_max_latency_increase_plus1[HEVC_MAX_SUB_LAYERS]; + + uint8_t log2_min_luma_coding_block_size_minus3; + uint8_t log2_diff_max_min_luma_coding_block_size; + uint8_t log2_min_luma_transform_block_size_minus2; + uint8_t log2_diff_max_min_luma_transform_block_size; + uint8_t max_transform_hierarchy_depth_inter; + uint8_t max_transform_hierarchy_depth_intra; + + uint8_t scaling_list_enabled_flag; + uint8_t sps_scaling_list_data_present_flag; + H265RawScalingList scaling_list; + + uint8_t amp_enabled_flag; + uint8_t sample_adaptive_offset_enabled_flag; + + uint8_t pcm_enabled_flag; + uint8_t pcm_sample_bit_depth_luma_minus1; + uint8_t pcm_sample_bit_depth_chroma_minus1; + uint8_t log2_min_pcm_luma_coding_block_size_minus3; + uint8_t log2_diff_max_min_pcm_luma_coding_block_size; + uint8_t pcm_loop_filter_disabled_flag; + + uint8_t num_short_term_ref_pic_sets; + H265RawSTRefPicSet st_ref_pic_set[HEVC_MAX_SHORT_TERM_REF_PIC_SETS]; + + uint8_t long_term_ref_pics_present_flag; + uint8_t num_long_term_ref_pics_sps; + uint16_t lt_ref_pic_poc_lsb_sps[HEVC_MAX_LONG_TERM_REF_PICS]; + uint8_t used_by_curr_pic_lt_sps_flag[HEVC_MAX_LONG_TERM_REF_PICS]; + + uint8_t sps_temporal_mvp_enabled_flag; + uint8_t strong_intra_smoothing_enabled_flag; + + uint8_t vui_parameters_present_flag; + H265RawVUI vui; + + uint8_t sps_extension_present_flag; + uint8_t sps_range_extension_flag; + uint8_t sps_multilayer_extension_flag; + uint8_t sps_3d_extension_flag; + uint8_t sps_scc_extension_flag; + uint8_t sps_extension_4bits; + + H265RawExtensionData extension_data; + + // Range extension. + uint8_t transform_skip_rotation_enabled_flag; + uint8_t transform_skip_context_enabled_flag; + uint8_t implicit_rdpcm_enabled_flag; + uint8_t explicit_rdpcm_enabled_flag; + uint8_t extended_precision_processing_flag; + uint8_t intra_smoothing_disabled_flag; + uint8_t high_precision_offsets_enabled_flag; + uint8_t persistent_rice_adaptation_enabled_flag; + uint8_t cabac_bypass_alignment_enabled_flag; + + // Screen content coding extension. + uint8_t sps_curr_pic_ref_enabled_flag; + uint8_t palette_mode_enabled_flag; + uint8_t palette_max_size; + uint8_t delta_palette_max_predictor_size; + uint8_t sps_palette_predictor_initializer_present_flag; + uint8_t sps_num_palette_predictor_initializer_minus1; + uint16_t sps_palette_predictor_initializers[3][128]; + + uint8_t motion_vector_resolution_control_idc; + uint8_t intra_boundary_filtering_disable_flag; +} H265RawSPS; + +typedef struct H265RawPPS { + H265RawNALUnitHeader nal_unit_header; + + uint8_t pps_pic_parameter_set_id; + uint8_t pps_seq_parameter_set_id; + + uint8_t dependent_slice_segments_enabled_flag; + uint8_t output_flag_present_flag; + uint8_t num_extra_slice_header_bits; + uint8_t sign_data_hiding_enabled_flag; + uint8_t cabac_init_present_flag; + + uint8_t num_ref_idx_l0_default_active_minus1; + uint8_t num_ref_idx_l1_default_active_minus1; + + int8_t init_qp_minus26; + + uint8_t constrained_intra_pred_flag; + uint8_t transform_skip_enabled_flag; + uint8_t cu_qp_delta_enabled_flag; + uint8_t diff_cu_qp_delta_depth; + + int8_t pps_cb_qp_offset; + int8_t pps_cr_qp_offset; + uint8_t pps_slice_chroma_qp_offsets_present_flag; + + uint8_t weighted_pred_flag; + uint8_t weighted_bipred_flag; + + uint8_t transquant_bypass_enabled_flag; + uint8_t tiles_enabled_flag; + uint8_t entropy_coding_sync_enabled_flag; + + uint8_t num_tile_columns_minus1; + uint8_t num_tile_rows_minus1; + uint8_t uniform_spacing_flag; + uint16_t column_width_minus1[HEVC_MAX_TILE_COLUMNS]; + uint16_t row_height_minus1[HEVC_MAX_TILE_ROWS]; + uint8_t loop_filter_across_tiles_enabled_flag; + + uint8_t pps_loop_filter_across_slices_enabled_flag; + uint8_t deblocking_filter_control_present_flag; + uint8_t deblocking_filter_override_enabled_flag; + uint8_t pps_deblocking_filter_disabled_flag; + int8_t pps_beta_offset_div2; + int8_t pps_tc_offset_div2; + + uint8_t pps_scaling_list_data_present_flag; + H265RawScalingList scaling_list; + + uint8_t lists_modification_present_flag; + uint8_t log2_parallel_merge_level_minus2; + + uint8_t slice_segment_header_extension_present_flag; + + uint8_t pps_extension_present_flag; + uint8_t pps_range_extension_flag; + uint8_t pps_multilayer_extension_flag; + uint8_t pps_3d_extension_flag; + uint8_t pps_scc_extension_flag; + uint8_t pps_extension_4bits; + + H265RawExtensionData extension_data; + + // Range extension. + uint8_t log2_max_transform_skip_block_size_minus2; + uint8_t cross_component_prediction_enabled_flag; + uint8_t chroma_qp_offset_list_enabled_flag; + uint8_t diff_cu_chroma_qp_offset_depth; + uint8_t chroma_qp_offset_list_len_minus1; + int8_t cb_qp_offset_list[6]; + int8_t cr_qp_offset_list[6]; + uint8_t log2_sao_offset_scale_luma; + uint8_t log2_sao_offset_scale_chroma; + + // Screen content coding extension. + uint8_t pps_curr_pic_ref_enabled_flag; + uint8_t residual_adaptive_colour_transform_enabled_flag; + uint8_t pps_slice_act_qp_offsets_present_flag; + int8_t pps_act_y_qp_offset_plus5; + int8_t pps_act_cb_qp_offset_plus5; + int8_t pps_act_cr_qp_offset_plus3; + + uint8_t pps_palette_predictor_initializer_present_flag; + uint8_t pps_num_palette_predictor_initializer; + uint8_t monochrome_palette_flag; + uint8_t luma_bit_depth_entry_minus8; + uint8_t chroma_bit_depth_entry_minus8; + uint16_t pps_palette_predictor_initializers[3][128]; +} H265RawPPS; + +typedef struct H265RawAUD { + H265RawNALUnitHeader nal_unit_header; + + uint8_t pic_type; +} H265RawAUD; + +typedef struct H265RawSliceHeader { + H265RawNALUnitHeader nal_unit_header; + + uint8_t first_slice_segment_in_pic_flag; + uint8_t no_output_of_prior_pics_flag; + uint8_t slice_pic_parameter_set_id; + + uint8_t dependent_slice_segment_flag; + uint16_t slice_segment_address; + + uint8_t slice_reserved_flag[8]; + uint8_t slice_type; + + uint8_t pic_output_flag; + uint8_t colour_plane_id; + + uint16_t slice_pic_order_cnt_lsb; + + uint8_t short_term_ref_pic_set_sps_flag; + H265RawSTRefPicSet short_term_ref_pic_set; + uint8_t short_term_ref_pic_set_idx; + + uint8_t num_long_term_sps; + uint8_t num_long_term_pics; + uint8_t lt_idx_sps[HEVC_MAX_REFS]; + uint8_t poc_lsb_lt[HEVC_MAX_REFS]; + uint8_t used_by_curr_pic_lt_flag[HEVC_MAX_REFS]; + uint8_t delta_poc_msb_present_flag[HEVC_MAX_REFS]; + uint32_t delta_poc_msb_cycle_lt[HEVC_MAX_REFS]; + + uint8_t slice_temporal_mvp_enabled_flag; + + uint8_t slice_sao_luma_flag; + uint8_t slice_sao_chroma_flag; + + uint8_t num_ref_idx_active_override_flag; + uint8_t num_ref_idx_l0_active_minus1; + uint8_t num_ref_idx_l1_active_minus1; + + uint8_t ref_pic_list_modification_flag_l0; + uint8_t list_entry_l0[HEVC_MAX_REFS]; + uint8_t ref_pic_list_modification_flag_l1; + uint8_t list_entry_l1[HEVC_MAX_REFS]; + + uint8_t mvd_l1_zero_flag; + uint8_t cabac_init_flag; + uint8_t collocated_from_l0_flag; + uint8_t collocated_ref_idx; + + uint8_t luma_log2_weight_denom; + int8_t delta_chroma_log2_weight_denom; + uint8_t luma_weight_l0_flag[HEVC_MAX_REFS]; + uint8_t chroma_weight_l0_flag[HEVC_MAX_REFS]; + int8_t delta_luma_weight_l0[HEVC_MAX_REFS]; + int16_t luma_offset_l0[HEVC_MAX_REFS]; + int8_t delta_chroma_weight_l0[HEVC_MAX_REFS][2]; + int16_t chroma_offset_l0[HEVC_MAX_REFS][2]; + uint8_t luma_weight_l1_flag[HEVC_MAX_REFS]; + uint8_t chroma_weight_l1_flag[HEVC_MAX_REFS]; + int8_t delta_luma_weight_l1[HEVC_MAX_REFS]; + int16_t luma_offset_l1[HEVC_MAX_REFS]; + int8_t delta_chroma_weight_l1[HEVC_MAX_REFS][2]; + int16_t chroma_offset_l1[HEVC_MAX_REFS][2]; + + uint8_t five_minus_max_num_merge_cand; + uint8_t use_integer_mv_flag; + + int8_t slice_qp_delta; + int8_t slice_cb_qp_offset; + int8_t slice_cr_qp_offset; + int8_t slice_act_y_qp_offset; + int8_t slice_act_cb_qp_offset; + int8_t slice_act_cr_qp_offset; + uint8_t cu_chroma_qp_offset_enabled_flag; + + uint8_t deblocking_filter_override_flag; + uint8_t slice_deblocking_filter_disabled_flag; + int8_t slice_beta_offset_div2; + int8_t slice_tc_offset_div2; + uint8_t slice_loop_filter_across_slices_enabled_flag; + + uint16_t num_entry_point_offsets; + uint8_t offset_len_minus1; + uint32_t entry_point_offset_minus1[HEVC_MAX_ENTRY_POINT_OFFSETS]; + + uint16_t slice_segment_header_extension_length; + uint8_t slice_segment_header_extension_data_byte[256]; +} H265RawSliceHeader; + + +typedef struct H265RawSlice { + H265RawSliceHeader header; + + uint8_t *data; + AVBufferRef *data_ref; + size_t data_size; + int data_bit_start; +} H265RawSlice; + + +typedef struct H265RawSEIBufferingPeriod { + uint8_t bp_seq_parameter_set_id; + uint8_t irap_cpb_params_present_flag; + uint32_t cpb_delay_offset; + uint32_t dpb_delay_offset; + uint8_t concatenation_flag; + uint32_t au_cpb_removal_delay_delta_minus1; + + uint32_t nal_initial_cpb_removal_delay[HEVC_MAX_CPB_CNT]; + uint32_t nal_initial_cpb_removal_offset[HEVC_MAX_CPB_CNT]; + uint32_t nal_initial_alt_cpb_removal_delay[HEVC_MAX_CPB_CNT]; + uint32_t nal_initial_alt_cpb_removal_offset[HEVC_MAX_CPB_CNT]; + + uint32_t vcl_initial_cpb_removal_delay[HEVC_MAX_CPB_CNT]; + uint32_t vcl_initial_cpb_removal_offset[HEVC_MAX_CPB_CNT]; + uint32_t vcl_initial_alt_cpb_removal_delay[HEVC_MAX_CPB_CNT]; + uint32_t vcl_initial_alt_cpb_removal_offset[HEVC_MAX_CPB_CNT]; + + uint8_t use_alt_cpb_params_flag; +} H265RawSEIBufferingPeriod; + +typedef struct H265RawSEIPicTiming { + uint8_t pic_struct; + uint8_t source_scan_type; + uint8_t duplicate_flag; + + uint32_t au_cpb_removal_delay_minus1; + uint32_t pic_dpb_output_delay; + uint32_t pic_dpb_output_du_delay; + + uint16_t num_decoding_units_minus1; + uint8_t du_common_cpb_removal_delay_flag; + uint32_t du_common_cpb_removal_delay_increment_minus1; + uint16_t num_nalus_in_du_minus1[HEVC_MAX_SLICE_SEGMENTS]; + uint32_t du_cpb_removal_delay_increment_minus1[HEVC_MAX_SLICE_SEGMENTS]; +} H265RawSEIPicTiming; + +typedef struct H265RawSEIPanScanRect { + uint32_t pan_scan_rect_id; + uint8_t pan_scan_rect_cancel_flag; + uint8_t pan_scan_cnt_minus1; + int32_t pan_scan_rect_left_offset[3]; + int32_t pan_scan_rect_right_offset[3]; + int32_t pan_scan_rect_top_offset[3]; + int32_t pan_scan_rect_bottom_offset[3]; + uint16_t pan_scan_rect_persistence_flag; +} H265RawSEIPanScanRect; + +typedef struct H265RawSEIRecoveryPoint { + int16_t recovery_poc_cnt; + uint8_t exact_match_flag; + uint8_t broken_link_flag; +} H265RawSEIRecoveryPoint; + +typedef struct H265RawSEIDisplayOrientation { + uint8_t display_orientation_cancel_flag; + uint8_t hor_flip; + uint8_t ver_flip; + uint16_t anticlockwise_rotation; + uint16_t display_orientation_repetition_period; + uint8_t display_orientation_persistence_flag; +} H265RawSEIDisplayOrientation; + +typedef struct H265RawSEIActiveParameterSets { + uint8_t active_video_parameter_set_id; + uint8_t self_contained_cvs_flag; + uint8_t no_parameter_set_update_flag; + uint8_t num_sps_ids_minus1; + uint8_t active_seq_parameter_set_id[HEVC_MAX_SPS_COUNT]; + uint8_t layer_sps_idx[HEVC_MAX_LAYERS]; +} H265RawSEIActiveParameterSets; + +typedef struct H265RawSEIDecodedPictureHash { + uint8_t hash_type; + uint8_t picture_md5[3][16]; + uint16_t picture_crc[3]; + uint32_t picture_checksum[3]; +} H265RawSEIDecodedPictureHash; + +typedef struct H265RawSEITimeCode { + uint8_t num_clock_ts; + uint8_t clock_timestamp_flag[3]; + uint8_t units_field_based_flag[3]; + uint8_t counting_type[3]; + uint8_t full_timestamp_flag[3]; + uint8_t discontinuity_flag[3]; + uint8_t cnt_dropped_flag[3]; + uint16_t n_frames[3]; + uint8_t seconds_value[3]; + uint8_t minutes_value[3]; + uint8_t hours_value[3]; + uint8_t seconds_flag[3]; + uint8_t minutes_flag[3]; + uint8_t hours_flag[3]; + uint8_t time_offset_length[3]; + int32_t time_offset_value[3]; +} H265RawSEITimeCode; + +typedef struct H265RawSEIAlphaChannelInfo { + uint8_t alpha_channel_cancel_flag; + uint8_t alpha_channel_use_idc; + uint8_t alpha_channel_bit_depth_minus8; + uint16_t alpha_transparent_value; + uint16_t alpha_opaque_value; + uint8_t alpha_channel_incr_flag; + uint8_t alpha_channel_clip_flag; + uint8_t alpha_channel_clip_type_flag; +} H265RawSEIAlphaChannelInfo; + +typedef struct H265RawSEI { + H265RawNALUnitHeader nal_unit_header; + SEIRawMessageList message_list; +} H265RawSEI; + +typedef struct CodedBitstreamH265Context { + // Reader/writer context in common with the H.264 implementation. + CodedBitstreamH2645Context common; + + // All currently available parameter sets. These are updated when + // any parameter set NAL unit is read/written with this context. + AVBufferRef *vps_ref[HEVC_MAX_VPS_COUNT]; + AVBufferRef *sps_ref[HEVC_MAX_SPS_COUNT]; + AVBufferRef *pps_ref[HEVC_MAX_PPS_COUNT]; + H265RawVPS *vps[HEVC_MAX_VPS_COUNT]; + H265RawSPS *sps[HEVC_MAX_SPS_COUNT]; + H265RawPPS *pps[HEVC_MAX_PPS_COUNT]; + + // The currently active parameter sets. These are updated when any + // NAL unit refers to the relevant parameter set. These pointers + // must also be present in the arrays above. + const H265RawVPS *active_vps; + const H265RawSPS *active_sps; + const H265RawPPS *active_pps; +} CodedBitstreamH265Context; + + +#endif /* AVCODEC_CBS_H265_H */ diff --git a/third-party/cbs/include/cbs/cbs_jpeg.h b/third-party/cbs/include/cbs/cbs_jpeg.h new file mode 100644 index 00000000..c2ee8c5e --- /dev/null +++ b/third-party/cbs/include/cbs/cbs_jpeg.h @@ -0,0 +1,123 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CBS_JPEG_H +#define AVCODEC_CBS_JPEG_H + +#include +#include + +#include + + +enum { + JPEG_MARKER_SOF0 = 0xc0, + JPEG_MARKER_SOF1 = 0xc1, + JPEG_MARKER_SOF2 = 0xc2, + JPEG_MARKER_SOF3 = 0xc3, + + JPEG_MARKER_DHT = 0xc4, + JPEG_MARKER_SOI = 0xd8, + JPEG_MARKER_EOI = 0xd9, + JPEG_MARKER_SOS = 0xda, + JPEG_MARKER_DQT = 0xdb, + + JPEG_MARKER_APPN = 0xe0, + JPEG_MARKER_JPGN = 0xf0, + JPEG_MARKER_COM = 0xfe, +}; + +enum { + JPEG_MAX_COMPONENTS = 255, + + JPEG_MAX_HEIGHT = 65535, + JPEG_MAX_WIDTH = 65535, +}; + + +typedef struct JPEGRawFrameHeader { + uint16_t Lf; + uint8_t P; + uint16_t Y; + uint16_t X; + uint16_t Nf; + + uint8_t C[JPEG_MAX_COMPONENTS]; + uint8_t H[JPEG_MAX_COMPONENTS]; + uint8_t V[JPEG_MAX_COMPONENTS]; + uint8_t Tq[JPEG_MAX_COMPONENTS]; +} JPEGRawFrameHeader; + +typedef struct JPEGRawScanHeader { + uint16_t Ls; + uint8_t Ns; + + uint8_t Cs[JPEG_MAX_COMPONENTS]; + uint8_t Td[JPEG_MAX_COMPONENTS]; + uint8_t Ta[JPEG_MAX_COMPONENTS]; + + uint8_t Ss; + uint8_t Se; + uint8_t Ah; + uint8_t Al; +} JPEGRawScanHeader; + +typedef struct JPEGRawScan { + JPEGRawScanHeader header; + uint8_t *data; + AVBufferRef *data_ref; + size_t data_size; +} JPEGRawScan; + +typedef struct JPEGRawQuantisationTable { + uint8_t Pq; + uint8_t Tq; + uint16_t Q[64]; +} JPEGRawQuantisationTable; + +typedef struct JPEGRawQuantisationTableSpecification { + uint16_t Lq; + JPEGRawQuantisationTable table[4]; +} JPEGRawQuantisationTableSpecification; + +typedef struct JPEGRawHuffmanTable { + uint8_t Tc; + uint8_t Th; + uint8_t L[16]; + uint8_t V[224]; +} JPEGRawHuffmanTable; + +typedef struct JPEGRawHuffmanTableSpecification { + uint16_t Lh; + JPEGRawHuffmanTable table[8]; +} JPEGRawHuffmanTableSpecification; + +typedef struct JPEGRawApplicationData { + uint16_t Lp; + uint8_t *Ap; + AVBufferRef *Ap_ref; +} JPEGRawApplicationData; + +typedef struct JPEGRawComment { + uint16_t Lc; + uint8_t *Cm; + AVBufferRef *Cm_ref; +} JPEGRawComment; + + +#endif /* AVCODEC_CBS_JPEG_H */ diff --git a/third-party/cbs/include/cbs/cbs_mpeg2.h b/third-party/cbs/include/cbs/cbs_mpeg2.h new file mode 100644 index 00000000..858cb543 --- /dev/null +++ b/third-party/cbs/include/cbs/cbs_mpeg2.h @@ -0,0 +1,231 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CBS_MPEG2_H +#define AVCODEC_CBS_MPEG2_H + +#include +#include + +#include + + +enum { + MPEG2_START_PICTURE = 0x00, + MPEG2_START_SLICE_MIN = 0x01, + MPEG2_START_SLICE_MAX = 0xaf, + MPEG2_START_USER_DATA = 0xb2, + MPEG2_START_SEQUENCE_HEADER = 0xb3, + MPEG2_START_SEQUENCE_ERROR = 0xb4, + MPEG2_START_EXTENSION = 0xb5, + MPEG2_START_SEQUENCE_END = 0xb7, + MPEG2_START_GROUP = 0xb8, +}; + +#define MPEG2_START_IS_SLICE(type) \ + ((type) >= MPEG2_START_SLICE_MIN && \ + (type) <= MPEG2_START_SLICE_MAX) + +enum { + MPEG2_EXTENSION_SEQUENCE = 0x1, + MPEG2_EXTENSION_SEQUENCE_DISPLAY = 0x2, + MPEG2_EXTENSION_QUANT_MATRIX = 0x3, + MPEG2_EXTENSION_COPYRIGHT = 0x4, + MPEG2_EXTENSION_SEQUENCE_SCALABLE = 0x5, + MPEG2_EXTENSION_PICTURE_DISPLAY = 0x7, + MPEG2_EXTENSION_PICTURE_CODING = 0x8, + MPEG2_EXTENSION_PICTURE_SPATIAL_SCALABLE = 0x9, + MPEG2_EXTENSION_PICTURE_TEMPORAL_SCALABLE = 0xa, + MPEG2_EXTENSION_CAMERA_PARAMETERS = 0xb, + MPEG2_EXTENSION_ITU_T = 0xc, +}; + + +typedef struct MPEG2RawSequenceHeader { + uint8_t sequence_header_code; + + uint16_t horizontal_size_value; + uint16_t vertical_size_value; + uint8_t aspect_ratio_information; + uint8_t frame_rate_code; + uint32_t bit_rate_value; + uint16_t vbv_buffer_size_value; + uint8_t constrained_parameters_flag; + + uint8_t load_intra_quantiser_matrix; + uint8_t intra_quantiser_matrix[64]; + uint8_t load_non_intra_quantiser_matrix; + uint8_t non_intra_quantiser_matrix[64]; +} MPEG2RawSequenceHeader; + +typedef struct MPEG2RawUserData { + uint8_t user_data_start_code; + + uint8_t *user_data; + AVBufferRef *user_data_ref; + size_t user_data_length; +} MPEG2RawUserData; + +typedef struct MPEG2RawSequenceExtension { + uint8_t profile_and_level_indication; + uint8_t progressive_sequence; + uint8_t chroma_format; + uint8_t horizontal_size_extension; + uint8_t vertical_size_extension; + uint16_t bit_rate_extension; + uint8_t vbv_buffer_size_extension; + uint8_t low_delay; + uint8_t frame_rate_extension_n; + uint8_t frame_rate_extension_d; +} MPEG2RawSequenceExtension; + +typedef struct MPEG2RawSequenceDisplayExtension { + uint8_t video_format; + + uint8_t colour_description; + uint8_t colour_primaries; + uint8_t transfer_characteristics; + uint8_t matrix_coefficients; + + uint16_t display_horizontal_size; + uint16_t display_vertical_size; +} MPEG2RawSequenceDisplayExtension; + +typedef struct MPEG2RawGroupOfPicturesHeader { + uint8_t group_start_code; + + uint32_t time_code; + uint8_t closed_gop; + uint8_t broken_link; +} MPEG2RawGroupOfPicturesHeader; + +typedef struct MPEG2RawExtraInformation { + uint8_t *extra_information; + AVBufferRef *extra_information_ref; + size_t extra_information_length; +} MPEG2RawExtraInformation; + +typedef struct MPEG2RawPictureHeader { + uint8_t picture_start_code; + + uint16_t temporal_reference; + uint8_t picture_coding_type; + uint16_t vbv_delay; + + uint8_t full_pel_forward_vector; + uint8_t forward_f_code; + uint8_t full_pel_backward_vector; + uint8_t backward_f_code; + + MPEG2RawExtraInformation extra_information_picture; +} MPEG2RawPictureHeader; + +typedef struct MPEG2RawPictureCodingExtension { + uint8_t f_code[2][2]; + + uint8_t intra_dc_precision; + uint8_t picture_structure; + uint8_t top_field_first; + uint8_t frame_pred_frame_dct; + uint8_t concealment_motion_vectors; + uint8_t q_scale_type; + uint8_t intra_vlc_format; + uint8_t alternate_scan; + uint8_t repeat_first_field; + uint8_t chroma_420_type; + uint8_t progressive_frame; + + uint8_t composite_display_flag; + uint8_t v_axis; + uint8_t field_sequence; + uint8_t sub_carrier; + uint8_t burst_amplitude; + uint8_t sub_carrier_phase; +} MPEG2RawPictureCodingExtension; + +typedef struct MPEG2RawQuantMatrixExtension { + uint8_t load_intra_quantiser_matrix; + uint8_t intra_quantiser_matrix[64]; + uint8_t load_non_intra_quantiser_matrix; + uint8_t non_intra_quantiser_matrix[64]; + uint8_t load_chroma_intra_quantiser_matrix; + uint8_t chroma_intra_quantiser_matrix[64]; + uint8_t load_chroma_non_intra_quantiser_matrix; + uint8_t chroma_non_intra_quantiser_matrix[64]; +} MPEG2RawQuantMatrixExtension; + +typedef struct MPEG2RawPictureDisplayExtension { + int16_t frame_centre_horizontal_offset[3]; + int16_t frame_centre_vertical_offset[3]; +} MPEG2RawPictureDisplayExtension; + +typedef struct MPEG2RawExtensionData { + uint8_t extension_start_code; + uint8_t extension_start_code_identifier; + + union { + MPEG2RawSequenceExtension sequence; + MPEG2RawSequenceDisplayExtension sequence_display; + MPEG2RawQuantMatrixExtension quant_matrix; + MPEG2RawPictureCodingExtension picture_coding; + MPEG2RawPictureDisplayExtension picture_display; + } data; +} MPEG2RawExtensionData; + +typedef struct MPEG2RawSliceHeader { + uint8_t slice_vertical_position; + + uint8_t slice_vertical_position_extension; + uint8_t priority_breakpoint; + + uint8_t quantiser_scale_code; + + uint8_t slice_extension_flag; + uint8_t intra_slice; + uint8_t slice_picture_id_enable; + uint8_t slice_picture_id; + + MPEG2RawExtraInformation extra_information_slice; +} MPEG2RawSliceHeader; + +typedef struct MPEG2RawSlice { + MPEG2RawSliceHeader header; + + uint8_t *data; + AVBufferRef *data_ref; + size_t data_size; + int data_bit_start; +} MPEG2RawSlice; + +typedef struct MPEG2RawSequenceEnd { + uint8_t sequence_end_code; +} MPEG2RawSequenceEnd; + + +typedef struct CodedBitstreamMPEG2Context { + // Elements stored in headers which are required for other decoding. + uint16_t horizontal_size; + uint16_t vertical_size; + uint8_t scalable; + uint8_t scalable_mode; + uint8_t progressive_sequence; + uint8_t number_of_frame_centre_offsets; +} CodedBitstreamMPEG2Context; + + +#endif /* AVCODEC_CBS_MPEG2_H */ diff --git a/third-party/cbs/include/cbs/cbs_sei.h b/third-party/cbs/include/cbs/cbs_sei.h new file mode 100644 index 00000000..d20bdda6 --- /dev/null +++ b/third-party/cbs/include/cbs/cbs_sei.h @@ -0,0 +1,200 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CBS_SEI_H +#define AVCODEC_CBS_SEI_H + +#include +#include + +#include + +#include "cbs.h" +#include "sei.h" + + +typedef struct SEIRawFillerPayload { + uint32_t payload_size; +} SEIRawFillerPayload; + +typedef struct SEIRawUserDataRegistered { + uint8_t itu_t_t35_country_code; + uint8_t itu_t_t35_country_code_extension_byte; + uint8_t *data; + AVBufferRef *data_ref; + size_t data_length; +} SEIRawUserDataRegistered; + +typedef struct SEIRawUserDataUnregistered { + uint8_t uuid_iso_iec_11578[16]; + uint8_t *data; + AVBufferRef *data_ref; + size_t data_length; +} SEIRawUserDataUnregistered; + +typedef struct SEIRawMasteringDisplayColourVolume { + uint16_t display_primaries_x[3]; + uint16_t display_primaries_y[3]; + uint16_t white_point_x; + uint16_t white_point_y; + uint32_t max_display_mastering_luminance; + uint32_t min_display_mastering_luminance; +} SEIRawMasteringDisplayColourVolume; + +typedef struct SEIRawContentLightLevelInfo { + uint16_t max_content_light_level; + uint16_t max_pic_average_light_level; +} SEIRawContentLightLevelInfo; + +typedef struct SEIRawAlternativeTransferCharacteristics { + uint8_t preferred_transfer_characteristics; +} SEIRawAlternativeTransferCharacteristics; + +typedef struct SEIRawMessage { + uint32_t payload_type; + uint32_t payload_size; + void *payload; + AVBufferRef *payload_ref; + uint8_t *extension_data; + AVBufferRef *extension_data_ref; + size_t extension_bit_length; +} SEIRawMessage; + +typedef struct SEIRawMessageList { + SEIRawMessage *messages; + int nb_messages; + int nb_messages_allocated; +} SEIRawMessageList; + + +typedef struct SEIMessageState { + // The type of the payload being written. + uint32_t payload_type; + // When reading, contains the size of the payload to allow finding the + // end of variable-length fields (such as user_data_payload_byte[]). + // (When writing, the size will be derived from the total number of + // bytes actually written.) + uint32_t payload_size; + // When writing, indicates that payload extension data is present so + // all extended fields must be written. May be updated by the writer + // to indicate that extended fields have been written, so the extension + // end bits must be written too. + uint8_t extension_present; +} SEIMessageState; + +struct GetBitContext; +struct PutBitContext; + +typedef int (*SEIMessageReadFunction)(CodedBitstreamContext *ctx, + struct GetBitContext *rw, + void *current, + SEIMessageState *sei); + +typedef int (*SEIMessageWriteFunction)(CodedBitstreamContext *ctx, + struct PutBitContext *rw, + void *current, + SEIMessageState *sei); + +typedef struct SEIMessageTypeDescriptor { + // Payload type for the message. (-1 in this field ends a list.) + int type; + // Valid in a prefix SEI NAL unit (always for H.264). + uint8_t prefix; + // Valid in a suffix SEI NAL unit (never for H.264). + uint8_t suffix; + // Size of the decomposed structure. + size_t size; + // Read bitstream into SEI message. + SEIMessageReadFunction read; + // Write bitstream from SEI message. + SEIMessageWriteFunction write; +} SEIMessageTypeDescriptor; + +// Macro for the read/write pair. The clumsy cast is needed because the +// current pointer is typed in all of the read/write functions but has to +// be void here to fit all cases. +#define SEI_MESSAGE_RW(codec, name) \ + .read = (SEIMessageReadFunction)cbs_##codec##_read_##name, \ + .write = (SEIMessageWriteFunction)cbs_##codec##_write_##name + +// End-of-list sentinel element. +#define SEI_MESSAGE_TYPE_END \ + { .type = -1 } + + +/** + * Find the type descriptor for the given payload type. + * + * Returns NULL if the payload type is not known. + */ +const SEIMessageTypeDescriptor *ff_cbs_sei_find_type(CodedBitstreamContext *ctx, + int payload_type); + +/** + * Allocate a new payload for the given SEI message. + */ +int ff_cbs_sei_alloc_message_payload(SEIRawMessage *message, + const SEIMessageTypeDescriptor *desc); + +/** + * Allocate a new empty SEI message in a message list. + * + * The new message is in place nb_messages - 1. + */ +int ff_cbs_sei_list_add(SEIRawMessageList *list); + +/** + * Free all SEI messages in a message list. + */ +void ff_cbs_sei_free_message_list(SEIRawMessageList *list); + +/** + * Add an SEI message to an access unit. + * + * Will add to an existing SEI NAL unit, or create a new one for the + * message if there is no suitable existing one. + * + * Takes a new reference to payload_buf, if set. If payload_buf is + * NULL then the new message will not be reference counted. + */ +int ff_cbs_sei_add_message(CodedBitstreamContext *ctx, + CodedBitstreamFragment *au, + int prefix, + uint32_t payload_type, + void *payload_data, + AVBufferRef *payload_buf); + +/** + * Iterate over messages with the given payload type in an access unit. + * + * Set message to NULL in the first call. Returns 0 while more messages + * are available, AVERROR(ENOENT) when all messages have been found. + */ +int ff_cbs_sei_find_message(CodedBitstreamContext *ctx, + CodedBitstreamFragment *au, + uint32_t payload_type, + SEIRawMessage **message); + +/** + * Delete all messages with the given payload type from an access unit. + */ +void ff_cbs_sei_delete_message_type(CodedBitstreamContext *ctx, + CodedBitstreamFragment *au, + uint32_t payload_type); + +#endif /* AVCODEC_CBS_SEI_H */ diff --git a/third-party/cbs/include/cbs/cbs_vp9.h b/third-party/cbs/include/cbs/cbs_vp9.h new file mode 100644 index 00000000..62754498 --- /dev/null +++ b/third-party/cbs/include/cbs/cbs_vp9.h @@ -0,0 +1,213 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CBS_VP9_H +#define AVCODEC_CBS_VP9_H + +#include +#include + +#include "cbs.h" + + +// Miscellaneous constants (section 3). +enum { + VP9_REFS_PER_FRAME = 3, + + VP9_MIN_TILE_WIDTH_B64 = 4, + VP9_MAX_TILE_WIDTH_B64 = 64, + + VP9_NUM_REF_FRAMES = 8, + VP9_MAX_REF_FRAMES = 4, + + VP9_MAX_SEGMENTS = 8, + VP9_SEG_LVL_MAX = 4, +}; + +// Frame types (section 7.2). +enum { + VP9_KEY_FRAME = 0, + VP9_NON_KEY_FRAME = 1, +}; + +// Frame sync bytes (section 7.2.1). +enum { + VP9_FRAME_SYNC_0 = 0x49, + VP9_FRAME_SYNC_1 = 0x83, + VP9_FRAME_SYNC_2 = 0x42, +}; + +// Color space values (section 7.2.2). +enum { + VP9_CS_UNKNOWN = 0, + VP9_CS_BT_601 = 1, + VP9_CS_BT_709 = 2, + VP9_CS_SMPTE_170 = 3, + VP9_CS_SMPTE_240 = 4, + VP9_CS_BT_2020 = 5, + VP9_CS_RESERVED = 6, + VP9_CS_RGB = 7, +}; + +// Reference frame types (section 7.4.12). +enum { + VP9_INTRA_FRAME = 0, + VP9_LAST_FRAME = 1, + VP9_GOLDEN_FRAME = 2, + VP9_ALTREF_FRAME = 3, +}; + +// Superframe properties (section B.3). +enum { + VP9_MAX_FRAMES_IN_SUPERFRAME = 8, + + VP9_SUPERFRAME_MARKER = 6, +}; + + +typedef struct VP9RawFrameHeader { + uint8_t frame_marker; + uint8_t profile_low_bit; + uint8_t profile_high_bit; + + uint8_t show_existing_frame; + uint8_t frame_to_show_map_idx; + + uint8_t frame_type; + uint8_t show_frame; + uint8_t error_resilient_mode; + + // Color config. + uint8_t ten_or_twelve_bit; + uint8_t color_space; + uint8_t color_range; + uint8_t subsampling_x; + uint8_t subsampling_y; + + uint8_t refresh_frame_flags; + + uint8_t intra_only; + uint8_t reset_frame_context; + + uint8_t ref_frame_idx[VP9_REFS_PER_FRAME]; + uint8_t ref_frame_sign_bias[VP9_MAX_REF_FRAMES]; + + uint8_t allow_high_precision_mv; + + uint8_t refresh_frame_context; + uint8_t frame_parallel_decoding_mode; + + uint8_t frame_context_idx; + + // Frame/render size. + uint8_t found_ref[VP9_REFS_PER_FRAME]; + uint16_t frame_width_minus_1; + uint16_t frame_height_minus_1; + uint8_t render_and_frame_size_different; + uint16_t render_width_minus_1; + uint16_t render_height_minus_1; + + // Interpolation filter. + uint8_t is_filter_switchable; + uint8_t raw_interpolation_filter_type; + + // Loop filter params. + uint8_t loop_filter_level; + uint8_t loop_filter_sharpness; + uint8_t loop_filter_delta_enabled; + uint8_t loop_filter_delta_update; + uint8_t update_ref_delta[VP9_MAX_REF_FRAMES]; + int8_t loop_filter_ref_deltas[VP9_MAX_REF_FRAMES]; + uint8_t update_mode_delta[2]; + int8_t loop_filter_mode_deltas[2]; + + // Quantization params. + uint8_t base_q_idx; + int8_t delta_q_y_dc; + int8_t delta_q_uv_dc; + int8_t delta_q_uv_ac; + + // Segmentation params. + uint8_t segmentation_enabled; + uint8_t segmentation_update_map; + uint8_t segmentation_tree_probs[7]; + uint8_t segmentation_temporal_update; + uint8_t segmentation_pred_prob[3]; + uint8_t segmentation_update_data; + uint8_t segmentation_abs_or_delta_update; + uint8_t feature_enabled[VP9_MAX_SEGMENTS][VP9_SEG_LVL_MAX]; + uint8_t feature_value[VP9_MAX_SEGMENTS][VP9_SEG_LVL_MAX]; + uint8_t feature_sign[VP9_MAX_SEGMENTS][VP9_SEG_LVL_MAX]; + + // Tile info. + uint8_t tile_cols_log2; + uint8_t tile_rows_log2; + + uint16_t header_size_in_bytes; +} VP9RawFrameHeader; + +typedef struct VP9RawFrame { + VP9RawFrameHeader header; + + uint8_t *data; + AVBufferRef *data_ref; + size_t data_size; +} VP9RawFrame; + +typedef struct VP9RawSuperframeIndex { + uint8_t superframe_marker; + uint8_t bytes_per_framesize_minus_1; + uint8_t frames_in_superframe_minus_1; + uint32_t frame_sizes[VP9_MAX_FRAMES_IN_SUPERFRAME]; +} VP9RawSuperframeIndex; + +typedef struct VP9RawSuperframe { + VP9RawFrame frames[VP9_MAX_FRAMES_IN_SUPERFRAME]; + VP9RawSuperframeIndex index; +} VP9RawSuperframe; + +typedef struct VP9ReferenceFrameState { + int frame_width; // RefFrameWidth + int frame_height; // RefFrameHeight + int subsampling_x; // RefSubsamplingX + int subsampling_y; // RefSubsamplingY + int bit_depth; // RefBitDepth +} VP9ReferenceFrameState; + +typedef struct CodedBitstreamVP9Context { + int profile; + + // Frame dimensions in 8x8 mode info blocks. + uint16_t mi_cols; + uint16_t mi_rows; + // Frame dimensions in 64x64 superblocks. + uint16_t sb64_cols; + uint16_t sb64_rows; + + int frame_width; + int frame_height; + + uint8_t subsampling_x; + uint8_t subsampling_y; + int bit_depth; + + VP9ReferenceFrameState ref[VP9_NUM_REF_FRAMES]; +} CodedBitstreamVP9Context; + + +#endif /* AVCODEC_CBS_VP9_H */ diff --git a/third-party/cbs/include/cbs/h264.h b/third-party/cbs/include/cbs/h264.h new file mode 100644 index 00000000..7fc4d07e --- /dev/null +++ b/third-party/cbs/include/cbs/h264.h @@ -0,0 +1,113 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * H.264 common definitions + */ + +#ifndef AVCODEC_H264_H +#define AVCODEC_H264_H + +#define QP_MAX_NUM (51 + 6 * 6) // The maximum supported qp + +/* + * Table 7-1 – NAL unit type codes, syntax element categories, and NAL unit type classes in + * T-REC-H.264-201704 + */ +enum { + H264_NAL_UNSPECIFIED = 0, + H264_NAL_SLICE = 1, + H264_NAL_DPA = 2, + H264_NAL_DPB = 3, + H264_NAL_DPC = 4, + H264_NAL_IDR_SLICE = 5, + H264_NAL_SEI = 6, + H264_NAL_SPS = 7, + H264_NAL_PPS = 8, + H264_NAL_AUD = 9, + H264_NAL_END_SEQUENCE = 10, + H264_NAL_END_STREAM = 11, + H264_NAL_FILLER_DATA = 12, + H264_NAL_SPS_EXT = 13, + H264_NAL_PREFIX = 14, + H264_NAL_SUB_SPS = 15, + H264_NAL_DPS = 16, + H264_NAL_RESERVED17 = 17, + H264_NAL_RESERVED18 = 18, + H264_NAL_AUXILIARY_SLICE = 19, + H264_NAL_EXTEN_SLICE = 20, + H264_NAL_DEPTH_EXTEN_SLICE = 21, + H264_NAL_RESERVED22 = 22, + H264_NAL_RESERVED23 = 23, + H264_NAL_UNSPECIFIED24 = 24, + H264_NAL_UNSPECIFIED25 = 25, + H264_NAL_UNSPECIFIED26 = 26, + H264_NAL_UNSPECIFIED27 = 27, + H264_NAL_UNSPECIFIED28 = 28, + H264_NAL_UNSPECIFIED29 = 29, + H264_NAL_UNSPECIFIED30 = 30, + H264_NAL_UNSPECIFIED31 = 31, +}; + + +enum { + // 7.4.2.1.1: seq_parameter_set_id is in [0, 31]. + H264_MAX_SPS_COUNT = 32, + // 7.4.2.2: pic_parameter_set_id is in [0, 255]. + H264_MAX_PPS_COUNT = 256, + + // A.3: MaxDpbFrames is bounded above by 16. + H264_MAX_DPB_FRAMES = 16, + // 7.4.2.1.1: max_num_ref_frames is in [0, MaxDpbFrames], and + // each reference frame can have two fields. + H264_MAX_REFS = 2 * H264_MAX_DPB_FRAMES, + + // 7.4.3.1: modification_of_pic_nums_idc is not equal to 3 at most + // num_ref_idx_lN_active_minus1 + 1 times (that is, once for each + // possible reference), then equal to 3 once. + H264_MAX_RPLM_COUNT = H264_MAX_REFS + 1, + + // 7.4.3.3: in the worst case, we begin with a full short-term + // reference picture list. Each picture in turn is moved to the + // long-term list (type 3) and then discarded from there (type 2). + // Then, we set the length of the long-term list (type 4), mark + // the current picture as long-term (type 6) and terminate the + // process (type 0). + H264_MAX_MMCO_COUNT = H264_MAX_REFS * 2 + 3, + + // A.2.1, A.2.3: profiles supporting FMO constrain + // num_slice_groups_minus1 to be in [0, 7]. + H264_MAX_SLICE_GROUPS = 8, + + // E.2.2: cpb_cnt_minus1 is in [0, 31]. + H264_MAX_CPB_CNT = 32, + + // A.3: in table A-1 the highest level allows a MaxFS of 139264. + H264_MAX_MB_PIC_SIZE = 139264, + // A.3.1, A.3.2: PicWidthInMbs and PicHeightInMbs are constrained + // to be not greater than sqrt(MaxFS * 8). Hence height/width are + // bounded above by sqrt(139264 * 8) = 1055.5 macroblocks. + H264_MAX_MB_WIDTH = 1055, + H264_MAX_MB_HEIGHT = 1055, + H264_MAX_WIDTH = H264_MAX_MB_WIDTH * 16, + H264_MAX_HEIGHT = H264_MAX_MB_HEIGHT * 16, +}; + + +#endif /* AVCODEC_H264_H */ diff --git a/third-party/cbs/include/cbs/h2645_parse.h b/third-party/cbs/include/cbs/h2645_parse.h new file mode 100644 index 00000000..4b445e01 --- /dev/null +++ b/third-party/cbs/include/cbs/h2645_parse.h @@ -0,0 +1,152 @@ +/* + * H.264/HEVC common parsing code + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_H2645_PARSE_H +#define AVCODEC_H2645_PARSE_H + +#include + +#include +#include + + +/** + * CACHED_BITSTREAM_READER can only be true if it's used by a decoder + * Thus, Sunshine doesn't need to worry about it + */ +typedef struct GetBitContext { + const uint8_t *buffer, *buffer_end; +#if CACHED_BITSTREAM_READER + uint64_t cache; + unsigned bits_left; +#endif + int index; + int size_in_bits; + int size_in_bits_plus8; +} GetBitContext; + +#define MAX_MBPAIR_SIZE (256 * 1024) // a tighter bound could be calculated if someone cares about a few bytes + +typedef struct H2645NAL { + uint8_t *rbsp_buffer; + + int size; + const uint8_t *data; + + /** + * Size, in bits, of just the data, excluding the stop bit and any trailing + * padding. I.e. what HEVC calls SODB. + */ + int size_bits; + + int raw_size; + const uint8_t *raw_data; + + GetBitContext gb; + + /** + * NAL unit type + */ + int type; + + /** + * HEVC only, nuh_temporal_id_plus_1 - 1 + */ + int temporal_id; + + /* + * HEVC only, identifier of layer to which nal unit belongs + */ + int nuh_layer_id; + + int skipped_bytes; + int skipped_bytes_pos_size; + int *skipped_bytes_pos; + /** + * H.264 only, nal_ref_idc + */ + int ref_idc; +} H2645NAL; + +typedef struct H2645RBSP { + uint8_t *rbsp_buffer; + AVBufferRef *rbsp_buffer_ref; + int rbsp_buffer_alloc_size; + int rbsp_buffer_size; +} H2645RBSP; + +/* an input packet split into unescaped NAL units */ +typedef struct H2645Packet { + H2645NAL *nals; + H2645RBSP rbsp; + int nb_nals; + int nals_allocated; + unsigned nal_buffer_size; +} H2645Packet; + +/** + * Extract the raw (unescaped) bitstream. + */ +int ff_h2645_extract_rbsp(const uint8_t *src, int length, H2645RBSP *rbsp, + H2645NAL *nal, int small_padding); + +/** + * Split an input packet into NAL units. + * + * If data == raw_data holds true for a NAL unit of the returned pkt, then + * said NAL unit does not contain any emulation_prevention_three_byte and + * the data is contained in the input buffer pointed to by buf. + * Otherwise, the unescaped data is part of the rbsp_buffer described by the + * packet's H2645RBSP. + * + * If the packet's rbsp_buffer_ref is not NULL, the underlying AVBuffer must + * own rbsp_buffer. If not and rbsp_buffer is not NULL, use_ref must be 0. + * If use_ref is set, rbsp_buffer will be reference-counted and owned by + * the underlying AVBuffer of rbsp_buffer_ref. + */ +int ff_h2645_packet_split(H2645Packet *pkt, const uint8_t *buf, int length, + void *logctx, int is_nalff, int nal_length_size, + enum AVCodecID codec_id, int small_padding, int use_ref); + +/** + * Free all the allocated memory in the packet. + */ +void ff_h2645_packet_uninit(H2645Packet *pkt); + +static inline int get_nalsize(int nal_length_size, const uint8_t *buf, + int buf_size, int *buf_index, void *logctx) { + int i, nalsize = 0; + + if(*buf_index >= buf_size - nal_length_size) { + // the end of the buffer is reached, refill it + return AVERROR(EAGAIN); + } + + for(i = 0; i < nal_length_size; i++) + nalsize = ((unsigned)nalsize << 8) | buf[(*buf_index)++]; + if(nalsize <= 0 || nalsize > buf_size - *buf_index) { + av_log(logctx, AV_LOG_ERROR, + "Invalid NAL unit size (%d > %d).\n", nalsize, buf_size - *buf_index); + return AVERROR_INVALIDDATA; + } + return nalsize; +} + +#endif /* AVCODEC_H2645_PARSE_H */ diff --git a/third-party/cbs/include/cbs/hevc.h b/third-party/cbs/include/cbs/hevc.h new file mode 100644 index 00000000..76707f1e --- /dev/null +++ b/third-party/cbs/include/cbs/hevc.h @@ -0,0 +1,160 @@ +/* + * HEVC shared code + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_HEVC_H +#define AVCODEC_HEVC_H + +/** + * Table 7-1 – NAL unit type codes and NAL unit type classes in + * T-REC-H.265-201802 + */ +enum HEVCNALUnitType { + HEVC_NAL_TRAIL_N = 0, + HEVC_NAL_TRAIL_R = 1, + HEVC_NAL_TSA_N = 2, + HEVC_NAL_TSA_R = 3, + HEVC_NAL_STSA_N = 4, + HEVC_NAL_STSA_R = 5, + HEVC_NAL_RADL_N = 6, + HEVC_NAL_RADL_R = 7, + HEVC_NAL_RASL_N = 8, + HEVC_NAL_RASL_R = 9, + HEVC_NAL_VCL_N10 = 10, + HEVC_NAL_VCL_R11 = 11, + HEVC_NAL_VCL_N12 = 12, + HEVC_NAL_VCL_R13 = 13, + HEVC_NAL_VCL_N14 = 14, + HEVC_NAL_VCL_R15 = 15, + HEVC_NAL_BLA_W_LP = 16, + HEVC_NAL_BLA_W_RADL = 17, + HEVC_NAL_BLA_N_LP = 18, + HEVC_NAL_IDR_W_RADL = 19, + HEVC_NAL_IDR_N_LP = 20, + HEVC_NAL_CRA_NUT = 21, + HEVC_NAL_RSV_IRAP_VCL22 = 22, + HEVC_NAL_RSV_IRAP_VCL23 = 23, + HEVC_NAL_RSV_VCL24 = 24, + HEVC_NAL_RSV_VCL25 = 25, + HEVC_NAL_RSV_VCL26 = 26, + HEVC_NAL_RSV_VCL27 = 27, + HEVC_NAL_RSV_VCL28 = 28, + HEVC_NAL_RSV_VCL29 = 29, + HEVC_NAL_RSV_VCL30 = 30, + HEVC_NAL_RSV_VCL31 = 31, + HEVC_NAL_VPS = 32, + HEVC_NAL_SPS = 33, + HEVC_NAL_PPS = 34, + HEVC_NAL_AUD = 35, + HEVC_NAL_EOS_NUT = 36, + HEVC_NAL_EOB_NUT = 37, + HEVC_NAL_FD_NUT = 38, + HEVC_NAL_SEI_PREFIX = 39, + HEVC_NAL_SEI_SUFFIX = 40, + HEVC_NAL_RSV_NVCL41 = 41, + HEVC_NAL_RSV_NVCL42 = 42, + HEVC_NAL_RSV_NVCL43 = 43, + HEVC_NAL_RSV_NVCL44 = 44, + HEVC_NAL_RSV_NVCL45 = 45, + HEVC_NAL_RSV_NVCL46 = 46, + HEVC_NAL_RSV_NVCL47 = 47, + HEVC_NAL_UNSPEC48 = 48, + HEVC_NAL_UNSPEC49 = 49, + HEVC_NAL_UNSPEC50 = 50, + HEVC_NAL_UNSPEC51 = 51, + HEVC_NAL_UNSPEC52 = 52, + HEVC_NAL_UNSPEC53 = 53, + HEVC_NAL_UNSPEC54 = 54, + HEVC_NAL_UNSPEC55 = 55, + HEVC_NAL_UNSPEC56 = 56, + HEVC_NAL_UNSPEC57 = 57, + HEVC_NAL_UNSPEC58 = 58, + HEVC_NAL_UNSPEC59 = 59, + HEVC_NAL_UNSPEC60 = 60, + HEVC_NAL_UNSPEC61 = 61, + HEVC_NAL_UNSPEC62 = 62, + HEVC_NAL_UNSPEC63 = 63, +}; + +enum HEVCSliceType { + HEVC_SLICE_B = 0, + HEVC_SLICE_P = 1, + HEVC_SLICE_I = 2, +}; + +enum { + // 7.4.3.1: vps_max_layers_minus1 is in [0, 62]. + HEVC_MAX_LAYERS = 63, + // 7.4.3.1: vps_max_sub_layers_minus1 is in [0, 6]. + HEVC_MAX_SUB_LAYERS = 7, + // 7.4.3.1: vps_num_layer_sets_minus1 is in [0, 1023]. + HEVC_MAX_LAYER_SETS = 1024, + + // 7.4.2.1: vps_video_parameter_set_id is u(4). + HEVC_MAX_VPS_COUNT = 16, + // 7.4.3.2.1: sps_seq_parameter_set_id is in [0, 15]. + HEVC_MAX_SPS_COUNT = 16, + // 7.4.3.3.1: pps_pic_parameter_set_id is in [0, 63]. + HEVC_MAX_PPS_COUNT = 64, + + // A.4.2: MaxDpbSize is bounded above by 16. + HEVC_MAX_DPB_SIZE = 16, + // 7.4.3.1: vps_max_dec_pic_buffering_minus1[i] is in [0, MaxDpbSize - 1]. + HEVC_MAX_REFS = HEVC_MAX_DPB_SIZE, + + // 7.4.3.2.1: num_short_term_ref_pic_sets is in [0, 64]. + HEVC_MAX_SHORT_TERM_REF_PIC_SETS = 64, + // 7.4.3.2.1: num_long_term_ref_pics_sps is in [0, 32]. + HEVC_MAX_LONG_TERM_REF_PICS = 32, + + // A.3: all profiles require that CtbLog2SizeY is in [4, 6]. + HEVC_MIN_LOG2_CTB_SIZE = 4, + HEVC_MAX_LOG2_CTB_SIZE = 6, + + // E.3.2: cpb_cnt_minus1[i] is in [0, 31]. + HEVC_MAX_CPB_CNT = 32, + + // A.4.1: in table A.6 the highest level allows a MaxLumaPs of 35 651 584. + HEVC_MAX_LUMA_PS = 35651584, + // A.4.1: pic_width_in_luma_samples and pic_height_in_luma_samples are + // constrained to be not greater than sqrt(MaxLumaPs * 8). Hence height/ + // width are bounded above by sqrt(8 * 35651584) = 16888.2 samples. + HEVC_MAX_WIDTH = 16888, + HEVC_MAX_HEIGHT = 16888, + + // A.4.1: table A.6 allows at most 22 tile rows for any level. + HEVC_MAX_TILE_ROWS = 22, + // A.4.1: table A.6 allows at most 20 tile columns for any level. + HEVC_MAX_TILE_COLUMNS = 20, + + // A.4.2: table A.6 allows at most 600 slice segments for any level. + HEVC_MAX_SLICE_SEGMENTS = 600, + + // 7.4.7.1: in the worst case (tiles_enabled_flag and + // entropy_coding_sync_enabled_flag are both set), entry points can be + // placed at the beginning of every Ctb row in every tile, giving an + // upper bound of (num_tile_columns_minus1 + 1) * PicHeightInCtbsY - 1. + // Only a stream with very high resolution and perverse parameters could + // get near that, though, so set a lower limit here with the maximum + // possible value for 4K video (at most 135 16x16 Ctb rows). + HEVC_MAX_ENTRY_POINT_OFFSETS = HEVC_MAX_TILE_COLUMNS * 135, +}; + + +#endif /* AVCODEC_HEVC_H */ diff --git a/third-party/cbs/include/cbs/sei.h b/third-party/cbs/include/cbs/sei.h new file mode 100644 index 00000000..4a35109b --- /dev/null +++ b/third-party/cbs/include/cbs/sei.h @@ -0,0 +1,140 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_SEI_H +#define AVCODEC_SEI_H + +// SEI payload types form a common namespace between the H.264, H.265 +// and H.266 standards. A given payload type always has the same +// meaning, but some names have different payload types in different +// standards (e.g. scalable-nesting is 30 in H.264 but 133 in H.265). +// The content of the payload data depends on the standard, though +// many generic parts have the same interpretation everywhere (such as +// mastering-display-colour-volume and user-data-unregistered). +enum { + SEI_TYPE_BUFFERING_PERIOD = 0, + SEI_TYPE_PIC_TIMING = 1, + SEI_TYPE_PAN_SCAN_RECT = 2, + SEI_TYPE_FILLER_PAYLOAD = 3, + SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35 = 4, + SEI_TYPE_USER_DATA_UNREGISTERED = 5, + SEI_TYPE_RECOVERY_POINT = 6, + SEI_TYPE_DEC_REF_PIC_MARKING_REPETITION = 7, + SEI_TYPE_SPARE_PIC = 8, + SEI_TYPE_SCENE_INFO = 9, + SEI_TYPE_SUB_SEQ_INFO = 10, + SEI_TYPE_SUB_SEQ_LAYER_CHARACTERISTICS = 11, + SEI_TYPE_SUB_SEQ_CHARACTERISTICS = 12, + SEI_TYPE_FULL_FRAME_FREEZE = 13, + SEI_TYPE_FULL_FRAME_FREEZE_RELEASE = 14, + SEI_TYPE_FULL_FRAME_SNAPSHOT = 15, + SEI_TYPE_PROGRESSIVE_REFINEMENT_SEGMENT_START = 16, + SEI_TYPE_PROGRESSIVE_REFINEMENT_SEGMENT_END = 17, + SEI_TYPE_MOTION_CONSTRAINED_SLICE_GROUP_SET = 18, + SEI_TYPE_FILM_GRAIN_CHARACTERISTICS = 19, + SEI_TYPE_DEBLOCKING_FILTER_DISPLAY_PREFERENCE = 20, + SEI_TYPE_STEREO_VIDEO_INFO = 21, + SEI_TYPE_POST_FILTER_HINT = 22, + SEI_TYPE_TONE_MAPPING_INFO = 23, + SEI_TYPE_SCALABILITY_INFO = 24, + SEI_TYPE_SUB_PIC_SCALABLE_LAYER = 25, + SEI_TYPE_NON_REQUIRED_LAYER_REP = 26, + SEI_TYPE_PRIORITY_LAYER_INFO = 27, + SEI_TYPE_LAYERS_NOT_PRESENT_4 = 28, + SEI_TYPE_LAYER_DEPENDENCY_CHANGE = 29, + SEI_TYPE_SCALABLE_NESTING_4 = 30, + SEI_TYPE_BASE_LAYER_TEMPORAL_HRD = 31, + SEI_TYPE_QUALITY_LAYER_INTEGRITY_CHECK = 32, + SEI_TYPE_REDUNDANT_PIC_PROPERTY = 33, + SEI_TYPE_TL0_DEP_REP_INDEX = 34, + SEI_TYPE_TL_SWITCHING_POINT = 35, + SEI_TYPE_PARALLEL_DECODING_INFO = 36, + SEI_TYPE_MVC_SCALABLE_NESTING = 37, + SEI_TYPE_VIEW_SCALABILITY_INFO = 38, + SEI_TYPE_MULTIVIEW_SCENE_INFO_4 = 39, + SEI_TYPE_MULTIVIEW_ACQUISITION_INFO_4 = 40, + SEI_TYPE_NON_REQUIRED_VIEW_COMPONENT = 41, + SEI_TYPE_VIEW_DEPENDENCY_CHANGE = 42, + SEI_TYPE_OPERATION_POINTS_NOT_PRESENT = 43, + SEI_TYPE_BASE_VIEW_TEMPORAL_HRD = 44, + SEI_TYPE_FRAME_PACKING_ARRANGEMENT = 45, + SEI_TYPE_MULTIVIEW_VIEW_POSITION_4 = 46, + SEI_TYPE_DISPLAY_ORIENTATION = 47, + SEI_TYPE_MVCD_SCALABLE_NESTING = 48, + SEI_TYPE_MVCD_VIEW_SCALABILITY_INFO = 49, + SEI_TYPE_DEPTH_REPRESENTATION_INFO_4 = 50, + SEI_TYPE_THREE_DIMENSIONAL_REFERENCE_DISPLAYS_INFO_4 = 51, + SEI_TYPE_DEPTH_TIMING = 52, + SEI_TYPE_DEPTH_SAMPLING_INFO = 53, + SEI_TYPE_CONSTRAINED_DEPTH_PARAMETER_SET_IDENTIFIER = 54, + SEI_TYPE_GREEN_METADATA = 56, + SEI_TYPE_STRUCTURE_OF_PICTURES_INFO = 128, + SEI_TYPE_ACTIVE_PARAMETER_SETS = 129, + SEI_TYPE_PARAMETER_SETS_INCLUSION_INDICATION = SEI_TYPE_ACTIVE_PARAMETER_SETS, + SEI_TYPE_DECODING_UNIT_INFO = 130, + SEI_TYPE_TEMPORAL_SUB_LAYER_ZERO_IDX = 131, + SEI_TYPE_DECODED_PICTURE_HASH = 132, + SEI_TYPE_SCALABLE_NESTING_5 = 133, + SEI_TYPE_REGION_REFRESH_INFO = 134, + SEI_TYPE_NO_DISPLAY = 135, + SEI_TYPE_TIME_CODE = 136, + SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME = 137, + SEI_TYPE_SEGMENTED_RECT_FRAME_PACKING_ARRANGEMENT = 138, + SEI_TYPE_TEMPORAL_MOTION_CONSTRAINED_TILE_SETS = 139, + SEI_TYPE_CHROMA_RESAMPLING_FILTER_HINT = 140, + SEI_TYPE_KNEE_FUNCTION_INFO = 141, + SEI_TYPE_COLOUR_REMAPPING_INFO = 142, + SEI_TYPE_DEINTERLACED_FIELD_IDENTIFICATION = 143, + SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO = 144, + SEI_TYPE_DEPENDENT_RAP_INDICATION = 145, + SEI_TYPE_CODED_REGION_COMPLETION = 146, + SEI_TYPE_ALTERNATIVE_TRANSFER_CHARACTERISTICS = 147, + SEI_TYPE_AMBIENT_VIEWING_ENVIRONMENT = 148, + SEI_TYPE_CONTENT_COLOUR_VOLUME = 149, + SEI_TYPE_EQUIRECTANGULAR_PROJECTION = 150, + SEI_TYPE_CUBEMAP_PROJECTION = 151, + SEI_TYPE_FISHEYE_VIDEO_INFO = 152, + SEI_TYPE_SPHERE_ROTATION = 154, + SEI_TYPE_REGIONWISE_PACKING = 155, + SEI_TYPE_OMNI_VIEWPORT = 156, + SEI_TYPE_REGIONAL_NESTING = 157, + SEI_TYPE_MCTS_EXTRACTION_INFO_SETS = 158, + SEI_TYPE_MCTS_EXTRACTION_INFO_NESTING = 159, + SEI_TYPE_LAYERS_NOT_PRESENT_5 = 160, + SEI_TYPE_INTER_LAYER_CONSTRAINED_TILE_SETS = 161, + SEI_TYPE_BSP_NESTING = 162, + SEI_TYPE_BSP_INITIAL_ARRIVAL_TIME = 163, + SEI_TYPE_SUB_BITSTREAM_PROPERTY = 164, + SEI_TYPE_ALPHA_CHANNEL_INFO = 165, + SEI_TYPE_OVERLAY_INFO = 166, + SEI_TYPE_TEMPORAL_MV_PREDICTION_CONSTRAINTS = 167, + SEI_TYPE_FRAME_FIELD_INFO = 168, + SEI_TYPE_THREE_DIMENSIONAL_REFERENCE_DISPLAYS_INFO = 176, + SEI_TYPE_DEPTH_REPRESENTATION_INFO_5 = 177, + SEI_TYPE_MULTIVIEW_SCENE_INFO_5 = 178, + SEI_TYPE_MULTIVIEW_ACQUISITION_INFO_5 = 179, + SEI_TYPE_MULTIVIEW_VIEW_POSITION_5 = 180, + SEI_TYPE_ALTERNATIVE_DEPTH_INFO = 181, + SEI_TYPE_SEI_MANIFEST = 200, + SEI_TYPE_SEI_PREFIX_INDICATION = 201, + SEI_TYPE_ANNOTATED_REGIONS = 202, + SEI_TYPE_SUBPIC_LEVEL_INFO = 203, + SEI_TYPE_SAMPLE_ASPECT_RATIO_INFO = 204, +}; + +#endif /* AVCODEC_SEI_H */ diff --git a/third-party/cbs/include/cbs/video_levels.h b/third-party/cbs/include/cbs/video_levels.h new file mode 100644 index 00000000..53ad277f --- /dev/null +++ b/third-party/cbs/include/cbs/video_levels.h @@ -0,0 +1,112 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_H264_LEVELS_H +#define AVCODEC_H264_LEVELS_H + + +#include + +#include "cbs_h265.h" + +typedef struct H265LevelDescriptor { + const char *name; + uint8_t level_idc; + + // Table A.6. + uint32_t max_luma_ps; + uint32_t max_cpb_main; + uint32_t max_cpb_high; + uint16_t max_slice_segments_per_picture; + uint8_t max_tile_rows; + uint8_t max_tile_cols; + + // Table A.7. + uint32_t max_luma_sr; + uint32_t max_br_main; + uint32_t max_br_high; + uint8_t min_cr_base_main; + uint8_t min_cr_base_high; +} H265LevelDescriptor; + +typedef struct H265ProfileDescriptor { + const char *name; + uint8_t profile_idc; + uint8_t high_throughput; + + // Tables A.2, A.3 and A.5. + uint8_t max_14bit; + uint8_t max_12bit; + uint8_t max_10bit; + uint8_t max_8bit; + uint8_t max_422chroma; + uint8_t max_420chroma; + uint8_t max_monochrome; + uint8_t intra; + uint8_t one_picture_only; + uint8_t lower_bit_rate; + + // Table A.8. + uint16_t cpb_vcl_factor; + uint16_t cpb_nal_factor; + float format_capability_factor; + float min_cr_scale_factor; + uint8_t max_dpb_pic_buf; +} H265ProfileDescriptor; + +typedef struct H264LevelDescriptor { + const char *name; + uint8_t level_idc; + uint8_t constraint_set3_flag; + uint32_t max_mbps; + uint32_t max_fs; + uint32_t max_dpb_mbs; + uint32_t max_br; + uint32_t max_cpb; + uint16_t max_v_mv_r; + uint8_t min_cr; + uint8_t max_mvs_per_2mb; +} H264LevelDescriptor; + +const H265ProfileDescriptor *ff_h265_get_profile(const H265RawProfileTierLevel *ptl); + +/** + * Guess the level of a stream from some parameters. + * + * Unknown parameters may be zero, in which case they are ignored. + */ +const H265LevelDescriptor *ff_h265_guess_level(const H265RawProfileTierLevel *ptl, + int64_t bitrate, + int width, int height, + int slice_segments, + int tile_rows, int tile_cols, + int max_dec_pic_buffering); + +/** + * Guess the level of a stream from some parameters. + * + * Unknown parameters may be zero, in which case they are ignored. + */ +const H264LevelDescriptor *ff_h264_guess_level(int profile_idc, + int64_t bitrate, + int framerate, + int width, int height, + int max_dec_frame_buffering); + + +#endif /* AVCODEC_H264_LEVELS_H */ diff --git a/third-party/cbs/intmath.h b/third-party/cbs/intmath.h new file mode 100644 index 00000000..377b7412 --- /dev/null +++ b/third-party/cbs/intmath.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2010 Mans Rullgard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_INTMATH_H +#define AVUTIL_INTMATH_H + +#include + +#include + +#if HAVE_FAST_CLZ +#if AV_GCC_VERSION_AT_LEAST(3, 4) +#ifndef ff_log2 +#define ff_log2(x) (31 - __builtin_clz((x) | 1)) +#ifndef ff_log2_16bit +#define ff_log2_16bit av_log2 +#endif +#endif /* ff_log2 */ +#endif /* AV_GCC_VERSION_AT_LEAST(3,4) */ +#endif + +extern const uint8_t ff_log2_tab[256]; + +#ifndef ff_log2 +#define ff_log2 ff_log2_c +static av_always_inline av_const int ff_log2_c(unsigned int v) { + int n = 0; + if(v & 0xffff0000) { + v >>= 16; + n += 16; + } + if(v & 0xff00) { + v >>= 8; + n += 8; + } + n += ff_log2_tab[v]; + + return n; +} +#endif + +#ifndef ff_log2_16bit +#define ff_log2_16bit ff_log2_16bit_c +static av_always_inline av_const int ff_log2_16bit_c(unsigned int v) { + int n = 0; + if(v & 0xff00) { + v >>= 8; + n += 8; + } + n += ff_log2_tab[v]; + + return n; +} +#endif + +#define av_log2 ff_log2 +#define av_log2_16bit ff_log2_16bit + +/** + * @addtogroup lavu_math + * @{ + */ + +#if HAVE_FAST_CLZ +#if AV_GCC_VERSION_AT_LEAST(3, 4) +#ifndef ff_ctz +#define ff_ctz(v) __builtin_ctz(v) +#endif +#ifndef ff_ctzll +#define ff_ctzll(v) __builtin_ctzll(v) +#endif +#ifndef ff_clz +#define ff_clz(v) __builtin_clz(v) +#endif +#endif +#endif + +#ifndef ff_ctz +#define ff_ctz ff_ctz_c +/** + * Trailing zero bit count. + * + * @param v input value. If v is 0, the result is undefined. + * @return the number of trailing 0-bits + */ +/* We use the De-Bruijn method outlined in: + * http://supertech.csail.mit.edu/papers/debruijn.pdf. */ +static av_always_inline av_const int ff_ctz_c(int v) { + static const uint8_t debruijn_ctz32[32] = { + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 + }; + return debruijn_ctz32[(uint32_t)((v & -v) * 0x077CB531U) >> 27]; +} +#endif + +#ifndef ff_ctzll +#define ff_ctzll ff_ctzll_c +/* We use the De-Bruijn method outlined in: + * http://supertech.csail.mit.edu/papers/debruijn.pdf. */ +static av_always_inline av_const int ff_ctzll_c(long long v) { + static const uint8_t debruijn_ctz64[64] = { + 0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28, + 62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11, + 63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10, + 51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12 + }; + return debruijn_ctz64[(uint64_t)((v & -v) * 0x022FDD63CC95386DU) >> 58]; +} +#endif + +#ifndef ff_clz +#define ff_clz ff_clz_c +static av_always_inline av_const unsigned ff_clz_c(unsigned x) { + unsigned i = sizeof(x) * 8; + + while(x) { + x >>= 1; + i--; + } + + return i; +} +#endif + +#if AV_GCC_VERSION_AT_LEAST(3, 4) +#ifndef av_parity +#define av_parity __builtin_parity +#endif +#endif + +/** + * @} + */ +#endif /* AVUTIL_INTMATH_H */ diff --git a/third-party/cbs/mathops.h b/third-party/cbs/mathops.h new file mode 100644 index 00000000..c0b8f3db --- /dev/null +++ b/third-party/cbs/mathops.h @@ -0,0 +1,243 @@ +/* + * simple math operations + * Copyright (c) 2001, 2002 Fabrice Bellard + * Copyright (c) 2006 Michael Niedermayer et al + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef AVCODEC_MATHOPS_H +#define AVCODEC_MATHOPS_H + +#include "config.h" + +#include + +#include + +#define MAX_NEG_CROP 1024 + +extern const uint8_t ff_reverse[256]; +extern const uint32_t ff_inverse[257]; +extern const uint8_t ff_sqrt_tab[256]; +extern const uint8_t ff_crop_tab[256 + 2 * MAX_NEG_CROP]; +extern const uint8_t ff_zigzag_direct[64]; +extern const uint8_t ff_zigzag_scan[16 + 1]; + +#ifndef MUL64 +#define MUL64(a, b) ((int64_t)(a) * (int64_t)(b)) +#endif + +#ifndef MULL +#define MULL(a, b, s) (MUL64(a, b) >> (s)) +#endif + +#ifndef MULH +static av_always_inline int MULH(int a, int b) { + return MUL64(a, b) >> 32; +} +#endif + +#ifndef UMULH +static av_always_inline unsigned UMULH(unsigned a, unsigned b) { + return ((uint64_t)(a) * (uint64_t)(b)) >> 32; +} +#endif + +#ifndef MAC64 +#define MAC64(d, a, b) ((d) += MUL64(a, b)) +#endif + +#ifndef MLS64 +#define MLS64(d, a, b) ((d) -= MUL64(a, b)) +#endif + +/* signed 16x16 -> 32 multiply add accumulate */ +#ifndef MAC16 +#define MAC16(rt, ra, rb) rt += (ra) * (rb) +#endif + +/* signed 16x16 -> 32 multiply */ +#ifndef MUL16 +#define MUL16(ra, rb) ((ra) * (rb)) +#endif + +#ifndef MLS16 +#define MLS16(rt, ra, rb) ((rt) -= (ra) * (rb)) +#endif + +/* median of 3 */ +#ifndef mid_pred +#define mid_pred mid_pred +static inline av_const int mid_pred(int a, int b, int c) { + if(a > b) { + if(c > b) { + if(c > a) b = a; + else + b = c; + } + } + else { + if(b > c) { + if(c > a) b = c; + else + b = a; + } + } + return b; +} +#endif + +#ifndef median4 +#define median4 median4 +static inline av_const int median4(int a, int b, int c, int d) { + if(a < b) { + if(c < d) return (FFMIN(b, d) + FFMAX(a, c)) / 2; + else + return (FFMIN(b, c) + FFMAX(a, d)) / 2; + } + else { + if(c < d) return (FFMIN(a, d) + FFMAX(b, c)) / 2; + else + return (FFMIN(a, c) + FFMAX(b, d)) / 2; + } +} +#endif + +#ifndef sign_extend +static inline av_const int sign_extend(int val, unsigned bits) { + unsigned shift = 8 * sizeof(int) - bits; + union { + unsigned u; + int s; + } v = { (unsigned)val << shift }; + return v.s >> shift; +} +#endif + +#ifndef zero_extend +static inline av_const unsigned zero_extend(unsigned val, unsigned bits) { + return (val << ((8 * sizeof(int)) - bits)) >> ((8 * sizeof(int)) - bits); +} +#endif + +#ifndef COPY3_IF_LT +#define COPY3_IF_LT(x, y, a, b, c, d) \ + if((y) < (x)) { \ + (x) = (y); \ + (a) = (b); \ + (c) = (d); \ + } +#endif + +#ifndef MASK_ABS +#define MASK_ABS(mask, level) \ + do { \ + mask = level >> 31; \ + level = (level ^ mask) - mask; \ + } while(0) +#endif + +#ifndef NEG_SSR32 +#define NEG_SSR32(a, s) (((int32_t)(a)) >> (32 - (s))) +#endif + +#ifndef NEG_USR32 +#define NEG_USR32(a, s) (((uint32_t)(a)) >> (32 - (s))) +#endif + +#if HAVE_BIGENDIAN +#ifndef PACK_2U8 +#define PACK_2U8(a, b) (((a) << 8) | (b)) +#endif +#ifndef PACK_4U8 +#define PACK_4U8(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) +#endif +#ifndef PACK_2U16 +#define PACK_2U16(a, b) (((a) << 16) | (b)) +#endif +#else +#ifndef PACK_2U8 +#define PACK_2U8(a, b) (((b) << 8) | (a)) +#endif +#ifndef PACK_4U2 +#define PACK_4U8(a, b, c, d) (((d) << 24) | ((c) << 16) | ((b) << 8) | (a)) +#endif +#ifndef PACK_2U16 +#define PACK_2U16(a, b) (((b) << 16) | (a)) +#endif +#endif + +#ifndef PACK_2S8 +#define PACK_2S8(a, b) PACK_2U8((a)&255, (b)&255) +#endif +#ifndef PACK_4S8 +#define PACK_4S8(a, b, c, d) PACK_4U8((a)&255, (b)&255, (c)&255, (d)&255) +#endif +#ifndef PACK_2S16 +#define PACK_2S16(a, b) PACK_2U16((a)&0xffff, (b)&0xffff) +#endif + +#ifndef FASTDIV +#define FASTDIV(a, b) ((uint32_t)((((uint64_t)a) * ff_inverse[b]) >> 32)) +#endif /* FASTDIV */ + +#ifndef ff_sqrt +#define ff_sqrt ff_sqrt +static inline av_const unsigned int ff_sqrt(unsigned int a) { + unsigned int b; + + if(a < 255) return (ff_sqrt_tab[a + 1] - 1) >> 4; + else if(a < (1 << 12)) + b = ff_sqrt_tab[a >> 4] >> 2; +#if !CONFIG_SMALL + else if(a < (1 << 14)) + b = ff_sqrt_tab[a >> 6] >> 1; + else if(a < (1 << 16)) + b = ff_sqrt_tab[a >> 8]; +#endif + else { + int s = av_log2_16bit(a >> 16) >> 1; + unsigned int c = a >> (s + 2); + b = ff_sqrt_tab[c >> (s + 8)]; + b = FASTDIV(c, b) + (b << s); + } + + return b - (a < b * b); +} +#endif + +static inline av_const float ff_sqrf(float a) { + return a * a; +} + +static inline int8_t ff_u8_to_s8(uint8_t a) { + union { + uint8_t u8; + int8_t s8; + } b; + b.u8 = a; + return b.s8; +} + +static av_always_inline uint32_t bitswap_32(uint32_t x) { + return (uint32_t)ff_reverse[x & 0xFF] << 24 | + (uint32_t)ff_reverse[(x >> 8) & 0xFF] << 16 | + (uint32_t)ff_reverse[(x >> 16) & 0xFF] << 8 | + (uint32_t)ff_reverse[x >> 24]; +} + +#endif /* AVCODEC_MATHOPS_H */ diff --git a/third-party/cbs/put_bits.h b/third-party/cbs/put_bits.h new file mode 100644 index 00000000..5d3f96c4 --- /dev/null +++ b/third-party/cbs/put_bits.h @@ -0,0 +1,406 @@ +/* + * copyright (c) 2004 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * bitstream writer API + */ + +#ifndef AVCODEC_PUT_BITS_H +#define AVCODEC_PUT_BITS_H + +#include "config.h" + +#include +#include + +#include +#include + +#if HAVE_FAST_64BIT +typedef uint64_t BitBuf; +#define AV_WBBUF AV_WB64 +#define AV_WLBUF AV_WL64 +#else +typedef uint32_t BitBuf; +#define AV_WBBUF AV_WB32 +#define AV_WLBUF AV_WL32 +#endif + +static const int BUF_BITS = 8 * sizeof(BitBuf); + +typedef struct PutBitContext { + BitBuf bit_buf; + int bit_left; + uint8_t *buf, *buf_ptr, *buf_end; +} PutBitContext; + +/** + * Initialize the PutBitContext s. + * + * @param buffer the buffer where to put bits + * @param buffer_size the size in bytes of buffer + */ +static inline void init_put_bits(PutBitContext *s, uint8_t *buffer, + int buffer_size) { + if(buffer_size < 0) { + buffer_size = 0; + buffer = NULL; + } + + s->buf = buffer; + s->buf_end = s->buf + buffer_size; + s->buf_ptr = s->buf; + s->bit_left = BUF_BITS; + s->bit_buf = 0; +} + +/** + * @return the total number of bits written to the bitstream. + */ +static inline int put_bits_count(PutBitContext *s) { + return (s->buf_ptr - s->buf) * 8 + BUF_BITS - s->bit_left; +} + +/** + * @return the number of bytes output so far; may only be called + * when the PutBitContext is freshly initialized or flushed. + */ +static inline int put_bytes_output(const PutBitContext *s) { + av_assert2(s->bit_left == BUF_BITS); + return s->buf_ptr - s->buf; +} + +/** + * @param round_up When set, the number of bits written so far will be + * rounded up to the next byte. + * @return the number of bytes output so far. + */ +static inline int put_bytes_count(const PutBitContext *s, int round_up) { + return s->buf_ptr - s->buf + ((BUF_BITS - s->bit_left + (round_up ? 7 : 0)) >> 3); +} + +/** + * Rebase the bit writer onto a reallocated buffer. + * + * @param buffer the buffer where to put bits + * @param buffer_size the size in bytes of buffer, + * must be large enough to hold everything written so far + */ +static inline void rebase_put_bits(PutBitContext *s, uint8_t *buffer, + int buffer_size) { + av_assert0(8 * buffer_size >= put_bits_count(s)); + + s->buf_end = buffer + buffer_size; + s->buf_ptr = buffer + (s->buf_ptr - s->buf); + s->buf = buffer; +} + +/** + * @return the number of bits available in the bitstream. + */ +static inline int put_bits_left(PutBitContext *s) { + return (s->buf_end - s->buf_ptr) * 8 - BUF_BITS + s->bit_left; +} + +/** + * @param round_up When set, the number of bits written will be + * rounded up to the next byte. + * @return the number of bytes left. + */ +static inline int put_bytes_left(const PutBitContext *s, int round_up) { + return s->buf_end - s->buf_ptr - ((BUF_BITS - s->bit_left + (round_up ? 7 : 0)) >> 3); +} + +/** + * Pad the end of the output stream with zeros. + */ +static inline void flush_put_bits(PutBitContext *s) { +#ifndef BITSTREAM_WRITER_LE + if(s->bit_left < BUF_BITS) + s->bit_buf <<= s->bit_left; +#endif + while(s->bit_left < BUF_BITS) { + av_assert0(s->buf_ptr < s->buf_end); +#ifdef BITSTREAM_WRITER_LE + *s->buf_ptr++ = s->bit_buf; + s->bit_buf >>= 8; +#else + *s->buf_ptr++ = s->bit_buf >> (BUF_BITS - 8); + s->bit_buf <<= 8; +#endif + s->bit_left += 8; + } + s->bit_left = BUF_BITS; + s->bit_buf = 0; +} + +static inline void flush_put_bits_le(PutBitContext *s) { + while(s->bit_left < BUF_BITS) { + av_assert0(s->buf_ptr < s->buf_end); + *s->buf_ptr++ = s->bit_buf; + s->bit_buf >>= 8; + s->bit_left += 8; + } + s->bit_left = BUF_BITS; + s->bit_buf = 0; +} + +#ifdef BITSTREAM_WRITER_LE +#define ff_put_string ff_put_string_unsupported_here +#define ff_copy_bits ff_copy_bits_unsupported_here +#else + +/** + * Put the string string in the bitstream. + * + * @param terminate_string 0-terminates the written string if value is 1 + */ +void ff_put_string(PutBitContext *pb, const char *string, + int terminate_string); + +/** + * Copy the content of src to the bitstream. + * + * @param length the number of bits of src to copy + */ +void ff_copy_bits(PutBitContext *pb, const uint8_t *src, int length); +#endif + +static inline void put_bits_no_assert(PutBitContext *s, int n, BitBuf value) { + BitBuf bit_buf; + int bit_left; + + bit_buf = s->bit_buf; + bit_left = s->bit_left; + + /* XXX: optimize */ +#ifdef BITSTREAM_WRITER_LE + bit_buf |= value << (BUF_BITS - bit_left); + if(n >= bit_left) { + if(s->buf_end - s->buf_ptr >= sizeof(BitBuf)) { + AV_WLBUF(s->buf_ptr, bit_buf); + s->buf_ptr += sizeof(BitBuf); + } + else { + av_log(NULL, AV_LOG_ERROR, "Internal error, put_bits buffer too small\n"); + av_assert2(0); + } + bit_buf = value >> bit_left; + bit_left += BUF_BITS; + } + bit_left -= n; +#else + if(n < bit_left) { + bit_buf = (bit_buf << n) | value; + bit_left -= n; + } + else { + bit_buf <<= bit_left; + bit_buf |= value >> (n - bit_left); + if(s->buf_end - s->buf_ptr >= sizeof(BitBuf)) { + AV_WBBUF(s->buf_ptr, bit_buf); + s->buf_ptr += sizeof(BitBuf); + } + else { + av_log(NULL, AV_LOG_ERROR, "Internal error, put_bits buffer too small\n"); + av_assert2(0); + } + bit_left += BUF_BITS - n; + bit_buf = value; + } +#endif + + s->bit_buf = bit_buf; + s->bit_left = bit_left; +} + +/** + * Write up to 31 bits into a bitstream. + * Use put_bits32 to write 32 bits. + */ +static inline void put_bits(PutBitContext *s, int n, BitBuf value) { + av_assert2(n <= 31 && value < (1UL << n)); + put_bits_no_assert(s, n, value); +} + +static inline void put_bits_le(PutBitContext *s, int n, BitBuf value) { + BitBuf bit_buf; + int bit_left; + + av_assert2(n <= 31 && value < (1UL << n)); + + bit_buf = s->bit_buf; + bit_left = s->bit_left; + + bit_buf |= value << (BUF_BITS - bit_left); + if(n >= bit_left) { + if(s->buf_end - s->buf_ptr >= sizeof(BitBuf)) { + AV_WLBUF(s->buf_ptr, bit_buf); + s->buf_ptr += sizeof(BitBuf); + } + else { + av_log(NULL, AV_LOG_ERROR, "Internal error, put_bits buffer too small\n"); + av_assert2(0); + } + bit_buf = value >> bit_left; + bit_left += BUF_BITS; + } + bit_left -= n; + + s->bit_buf = bit_buf; + s->bit_left = bit_left; +} + +static inline void put_sbits(PutBitContext *pb, int n, int32_t value) { + av_assert2(n >= 0 && n <= 31); + + put_bits(pb, n, av_mod_uintp2(value, n)); +} + +/** + * Write exactly 32 bits into a bitstream. + */ +static void av_unused put_bits32(PutBitContext *s, uint32_t value) { + BitBuf bit_buf; + int bit_left; + + if(BUF_BITS > 32) { + put_bits_no_assert(s, 32, value); + return; + } + + bit_buf = s->bit_buf; + bit_left = s->bit_left; + +#ifdef BITSTREAM_WRITER_LE + bit_buf |= (BitBuf)value << (BUF_BITS - bit_left); + if(s->buf_end - s->buf_ptr >= sizeof(BitBuf)) { + AV_WLBUF(s->buf_ptr, bit_buf); + s->buf_ptr += sizeof(BitBuf); + } + else { + av_log(NULL, AV_LOG_ERROR, "Internal error, put_bits buffer too small\n"); + av_assert2(0); + } + bit_buf = (uint64_t)value >> bit_left; +#else + bit_buf = (uint64_t)bit_buf << bit_left; + bit_buf |= (BitBuf)value >> (BUF_BITS - bit_left); + if(s->buf_end - s->buf_ptr >= sizeof(BitBuf)) { + AV_WBBUF(s->buf_ptr, bit_buf); + s->buf_ptr += sizeof(BitBuf); + } + else { + av_log(NULL, AV_LOG_ERROR, "Internal error, put_bits buffer too small\n"); + av_assert2(0); + } + bit_buf = value; +#endif + + s->bit_buf = bit_buf; + s->bit_left = bit_left; +} + +/** + * Write up to 64 bits into a bitstream. + */ +static inline void put_bits64(PutBitContext *s, int n, uint64_t value) { + av_assert2((n == 64) || (n < 64 && value < (UINT64_C(1) << n))); + + if(n < 32) + put_bits(s, n, value); + else if(n == 32) + put_bits32(s, value); + else if(n < 64) { + uint32_t lo = value & 0xffffffff; + uint32_t hi = value >> 32; +#ifdef BITSTREAM_WRITER_LE + put_bits32(s, lo); + put_bits(s, n - 32, hi); +#else + put_bits(s, n - 32, hi); + put_bits32(s, lo); +#endif + } + else { + uint32_t lo = value & 0xffffffff; + uint32_t hi = value >> 32; +#ifdef BITSTREAM_WRITER_LE + put_bits32(s, lo); + put_bits32(s, hi); +#else + put_bits32(s, hi); + put_bits32(s, lo); +#endif + } +} + +/** + * Return the pointer to the byte where the bitstream writer will put + * the next bit. + */ +static inline uint8_t *put_bits_ptr(PutBitContext *s) { + return s->buf_ptr; +} + +/** + * Skip the given number of bytes. + * PutBitContext must be flushed & aligned to a byte boundary before calling this. + */ +static inline void skip_put_bytes(PutBitContext *s, int n) { + av_assert2((put_bits_count(s) & 7) == 0); + av_assert2(s->bit_left == BUF_BITS); + av_assert0(n <= s->buf_end - s->buf_ptr); + s->buf_ptr += n; +} + +/** + * Skip the given number of bits. + * Must only be used if the actual values in the bitstream do not matter. + * If n is < 0 the behavior is undefined. + */ +static inline void skip_put_bits(PutBitContext *s, int n) { + unsigned bits = BUF_BITS - s->bit_left + n; + s->buf_ptr += sizeof(BitBuf) * (bits / BUF_BITS); + s->bit_left = BUF_BITS - (bits & (BUF_BITS - 1)); +} + +/** + * Change the end of the buffer. + * + * @param size the new size in bytes of the buffer where to put bits + */ +static inline void set_put_bits_buffer_size(PutBitContext *s, int size) { + av_assert0(size <= INT_MAX / 8 - BUF_BITS); + s->buf_end = s->buf + size; +} + +/** + * Pad the bitstream with zeros up to the next byte boundary. + */ +static inline void align_put_bits(PutBitContext *s) { + put_bits(s, s->bit_left & 7, 0); +} + +#undef AV_WBBUF +#undef AV_WLBUF + +#endif /* AVCODEC_PUT_BITS_H */ diff --git a/third-party/cbs/video_levels.c b/third-party/cbs/video_levels.c new file mode 100644 index 00000000..24143dfc --- /dev/null +++ b/third-party/cbs/video_levels.c @@ -0,0 +1,349 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "include/cbs/video_levels.h" + +// H.264 table A-1. +static const H264LevelDescriptor h264_levels[] = { + // Name MaxMBPS MaxBR MinCR + // | level_idc | MaxFS | MaxCPB | MaxMvsPer2Mb + // | | cs3f | | MaxDpbMbs | | MaxVmvR | | + { "1", 10, 0, 1485, 99, 396, 64, 175, 64, 2, 0 }, + { "1b", 11, 1, 1485, 99, 396, 128, 350, 64, 2, 0 }, + { "1b", 9, 0, 1485, 99, 396, 128, 350, 64, 2, 0 }, + { "1.1", 11, 0, 3000, 396, 900, 192, 500, 128, 2, 0 }, + { "1.2", 12, 0, 6000, 396, 2376, 384, 1000, 128, 2, 0 }, + { "1.3", 13, 0, 11880, 396, 2376, 768, 2000, 128, 2, 0 }, + { "2", 20, 0, 11880, 396, 2376, 2000, 2000, 128, 2, 0 }, + { "2.1", 21, 0, 19800, 792, 4752, 4000, 4000, 256, 2, 0 }, + { "2.2", 22, 0, 20250, 1620, 8100, 4000, 4000, 256, 2, 0 }, + { "3", 30, 0, 40500, 1620, 8100, 10000, 10000, 256, 2, 32 }, + { "3.1", 31, 0, 108000, 3600, 18000, 14000, 14000, 512, 4, 16 }, + { "3.2", 32, 0, 216000, 5120, 20480, 20000, 20000, 512, 4, 16 }, + { "4", 40, 0, 245760, 8192, 32768, 20000, 25000, 512, 4, 16 }, + { "4.1", 41, 0, 245760, 8192, 32768, 50000, 62500, 512, 2, 16 }, + { "4.2", 42, 0, 522240, 8704, 34816, 50000, 62500, 512, 2, 16 }, + { "5", 50, 0, 589824, 22080, 110400, 135000, 135000, 512, 2, 16 }, + { "5.1", 51, 0, 983040, 36864, 184320, 240000, 240000, 512, 2, 16 }, + { "5.2", 52, 0, 2073600, 36864, 184320, 240000, 240000, 512, 2, 16 }, + { "6", 60, 0, 4177920, 139264, 696320, 240000, 240000, 8192, 2, 16 }, + { "6.1", 61, 0, 8355840, 139264, 696320, 480000, 480000, 8192, 2, 16 }, + { "6.2", 62, 0, 16711680, 139264, 696320, 800000, 800000, 8192, 2, 16 }, +}; + +// H.264 table A-2 plus values from A-1. +static const struct { + int profile_idc; + int cpb_br_vcl_factor; + int cpb_br_nal_factor; +} h264_br_factors[] = { + { 66, 1000, 1200 }, + { 77, 1000, 1200 }, + { 88, 1000, 1200 }, + { 100, 1250, 1500 }, + { 110, 3000, 3600 }, + { 122, 4000, 4800 }, + { 244, 4000, 4800 }, + { 44, 4000, 4800 }, +}; + +// We are only ever interested in the NAL bitrate factor. +static int h264_get_br_factor(int profile_idc) { + int i; + for(i = 0; i < FF_ARRAY_ELEMS(h264_br_factors); i++) { + if(h264_br_factors[i].profile_idc == profile_idc) + return h264_br_factors[i].cpb_br_nal_factor; + } + // Default to the non-high profile value if not specified. + return 1200; +} + +const H264LevelDescriptor *ff_h264_guess_level(int profile_idc, + int64_t bitrate, + int framerate, + int width, int height, + int max_dec_frame_buffering) { + int width_mbs = (width + 15) / 16; + int height_mbs = (height + 15) / 16; + int no_cs3f = !(profile_idc == 66 || + profile_idc == 77 || + profile_idc == 88); + int i; + + for(i = 0; i < FF_ARRAY_ELEMS(h264_levels); i++) { + const H264LevelDescriptor *level = &h264_levels[i]; + + if(level->constraint_set3_flag && no_cs3f) + continue; + + if(bitrate > (int64_t)level->max_br * h264_get_br_factor(profile_idc)) + continue; + + if(width_mbs * height_mbs > level->max_fs) + continue; + if(width_mbs * width_mbs > 8 * level->max_fs) + continue; + if(height_mbs * height_mbs > 8 * level->max_fs) + continue; + + if(width_mbs && height_mbs) { + int max_dpb_frames = + FFMIN(level->max_dpb_mbs / (width_mbs * height_mbs), 16); + if(max_dec_frame_buffering > max_dpb_frames) + continue; + + if(framerate > (level->max_mbps / (width_mbs * height_mbs))) + continue; + } + + return level; + } + + // No usable levels found - frame is too big or bitrate is too high. + return NULL; +} + +static const H265LevelDescriptor h265_levels[] = { + // Name CpbFactor-Main MaxSliceSegmentsPerPicture + // | level_idc | CpbFactor-High MaxLumaSr BrFactor-High + // | | MaxLumaPs | | | MaxTileRows | BrFactor-Main | MinCr-Main + // | | | | | | | MaxTileCols | | | MinCr-High + { "1", 30, 36864, 350, 0, 16, 1, 1, 552960, 128, 0, 2, 2 }, + { "2", 60, 122880, 1500, 0, 16, 1, 1, 3686400, 1500, 0, 2, 2 }, + { "2.1", 63, 245760, 3000, 0, 20, 1, 1, 7372800, 3000, 0, 2, 2 }, + { "3", 90, 552960, 6000, 0, 30, 2, 2, 16588800, 6000, 0, 2, 2 }, + { "3.1", 93, 983040, 10000, 0, 40, 3, 3, 33177600, 10000, 0, 2, 2 }, + { "4", 120, 2228224, 12000, 30000, 75, 5, 5, 66846720, 12000, 30000, 4, 4 }, + { "4.1", 123, 2228224, 20000, 50000, 75, 5, 5, 133693440, 20000, 50000, 4, 4 }, + { "5", 150, 8912896, 25000, 100000, 200, 11, 10, 267386880, 25000, 100000, 6, 4 }, + { "5.1", 153, 8912896, 40000, 160000, 200, 11, 10, 534773760, 40000, 160000, 8, 4 }, + { "5.2", 156, 8912896, 60000, 240000, 200, 11, 10, 1069547520, 60000, 240000, 8, 4 }, + { "6", 180, 35651584, 60000, 240000, 600, 22, 20, 1069547520, 60000, 240000, 8, 4 }, + { "6.1", 183, 35651584, 120000, 480000, 600, 22, 20, 2139095040, 120000, 480000, 8, 4 }, + { "6.2", 186, 35651584, 240000, 800000, 600, 22, 20, 4278190080, 240000, 800000, 6, 4 }, +}; + +static const H265ProfileDescriptor h265_profiles[] = { + // profile_idc 8bit one-picture + // HT-profile | 422chroma | lower-bit-rate + // | 14bit | | 420chroma | | CpbVclFactor MinCrScaleFactor + // | | 12bit | | | monochrome| | CpbNalFactor | maxDpbPicBuf + // | | | 10bit | | | intra | | | FormatCapabilityFactor + { "Monochrome", // | | | | | | | | | | | + 4, 0, 2, 1, 1, 1, 1, 1, 1, 0, 0, 1, 667, 733, 1.000, 1.0, 6 }, + { "Monochrome 10", + 4, 0, 2, 1, 1, 0, 1, 1, 1, 0, 0, 1, 833, 917, 1.250, 1.0, 6 }, + { "Monochrome 12", + 4, 0, 2, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1000, 1100, 1.500, 1.0, 6 }, + { "Monochrome 16", + 4, 0, 2, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1333, 1467, 2.000, 1.0, 6 }, + { "Main", + 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1000, 1100, 1.500, 1.0, 6 }, + { "Screen-Extended Main", + 9, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1000, 1100, 1.500, 1.0, 7 }, + { "Main 10", + 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 1000, 1100, 1.875, 1.0, 6 }, + { "Screen-Extended Main 10", + 9, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1000, 1100, 1.875, 1.0, 7 }, + { "Main 12", + 4, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1500, 1650, 2.250, 1.0, 6 }, + { "Main Still Picture", + 3, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1000, 1100, 1.500, 1.0, 6 }, + { "Main 10 Still Picture", + 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1000, 1100, 1.875, 1.0, 6 }, + { "Main 4:2:2 10", + 4, 0, 2, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1667, 1833, 2.500, 0.5, 6 }, + { "Main 4:2:2 12", + 4, 0, 2, 1, 0, 0, 1, 0, 0, 0, 0, 1, 2000, 2200, 3.000, 0.5, 6 }, + { "Main 4:4:4", + 4, 0, 2, 1, 1, 1, 0, 0, 0, 0, 0, 1, 2000, 2200, 3.000, 0.5, 6 }, + { "High Throughput 4:4:4", + 5, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 2000, 2200, 3.000, 0.5, 6 }, + { "Screen-Extended Main 4:4:4", + 9, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 2000, 2200, 3.000, 0.5, 7 }, + { "Screen-Extended High Throughput 4:4:4", + 9, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 2000, 2200, 3.000, 0.5, 7 }, + { "Main 4:4:4 10", + 4, 0, 2, 1, 1, 0, 0, 0, 0, 0, 0, 1, 2500, 2750, 3.750, 0.5, 6 }, + { "High Throughput 4:4:4 10", + 5, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 2500, 2750, 3.750, 0.5, 6 }, + { "Screen-Extended Main 4:4:4 10", + 9, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 2500, 2750, 3.750, 0.5, 7 }, + { "Screen-Extended High Throughput 4:4:4 10", + 9, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 2500, 2750, 3.750, 0.5, 7 }, + { "Main 4:4:4 12", + 4, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 3000, 3300, 4.500, 0.5, 6 }, + { "High Throughput 4:4:4 14", + 5, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3500, 3850, 5.250, 0.5, 6 }, + { "Screen-Extended High Throughput 4:4:4 14", + 9, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3500, 3850, 5.250, 0.5, 7 }, + { "Main Intra", + 4, 0, 2, 1, 1, 1, 1, 1, 0, 1, 0, 2, 1000, 1100, 1.500, 1.0, 6 }, + { "Main 10 Intra", + 4, 0, 2, 1, 1, 0, 1, 1, 0, 1, 0, 2, 1000, 1100, 1.875, 1.0, 6 }, + { "Main 12 Intra", + 4, 0, 2, 1, 0, 0, 1, 1, 0, 1, 0, 2, 1500, 1650, 2.250, 1.0, 6 }, + { "Main 4:2:2 10 Intra", + 4, 0, 2, 1, 1, 0, 1, 0, 0, 1, 0, 2, 1667, 1833, 2.500, 0.5, 6 }, + { "Main 4:2:2 12 Intra", + 4, 0, 2, 1, 0, 0, 1, 0, 0, 1, 0, 2, 2000, 2200, 3.000, 0.5, 6 }, + { "Main 4:4:4 Intra", + 4, 0, 2, 1, 1, 1, 0, 0, 0, 1, 0, 2, 2000, 2200, 3.000, 0.5, 6 }, + { "Main 4:4:4 10 Intra", + 4, 0, 2, 1, 1, 0, 0, 0, 0, 1, 0, 2, 2500, 2750, 3.750, 0.5, 6 }, + { "Main 4:4:4 12 Intra", + 4, 0, 2, 1, 0, 0, 0, 0, 0, 1, 0, 2, 3000, 3300, 4.500, 0.5, 6 }, + { "Main 4:4:4 16 Intra", + 4, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 2, 4000, 4400, 6.000, 0.5, 6 }, + { "Main 4:4:4 Still Picture", + 4, 0, 2, 1, 1, 1, 0, 0, 0, 1, 1, 2, 2000, 2200, 3.000, 0.5, 6 }, + { "Main 4:4:4 16 Still Picture", + 4, 0, 2, 0, 0, 0, 0, 0, 0, 1, 1, 2, 4000, 4400, 6.000, 0.5, 6 }, + { "High Throughput 4:4:4 16 Intra", + 5, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 4000, 4400, 6.000, 0.5, 6 }, +}; + + +const H265ProfileDescriptor *ff_h265_get_profile(const H265RawProfileTierLevel *ptl) { + int i; + + if(ptl->general_profile_space) + return NULL; + + for(i = 0; i < FF_ARRAY_ELEMS(h265_profiles); i++) { + const H265ProfileDescriptor *profile = &h265_profiles[i]; + + if(ptl->general_profile_idc && + ptl->general_profile_idc != profile->profile_idc) + continue; + if(!ptl->general_profile_compatibility_flag[profile->profile_idc]) + continue; + +#define check_flag(name) \ + if(profile->name < 2) { \ + if(profile->name != ptl->general_##name##_constraint_flag) \ + continue; \ + } + check_flag(max_14bit); + check_flag(max_12bit); + check_flag(max_10bit); + check_flag(max_8bit); + check_flag(max_422chroma); + check_flag(max_420chroma); + check_flag(max_monochrome); + check_flag(intra); + check_flag(one_picture_only); + check_flag(lower_bit_rate); +#undef check_flag + + return profile; + } + + return NULL; +} + +const H265LevelDescriptor *ff_h265_guess_level(const H265RawProfileTierLevel *ptl, + int64_t bitrate, + int width, int height, + int slice_segments, + int tile_rows, int tile_cols, + int max_dec_pic_buffering) { + const H265ProfileDescriptor *profile; + int pic_size, tier_flag, lbr_flag, hbr_factor; + int i; + + if(ptl) + profile = ff_h265_get_profile(ptl); + else + profile = NULL; + if(!profile) { + // Default to using multiplication factors for Main profile. + profile = &h265_profiles[4]; + } + + pic_size = width * height; + + if(ptl) { + tier_flag = ptl->general_tier_flag; + lbr_flag = ptl->general_lower_bit_rate_constraint_flag; + } + else { + tier_flag = 0; + lbr_flag = profile->lower_bit_rate > 0; + } + if(profile->profile_idc == 1 || profile->profile_idc == 2) { + hbr_factor = 1; + } + else if(profile->high_throughput) { + if(profile->intra) + hbr_factor = 24 - 12 * lbr_flag; + else + hbr_factor = 6; + } + else { + hbr_factor = 2 - lbr_flag; + } + + for(i = 0; i < FF_ARRAY_ELEMS(h265_levels); i++) { + const H265LevelDescriptor *level = &h265_levels[i]; + int max_br, max_dpb_size; + + if(tier_flag && !level->max_br_high) + continue; + + if(pic_size > level->max_luma_ps) + continue; + if(width * width > 8 * level->max_luma_ps) + continue; + if(height * height > 8 * level->max_luma_ps) + continue; + + if(slice_segments > level->max_slice_segments_per_picture) + continue; + if(tile_rows > level->max_tile_rows) + continue; + if(tile_cols > level->max_tile_cols) + continue; + + if(tier_flag) + max_br = level->max_br_high; + else + max_br = level->max_br_main; + if(!max_br) + continue; + if(bitrate > (int64_t)profile->cpb_nal_factor * hbr_factor * max_br) + continue; + + if(pic_size <= (level->max_luma_ps >> 2)) + max_dpb_size = FFMIN(4 * profile->max_dpb_pic_buf, 16); + else if(pic_size <= (level->max_luma_ps >> 1)) + max_dpb_size = FFMIN(2 * profile->max_dpb_pic_buf, 16); + else if(pic_size <= (3 * level->max_luma_ps >> 2)) + max_dpb_size = FFMIN(4 * profile->max_dpb_pic_buf / 3, 16); + else + max_dpb_size = profile->max_dpb_pic_buf; + if(max_dec_pic_buffering > max_dpb_size) + continue; + + return level; + } + + return NULL; +} \ No newline at end of file diff --git a/third-party/cbs/vlc.h b/third-party/cbs/vlc.h new file mode 100644 index 00000000..aaa21a9c --- /dev/null +++ b/third-party/cbs/vlc.h @@ -0,0 +1,140 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VLC_H +#define AVCODEC_VLC_H + +#include + +#define VLC_TYPE int16_t + +typedef struct VLC { + int bits; + VLC_TYPE (*table) + [2]; ///< code, bits + int table_size, table_allocated; +} VLC; + +typedef struct RL_VLC_ELEM { + int16_t level; + int8_t len; + uint8_t run; +} RL_VLC_ELEM; + +#define init_vlc(vlc, nb_bits, nb_codes, \ + bits, bits_wrap, bits_size, \ + codes, codes_wrap, codes_size, \ + flags) \ + ff_init_vlc_sparse(vlc, nb_bits, nb_codes, \ + bits, bits_wrap, bits_size, \ + codes, codes_wrap, codes_size, \ + NULL, 0, 0, flags) + +int ff_init_vlc_sparse(VLC *vlc, int nb_bits, int nb_codes, + const void *bits, int bits_wrap, int bits_size, + const void *codes, int codes_wrap, int codes_size, + const void *symbols, int symbols_wrap, int symbols_size, + int flags); + +/** + * Build VLC decoding tables suitable for use with get_vlc2() + * + * This function takes lengths and symbols and calculates the codes from them. + * For this the input lengths and symbols have to be sorted according to "left + * nodes in the corresponding tree first". + * + * @param[in,out] vlc The VLC to be initialized; table and table_allocated + * must have been set when initializing a static VLC, + * otherwise this will be treated as uninitialized. + * @param[in] nb_bits The number of bits to use for the VLC table; + * higher values take up more memory and cache, but + * allow to read codes with fewer reads. + * @param[in] nb_codes The number of provided length and (if supplied) symbol + * entries. + * @param[in] lens The lengths of the codes. Entries > 0 correspond to + * valid codes; entries == 0 will be skipped and entries + * with len < 0 indicate that the tree is incomplete and + * has an open end of length -len at this position. + * @param[in] lens_wrap Stride (in bytes) of the lengths. + * @param[in] symbols The symbols, i.e. what is returned from get_vlc2() + * when the corresponding code is encountered. + * May be NULL, then 0, 1, 2, 3, 4,... will be used. + * @param[in] symbols_wrap Stride (in bytes) of the symbols. + * @param[in] symbols_size Size of the symbols. 1 and 2 are supported. + * @param[in] offset An offset to apply to all the valid symbols. + * @param[in] flags A combination of the INIT_VLC_* flags; notice that + * INIT_VLC_INPUT_LE is pointless and ignored. + */ +int ff_init_vlc_from_lengths(VLC *vlc, int nb_bits, int nb_codes, + const int8_t *lens, int lens_wrap, + const void *symbols, int symbols_wrap, int symbols_size, + int offset, int flags, void *logctx); + +void ff_free_vlc(VLC *vlc); + +/* If INIT_VLC_INPUT_LE is set, the LSB bit of the codes used to + * initialize the VLC table is the first bit to be read. */ +#define INIT_VLC_INPUT_LE 2 +/* If set the VLC is intended for a little endian bitstream reader. */ +#define INIT_VLC_OUTPUT_LE 8 +#define INIT_VLC_LE (INIT_VLC_INPUT_LE | INIT_VLC_OUTPUT_LE) +#define INIT_VLC_USE_NEW_STATIC 4 +#define INIT_VLC_STATIC_OVERLONG (1 | INIT_VLC_USE_NEW_STATIC) + +#define INIT_CUSTOM_VLC_SPARSE_STATIC(vlc, bits, a, b, c, d, e, f, g, \ + h, i, j, flags, static_size) \ + do { \ + static VLC_TYPE table[static_size][2]; \ + (vlc)->table = table; \ + (vlc)->table_allocated = static_size; \ + ff_init_vlc_sparse(vlc, bits, a, b, c, d, e, f, g, h, i, j, \ + flags | INIT_VLC_USE_NEW_STATIC); \ + } while(0) + +#define INIT_VLC_SPARSE_STATIC(vlc, bits, a, b, c, d, e, f, g, h, i, j, static_size) \ + INIT_CUSTOM_VLC_SPARSE_STATIC(vlc, bits, a, b, c, d, e, f, g, \ + h, i, j, 0, static_size) + +#define INIT_LE_VLC_SPARSE_STATIC(vlc, bits, a, b, c, d, e, f, g, h, i, j, static_size) \ + INIT_CUSTOM_VLC_SPARSE_STATIC(vlc, bits, a, b, c, d, e, f, g, \ + h, i, j, INIT_VLC_LE, static_size) + +#define INIT_CUSTOM_VLC_STATIC(vlc, bits, a, b, c, d, e, f, g, flags, static_size) \ + INIT_CUSTOM_VLC_SPARSE_STATIC(vlc, bits, a, b, c, d, e, f, g, \ + NULL, 0, 0, flags, static_size) + +#define INIT_VLC_STATIC(vlc, bits, a, b, c, d, e, f, g, static_size) \ + INIT_VLC_SPARSE_STATIC(vlc, bits, a, b, c, d, e, f, g, NULL, 0, 0, static_size) + +#define INIT_LE_VLC_STATIC(vlc, bits, a, b, c, d, e, f, g, static_size) \ + INIT_LE_VLC_SPARSE_STATIC(vlc, bits, a, b, c, d, e, f, g, NULL, 0, 0, static_size) + +#define INIT_VLC_STATIC_FROM_LENGTHS(vlc, bits, nb_codes, lens, len_wrap, \ + symbols, symbols_wrap, symbols_size, \ + offset, flags, static_size) \ + do { \ + static VLC_TYPE table[static_size][2]; \ + (vlc)->table = table; \ + (vlc)->table_allocated = static_size; \ + ff_init_vlc_from_lengths(vlc, bits, nb_codes, lens, len_wrap, \ + symbols, symbols_wrap, symbols_size, \ + offset, flags | INIT_VLC_USE_NEW_STATIC, \ + NULL); \ + } while(0) + +#endif /* AVCODEC_VLC_H */ diff --git a/third-party/glad/include/EGL/eglplatform.h b/third-party/glad/include/EGL/eglplatform.h new file mode 100644 index 00000000..53fb562e --- /dev/null +++ b/third-party/glad/include/EGL/eglplatform.h @@ -0,0 +1,169 @@ +#ifndef __eglplatform_h_ +#define __eglplatform_h_ + +/* +** Copyright 2007-2020 The Khronos Group Inc. +** SPDX-License-Identifier: Apache-2.0 +*/ + +/* Platform-specific types and definitions for egl.h + * + * Adopters may modify khrplatform.h and this file to suit their platform. + * You are encouraged to submit all modifications to the Khronos group so that + * they can be included in future versions of this file. Please submit changes + * by filing an issue or pull request on the public Khronos EGL Registry, at + * https://www.github.com/KhronosGroup/EGL-Registry/ + */ + +#include + +/* Macros used in EGL function prototype declarations. + * + * EGL functions should be prototyped as: + * + * EGLAPI return-type EGLAPIENTRY eglFunction(arguments); + * typedef return-type (EXPAPIENTRYP PFNEGLFUNCTIONPROC) (arguments); + * + * KHRONOS_APICALL and KHRONOS_APIENTRY are defined in KHR/khrplatform.h + */ + +#ifndef EGLAPI +#define EGLAPI KHRONOS_APICALL +#endif + +#ifndef EGLAPIENTRY +#define EGLAPIENTRY KHRONOS_APIENTRY +#endif +#define EGLAPIENTRYP EGLAPIENTRY * + +/* The types NativeDisplayType, NativeWindowType, and NativePixmapType + * are aliases of window-system-dependent types, such as X Display * or + * Windows Device Context. They must be defined in platform-specific + * code below. The EGL-prefixed versions of Native*Type are the same + * types, renamed in EGL 1.3 so all types in the API start with "EGL". + * + * Khronos STRONGLY RECOMMENDS that you use the default definitions + * provided below, since these changes affect both binary and source + * portability of applications using EGL running on different EGL + * implementations. + */ + +#if defined(EGL_NO_PLATFORM_SPECIFIC_TYPES) + +typedef void *EGLNativeDisplayType; +typedef void *EGLNativePixmapType; +typedef void *EGLNativeWindowType; + +#elif defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include + +typedef HDC EGLNativeDisplayType; +typedef HBITMAP EGLNativePixmapType; +typedef HWND EGLNativeWindowType; + +#elif defined(__EMSCRIPTEN__) + +typedef int EGLNativeDisplayType; +typedef int EGLNativePixmapType; +typedef int EGLNativeWindowType; + +#elif defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */ + +typedef int EGLNativeDisplayType; +typedef void *EGLNativePixmapType; +typedef void *EGLNativeWindowType; + +#elif defined(WL_EGL_PLATFORM) + +typedef struct wl_display *EGLNativeDisplayType; +typedef struct wl_egl_pixmap *EGLNativePixmapType; +typedef struct wl_egl_window *EGLNativeWindowType; + +#elif defined(__GBM__) + +typedef struct gbm_device *EGLNativeDisplayType; +typedef struct gbm_bo *EGLNativePixmapType; +typedef void *EGLNativeWindowType; + +#elif defined(__ANDROID__) || defined(ANDROID) + +struct ANativeWindow; +struct egl_native_pixmap_t; + +typedef void *EGLNativeDisplayType; +typedef struct egl_native_pixmap_t *EGLNativePixmapType; +typedef struct ANativeWindow *EGLNativeWindowType; + +#elif defined(USE_OZONE) + +typedef intptr_t EGLNativeDisplayType; +typedef intptr_t EGLNativePixmapType; +typedef intptr_t EGLNativeWindowType; + +#elif defined(__unix__) && defined(EGL_NO_X11) + +typedef void *EGLNativeDisplayType; +typedef khronos_uintptr_t EGLNativePixmapType; +typedef khronos_uintptr_t EGLNativeWindowType; + +#elif defined(__unix__) || defined(USE_X11) + +/* X11 (tentative) */ +#include +#include + +typedef Display *EGLNativeDisplayType; +typedef Pixmap EGLNativePixmapType; +typedef Window EGLNativeWindowType; + +#elif defined(__APPLE__) + +typedef int EGLNativeDisplayType; +typedef void *EGLNativePixmapType; +typedef void *EGLNativeWindowType; + +#elif defined(__HAIKU__) + +#include + +typedef void *EGLNativeDisplayType; +typedef khronos_uintptr_t EGLNativePixmapType; +typedef khronos_uintptr_t EGLNativeWindowType; + +#elif defined(__Fuchsia__) + +typedef void *EGLNativeDisplayType; +typedef khronos_uintptr_t EGLNativePixmapType; +typedef khronos_uintptr_t EGLNativeWindowType; + +#else +#error "Platform not recognized" +#endif + +/* EGL 1.2 types, renamed for consistency in EGL 1.3 */ +typedef EGLNativeDisplayType NativeDisplayType; +typedef EGLNativePixmapType NativePixmapType; +typedef EGLNativeWindowType NativeWindowType; + + +/* Define EGLint. This must be a signed integral type large enough to contain + * all legal attribute names and values passed into and out of EGL, whether + * their type is boolean, bitmask, enumerant (symbolic constant), integer, + * handle, or other. While in general a 32-bit integer will suffice, if + * handles are 64 bit types, then EGLint should be defined as a signed 64-bit + * integer type. + */ +typedef khronos_int32_t EGLint; + + +/* C++ / C typecast macros for special EGL handle values */ +#if defined(__cplusplus) +#define EGL_CAST(type, value) (static_cast(value)) +#else +#define EGL_CAST(type, value) ((type)(value)) +#endif + +#endif /* __eglplatform_h */ \ No newline at end of file diff --git a/third-party/glad/include/KHR/khrplatform.h b/third-party/glad/include/KHR/khrplatform.h new file mode 100644 index 00000000..7e3671ce --- /dev/null +++ b/third-party/glad/include/KHR/khrplatform.h @@ -0,0 +1,290 @@ +#ifndef __khrplatform_h_ +#define __khrplatform_h_ + +/* +** Copyright (c) 2008-2018 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE 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 +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* Khronos platform-specific types and definitions. + * + * The master copy of khrplatform.h is maintained in the Khronos EGL + * Registry repository at https://github.com/KhronosGroup/EGL-Registry + * The last semantic modification to khrplatform.h was at commit ID: + * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 + * + * Adopters may modify this file to suit their platform. Adopters are + * encouraged to submit platform specific modifications to the Khronos + * group so that they can be included in future versions of this file. + * Please submit changes by filing pull requests or issues on + * the EGL Registry repository linked above. + * + * + * See the Implementer's Guidelines for information about where this file + * should be located on your system and for more details of its use: + * http://www.khronos.org/registry/implementers_guide.pdf + * + * This file should be included as + * #include + * by Khronos client API header files that use its types and defines. + * + * The types in khrplatform.h should only be used to define API-specific types. + * + * Types defined in khrplatform.h: + * khronos_int8_t signed 8 bit + * khronos_uint8_t unsigned 8 bit + * khronos_int16_t signed 16 bit + * khronos_uint16_t unsigned 16 bit + * khronos_int32_t signed 32 bit + * khronos_uint32_t unsigned 32 bit + * khronos_int64_t signed 64 bit + * khronos_uint64_t unsigned 64 bit + * khronos_intptr_t signed same number of bits as a pointer + * khronos_uintptr_t unsigned same number of bits as a pointer + * khronos_ssize_t signed size + * khronos_usize_t unsigned size + * khronos_float_t signed 32 bit floating point + * khronos_time_ns_t unsigned 64 bit time in nanoseconds + * khronos_utime_nanoseconds_t unsigned time interval or absolute time in + * nanoseconds + * khronos_stime_nanoseconds_t signed time interval in nanoseconds + * khronos_boolean_enum_t enumerated boolean type. This should + * only be used as a base type when a client API's boolean type is + * an enum. Client APIs which use an integer or other type for + * booleans cannot use this as the base type for their boolean. + * + * Tokens defined in khrplatform.h: + * + * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. + * + * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. + * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. + * + * Calling convention macros defined in this file: + * KHRONOS_APICALL + * KHRONOS_APIENTRY + * KHRONOS_APIATTRIBUTES + * + * These may be used in function prototypes as: + * + * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( + * int arg1, + * int arg2) KHRONOS_APIATTRIBUTES; + */ + +#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) +#define KHRONOS_STATIC 1 +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APICALL + *------------------------------------------------------------------------- + * This precedes the return type of the function in the function prototype. + */ +#if defined(KHRONOS_STATIC) +/* If the preprocessor constant KHRONOS_STATIC is defined, make the + * header compatible with static linking. */ +#define KHRONOS_APICALL +#elif defined(_WIN32) +#define KHRONOS_APICALL __declspec(dllimport) +#elif defined(__SYMBIAN32__) +#define KHRONOS_APICALL IMPORT_C +#elif defined(__ANDROID__) +#define KHRONOS_APICALL __attribute__((visibility("default"))) +#else +#define KHRONOS_APICALL +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIENTRY + *------------------------------------------------------------------------- + * This follows the return type of the function and precedes the function + * name in the function prototype. + */ +#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) +/* Win32 but not WinCE */ +#define KHRONOS_APIENTRY __stdcall +#else +#define KHRONOS_APIENTRY +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIATTRIBUTES + *------------------------------------------------------------------------- + * This follows the closing parenthesis of the function prototype arguments. + */ +#if defined(__ARMCC_2__) +#define KHRONOS_APIATTRIBUTES __softfp +#else +#define KHRONOS_APIATTRIBUTES +#endif + +/*------------------------------------------------------------------------- + * basic type definitions + *-----------------------------------------------------------------------*/ +#if(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) + + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__VMS) || defined(__sgi) + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(_WIN32) && !defined(__SCITECH_SNAP__) + +/* + * Win32 + */ +typedef __int32 khronos_int32_t; +typedef unsigned __int32 khronos_uint32_t; +typedef __int64 khronos_int64_t; +typedef unsigned __int64 khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__sun__) || defined(__digital__) + +/* + * Sun or Digital + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#if defined(__arch64__) || defined(_LP64) +typedef long int khronos_int64_t; +typedef unsigned long int khronos_uint64_t; +#else +typedef long long int khronos_int64_t; +typedef unsigned long long int khronos_uint64_t; +#endif /* __arch64__ */ +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif 0 + +/* + * Hypothetical platform with no float or int64 support + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#define KHRONOS_SUPPORT_INT64 0 +#define KHRONOS_SUPPORT_FLOAT 0 + +#else + +/* + * Generic fallback + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#endif + + +/* + * Types that are (so far) the same on all platforms + */ +typedef signed char khronos_int8_t; +typedef unsigned char khronos_uint8_t; +typedef signed short int khronos_int16_t; +typedef unsigned short int khronos_uint16_t; + +/* + * Types that differ between LLP64 and LP64 architectures - in LLP64, + * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears + * to be the only LLP64 architecture in current use. + */ +#ifdef _WIN64 +typedef signed long long int khronos_intptr_t; +typedef unsigned long long int khronos_uintptr_t; +typedef signed long long int khronos_ssize_t; +typedef unsigned long long int khronos_usize_t; +#else +typedef signed long int khronos_intptr_t; +typedef unsigned long int khronos_uintptr_t; +typedef signed long int khronos_ssize_t; +typedef unsigned long int khronos_usize_t; +#endif + +#if KHRONOS_SUPPORT_FLOAT +/* + * Float type + */ +typedef float khronos_float_t; +#endif + +#if KHRONOS_SUPPORT_INT64 +/* Time types + * + * These types can be used to represent a time interval in nanoseconds or + * an absolute Unadjusted System Time. Unadjusted System Time is the number + * of nanoseconds since some arbitrary system event (e.g. since the last + * time the system booted). The Unadjusted System Time is an unsigned + * 64 bit value that wraps back to 0 every 584 years. Time intervals + * may be either signed or unsigned. + */ +typedef khronos_uint64_t khronos_utime_nanoseconds_t; +typedef khronos_int64_t khronos_stime_nanoseconds_t; +#endif + +/* + * Dummy value used to pad enum types to 32 bits. + */ +#ifndef KHRONOS_MAX_ENUM +#define KHRONOS_MAX_ENUM 0x7FFFFFFF +#endif + +/* + * Enumerated boolean type + * + * Values other than zero should be considered to be true. Therefore + * comparisons should not be made against KHRONOS_TRUE. + */ +typedef enum { + KHRONOS_FALSE = 0, + KHRONOS_TRUE = 1, + KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM +} khronos_boolean_enum_t; + +#endif /* __khrplatform_h_ */ \ No newline at end of file diff --git a/third-party/glad/include/glad/egl.h b/third-party/glad/include/glad/egl.h new file mode 100644 index 00000000..820de9da --- /dev/null +++ b/third-party/glad/include/glad/egl.h @@ -0,0 +1,584 @@ +/** + * Loader generated by glad 2.0.0-beta on Tue Jun 1 10:22:05 2021 + * + * Generator: C/C++ + * Specification: egl + * Extensions: 0 + * + * APIs: + * - egl=1.5 + * + * Options: + * - ALIAS = False + * - DEBUG = False + * - HEADER_ONLY = False + * - LOADER = True + * - MX = True + * - MX_GLOBAL = False + * - ON_DEMAND = False + * + * Commandline: + * --api='egl=1.5' --extensions='' c --loader --mx + * + * Online: + * http://glad.sh/#api=egl%3D1.5&extensions=&generator=c&options=LOADER%2CMX + * + */ + +#ifndef GLAD_EGL_H_ +#define GLAD_EGL_H_ + + +#define GLAD_EGL +#define GLAD_OPTION_EGL_LOADER + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef GLAD_PLATFORM_H_ +#define GLAD_PLATFORM_H_ + +#ifndef GLAD_PLATFORM_WIN32 +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__) +#define GLAD_PLATFORM_WIN32 1 +#else +#define GLAD_PLATFORM_WIN32 0 +#endif +#endif + +#ifndef GLAD_PLATFORM_APPLE +#ifdef __APPLE__ +#define GLAD_PLATFORM_APPLE 1 +#else +#define GLAD_PLATFORM_APPLE 0 +#endif +#endif + +#ifndef GLAD_PLATFORM_EMSCRIPTEN +#ifdef __EMSCRIPTEN__ +#define GLAD_PLATFORM_EMSCRIPTEN 1 +#else +#define GLAD_PLATFORM_EMSCRIPTEN 0 +#endif +#endif + +#ifndef GLAD_PLATFORM_UWP +#if defined(_MSC_VER) && !defined(GLAD_INTERNAL_HAVE_WINAPIFAMILY) +#ifdef __has_include +#if __has_include() +#define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 +#endif +#elif _MSC_VER >= 1700 && !_USING_V110_SDK71_ +#define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 +#endif +#endif + +#ifdef GLAD_INTERNAL_HAVE_WINAPIFAMILY +#include +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) +#define GLAD_PLATFORM_UWP 1 +#endif +#endif + +#ifndef GLAD_PLATFORM_UWP +#define GLAD_PLATFORM_UWP 0 +#endif +#endif + +#ifdef __GNUC__ +#define GLAD_GNUC_EXTENSION __extension__ +#else +#define GLAD_GNUC_EXTENSION +#endif + +#ifndef GLAD_API_CALL +#if defined(GLAD_API_CALL_EXPORT) +#if GLAD_PLATFORM_WIN32 || defined(__CYGWIN__) +#if defined(GLAD_API_CALL_EXPORT_BUILD) +#if defined(__GNUC__) +#define GLAD_API_CALL __attribute__((dllexport)) extern +#else +#define GLAD_API_CALL __declspec(dllexport) extern +#endif +#else +#if defined(__GNUC__) +#define GLAD_API_CALL __attribute__((dllimport)) extern +#else +#define GLAD_API_CALL __declspec(dllimport) extern +#endif +#endif +#elif defined(__GNUC__) && defined(GLAD_API_CALL_EXPORT_BUILD) +#define GLAD_API_CALL __attribute__((visibility("default"))) extern +#else +#define GLAD_API_CALL extern +#endif +#else +#define GLAD_API_CALL extern +#endif +#endif + +#ifdef APIENTRY +#define GLAD_API_PTR APIENTRY +#elif GLAD_PLATFORM_WIN32 +#define GLAD_API_PTR __stdcall +#else +#define GLAD_API_PTR +#endif + +#ifndef GLAPI +#define GLAPI GLAD_API_CALL +#endif + +#ifndef GLAPIENTRY +#define GLAPIENTRY GLAD_API_PTR +#endif + +#define GLAD_MAKE_VERSION(major, minor) (major * 10000 + minor) +#define GLAD_VERSION_MAJOR(version) (version / 10000) +#define GLAD_VERSION_MINOR(version) (version % 10000) + +#define GLAD_GENERATOR_VERSION "2.0.0-beta" + +typedef void (*GLADapiproc)(void); + +typedef GLADapiproc (*GLADloadfunc)(const char *name); +typedef GLADapiproc (*GLADuserptrloadfunc)(void *userptr, const char *name); + +typedef void (*GLADprecallback)(const char *name, GLADapiproc apiproc, int len_args, ...); +typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apiproc, int len_args, ...); + +#endif /* GLAD_PLATFORM_H_ */ + +#define EGL_ALPHA_FORMAT 0x3088 +#define EGL_ALPHA_FORMAT_NONPRE 0x308B +#define EGL_ALPHA_FORMAT_PRE 0x308C +#define EGL_ALPHA_MASK_SIZE 0x303E +#define EGL_ALPHA_SIZE 0x3021 +#define EGL_BACK_BUFFER 0x3084 +#define EGL_BAD_ACCESS 0x3002 +#define EGL_BAD_ALLOC 0x3003 +#define EGL_BAD_ATTRIBUTE 0x3004 +#define EGL_BAD_CONFIG 0x3005 +#define EGL_BAD_CONTEXT 0x3006 +#define EGL_BAD_CURRENT_SURFACE 0x3007 +#define EGL_BAD_DISPLAY 0x3008 +#define EGL_BAD_MATCH 0x3009 +#define EGL_BAD_NATIVE_PIXMAP 0x300A +#define EGL_BAD_NATIVE_WINDOW 0x300B +#define EGL_BAD_PARAMETER 0x300C +#define EGL_BAD_SURFACE 0x300D +#define EGL_BIND_TO_TEXTURE_RGB 0x3039 +#define EGL_BIND_TO_TEXTURE_RGBA 0x303A +#define EGL_BLUE_SIZE 0x3022 +#define EGL_BUFFER_DESTROYED 0x3095 +#define EGL_BUFFER_PRESERVED 0x3094 +#define EGL_BUFFER_SIZE 0x3020 +#define EGL_CLIENT_APIS 0x308D +#define EGL_CL_EVENT_HANDLE 0x309C +#define EGL_COLORSPACE 0x3087 +#define EGL_COLORSPACE_LINEAR 0x308A +#define EGL_COLORSPACE_sRGB 0x3089 +#define EGL_COLOR_BUFFER_TYPE 0x303F +#define EGL_CONDITION_SATISFIED 0x30F6 +#define EGL_CONFIG_CAVEAT 0x3027 +#define EGL_CONFIG_ID 0x3028 +#define EGL_CONFORMANT 0x3042 +#define EGL_CONTEXT_CLIENT_TYPE 0x3097 +#define EGL_CONTEXT_CLIENT_VERSION 0x3098 +#define EGL_CONTEXT_LOST 0x300E +#define EGL_CONTEXT_MAJOR_VERSION 0x3098 +#define EGL_CONTEXT_MINOR_VERSION 0x30FB +#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT 0x00000001 +#define EGL_CONTEXT_OPENGL_DEBUG 0x31B0 +#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE 0x31B1 +#define EGL_CONTEXT_OPENGL_PROFILE_MASK 0x30FD +#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY 0x31BD +#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS 0x31B2 +#define EGL_CORE_NATIVE_ENGINE 0x305B +#define EGL_DEFAULT_DISPLAY EGL_CAST(EGLNativeDisplayType, 0) +#define EGL_DEPTH_SIZE 0x3025 +#define EGL_DISPLAY_SCALING 10000 +#define EGL_DONT_CARE EGL_CAST(EGLint, -1) +#define EGL_DRAW 0x3059 +#define EGL_EXTENSIONS 0x3055 +#define EGL_FALSE 0 +#define EGL_FOREVER 0xFFFFFFFFFFFFFFFF +#define EGL_GL_COLORSPACE 0x309D +#define EGL_GL_COLORSPACE_LINEAR 0x308A +#define EGL_GL_COLORSPACE_SRGB 0x3089 +#define EGL_GL_RENDERBUFFER 0x30B9 +#define EGL_GL_TEXTURE_2D 0x30B1 +#define EGL_GL_TEXTURE_3D 0x30B2 +#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x30B4 +#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x30B6 +#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x30B8 +#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x30B3 +#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x30B5 +#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x30B7 +#define EGL_GL_TEXTURE_LEVEL 0x30BC +#define EGL_GL_TEXTURE_ZOFFSET 0x30BD +#define EGL_GREEN_SIZE 0x3023 +#define EGL_HEIGHT 0x3056 +#define EGL_HORIZONTAL_RESOLUTION 0x3090 +#define EGL_IMAGE_PRESERVED 0x30D2 +#define EGL_LARGEST_PBUFFER 0x3058 +#define EGL_LEVEL 0x3029 +#define EGL_LOSE_CONTEXT_ON_RESET 0x31BF +#define EGL_LUMINANCE_BUFFER 0x308F +#define EGL_LUMINANCE_SIZE 0x303D +#define EGL_MATCH_NATIVE_PIXMAP 0x3041 +#define EGL_MAX_PBUFFER_HEIGHT 0x302A +#define EGL_MAX_PBUFFER_PIXELS 0x302B +#define EGL_MAX_PBUFFER_WIDTH 0x302C +#define EGL_MAX_SWAP_INTERVAL 0x303C +#define EGL_MIN_SWAP_INTERVAL 0x303B +#define EGL_MIPMAP_LEVEL 0x3083 +#define EGL_MIPMAP_TEXTURE 0x3082 +#define EGL_MULTISAMPLE_RESOLVE 0x3099 +#define EGL_MULTISAMPLE_RESOLVE_BOX 0x309B +#define EGL_MULTISAMPLE_RESOLVE_BOX_BIT 0x0200 +#define EGL_MULTISAMPLE_RESOLVE_DEFAULT 0x309A +#define EGL_NATIVE_RENDERABLE 0x302D +#define EGL_NATIVE_VISUAL_ID 0x302E +#define EGL_NATIVE_VISUAL_TYPE 0x302F +#define EGL_NONE 0x3038 +#define EGL_NON_CONFORMANT_CONFIG 0x3051 +#define EGL_NOT_INITIALIZED 0x3001 +#define EGL_NO_CONTEXT EGL_CAST(EGLContext, 0) +#define EGL_NO_DISPLAY EGL_CAST(EGLDisplay, 0) +#define EGL_NO_IMAGE EGL_CAST(EGLImage, 0) +#define EGL_NO_RESET_NOTIFICATION 0x31BE +#define EGL_NO_SURFACE EGL_CAST(EGLSurface, 0) +#define EGL_NO_SYNC EGL_CAST(EGLSync, 0) +#define EGL_NO_TEXTURE 0x305C +#define EGL_OPENGL_API 0x30A2 +#define EGL_OPENGL_BIT 0x0008 +#define EGL_OPENGL_ES2_BIT 0x0004 +#define EGL_OPENGL_ES3_BIT 0x00000040 +#define EGL_OPENGL_ES_API 0x30A0 +#define EGL_OPENGL_ES_BIT 0x0001 +#define EGL_OPENVG_API 0x30A1 +#define EGL_OPENVG_BIT 0x0002 +#define EGL_OPENVG_IMAGE 0x3096 +#define EGL_PBUFFER_BIT 0x0001 +#define EGL_PIXEL_ASPECT_RATIO 0x3092 +#define EGL_PIXMAP_BIT 0x0002 +#define EGL_READ 0x305A +#define EGL_RED_SIZE 0x3024 +#define EGL_RENDERABLE_TYPE 0x3040 +#define EGL_RENDER_BUFFER 0x3086 +#define EGL_RGB_BUFFER 0x308E +#define EGL_SAMPLES 0x3031 +#define EGL_SAMPLE_BUFFERS 0x3032 +#define EGL_SIGNALED 0x30F2 +#define EGL_SINGLE_BUFFER 0x3085 +#define EGL_SLOW_CONFIG 0x3050 +#define EGL_STENCIL_SIZE 0x3026 +#define EGL_SUCCESS 0x3000 +#define EGL_SURFACE_TYPE 0x3033 +#define EGL_SWAP_BEHAVIOR 0x3093 +#define EGL_SWAP_BEHAVIOR_PRESERVED_BIT 0x0400 +#define EGL_SYNC_CL_EVENT 0x30FE +#define EGL_SYNC_CL_EVENT_COMPLETE 0x30FF +#define EGL_SYNC_CONDITION 0x30F8 +#define EGL_SYNC_FENCE 0x30F9 +#define EGL_SYNC_FLUSH_COMMANDS_BIT 0x0001 +#define EGL_SYNC_PRIOR_COMMANDS_COMPLETE 0x30F0 +#define EGL_SYNC_STATUS 0x30F1 +#define EGL_SYNC_TYPE 0x30F7 +#define EGL_TEXTURE_2D 0x305F +#define EGL_TEXTURE_FORMAT 0x3080 +#define EGL_TEXTURE_RGB 0x305D +#define EGL_TEXTURE_RGBA 0x305E +#define EGL_TEXTURE_TARGET 0x3081 +#define EGL_TIMEOUT_EXPIRED 0x30F5 +#define EGL_TRANSPARENT_BLUE_VALUE 0x3035 +#define EGL_TRANSPARENT_GREEN_VALUE 0x3036 +#define EGL_TRANSPARENT_RED_VALUE 0x3037 +#define EGL_TRANSPARENT_RGB 0x3052 +#define EGL_TRANSPARENT_TYPE 0x3034 +#define EGL_TRUE 1 +#define EGL_UNKNOWN EGL_CAST(EGLint, -1) +#define EGL_UNSIGNALED 0x30F3 +#define EGL_VENDOR 0x3053 +#define EGL_VERSION 0x3054 +#define EGL_VERTICAL_RESOLUTION 0x3091 +#define EGL_VG_ALPHA_FORMAT 0x3088 +#define EGL_VG_ALPHA_FORMAT_NONPRE 0x308B +#define EGL_VG_ALPHA_FORMAT_PRE 0x308C +#define EGL_VG_ALPHA_FORMAT_PRE_BIT 0x0040 +#define EGL_VG_COLORSPACE 0x3087 +#define EGL_VG_COLORSPACE_LINEAR 0x308A +#define EGL_VG_COLORSPACE_LINEAR_BIT 0x0020 +#define EGL_VG_COLORSPACE_sRGB 0x3089 +#define EGL_WIDTH 0x3057 +#define EGL_WINDOW_BIT 0x0004 + + +#include + +#include + + +struct AHardwareBuffer; + +struct wl_buffer; + +struct wl_display; + +struct wl_resource; + + +typedef unsigned int EGLBoolean; + +typedef unsigned int EGLenum; + +typedef intptr_t EGLAttribKHR; + +typedef intptr_t EGLAttrib; + +typedef void *EGLClientBuffer; + +typedef void *EGLConfig; + +typedef void *EGLContext; + +typedef void *EGLDeviceEXT; + +typedef void *EGLDisplay; + +typedef void *EGLImage; + +typedef void *EGLImageKHR; + +typedef void *EGLLabelKHR; + +typedef void *EGLObjectKHR; + +typedef void *EGLOutputLayerEXT; + +typedef void *EGLOutputPortEXT; + +typedef void *EGLStreamKHR; + +typedef void *EGLSurface; + +typedef void *EGLSync; + +typedef void *EGLSyncKHR; + +typedef void *EGLSyncNV; + +typedef void (*__eglMustCastToProperFunctionPointerType)(void); + +typedef khronos_utime_nanoseconds_t EGLTimeKHR; + +typedef khronos_utime_nanoseconds_t EGLTime; + +typedef khronos_utime_nanoseconds_t EGLTimeNV; + +typedef khronos_utime_nanoseconds_t EGLuint64NV; + +typedef khronos_uint64_t EGLuint64KHR; + +typedef khronos_stime_nanoseconds_t EGLnsecsANDROID; + +typedef int EGLNativeFileDescriptorKHR; + +typedef khronos_ssize_t EGLsizeiANDROID; + +typedef void (*EGLSetBlobFuncANDROID)(const void *key, EGLsizeiANDROID keySize, const void *value, EGLsizeiANDROID valueSize); + +typedef EGLsizeiANDROID (*EGLGetBlobFuncANDROID)(const void *key, EGLsizeiANDROID keySize, void *value, EGLsizeiANDROID valueSize); + +struct EGLClientPixmapHI { + void *pData; + EGLint iWidth; + EGLint iHeight; + EGLint iStride; +}; + +typedef void(GLAD_API_PTR *EGLDEBUGPROCKHR)(EGLenum error, const char *command, EGLint messageType, EGLLabelKHR threadLabel, EGLLabelKHR objectLabel, const char *message); + +#define PFNEGLBINDWAYLANDDISPLAYWL PFNEGLBINDWAYLANDDISPLAYWLPROC + +#define PFNEGLUNBINDWAYLANDDISPLAYWL PFNEGLUNBINDWAYLANDDISPLAYWLPROC + +#define PFNEGLQUERYWAYLANDBUFFERWL PFNEGLQUERYWAYLANDBUFFERWLPROC + +#define PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWLPROC + + +#define EGL_VERSION_1_0 1 +GLAD_API_CALL int GLAD_EGL_VERSION_1_0; +#define EGL_VERSION_1_1 1 +GLAD_API_CALL int GLAD_EGL_VERSION_1_1; +#define EGL_VERSION_1_2 1 +GLAD_API_CALL int GLAD_EGL_VERSION_1_2; +#define EGL_VERSION_1_3 1 +GLAD_API_CALL int GLAD_EGL_VERSION_1_3; +#define EGL_VERSION_1_4 1 +GLAD_API_CALL int GLAD_EGL_VERSION_1_4; +#define EGL_VERSION_1_5 1 +GLAD_API_CALL int GLAD_EGL_VERSION_1_5; + + +typedef EGLBoolean(GLAD_API_PTR *PFNEGLBINDAPIPROC)(EGLenum api); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLBINDTEXIMAGEPROC)(EGLDisplay dpy, EGLSurface surface, EGLint buffer); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLCHOOSECONFIGPROC)(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config); +typedef EGLint(GLAD_API_PTR *PFNEGLCLIENTWAITSYNCPROC)(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLCOPYBUFFERSPROC)(EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target); +typedef EGLContext(GLAD_API_PTR *PFNEGLCREATECONTEXTPROC)(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list); +typedef EGLImage(GLAD_API_PTR *PFNEGLCREATEIMAGEPROC)(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list); +typedef EGLSurface(GLAD_API_PTR *PFNEGLCREATEPBUFFERFROMCLIENTBUFFERPROC)(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list); +typedef EGLSurface(GLAD_API_PTR *PFNEGLCREATEPBUFFERSURFACEPROC)(EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list); +typedef EGLSurface(GLAD_API_PTR *PFNEGLCREATEPIXMAPSURFACEPROC)(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list); +typedef EGLSurface(GLAD_API_PTR *PFNEGLCREATEPLATFORMPIXMAPSURFACEPROC)(EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list); +typedef EGLSurface(GLAD_API_PTR *PFNEGLCREATEPLATFORMWINDOWSURFACEPROC)(EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list); +typedef EGLSync(GLAD_API_PTR *PFNEGLCREATESYNCPROC)(EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list); +typedef EGLSurface(GLAD_API_PTR *PFNEGLCREATEWINDOWSURFACEPROC)(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLDESTROYCONTEXTPROC)(EGLDisplay dpy, EGLContext ctx); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLDESTROYIMAGEPROC)(EGLDisplay dpy, EGLImage image); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLDESTROYSURFACEPROC)(EGLDisplay dpy, EGLSurface surface); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLDESTROYSYNCPROC)(EGLDisplay dpy, EGLSync sync); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLGETCONFIGATTRIBPROC)(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLGETCONFIGSPROC)(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config); +typedef EGLContext(GLAD_API_PTR *PFNEGLGETCURRENTCONTEXTPROC)(void); +typedef EGLDisplay(GLAD_API_PTR *PFNEGLGETCURRENTDISPLAYPROC)(void); +typedef EGLSurface(GLAD_API_PTR *PFNEGLGETCURRENTSURFACEPROC)(EGLint readdraw); +typedef EGLDisplay(GLAD_API_PTR *PFNEGLGETDISPLAYPROC)(EGLNativeDisplayType display_id); +typedef EGLint(GLAD_API_PTR *PFNEGLGETERRORPROC)(void); +typedef EGLDisplay(GLAD_API_PTR *PFNEGLGETPLATFORMDISPLAYPROC)(EGLenum platform, void *native_display, const EGLAttrib *attrib_list); +typedef __eglMustCastToProperFunctionPointerType(GLAD_API_PTR *PFNEGLGETPROCADDRESSPROC)(const char *procname); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLGETSYNCATTRIBPROC)(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLINITIALIZEPROC)(EGLDisplay dpy, EGLint *major, EGLint *minor); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLMAKECURRENTPROC)(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx); +typedef EGLenum(GLAD_API_PTR *PFNEGLQUERYAPIPROC)(void); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLQUERYCONTEXTPROC)(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value); +typedef const char *(GLAD_API_PTR *PFNEGLQUERYSTRINGPROC)(EGLDisplay dpy, EGLint name); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLQUERYSURFACEPROC)(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLRELEASETEXIMAGEPROC)(EGLDisplay dpy, EGLSurface surface, EGLint buffer); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLRELEASETHREADPROC)(void); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLSURFACEATTRIBPROC)(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLSWAPBUFFERSPROC)(EGLDisplay dpy, EGLSurface surface); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLSWAPINTERVALPROC)(EGLDisplay dpy, EGLint interval); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLTERMINATEPROC)(EGLDisplay dpy); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLWAITCLIENTPROC)(void); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLWAITGLPROC)(void); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLWAITNATIVEPROC)(EGLint engine); +typedef EGLBoolean(GLAD_API_PTR *PFNEGLWAITSYNCPROC)(EGLDisplay dpy, EGLSync sync, EGLint flags); + +typedef EGLImageKHR(EGLAPIENTRYP PFNEGLCREATEIMAGEKHRPROC)(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list); +typedef EGLBoolean(EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC)(EGLDisplay dpy, EGLImageKHR image); + +GLAD_API_CALL PFNEGLCREATEIMAGEKHRPROC glad_eglCreateImageKHR; +#define eglCreateImageKHR glad_eglCreateImageKHR +GLAD_API_CALL PFNEGLDESTROYIMAGEKHRPROC glad_eglDestroyImageKHR; +#define eglDestroyImageKHR glad_eglDestroyImageKHR + +GLAD_API_CALL PFNEGLBINDAPIPROC glad_eglBindAPI; +#define eglBindAPI glad_eglBindAPI +GLAD_API_CALL PFNEGLBINDTEXIMAGEPROC glad_eglBindTexImage; +#define eglBindTexImage glad_eglBindTexImage +GLAD_API_CALL PFNEGLCHOOSECONFIGPROC glad_eglChooseConfig; +#define eglChooseConfig glad_eglChooseConfig +GLAD_API_CALL PFNEGLCLIENTWAITSYNCPROC glad_eglClientWaitSync; +#define eglClientWaitSync glad_eglClientWaitSync +GLAD_API_CALL PFNEGLCOPYBUFFERSPROC glad_eglCopyBuffers; +#define eglCopyBuffers glad_eglCopyBuffers +GLAD_API_CALL PFNEGLCREATECONTEXTPROC glad_eglCreateContext; +#define eglCreateContext glad_eglCreateContext +GLAD_API_CALL PFNEGLCREATEIMAGEPROC glad_eglCreateImage; +#define eglCreateImage glad_eglCreateImage +GLAD_API_CALL PFNEGLCREATEPBUFFERFROMCLIENTBUFFERPROC glad_eglCreatePbufferFromClientBuffer; +#define eglCreatePbufferFromClientBuffer glad_eglCreatePbufferFromClientBuffer +GLAD_API_CALL PFNEGLCREATEPBUFFERSURFACEPROC glad_eglCreatePbufferSurface; +#define eglCreatePbufferSurface glad_eglCreatePbufferSurface +GLAD_API_CALL PFNEGLCREATEPIXMAPSURFACEPROC glad_eglCreatePixmapSurface; +#define eglCreatePixmapSurface glad_eglCreatePixmapSurface +GLAD_API_CALL PFNEGLCREATEPLATFORMPIXMAPSURFACEPROC glad_eglCreatePlatformPixmapSurface; +#define eglCreatePlatformPixmapSurface glad_eglCreatePlatformPixmapSurface +GLAD_API_CALL PFNEGLCREATEPLATFORMWINDOWSURFACEPROC glad_eglCreatePlatformWindowSurface; +#define eglCreatePlatformWindowSurface glad_eglCreatePlatformWindowSurface +GLAD_API_CALL PFNEGLCREATESYNCPROC glad_eglCreateSync; +#define eglCreateSync glad_eglCreateSync +GLAD_API_CALL PFNEGLCREATEWINDOWSURFACEPROC glad_eglCreateWindowSurface; +#define eglCreateWindowSurface glad_eglCreateWindowSurface +GLAD_API_CALL PFNEGLDESTROYCONTEXTPROC glad_eglDestroyContext; +#define eglDestroyContext glad_eglDestroyContext +GLAD_API_CALL PFNEGLDESTROYIMAGEPROC glad_eglDestroyImage; +#define eglDestroyImage glad_eglDestroyImage +GLAD_API_CALL PFNEGLDESTROYSURFACEPROC glad_eglDestroySurface; +#define eglDestroySurface glad_eglDestroySurface +GLAD_API_CALL PFNEGLDESTROYSYNCPROC glad_eglDestroySync; +#define eglDestroySync glad_eglDestroySync +GLAD_API_CALL PFNEGLGETCONFIGATTRIBPROC glad_eglGetConfigAttrib; +#define eglGetConfigAttrib glad_eglGetConfigAttrib +GLAD_API_CALL PFNEGLGETCONFIGSPROC glad_eglGetConfigs; +#define eglGetConfigs glad_eglGetConfigs +GLAD_API_CALL PFNEGLGETCURRENTCONTEXTPROC glad_eglGetCurrentContext; +#define eglGetCurrentContext glad_eglGetCurrentContext +GLAD_API_CALL PFNEGLGETCURRENTDISPLAYPROC glad_eglGetCurrentDisplay; +#define eglGetCurrentDisplay glad_eglGetCurrentDisplay +GLAD_API_CALL PFNEGLGETCURRENTSURFACEPROC glad_eglGetCurrentSurface; +#define eglGetCurrentSurface glad_eglGetCurrentSurface +GLAD_API_CALL PFNEGLGETDISPLAYPROC glad_eglGetDisplay; +#define eglGetDisplay glad_eglGetDisplay +GLAD_API_CALL PFNEGLGETERRORPROC glad_eglGetError; +#define eglGetError glad_eglGetError +GLAD_API_CALL PFNEGLGETPLATFORMDISPLAYPROC glad_eglGetPlatformDisplay; +#define eglGetPlatformDisplay glad_eglGetPlatformDisplay +GLAD_API_CALL PFNEGLGETPROCADDRESSPROC glad_eglGetProcAddress; +#define eglGetProcAddress glad_eglGetProcAddress +GLAD_API_CALL PFNEGLGETSYNCATTRIBPROC glad_eglGetSyncAttrib; +#define eglGetSyncAttrib glad_eglGetSyncAttrib +GLAD_API_CALL PFNEGLINITIALIZEPROC glad_eglInitialize; +#define eglInitialize glad_eglInitialize +GLAD_API_CALL PFNEGLMAKECURRENTPROC glad_eglMakeCurrent; +#define eglMakeCurrent glad_eglMakeCurrent +GLAD_API_CALL PFNEGLQUERYAPIPROC glad_eglQueryAPI; +#define eglQueryAPI glad_eglQueryAPI +GLAD_API_CALL PFNEGLQUERYCONTEXTPROC glad_eglQueryContext; +#define eglQueryContext glad_eglQueryContext +GLAD_API_CALL PFNEGLQUERYSTRINGPROC glad_eglQueryString; +#define eglQueryString glad_eglQueryString +GLAD_API_CALL PFNEGLQUERYSURFACEPROC glad_eglQuerySurface; +#define eglQuerySurface glad_eglQuerySurface +GLAD_API_CALL PFNEGLRELEASETEXIMAGEPROC glad_eglReleaseTexImage; +#define eglReleaseTexImage glad_eglReleaseTexImage +GLAD_API_CALL PFNEGLRELEASETHREADPROC glad_eglReleaseThread; +#define eglReleaseThread glad_eglReleaseThread +GLAD_API_CALL PFNEGLSURFACEATTRIBPROC glad_eglSurfaceAttrib; +#define eglSurfaceAttrib glad_eglSurfaceAttrib +GLAD_API_CALL PFNEGLSWAPBUFFERSPROC glad_eglSwapBuffers; +#define eglSwapBuffers glad_eglSwapBuffers +GLAD_API_CALL PFNEGLSWAPINTERVALPROC glad_eglSwapInterval; +#define eglSwapInterval glad_eglSwapInterval +GLAD_API_CALL PFNEGLTERMINATEPROC glad_eglTerminate; +#define eglTerminate glad_eglTerminate +GLAD_API_CALL PFNEGLWAITCLIENTPROC glad_eglWaitClient; +#define eglWaitClient glad_eglWaitClient +GLAD_API_CALL PFNEGLWAITGLPROC glad_eglWaitGL; +#define eglWaitGL glad_eglWaitGL +GLAD_API_CALL PFNEGLWAITNATIVEPROC glad_eglWaitNative; +#define eglWaitNative glad_eglWaitNative +GLAD_API_CALL PFNEGLWAITSYNCPROC glad_eglWaitSync; +#define eglWaitSync glad_eglWaitSync + + +GLAD_API_CALL int gladLoadEGLUserPtr(EGLDisplay display, GLADuserptrloadfunc load, void *userptr); +GLAD_API_CALL int gladLoadEGL(EGLDisplay display, GLADloadfunc load); + +#ifdef GLAD_EGL + +GLAD_API_CALL int gladLoaderLoadEGL(EGLDisplay display); + +GLAD_API_CALL void gladLoaderUnloadEGL(void); + +#endif +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/third-party/glad/include/glad/gl.h b/third-party/glad/include/glad/gl.h new file mode 100644 index 00000000..4dfc0038 --- /dev/null +++ b/third-party/glad/include/glad/gl.h @@ -0,0 +1,4253 @@ +/** + * Loader generated by glad 2.0.0-beta on Tue Jun 1 10:22:06 2021 + * + * Generator: C/C++ + * Specification: gl + * Extensions: 0 + * + * APIs: + * - gl:compatibility=4.6 + * + * Options: + * - ALIAS = False + * - DEBUG = False + * - HEADER_ONLY = False + * - LOADER = True + * - MX = True + * - MX_GLOBAL = False + * - ON_DEMAND = False + * + * Commandline: + * --api='gl:compatibility=4.6' --extensions='' c --loader --mx + * + * Online: + * http://glad.sh/#api=gl%3Acompatibility%3D4.6&extensions=&generator=c&options=LOADER%2CMX + * + */ + +#ifndef GLAD_GL_H_ +#define GLAD_GL_H_ + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif +#ifdef __gl_h_ +#error OpenGL (gl.h) header already included (API: gl), remove previous include! +#endif +#define __gl_h_ 1 +#ifdef __gl3_h_ +#error OpenGL (gl3.h) header already included (API: gl), remove previous include! +#endif +#define __gl3_h_ 1 +#ifdef __glext_h_ +#error OpenGL (glext.h) header already included (API: gl), remove previous include! +#endif +#define __glext_h_ 1 +#ifdef __gl3ext_h_ +#error OpenGL (gl3ext.h) header already included (API: gl), remove previous include! +#endif +#define __gl3ext_h_ 1 +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#define GLAD_GL +#define GLAD_OPTION_GL_LOADER +#define GLAD_OPTION_GL_MX + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef GLAD_PLATFORM_H_ +#define GLAD_PLATFORM_H_ + +#ifndef GLAD_PLATFORM_WIN32 +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__) +#define GLAD_PLATFORM_WIN32 1 +#else +#define GLAD_PLATFORM_WIN32 0 +#endif +#endif + +#ifndef GLAD_PLATFORM_APPLE +#ifdef __APPLE__ +#define GLAD_PLATFORM_APPLE 1 +#else +#define GLAD_PLATFORM_APPLE 0 +#endif +#endif + +#ifndef GLAD_PLATFORM_EMSCRIPTEN +#ifdef __EMSCRIPTEN__ +#define GLAD_PLATFORM_EMSCRIPTEN 1 +#else +#define GLAD_PLATFORM_EMSCRIPTEN 0 +#endif +#endif + +#ifndef GLAD_PLATFORM_UWP +#if defined(_MSC_VER) && !defined(GLAD_INTERNAL_HAVE_WINAPIFAMILY) +#ifdef __has_include +#if __has_include() +#define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 +#endif +#elif _MSC_VER >= 1700 && !_USING_V110_SDK71_ +#define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 +#endif +#endif + +#ifdef GLAD_INTERNAL_HAVE_WINAPIFAMILY +#include +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) +#define GLAD_PLATFORM_UWP 1 +#endif +#endif + +#ifndef GLAD_PLATFORM_UWP +#define GLAD_PLATFORM_UWP 0 +#endif +#endif + +#ifdef __GNUC__ +#define GLAD_GNUC_EXTENSION __extension__ +#else +#define GLAD_GNUC_EXTENSION +#endif + +#ifndef GLAD_API_CALL +#if defined(GLAD_API_CALL_EXPORT) +#if GLAD_PLATFORM_WIN32 || defined(__CYGWIN__) +#if defined(GLAD_API_CALL_EXPORT_BUILD) +#if defined(__GNUC__) +#define GLAD_API_CALL __attribute__((dllexport)) extern +#else +#define GLAD_API_CALL __declspec(dllexport) extern +#endif +#else +#if defined(__GNUC__) +#define GLAD_API_CALL __attribute__((dllimport)) extern +#else +#define GLAD_API_CALL __declspec(dllimport) extern +#endif +#endif +#elif defined(__GNUC__) && defined(GLAD_API_CALL_EXPORT_BUILD) +#define GLAD_API_CALL __attribute__((visibility("default"))) extern +#else +#define GLAD_API_CALL extern +#endif +#else +#define GLAD_API_CALL extern +#endif +#endif + +#ifdef APIENTRY +#define GLAD_API_PTR APIENTRY +#elif GLAD_PLATFORM_WIN32 +#define GLAD_API_PTR __stdcall +#else +#define GLAD_API_PTR +#endif + +#ifndef GLAPI +#define GLAPI GLAD_API_CALL +#endif + +#ifndef GLAPIENTRY +#define GLAPIENTRY GLAD_API_PTR +#endif + +#define GLAD_MAKE_VERSION(major, minor) (major * 10000 + minor) +#define GLAD_VERSION_MAJOR(version) (version / 10000) +#define GLAD_VERSION_MINOR(version) (version % 10000) + +#define GLAD_GENERATOR_VERSION "2.0.0-beta" + +typedef void (*GLADapiproc)(void); + +typedef GLADapiproc (*GLADloadfunc)(const char *name); +typedef GLADapiproc (*GLADuserptrloadfunc)(void *userptr, const char *name); + +typedef void (*GLADprecallback)(const char *name, GLADapiproc apiproc, int len_args, ...); +typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apiproc, int len_args, ...); + +#endif /* GLAD_PLATFORM_H_ */ + +#define GL_2D 0x0600 +#define GL_2_BYTES 0x1407 +#define GL_3D 0x0601 +#define GL_3D_COLOR 0x0602 +#define GL_3D_COLOR_TEXTURE 0x0603 +#define GL_3_BYTES 0x1408 +#define GL_4D_COLOR_TEXTURE 0x0604 +#define GL_4_BYTES 0x1409 +#define GL_ACCUM 0x0100 +#define GL_ACCUM_ALPHA_BITS 0x0D5B +#define GL_ACCUM_BLUE_BITS 0x0D5A +#define GL_ACCUM_BUFFER_BIT 0x00000200 +#define GL_ACCUM_CLEAR_VALUE 0x0B80 +#define GL_ACCUM_GREEN_BITS 0x0D59 +#define GL_ACCUM_RED_BITS 0x0D58 +#define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_ACTIVE_PROGRAM 0x8259 +#define GL_ACTIVE_RESOURCES 0x92F5 +#define GL_ACTIVE_SUBROUTINES 0x8DE5 +#define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48 +#define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49 +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_ACTIVE_VARIABLES 0x9305 +#define GL_ADD 0x0104 +#define GL_ADD_SIGNED 0x8574 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +#define GL_ALL_ATTRIB_BITS 0xFFFFFFFF +#define GL_ALL_BARRIER_BITS 0xFFFFFFFF +#define GL_ALL_SHADER_BITS 0xFFFFFFFF +#define GL_ALPHA 0x1906 +#define GL_ALPHA12 0x803D +#define GL_ALPHA16 0x803E +#define GL_ALPHA4 0x803B +#define GL_ALPHA8 0x803C +#define GL_ALPHA_BIAS 0x0D1D +#define GL_ALPHA_BITS 0x0D55 +#define GL_ALPHA_INTEGER 0x8D97 +#define GL_ALPHA_SCALE 0x0D1C +#define GL_ALPHA_TEST 0x0BC0 +#define GL_ALPHA_TEST_FUNC 0x0BC1 +#define GL_ALPHA_TEST_REF 0x0BC2 +#define GL_ALREADY_SIGNALED 0x911A +#define GL_ALWAYS 0x0207 +#define GL_AMBIENT 0x1200 +#define GL_AMBIENT_AND_DIFFUSE 0x1602 +#define GL_AND 0x1501 +#define GL_AND_INVERTED 0x1504 +#define GL_AND_REVERSE 0x1502 +#define GL_ANY_SAMPLES_PASSED 0x8C2F +#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ARRAY_SIZE 0x92FB +#define GL_ARRAY_STRIDE 0x92FE +#define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 +#define GL_ATOMIC_COUNTER_BUFFER 0x92C0 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6 +#define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1 +#define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4 +#define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7 +#define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3 +#define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_ATTRIB_STACK_DEPTH 0x0BB0 +#define GL_AUTO_GENERATE_MIPMAP 0x8295 +#define GL_AUTO_NORMAL 0x0D80 +#define GL_AUX0 0x0409 +#define GL_AUX1 0x040A +#define GL_AUX2 0x040B +#define GL_AUX3 0x040C +#define GL_AUX_BUFFERS 0x0C00 +#define GL_BACK 0x0405 +#define GL_BACK_LEFT 0x0402 +#define GL_BACK_RIGHT 0x0403 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#define GL_BGRA_INTEGER 0x8D9B +#define GL_BGR_INTEGER 0x8D9A +#define GL_BITMAP 0x1A00 +#define GL_BITMAP_TOKEN 0x0704 +#define GL_BLEND 0x0BE2 +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_DST 0x0BE0 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_EQUATION 0x8009 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_BLEND_SRC 0x0BE1 +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLOCK_INDEX 0x92FD +#define GL_BLUE 0x1905 +#define GL_BLUE_BIAS 0x0D1B +#define GL_BLUE_BITS 0x0D54 +#define GL_BLUE_INTEGER 0x8D96 +#define GL_BLUE_SCALE 0x0D1A +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_BUFFER 0x82E0 +#define GL_BUFFER_ACCESS 0x88BB +#define GL_BUFFER_ACCESS_FLAGS 0x911F +#define GL_BUFFER_BINDING 0x9302 +#define GL_BUFFER_DATA_SIZE 0x9303 +#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F +#define GL_BUFFER_MAPPED 0x88BC +#define GL_BUFFER_MAP_LENGTH 0x9120 +#define GL_BUFFER_MAP_OFFSET 0x9121 +#define GL_BUFFER_MAP_POINTER 0x88BD +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_STORAGE_FLAGS 0x8220 +#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 +#define GL_BUFFER_USAGE 0x8765 +#define GL_BUFFER_VARIABLE 0x92E5 +#define GL_BYTE 0x1400 +#define GL_C3F_V3F 0x2A24 +#define GL_C4F_N3F_V3F 0x2A26 +#define GL_C4UB_V2F 0x2A22 +#define GL_C4UB_V3F 0x2A23 +#define GL_CAVEAT_SUPPORT 0x82B8 +#define GL_CCW 0x0901 +#define GL_CLAMP 0x2900 +#define GL_CLAMP_FRAGMENT_COLOR 0x891B +#define GL_CLAMP_READ_COLOR 0x891C +#define GL_CLAMP_TO_BORDER 0x812D +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_CLAMP_VERTEX_COLOR 0x891A +#define GL_CLEAR 0x1500 +#define GL_CLEAR_BUFFER 0x82B4 +#define GL_CLEAR_TEXTURE 0x9365 +#define GL_CLIENT_ACTIVE_TEXTURE 0x84E1 +#define GL_CLIENT_ALL_ATTRIB_BITS 0xFFFFFFFF +#define GL_CLIENT_ATTRIB_STACK_DEPTH 0x0BB1 +#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000 +#define GL_CLIENT_PIXEL_STORE_BIT 0x00000001 +#define GL_CLIENT_STORAGE_BIT 0x0200 +#define GL_CLIENT_VERTEX_ARRAY_BIT 0x00000002 +#define GL_CLIPPING_INPUT_PRIMITIVES 0x82F6 +#define GL_CLIPPING_OUTPUT_PRIMITIVES 0x82F7 +#define GL_CLIP_DEPTH_MODE 0x935D +#define GL_CLIP_DISTANCE0 0x3000 +#define GL_CLIP_DISTANCE1 0x3001 +#define GL_CLIP_DISTANCE2 0x3002 +#define GL_CLIP_DISTANCE3 0x3003 +#define GL_CLIP_DISTANCE4 0x3004 +#define GL_CLIP_DISTANCE5 0x3005 +#define GL_CLIP_DISTANCE6 0x3006 +#define GL_CLIP_DISTANCE7 0x3007 +#define GL_CLIP_ORIGIN 0x935C +#define GL_CLIP_PLANE0 0x3000 +#define GL_CLIP_PLANE1 0x3001 +#define GL_CLIP_PLANE2 0x3002 +#define GL_CLIP_PLANE3 0x3003 +#define GL_CLIP_PLANE4 0x3004 +#define GL_CLIP_PLANE5 0x3005 +#define GL_COEFF 0x0A00 +#define GL_COLOR 0x1800 +#define GL_COLOR_ARRAY 0x8076 +#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898 +#define GL_COLOR_ARRAY_POINTER 0x8090 +#define GL_COLOR_ARRAY_SIZE 0x8081 +#define GL_COLOR_ARRAY_STRIDE 0x8083 +#define GL_COLOR_ARRAY_TYPE 0x8082 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_COLOR_ATTACHMENT10 0x8CEA +#define GL_COLOR_ATTACHMENT11 0x8CEB +#define GL_COLOR_ATTACHMENT12 0x8CEC +#define GL_COLOR_ATTACHMENT13 0x8CED +#define GL_COLOR_ATTACHMENT14 0x8CEE +#define GL_COLOR_ATTACHMENT15 0x8CEF +#define GL_COLOR_ATTACHMENT16 0x8CF0 +#define GL_COLOR_ATTACHMENT17 0x8CF1 +#define GL_COLOR_ATTACHMENT18 0x8CF2 +#define GL_COLOR_ATTACHMENT19 0x8CF3 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#define GL_COLOR_ATTACHMENT20 0x8CF4 +#define GL_COLOR_ATTACHMENT21 0x8CF5 +#define GL_COLOR_ATTACHMENT22 0x8CF6 +#define GL_COLOR_ATTACHMENT23 0x8CF7 +#define GL_COLOR_ATTACHMENT24 0x8CF8 +#define GL_COLOR_ATTACHMENT25 0x8CF9 +#define GL_COLOR_ATTACHMENT26 0x8CFA +#define GL_COLOR_ATTACHMENT27 0x8CFB +#define GL_COLOR_ATTACHMENT28 0x8CFC +#define GL_COLOR_ATTACHMENT29 0x8CFD +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#define GL_COLOR_ATTACHMENT30 0x8CFE +#define GL_COLOR_ATTACHMENT31 0x8CFF +#define GL_COLOR_ATTACHMENT4 0x8CE4 +#define GL_COLOR_ATTACHMENT5 0x8CE5 +#define GL_COLOR_ATTACHMENT6 0x8CE6 +#define GL_COLOR_ATTACHMENT7 0x8CE7 +#define GL_COLOR_ATTACHMENT8 0x8CE8 +#define GL_COLOR_ATTACHMENT9 0x8CE9 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_COMPONENTS 0x8283 +#define GL_COLOR_ENCODING 0x8296 +#define GL_COLOR_INDEX 0x1900 +#define GL_COLOR_INDEXES 0x1603 +#define GL_COLOR_LOGIC_OP 0x0BF2 +#define GL_COLOR_MATERIAL 0x0B57 +#define GL_COLOR_MATERIAL_FACE 0x0B55 +#define GL_COLOR_MATERIAL_PARAMETER 0x0B56 +#define GL_COLOR_RENDERABLE 0x8286 +#define GL_COLOR_SUM 0x8458 +#define GL_COLOR_TABLE 0x80D0 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_COMBINE 0x8570 +#define GL_COMBINE_ALPHA 0x8572 +#define GL_COMBINE_RGB 0x8571 +#define GL_COMMAND_BARRIER_BIT 0x00000040 +#define GL_COMPARE_REF_TO_TEXTURE 0x884E +#define GL_COMPARE_R_TO_TEXTURE 0x884E +#define GL_COMPATIBLE_SUBROUTINES 0x8E4B +#define GL_COMPILE 0x1300 +#define GL_COMPILE_AND_EXECUTE 0x1301 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_COMPRESSED_ALPHA 0x84E9 +#define GL_COMPRESSED_INTENSITY 0x84EC +#define GL_COMPRESSED_LUMINANCE 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB +#define GL_COMPRESSED_R11_EAC 0x9270 +#define GL_COMPRESSED_RED 0x8225 +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_RG 0x8226 +#define GL_COMPRESSED_RG11_EAC 0x9272 +#define GL_COMPRESSED_RGB 0x84ED +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_RGBA 0x84EE +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#define GL_COMPRESSED_SLUMINANCE 0x8C4A +#define GL_COMPRESSED_SLUMINANCE_ALPHA 0x8C4B +#define GL_COMPRESSED_SRGB 0x8C48 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_COMPUTE_SHADER 0x91B9 +#define GL_COMPUTE_SHADER_BIT 0x00000020 +#define GL_COMPUTE_SHADER_INVOCATIONS 0x82F5 +#define GL_COMPUTE_SUBROUTINE 0x92ED +#define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3 +#define GL_COMPUTE_TEXTURE 0x82A0 +#define GL_COMPUTE_WORK_GROUP_SIZE 0x8267 +#define GL_CONDITION_SATISFIED 0x911C +#define GL_CONSTANT 0x8576 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_CONSTANT_ATTENUATION 0x1207 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_CONTEXT_FLAGS 0x821E +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 +#define GL_CONTEXT_FLAG_NO_ERROR_BIT 0x00000008 +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT 0x00000004 +#define GL_CONTEXT_LOST 0x0507 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB +#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC +#define GL_CONVOLUTION_1D 0x8010 +#define GL_CONVOLUTION_2D 0x8011 +#define GL_COORD_REPLACE 0x8862 +#define GL_COPY 0x1503 +#define GL_COPY_INVERTED 0x150C +#define GL_COPY_PIXEL_TOKEN 0x0706 +#define GL_COPY_READ_BUFFER 0x8F36 +#define GL_COPY_READ_BUFFER_BINDING 0x8F36 +#define GL_COPY_WRITE_BUFFER 0x8F37 +#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37 +#define GL_CULL_FACE 0x0B44 +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_CURRENT_BIT 0x00000001 +#define GL_CURRENT_COLOR 0x0B00 +#define GL_CURRENT_FOG_COORD 0x8453 +#define GL_CURRENT_FOG_COORDINATE 0x8453 +#define GL_CURRENT_INDEX 0x0B01 +#define GL_CURRENT_NORMAL 0x0B02 +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_CURRENT_QUERY 0x8865 +#define GL_CURRENT_RASTER_COLOR 0x0B04 +#define GL_CURRENT_RASTER_DISTANCE 0x0B09 +#define GL_CURRENT_RASTER_INDEX 0x0B05 +#define GL_CURRENT_RASTER_POSITION 0x0B07 +#define GL_CURRENT_RASTER_POSITION_VALID 0x0B08 +#define GL_CURRENT_RASTER_SECONDARY_COLOR 0x845F +#define GL_CURRENT_RASTER_TEXTURE_COORDS 0x0B06 +#define GL_CURRENT_SECONDARY_COLOR 0x8459 +#define GL_CURRENT_TEXTURE_COORDS 0x0B03 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_CW 0x0900 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DECAL 0x2101 +#define GL_DECR 0x1E03 +#define GL_DECR_WRAP 0x8508 +#define GL_DELETE_STATUS 0x8B80 +#define GL_DEPTH 0x1801 +#define GL_DEPTH24_STENCIL8 0x88F0 +#define GL_DEPTH32F_STENCIL8 0x8CAD +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_DEPTH_BIAS 0x0D1F +#define GL_DEPTH_BITS 0x0D56 +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_DEPTH_CLAMP 0x864F +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#define GL_DEPTH_COMPONENT32F 0x8CAC +#define GL_DEPTH_COMPONENTS 0x8284 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_RENDERABLE 0x8287 +#define GL_DEPTH_SCALE 0x0D1E +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA +#define GL_DEPTH_TEST 0x0B71 +#define GL_DEPTH_TEXTURE_MODE 0x884B +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DIFFUSE 0x1201 +#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE +#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF +#define GL_DISPLAY_LIST 0x82E7 +#define GL_DITHER 0x0BD0 +#define GL_DOMAIN 0x0A02 +#define GL_DONT_CARE 0x1100 +#define GL_DOT3_RGB 0x86AE +#define GL_DOT3_RGBA 0x86AF +#define GL_DOUBLE 0x140A +#define GL_DOUBLEBUFFER 0x0C32 +#define GL_DOUBLE_MAT2 0x8F46 +#define GL_DOUBLE_MAT2x3 0x8F49 +#define GL_DOUBLE_MAT2x4 0x8F4A +#define GL_DOUBLE_MAT3 0x8F47 +#define GL_DOUBLE_MAT3x2 0x8F4B +#define GL_DOUBLE_MAT3x4 0x8F4C +#define GL_DOUBLE_MAT4 0x8F48 +#define GL_DOUBLE_MAT4x2 0x8F4D +#define GL_DOUBLE_MAT4x3 0x8F4E +#define GL_DOUBLE_VEC2 0x8FFC +#define GL_DOUBLE_VEC3 0x8FFD +#define GL_DOUBLE_VEC4 0x8FFE +#define GL_DRAW_BUFFER 0x0C01 +#define GL_DRAW_BUFFER0 0x8825 +#define GL_DRAW_BUFFER1 0x8826 +#define GL_DRAW_BUFFER10 0x882F +#define GL_DRAW_BUFFER11 0x8830 +#define GL_DRAW_BUFFER12 0x8831 +#define GL_DRAW_BUFFER13 0x8832 +#define GL_DRAW_BUFFER14 0x8833 +#define GL_DRAW_BUFFER15 0x8834 +#define GL_DRAW_BUFFER2 0x8827 +#define GL_DRAW_BUFFER3 0x8828 +#define GL_DRAW_BUFFER4 0x8829 +#define GL_DRAW_BUFFER5 0x882A +#define GL_DRAW_BUFFER6 0x882B +#define GL_DRAW_BUFFER7 0x882C +#define GL_DRAW_BUFFER8 0x882D +#define GL_DRAW_BUFFER9 0x882E +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_DRAW_INDIRECT_BUFFER 0x8F3F +#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43 +#define GL_DRAW_PIXEL_TOKEN 0x0705 +#define GL_DST_ALPHA 0x0304 +#define GL_DST_COLOR 0x0306 +#define GL_DYNAMIC_COPY 0x88EA +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_DYNAMIC_STORAGE_BIT 0x0100 +#define GL_EDGE_FLAG 0x0B43 +#define GL_EDGE_FLAG_ARRAY 0x8079 +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING 0x889B +#define GL_EDGE_FLAG_ARRAY_POINTER 0x8093 +#define GL_EDGE_FLAG_ARRAY_STRIDE 0x808C +#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_EMISSION 0x1600 +#define GL_ENABLE_BIT 0x00002000 +#define GL_EQUAL 0x0202 +#define GL_EQUIV 0x1509 +#define GL_EVAL_BIT 0x00010000 +#define GL_EXP 0x0800 +#define GL_EXP2 0x0801 +#define GL_EXTENSIONS 0x1F03 +#define GL_EYE_LINEAR 0x2400 +#define GL_EYE_PLANE 0x2502 +#define GL_FALSE 0 +#define GL_FASTEST 0x1101 +#define GL_FEEDBACK 0x1C01 +#define GL_FEEDBACK_BUFFER_POINTER 0x0DF0 +#define GL_FEEDBACK_BUFFER_SIZE 0x0DF1 +#define GL_FEEDBACK_BUFFER_TYPE 0x0DF2 +#define GL_FILL 0x1B02 +#define GL_FILTER 0x829A +#define GL_FIRST_VERTEX_CONVENTION 0x8E4D +#define GL_FIXED 0x140C +#define GL_FIXED_ONLY 0x891D +#define GL_FLAT 0x1D00 +#define GL_FLOAT 0x1406 +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4 0x8B5C +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_FOG 0x0B60 +#define GL_FOG_BIT 0x00000080 +#define GL_FOG_COLOR 0x0B66 +#define GL_FOG_COORD 0x8451 +#define GL_FOG_COORDINATE 0x8451 +#define GL_FOG_COORDINATE_ARRAY 0x8457 +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING 0x889D +#define GL_FOG_COORDINATE_ARRAY_POINTER 0x8456 +#define GL_FOG_COORDINATE_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORDINATE_ARRAY_TYPE 0x8454 +#define GL_FOG_COORDINATE_SOURCE 0x8450 +#define GL_FOG_COORD_ARRAY 0x8457 +#define GL_FOG_COORD_ARRAY_BUFFER_BINDING 0x889D +#define GL_FOG_COORD_ARRAY_POINTER 0x8456 +#define GL_FOG_COORD_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORD_ARRAY_TYPE 0x8454 +#define GL_FOG_COORD_SRC 0x8450 +#define GL_FOG_DENSITY 0x0B62 +#define GL_FOG_END 0x0B64 +#define GL_FOG_HINT 0x0C54 +#define GL_FOG_INDEX 0x0B61 +#define GL_FOG_MODE 0x0B65 +#define GL_FOG_START 0x0B63 +#define GL_FRACTIONAL_EVEN 0x8E7C +#define GL_FRACTIONAL_ODD 0x8E7B +#define GL_FRAGMENT_DEPTH 0x8452 +#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B +#define GL_FRAGMENT_SHADER_INVOCATIONS 0x82F4 +#define GL_FRAGMENT_SUBROUTINE 0x92EC +#define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2 +#define GL_FRAGMENT_TEXTURE 0x829F +#define GL_FRAMEBUFFER 0x8D40 +#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 +#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 +#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 +#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 +#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_FRAMEBUFFER_BLEND 0x828B +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_DEFAULT 0x8218 +#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314 +#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 +#define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312 +#define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313 +#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#define GL_FRAMEBUFFER_RENDERABLE 0x8289 +#define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#define GL_FRAMEBUFFER_UNDEFINED 0x8219 +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_FRONT 0x0404 +#define GL_FRONT_AND_BACK 0x0408 +#define GL_FRONT_FACE 0x0B46 +#define GL_FRONT_LEFT 0x0400 +#define GL_FRONT_RIGHT 0x0401 +#define GL_FULL_SUPPORT 0x82B7 +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_FUNC_SUBTRACT 0x800A +#define GL_GENERATE_MIPMAP 0x8191 +#define GL_GENERATE_MIPMAP_HINT 0x8192 +#define GL_GEOMETRY_INPUT_TYPE 0x8917 +#define GL_GEOMETRY_OUTPUT_TYPE 0x8918 +#define GL_GEOMETRY_SHADER 0x8DD9 +#define GL_GEOMETRY_SHADER_BIT 0x00000004 +#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F +#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED 0x82F3 +#define GL_GEOMETRY_SUBROUTINE 0x92EB +#define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1 +#define GL_GEOMETRY_TEXTURE 0x829E +#define GL_GEOMETRY_VERTICES_OUT 0x8916 +#define GL_GEQUAL 0x0206 +#define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291 +#define GL_GET_TEXTURE_IMAGE_TYPE 0x8292 +#define GL_GREATER 0x0204 +#define GL_GREEN 0x1904 +#define GL_GREEN_BIAS 0x0D19 +#define GL_GREEN_BITS 0x0D53 +#define GL_GREEN_INTEGER 0x8D95 +#define GL_GREEN_SCALE 0x0D18 +#define GL_GUILTY_CONTEXT_RESET 0x8253 +#define GL_HALF_FLOAT 0x140B +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_HIGH_INT 0x8DF5 +#define GL_HINT_BIT 0x00008000 +#define GL_HISTOGRAM 0x8024 +#define GL_IMAGE_1D 0x904C +#define GL_IMAGE_1D_ARRAY 0x9052 +#define GL_IMAGE_2D 0x904D +#define GL_IMAGE_2D_ARRAY 0x9053 +#define GL_IMAGE_2D_MULTISAMPLE 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 +#define GL_IMAGE_2D_RECT 0x904F +#define GL_IMAGE_3D 0x904E +#define GL_IMAGE_BINDING_ACCESS 0x8F3E +#define GL_IMAGE_BINDING_FORMAT 0x906E +#define GL_IMAGE_BINDING_LAYER 0x8F3D +#define GL_IMAGE_BINDING_LAYERED 0x8F3C +#define GL_IMAGE_BINDING_LEVEL 0x8F3B +#define GL_IMAGE_BINDING_NAME 0x8F3A +#define GL_IMAGE_BUFFER 0x9051 +#define GL_IMAGE_CLASS_10_10_10_2 0x82C3 +#define GL_IMAGE_CLASS_11_11_10 0x82C2 +#define GL_IMAGE_CLASS_1_X_16 0x82BE +#define GL_IMAGE_CLASS_1_X_32 0x82BB +#define GL_IMAGE_CLASS_1_X_8 0x82C1 +#define GL_IMAGE_CLASS_2_X_16 0x82BD +#define GL_IMAGE_CLASS_2_X_32 0x82BA +#define GL_IMAGE_CLASS_2_X_8 0x82C0 +#define GL_IMAGE_CLASS_4_X_16 0x82BC +#define GL_IMAGE_CLASS_4_X_32 0x82B9 +#define GL_IMAGE_CLASS_4_X_8 0x82BF +#define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8 +#define GL_IMAGE_CUBE 0x9050 +#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8 +#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7 +#define GL_IMAGE_PIXEL_FORMAT 0x82A9 +#define GL_IMAGE_PIXEL_TYPE 0x82AA +#define GL_IMAGE_TEXEL_SIZE 0x82A7 +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_INCR 0x1E02 +#define GL_INCR_WRAP 0x8507 +#define GL_INDEX 0x8222 +#define GL_INDEX_ARRAY 0x8077 +#define GL_INDEX_ARRAY_BUFFER_BINDING 0x8899 +#define GL_INDEX_ARRAY_POINTER 0x8091 +#define GL_INDEX_ARRAY_STRIDE 0x8086 +#define GL_INDEX_ARRAY_TYPE 0x8085 +#define GL_INDEX_BITS 0x0D51 +#define GL_INDEX_CLEAR_VALUE 0x0C20 +#define GL_INDEX_LOGIC_OP 0x0BF1 +#define GL_INDEX_MODE 0x0C30 +#define GL_INDEX_OFFSET 0x0D13 +#define GL_INDEX_SHIFT 0x0D12 +#define GL_INDEX_WRITEMASK 0x0C21 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_INNOCENT_CONTEXT_RESET 0x8254 +#define GL_INT 0x1404 +#define GL_INTENSITY 0x8049 +#define GL_INTENSITY12 0x804C +#define GL_INTENSITY16 0x804D +#define GL_INTENSITY4 0x804A +#define GL_INTENSITY8 0x804B +#define GL_INTERLEAVED_ATTRIBS 0x8C8C +#define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274 +#define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B +#define GL_INTERNALFORMAT_BLUE_SIZE 0x8273 +#define GL_INTERNALFORMAT_BLUE_TYPE 0x827A +#define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275 +#define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C +#define GL_INTERNALFORMAT_GREEN_SIZE 0x8272 +#define GL_INTERNALFORMAT_GREEN_TYPE 0x8279 +#define GL_INTERNALFORMAT_PREFERRED 0x8270 +#define GL_INTERNALFORMAT_RED_SIZE 0x8271 +#define GL_INTERNALFORMAT_RED_TYPE 0x8278 +#define GL_INTERNALFORMAT_SHARED_SIZE 0x8277 +#define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276 +#define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D +#define GL_INTERNALFORMAT_SUPPORTED 0x826F +#define GL_INTERPOLATE 0x8575 +#define GL_INT_2_10_10_10_REV 0x8D9F +#define GL_INT_IMAGE_1D 0x9057 +#define GL_INT_IMAGE_1D_ARRAY 0x905D +#define GL_INT_IMAGE_2D 0x9058 +#define GL_INT_IMAGE_2D_ARRAY 0x905E +#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 +#define GL_INT_IMAGE_2D_RECT 0x905A +#define GL_INT_IMAGE_3D 0x9059 +#define GL_INT_IMAGE_BUFFER 0x905C +#define GL_INT_IMAGE_CUBE 0x905B +#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F +#define GL_INT_SAMPLER_1D 0x8DC9 +#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE +#define GL_INT_SAMPLER_2D 0x8DCA +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C +#define GL_INT_SAMPLER_2D_RECT 0x8DCD +#define GL_INT_SAMPLER_3D 0x8DCB +#define GL_INT_SAMPLER_BUFFER 0x8DD0 +#define GL_INT_SAMPLER_CUBE 0x8DCC +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_INVALID_INDEX 0xFFFFFFFF +#define GL_INVALID_OPERATION 0x0502 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVERT 0x150A +#define GL_ISOLINES 0x8E7A +#define GL_IS_PER_PATCH 0x92E7 +#define GL_IS_ROW_MAJOR 0x9300 +#define GL_KEEP 0x1E00 +#define GL_LAST_VERTEX_CONVENTION 0x8E4E +#define GL_LAYER_PROVOKING_VERTEX 0x825E +#define GL_LEFT 0x0406 +#define GL_LEQUAL 0x0203 +#define GL_LESS 0x0201 +#define GL_LIGHT0 0x4000 +#define GL_LIGHT1 0x4001 +#define GL_LIGHT2 0x4002 +#define GL_LIGHT3 0x4003 +#define GL_LIGHT4 0x4004 +#define GL_LIGHT5 0x4005 +#define GL_LIGHT6 0x4006 +#define GL_LIGHT7 0x4007 +#define GL_LIGHTING 0x0B50 +#define GL_LIGHTING_BIT 0x00000040 +#define GL_LIGHT_MODEL_AMBIENT 0x0B53 +#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8 +#define GL_LIGHT_MODEL_LOCAL_VIEWER 0x0B51 +#define GL_LIGHT_MODEL_TWO_SIDE 0x0B52 +#define GL_LINE 0x1B01 +#define GL_LINEAR 0x2601 +#define GL_LINEAR_ATTENUATION 0x1208 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_LINES 0x0001 +#define GL_LINES_ADJACENCY 0x000A +#define GL_LINE_BIT 0x00000004 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_RESET_TOKEN 0x0707 +#define GL_LINE_SMOOTH 0x0B20 +#define GL_LINE_SMOOTH_HINT 0x0C52 +#define GL_LINE_STIPPLE 0x0B24 +#define GL_LINE_STIPPLE_PATTERN 0x0B25 +#define GL_LINE_STIPPLE_REPEAT 0x0B26 +#define GL_LINE_STRIP 0x0003 +#define GL_LINE_STRIP_ADJACENCY 0x000B +#define GL_LINE_TOKEN 0x0702 +#define GL_LINE_WIDTH 0x0B21 +#define GL_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_LINE_WIDTH_RANGE 0x0B22 +#define GL_LINK_STATUS 0x8B82 +#define GL_LIST_BASE 0x0B32 +#define GL_LIST_BIT 0x00020000 +#define GL_LIST_INDEX 0x0B33 +#define GL_LIST_MODE 0x0B30 +#define GL_LOAD 0x0101 +#define GL_LOCATION 0x930E +#define GL_LOCATION_COMPONENT 0x934A +#define GL_LOCATION_INDEX 0x930F +#define GL_LOGIC_OP 0x0BF1 +#define GL_LOGIC_OP_MODE 0x0BF0 +#define GL_LOSE_CONTEXT_ON_RESET 0x8252 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_LOW_FLOAT 0x8DF0 +#define GL_LOW_INT 0x8DF3 +#define GL_LUMINANCE 0x1909 +#define GL_LUMINANCE12 0x8041 +#define GL_LUMINANCE12_ALPHA12 0x8047 +#define GL_LUMINANCE12_ALPHA4 0x8046 +#define GL_LUMINANCE16 0x8042 +#define GL_LUMINANCE16_ALPHA16 0x8048 +#define GL_LUMINANCE4 0x803F +#define GL_LUMINANCE4_ALPHA4 0x8043 +#define GL_LUMINANCE6_ALPHA2 0x8044 +#define GL_LUMINANCE8 0x8040 +#define GL_LUMINANCE8_ALPHA8 0x8045 +#define GL_LUMINANCE_ALPHA 0x190A +#define GL_MAJOR_VERSION 0x821B +#define GL_MANUAL_GENERATE_MIPMAP 0x8294 +#define GL_MAP1_COLOR_4 0x0D90 +#define GL_MAP1_GRID_DOMAIN 0x0DD0 +#define GL_MAP1_GRID_SEGMENTS 0x0DD1 +#define GL_MAP1_INDEX 0x0D91 +#define GL_MAP1_NORMAL 0x0D92 +#define GL_MAP1_TEXTURE_COORD_1 0x0D93 +#define GL_MAP1_TEXTURE_COORD_2 0x0D94 +#define GL_MAP1_TEXTURE_COORD_3 0x0D95 +#define GL_MAP1_TEXTURE_COORD_4 0x0D96 +#define GL_MAP1_VERTEX_3 0x0D97 +#define GL_MAP1_VERTEX_4 0x0D98 +#define GL_MAP2_COLOR_4 0x0DB0 +#define GL_MAP2_GRID_DOMAIN 0x0DD2 +#define GL_MAP2_GRID_SEGMENTS 0x0DD3 +#define GL_MAP2_INDEX 0x0DB1 +#define GL_MAP2_NORMAL 0x0DB2 +#define GL_MAP2_TEXTURE_COORD_1 0x0DB3 +#define GL_MAP2_TEXTURE_COORD_2 0x0DB4 +#define GL_MAP2_TEXTURE_COORD_3 0x0DB5 +#define GL_MAP2_TEXTURE_COORD_4 0x0DB6 +#define GL_MAP2_VERTEX_3 0x0DB7 +#define GL_MAP2_VERTEX_4 0x0DB8 +#define GL_MAP_COHERENT_BIT 0x0080 +#define GL_MAP_COLOR 0x0D10 +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_PERSISTENT_BIT 0x0040 +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_STENCIL 0x0D11 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MATRIX_MODE 0x0BA0 +#define GL_MATRIX_STRIDE 0x92FF +#define GL_MAX 0x8008 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF +#define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC +#define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8 +#define GL_MAX_ATTRIB_STACK_DEPTH 0x0D35 +#define GL_MAX_CLIENT_ATTRIB_STACK_DEPTH 0x0D3B +#define GL_MAX_CLIP_DISTANCES 0x0D32 +#define GL_MAX_CLIP_PLANES 0x0D32 +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E +#define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7 +#define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1 +#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES 0x82FA +#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266 +#define GL_MAX_COMBINED_DIMENSIONS 0x8282 +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39 +#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39 +#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC +#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E +#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265 +#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264 +#define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD +#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB +#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262 +#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC +#define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB +#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263 +#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE +#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB +#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_MAX_CULL_DISTANCES 0x82F9 +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEPTH 0x8280 +#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F +#define GL_MAX_DRAW_BUFFERS 0x8824 +#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENT_INDEX 0x8D6B +#define GL_MAX_EVAL_ORDER 0x0D30 +#define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6 +#define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0 +#define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE +#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C +#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316 +#define GL_MAX_FRAMEBUFFER_LAYERS 0x9317 +#define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318 +#define GL_MAX_FRAMEBUFFER_WIDTH 0x9315 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF +#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD +#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 +#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 +#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A +#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF +#define GL_MAX_HEIGHT 0x827F +#define GL_MAX_IMAGE_SAMPLES 0x906D +#define GL_MAX_IMAGE_UNITS 0x8F38 +#define GL_MAX_INTEGER_SAMPLES 0x9110 +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_MAX_LAYERS 0x8281 +#define GL_MAX_LIGHTS 0x0D31 +#define GL_MAX_LIST_NESTING 0x0B31 +#define GL_MAX_MODELVIEW_STACK_DEPTH 0x0D36 +#define GL_MAX_NAME_LENGTH 0x92F6 +#define GL_MAX_NAME_STACK_DEPTH 0x0D37 +#define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7 +#define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8 +#define GL_MAX_PATCH_VERTICES 0x8E7D +#define GL_MAX_PIXEL_MAP_TABLE 0x0D34 +#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F +#define GL_MAX_PROJECTION_STACK_DEPTH 0x0D38 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_MAX_SAMPLES 0x8D57 +#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 +#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 +#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE +#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD +#define GL_MAX_SUBROUTINES 0x8DE7 +#define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD +#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB +#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C +#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83 +#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8 +#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81 +#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85 +#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89 +#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4 +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE +#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC +#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D +#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86 +#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9 +#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82 +#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A +#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80 +#define GL_MAX_TESS_GEN_LEVEL 0x8E7E +#define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84 +#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B +#define GL_MAX_TEXTURE_COORDS 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD +#define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_TEXTURE_STACK_DEPTH 0x0D39 +#define GL_MAX_TEXTURE_UNITS 0x84E2 +#define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_UNIFORM_LOCATIONS 0x826E +#define GL_MAX_VARYING_COMPONENTS 0x8B4B +#define GL_MAX_VARYING_FLOATS 0x8B4B +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2 +#define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA +#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9 +#define GL_MAX_VERTEX_ATTRIB_STRIDE 0x82E5 +#define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA +#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 +#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6 +#define GL_MAX_VERTEX_STREAMS 0x8E71 +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VIEWPORTS 0x825B +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_MAX_WIDTH 0x827E +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_MIN 0x8007 +#define GL_MINMAX 0x802E +#define GL_MINOR_VERSION 0x821C +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B +#define GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC +#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E +#define GL_MIN_SAMPLE_SHADING_VALUE 0x8C37 +#define GL_MIPMAP 0x8293 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_MIRROR_CLAMP_TO_EDGE 0x8743 +#define GL_MODELVIEW 0x1700 +#define GL_MODELVIEW_MATRIX 0x0BA6 +#define GL_MODELVIEW_STACK_DEPTH 0x0BA3 +#define GL_MODULATE 0x2100 +#define GL_MULT 0x0103 +#define GL_MULTISAMPLE 0x809D +#define GL_MULTISAMPLE_BIT 0x20000000 +#define GL_N3F_V3F 0x2A25 +#define GL_NAME_LENGTH 0x92F9 +#define GL_NAME_STACK_DEPTH 0x0D70 +#define GL_NAND 0x150E +#define GL_NEAREST 0x2600 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_NEGATIVE_ONE_TO_ONE 0x935E +#define GL_NEVER 0x0200 +#define GL_NICEST 0x1102 +#define GL_NONE 0 +#define GL_NOOP 0x1505 +#define GL_NOR 0x1508 +#define GL_NORMALIZE 0x0BA1 +#define GL_NORMAL_ARRAY 0x8075 +#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897 +#define GL_NORMAL_ARRAY_POINTER 0x808F +#define GL_NORMAL_ARRAY_STRIDE 0x807F +#define GL_NORMAL_ARRAY_TYPE 0x807E +#define GL_NORMAL_MAP 0x8511 +#define GL_NOTEQUAL 0x0205 +#define GL_NO_ERROR 0 +#define GL_NO_RESET_NOTIFICATION 0x8261 +#define GL_NUM_ACTIVE_VARIABLES 0x9304 +#define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_NUM_EXTENSIONS 0x821D +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#define GL_NUM_SAMPLE_COUNTS 0x9380 +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#define GL_NUM_SHADING_LANGUAGE_VERSIONS 0x82E9 +#define GL_NUM_SPIR_V_EXTENSIONS 0x9554 +#define GL_OBJECT_LINEAR 0x2401 +#define GL_OBJECT_PLANE 0x2501 +#define GL_OBJECT_TYPE 0x9112 +#define GL_OFFSET 0x92FC +#define GL_ONE 1 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB +#define GL_ONE_MINUS_SRC1_COLOR 0x88FA +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_OPERAND0_ALPHA 0x8598 +#define GL_OPERAND0_RGB 0x8590 +#define GL_OPERAND1_ALPHA 0x8599 +#define GL_OPERAND1_RGB 0x8591 +#define GL_OPERAND2_ALPHA 0x859A +#define GL_OPERAND2_RGB 0x8592 +#define GL_OR 0x1507 +#define GL_ORDER 0x0A01 +#define GL_OR_INVERTED 0x150D +#define GL_OR_REVERSE 0x150B +#define GL_OUT_OF_MEMORY 0x0505 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D +#define GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C +#define GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E +#define GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_PACK_LSB_FIRST 0x0D01 +#define GL_PACK_ROW_LENGTH 0x0D02 +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_SKIP_PIXELS 0x0D04 +#define GL_PACK_SKIP_ROWS 0x0D03 +#define GL_PACK_SWAP_BYTES 0x0D00 +#define GL_PARAMETER_BUFFER 0x80EE +#define GL_PARAMETER_BUFFER_BINDING 0x80EF +#define GL_PASS_THROUGH_TOKEN 0x0700 +#define GL_PATCHES 0x000E +#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 +#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 +#define GL_PATCH_VERTICES 0x8E72 +#define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50 +#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080 +#define GL_PIXEL_MAP_A_TO_A 0x0C79 +#define GL_PIXEL_MAP_A_TO_A_SIZE 0x0CB9 +#define GL_PIXEL_MAP_B_TO_B 0x0C78 +#define GL_PIXEL_MAP_B_TO_B_SIZE 0x0CB8 +#define GL_PIXEL_MAP_G_TO_G 0x0C77 +#define GL_PIXEL_MAP_G_TO_G_SIZE 0x0CB7 +#define GL_PIXEL_MAP_I_TO_A 0x0C75 +#define GL_PIXEL_MAP_I_TO_A_SIZE 0x0CB5 +#define GL_PIXEL_MAP_I_TO_B 0x0C74 +#define GL_PIXEL_MAP_I_TO_B_SIZE 0x0CB4 +#define GL_PIXEL_MAP_I_TO_G 0x0C73 +#define GL_PIXEL_MAP_I_TO_G_SIZE 0x0CB3 +#define GL_PIXEL_MAP_I_TO_I 0x0C70 +#define GL_PIXEL_MAP_I_TO_I_SIZE 0x0CB0 +#define GL_PIXEL_MAP_I_TO_R 0x0C72 +#define GL_PIXEL_MAP_I_TO_R_SIZE 0x0CB2 +#define GL_PIXEL_MAP_R_TO_R 0x0C76 +#define GL_PIXEL_MAP_R_TO_R_SIZE 0x0CB6 +#define GL_PIXEL_MAP_S_TO_S 0x0C71 +#define GL_PIXEL_MAP_S_TO_S_SIZE 0x0CB1 +#define GL_PIXEL_MODE_BIT 0x00000020 +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF +#define GL_POINT 0x1B00 +#define GL_POINTS 0x0000 +#define GL_POINT_BIT 0x00000002 +#define GL_POINT_DISTANCE_ATTENUATION 0x8129 +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_POINT_SIZE 0x0B11 +#define GL_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_POINT_SIZE_MAX 0x8127 +#define GL_POINT_SIZE_MIN 0x8126 +#define GL_POINT_SIZE_RANGE 0x0B12 +#define GL_POINT_SMOOTH 0x0B10 +#define GL_POINT_SMOOTH_HINT 0x0C51 +#define GL_POINT_SPRITE 0x8861 +#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 +#define GL_POINT_TOKEN 0x0701 +#define GL_POLYGON 0x0009 +#define GL_POLYGON_BIT 0x00000008 +#define GL_POLYGON_MODE 0x0B40 +#define GL_POLYGON_OFFSET_CLAMP 0x8E1B +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_POLYGON_OFFSET_LINE 0x2A02 +#define GL_POLYGON_OFFSET_POINT 0x2A01 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#define GL_POLYGON_SMOOTH 0x0B41 +#define GL_POLYGON_SMOOTH_HINT 0x0C53 +#define GL_POLYGON_STIPPLE 0x0B42 +#define GL_POLYGON_STIPPLE_BIT 0x00000010 +#define GL_POLYGON_TOKEN 0x0703 +#define GL_POSITION 0x1203 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2 +#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1 +#define GL_PREVIOUS 0x8578 +#define GL_PRIMARY_COLOR 0x8577 +#define GL_PRIMITIVES_GENERATED 0x8C87 +#define GL_PRIMITIVES_SUBMITTED 0x82EF +#define GL_PRIMITIVE_RESTART 0x8F9D +#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 +#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221 +#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E +#define GL_PROGRAM 0x82E2 +#define GL_PROGRAM_BINARY_FORMATS 0x87FF +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 +#define GL_PROGRAM_INPUT 0x92E3 +#define GL_PROGRAM_OUTPUT 0x92E4 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_PROGRAM_PIPELINE_BINDING 0x825A +#define GL_PROGRAM_POINT_SIZE 0x8642 +#define GL_PROGRAM_SEPARABLE 0x8258 +#define GL_PROJECTION 0x1701 +#define GL_PROJECTION_MATRIX 0x0BA7 +#define GL_PROJECTION_STACK_DEPTH 0x0BA4 +#define GL_PROVOKING_VERTEX 0x8E4F +#define GL_PROXY_COLOR_TABLE 0x80D3 +#define GL_PROXY_HISTOGRAM 0x8025 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4 +#define GL_PROXY_TEXTURE_1D 0x8063 +#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 +#define GL_PROXY_TEXTURE_2D 0x8064 +#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY 0x900B +#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 +#define GL_Q 0x2003 +#define GL_QUADRATIC_ATTENUATION 0x1209 +#define GL_QUADS 0x0007 +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C +#define GL_QUAD_STRIP 0x0008 +#define GL_QUERY 0x82E3 +#define GL_QUERY_BUFFER 0x9192 +#define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000 +#define GL_QUERY_BUFFER_BINDING 0x9193 +#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 +#define GL_QUERY_BY_REGION_NO_WAIT_INVERTED 0x8E1A +#define GL_QUERY_BY_REGION_WAIT 0x8E15 +#define GL_QUERY_BY_REGION_WAIT_INVERTED 0x8E19 +#define GL_QUERY_COUNTER_BITS 0x8864 +#define GL_QUERY_NO_WAIT 0x8E14 +#define GL_QUERY_NO_WAIT_INVERTED 0x8E18 +#define GL_QUERY_RESULT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_QUERY_RESULT_NO_WAIT 0x9194 +#define GL_QUERY_TARGET 0x82EA +#define GL_QUERY_WAIT 0x8E13 +#define GL_QUERY_WAIT_INVERTED 0x8E17 +#define GL_R 0x2002 +#define GL_R11F_G11F_B10F 0x8C3A +#define GL_R16 0x822A +#define GL_R16F 0x822D +#define GL_R16I 0x8233 +#define GL_R16UI 0x8234 +#define GL_R16_SNORM 0x8F98 +#define GL_R32F 0x822E +#define GL_R32I 0x8235 +#define GL_R32UI 0x8236 +#define GL_R3_G3_B2 0x2A10 +#define GL_R8 0x8229 +#define GL_R8I 0x8231 +#define GL_R8UI 0x8232 +#define GL_R8_SNORM 0x8F94 +#define GL_RASTERIZER_DISCARD 0x8C89 +#define GL_READ_BUFFER 0x0C02 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA +#define GL_READ_ONLY 0x88B8 +#define GL_READ_PIXELS 0x828C +#define GL_READ_PIXELS_FORMAT 0x828D +#define GL_READ_PIXELS_TYPE 0x828E +#define GL_READ_WRITE 0x88BA +#define GL_RED 0x1903 +#define GL_RED_BIAS 0x0D15 +#define GL_RED_BITS 0x0D52 +#define GL_RED_INTEGER 0x8D94 +#define GL_RED_SCALE 0x0D14 +#define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B +#define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A +#define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309 +#define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307 +#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308 +#define GL_REFERENCED_BY_VERTEX_SHADER 0x9306 +#define GL_REFLECTION_MAP 0x8512 +#define GL_RENDER 0x1C00 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_SAMPLES 0x8CAB +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERER 0x1F01 +#define GL_RENDER_MODE 0x0C40 +#define GL_REPEAT 0x2901 +#define GL_REPLACE 0x1E01 +#define GL_RESCALE_NORMAL 0x803A +#define GL_RESET_NOTIFICATION_STRATEGY 0x8256 +#define GL_RETURN 0x0102 +#define GL_RG 0x8227 +#define GL_RG16 0x822C +#define GL_RG16F 0x822F +#define GL_RG16I 0x8239 +#define GL_RG16UI 0x823A +#define GL_RG16_SNORM 0x8F99 +#define GL_RG32F 0x8230 +#define GL_RG32I 0x823B +#define GL_RG32UI 0x823C +#define GL_RG8 0x822B +#define GL_RG8I 0x8237 +#define GL_RG8UI 0x8238 +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB 0x1907 +#define GL_RGB10 0x8052 +#define GL_RGB10_A2 0x8059 +#define GL_RGB10_A2UI 0x906F +#define GL_RGB12 0x8053 +#define GL_RGB16 0x8054 +#define GL_RGB16F 0x881B +#define GL_RGB16I 0x8D89 +#define GL_RGB16UI 0x8D77 +#define GL_RGB16_SNORM 0x8F9A +#define GL_RGB32F 0x8815 +#define GL_RGB32I 0x8D83 +#define GL_RGB32UI 0x8D71 +#define GL_RGB4 0x804F +#define GL_RGB5 0x8050 +#define GL_RGB565 0x8D62 +#define GL_RGB5_A1 0x8057 +#define GL_RGB8 0x8051 +#define GL_RGB8I 0x8D8F +#define GL_RGB8UI 0x8D7D +#define GL_RGB8_SNORM 0x8F96 +#define GL_RGB9_E5 0x8C3D +#define GL_RGBA 0x1908 +#define GL_RGBA12 0x805A +#define GL_RGBA16 0x805B +#define GL_RGBA16F 0x881A +#define GL_RGBA16I 0x8D88 +#define GL_RGBA16UI 0x8D76 +#define GL_RGBA16_SNORM 0x8F9B +#define GL_RGBA2 0x8055 +#define GL_RGBA32F 0x8814 +#define GL_RGBA32I 0x8D82 +#define GL_RGBA32UI 0x8D70 +#define GL_RGBA4 0x8056 +#define GL_RGBA8 0x8058 +#define GL_RGBA8I 0x8D8E +#define GL_RGBA8UI 0x8D7C +#define GL_RGBA8_SNORM 0x8F97 +#define GL_RGBA_INTEGER 0x8D99 +#define GL_RGBA_MODE 0x0C31 +#define GL_RGB_INTEGER 0x8D98 +#define GL_RGB_SCALE 0x8573 +#define GL_RG_INTEGER 0x8228 +#define GL_RIGHT 0x0407 +#define GL_S 0x2000 +#define GL_SAMPLER 0x82E6 +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_1D_ARRAY 0x8DC0 +#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B +#define GL_SAMPLER_2D_RECT 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_BINDING 0x8919 +#define GL_SAMPLER_BUFFER 0x8DC2 +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLES_PASSED 0x8914 +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_MASK 0x8E51 +#define GL_SAMPLE_MASK_VALUE 0x8E52 +#define GL_SAMPLE_POSITION 0x8E50 +#define GL_SAMPLE_SHADING 0x8C36 +#define GL_SCISSOR_BIT 0x00080000 +#define GL_SCISSOR_BOX 0x0C10 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_SECONDARY_COLOR_ARRAY 0x845E +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING 0x889C +#define GL_SECONDARY_COLOR_ARRAY_POINTER 0x845D +#define GL_SECONDARY_COLOR_ARRAY_SIZE 0x845A +#define GL_SECONDARY_COLOR_ARRAY_STRIDE 0x845C +#define GL_SECONDARY_COLOR_ARRAY_TYPE 0x845B +#define GL_SELECT 0x1C02 +#define GL_SELECTION_BUFFER_POINTER 0x0DF3 +#define GL_SELECTION_BUFFER_SIZE 0x0DF4 +#define GL_SEPARABLE_2D 0x8012 +#define GL_SEPARATE_ATTRIBS 0x8C8D +#define GL_SEPARATE_SPECULAR_COLOR 0x81FA +#define GL_SET 0x150F +#define GL_SHADER 0x82E1 +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_SHADER_BINARY_FORMAT_SPIR_V 0x9551 +#define GL_SHADER_COMPILER 0x8DFA +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 +#define GL_SHADER_IMAGE_ATOMIC 0x82A6 +#define GL_SHADER_IMAGE_LOAD 0x82A4 +#define GL_SHADER_IMAGE_STORE 0x82A5 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000 +#define GL_SHADER_STORAGE_BLOCK 0x92E6 +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3 +#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF +#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5 +#define GL_SHADER_STORAGE_BUFFER_START 0x90D4 +#define GL_SHADER_TYPE 0x8B4F +#define GL_SHADE_MODEL 0x0B54 +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_SHININESS 0x1601 +#define GL_SHORT 0x1402 +#define GL_SIGNALED 0x9119 +#define GL_SIGNED_NORMALIZED 0x8F9C +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF +#define GL_SINGLE_COLOR 0x81F9 +#define GL_SLUMINANCE 0x8C46 +#define GL_SLUMINANCE8 0x8C47 +#define GL_SLUMINANCE8_ALPHA8 0x8C45 +#define GL_SLUMINANCE_ALPHA 0x8C44 +#define GL_SMOOTH 0x1D01 +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SOURCE0_ALPHA 0x8588 +#define GL_SOURCE0_RGB 0x8580 +#define GL_SOURCE1_ALPHA 0x8589 +#define GL_SOURCE1_RGB 0x8581 +#define GL_SOURCE2_ALPHA 0x858A +#define GL_SOURCE2_RGB 0x8582 +#define GL_SPECULAR 0x1202 +#define GL_SPHERE_MAP 0x2402 +#define GL_SPIR_V_BINARY 0x9552 +#define GL_SPIR_V_EXTENSIONS 0x9553 +#define GL_SPOT_CUTOFF 0x1206 +#define GL_SPOT_DIRECTION 0x1204 +#define GL_SPOT_EXPONENT 0x1205 +#define GL_SRC0_ALPHA 0x8588 +#define GL_SRC0_RGB 0x8580 +#define GL_SRC1_ALPHA 0x8589 +#define GL_SRC1_COLOR 0x88F9 +#define GL_SRC1_RGB 0x8581 +#define GL_SRC2_ALPHA 0x858A +#define GL_SRC2_RGB 0x8582 +#define GL_SRC_ALPHA 0x0302 +#define GL_SRC_ALPHA_SATURATE 0x0308 +#define GL_SRC_COLOR 0x0300 +#define GL_SRGB 0x8C40 +#define GL_SRGB8 0x8C41 +#define GL_SRGB8_ALPHA8 0x8C43 +#define GL_SRGB_ALPHA 0x8C42 +#define GL_SRGB_READ 0x8297 +#define GL_SRGB_WRITE 0x8298 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_STATIC_COPY 0x88E6 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STENCIL 0x1802 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#define GL_STENCIL_BITS 0x0D57 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_COMPONENTS 0x8285 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_INDEX 0x1901 +#define GL_STENCIL_INDEX1 0x8D46 +#define GL_STENCIL_INDEX16 0x8D49 +#define GL_STENCIL_INDEX4 0x8D47 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_RENDERABLE 0x8288 +#define GL_STENCIL_TEST 0x0B90 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_STEREO 0x0C33 +#define GL_STREAM_COPY 0x88E2 +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_SUBTRACT 0x84E7 +#define GL_SYNC_CONDITION 0x9113 +#define GL_SYNC_FENCE 0x9116 +#define GL_SYNC_FLAGS 0x9115 +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 +#define GL_SYNC_STATUS 0x9114 +#define GL_T 0x2001 +#define GL_T2F_C3F_V3F 0x2A2A +#define GL_T2F_C4F_N3F_V3F 0x2A2C +#define GL_T2F_C4UB_V3F 0x2A29 +#define GL_T2F_N3F_V3F 0x2A2B +#define GL_T2F_V3F 0x2A27 +#define GL_T4F_C4F_N3F_V4F 0x2A2D +#define GL_T4F_V4F 0x2A28 +#define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75 +#define GL_TESS_CONTROL_SHADER 0x8E88 +#define GL_TESS_CONTROL_SHADER_BIT 0x00000008 +#define GL_TESS_CONTROL_SHADER_PATCHES 0x82F1 +#define GL_TESS_CONTROL_SUBROUTINE 0x92E9 +#define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF +#define GL_TESS_CONTROL_TEXTURE 0x829C +#define GL_TESS_EVALUATION_SHADER 0x8E87 +#define GL_TESS_EVALUATION_SHADER_BIT 0x00000010 +#define GL_TESS_EVALUATION_SHADER_INVOCATIONS 0x82F2 +#define GL_TESS_EVALUATION_SUBROUTINE 0x92EA +#define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0 +#define GL_TESS_EVALUATION_TEXTURE 0x829D +#define GL_TESS_GEN_MODE 0x8E76 +#define GL_TESS_GEN_POINT_MODE 0x8E79 +#define GL_TESS_GEN_SPACING 0x8E77 +#define GL_TESS_GEN_VERTEX_ORDER 0x8E78 +#define GL_TEXTURE 0x1702 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE_1D 0x0DE0 +#define GL_TEXTURE_1D_ARRAY 0x8C18 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#define GL_TEXTURE_3D 0x806F +#define GL_TEXTURE_ALPHA_SIZE 0x805F +#define GL_TEXTURE_ALPHA_TYPE 0x8C13 +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_TEXTURE_BINDING_1D 0x8068 +#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 +#define GL_TEXTURE_BINDING_3D 0x806A +#define GL_TEXTURE_BINDING_BUFFER 0x8C2C +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A +#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 +#define GL_TEXTURE_BIT 0x00040000 +#define GL_TEXTURE_BLUE_SIZE 0x805E +#define GL_TEXTURE_BLUE_TYPE 0x8C12 +#define GL_TEXTURE_BORDER 0x1005 +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#define GL_TEXTURE_BUFFER 0x8C2A +#define GL_TEXTURE_BUFFER_BINDING 0x8C2A +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D +#define GL_TEXTURE_BUFFER_OFFSET 0x919D +#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F +#define GL_TEXTURE_BUFFER_SIZE 0x919E +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#define GL_TEXTURE_COMPARE_MODE 0x884C +#define GL_TEXTURE_COMPONENTS 0x1003 +#define GL_TEXTURE_COMPRESSED 0x86A1 +#define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2 +#define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3 +#define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1 +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 +#define GL_TEXTURE_COMPRESSION_HINT 0x84EF +#define GL_TEXTURE_COORD_ARRAY 0x8078 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A +#define GL_TEXTURE_COORD_ARRAY_POINTER 0x8092 +#define GL_TEXTURE_COORD_ARRAY_SIZE 0x8088 +#define GL_TEXTURE_COORD_ARRAY_STRIDE 0x808A +#define GL_TEXTURE_COORD_ARRAY_TYPE 0x8089 +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_DEPTH_SIZE 0x884A +#define GL_TEXTURE_DEPTH_TYPE 0x8C16 +#define GL_TEXTURE_ENV 0x2300 +#define GL_TEXTURE_ENV_COLOR 0x2201 +#define GL_TEXTURE_ENV_MODE 0x2200 +#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 +#define GL_TEXTURE_FILTER_CONTROL 0x8500 +#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 +#define GL_TEXTURE_GATHER 0x82A2 +#define GL_TEXTURE_GATHER_SHADOW 0x82A3 +#define GL_TEXTURE_GEN_MODE 0x2500 +#define GL_TEXTURE_GEN_Q 0x0C63 +#define GL_TEXTURE_GEN_R 0x0C62 +#define GL_TEXTURE_GEN_S 0x0C60 +#define GL_TEXTURE_GEN_T 0x0C61 +#define GL_TEXTURE_GREEN_SIZE 0x805D +#define GL_TEXTURE_GREEN_TYPE 0x8C11 +#define GL_TEXTURE_HEIGHT 0x1001 +#define GL_TEXTURE_IMAGE_FORMAT 0x828F +#define GL_TEXTURE_IMAGE_TYPE 0x8290 +#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F +#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF +#define GL_TEXTURE_INTENSITY_SIZE 0x8061 +#define GL_TEXTURE_INTENSITY_TYPE 0x8C15 +#define GL_TEXTURE_INTERNAL_FORMAT 0x1003 +#define GL_TEXTURE_LOD_BIAS 0x8501 +#define GL_TEXTURE_LUMINANCE_SIZE 0x8060 +#define GL_TEXTURE_LUMINANCE_TYPE 0x8C14 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MATRIX 0x0BA8 +#define GL_TEXTURE_MAX_ANISOTROPY 0x84FE +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_PRIORITY 0x8066 +#define GL_TEXTURE_RECTANGLE 0x84F5 +#define GL_TEXTURE_RED_SIZE 0x805C +#define GL_TEXTURE_RED_TYPE 0x8C10 +#define GL_TEXTURE_RESIDENT 0x8067 +#define GL_TEXTURE_SAMPLES 0x9106 +#define GL_TEXTURE_SHADOW 0x82A1 +#define GL_TEXTURE_SHARED_SIZE 0x8C3F +#define GL_TEXTURE_STACK_DEPTH 0x0BA5 +#define GL_TEXTURE_STENCIL_SIZE 0x88F1 +#define GL_TEXTURE_SWIZZLE_A 0x8E45 +#define GL_TEXTURE_SWIZZLE_B 0x8E44 +#define GL_TEXTURE_SWIZZLE_G 0x8E43 +#define GL_TEXTURE_SWIZZLE_R 0x8E42 +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#define GL_TEXTURE_TARGET 0x1006 +#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100 +#define GL_TEXTURE_VIEW 0x82B5 +#define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD +#define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB +#define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE +#define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC +#define GL_TEXTURE_WIDTH 0x1000 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_TIMEOUT_EXPIRED 0x911B +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFF +#define GL_TIMESTAMP 0x8E28 +#define GL_TIME_ELAPSED 0x88BF +#define GL_TOP_LEVEL_ARRAY_SIZE 0x930C +#define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D +#define GL_TRANSFORM_BIT 0x00001000 +#define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800 +#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 +#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F +#define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C +#define GL_TRANSFORM_FEEDBACK_OVERFLOW 0x82EC +#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 +#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW 0x82ED +#define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4 +#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 +#define GL_TRANSPOSE_COLOR_MATRIX 0x84E6 +#define GL_TRANSPOSE_MODELVIEW_MATRIX 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX 0x84E5 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLES_ADJACENCY 0x000C +#define GL_TRIANGLE_FAN 0x0006 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D +#define GL_TRUE 1 +#define GL_TYPE 0x92FA +#define GL_UNDEFINED_VERTEX 0x8260 +#define GL_UNIFORM 0x92E1 +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA +#define GL_UNIFORM_BARRIER_BIT 0x00000004 +#define GL_UNIFORM_BLOCK 0x92E2 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNKNOWN_CONTEXT_RESET 0x8255 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129 +#define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128 +#define GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A +#define GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127 +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_UNPACK_LSB_FIRST 0x0CF1 +#define GL_UNPACK_ROW_LENGTH 0x0CF2 +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_SKIP_PIXELS 0x0CF4 +#define GL_UNPACK_SKIP_ROWS 0x0CF3 +#define GL_UNPACK_SWAP_BYTES 0x0CF0 +#define GL_UNSIGNALED 0x9118 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_INT 0x1405 +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_UNSIGNED_INT_24_8 0x84FA +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB +#define GL_UNSIGNED_INT_IMAGE_1D 0x9062 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D 0x9063 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C +#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 +#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 +#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A +#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 +#define GL_UNSIGNED_NORMALIZED 0x8C17 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_V2F 0x2A20 +#define GL_V3F 0x2A21 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_VENDOR 0x1F00 +#define GL_VERSION 0x1F02 +#define GL_VERTEX_ARRAY 0x8074 +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896 +#define GL_VERTEX_ARRAY_POINTER 0x808E +#define GL_VERTEX_ARRAY_SIZE 0x807A +#define GL_VERTEX_ARRAY_STRIDE 0x807C +#define GL_VERTEX_ARRAY_TYPE 0x807B +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD +#define GL_VERTEX_ATTRIB_ARRAY_LONG 0x874E +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_VERTEX_ATTRIB_BINDING 0x82D4 +#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5 +#define GL_VERTEX_BINDING_BUFFER 0x8F4F +#define GL_VERTEX_BINDING_DIVISOR 0x82D6 +#define GL_VERTEX_BINDING_OFFSET 0x82D7 +#define GL_VERTEX_BINDING_STRIDE 0x82D8 +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE 0x8643 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_VERTEX_SHADER_BIT 0x00000001 +#define GL_VERTEX_SHADER_INVOCATIONS 0x82F0 +#define GL_VERTEX_SUBROUTINE 0x92E8 +#define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE +#define GL_VERTEX_TEXTURE 0x829B +#define GL_VERTICES_SUBMITTED 0x82EE +#define GL_VIEWPORT 0x0BA2 +#define GL_VIEWPORT_BIT 0x00000800 +#define GL_VIEWPORT_BOUNDS_RANGE 0x825D +#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F +#define GL_VIEWPORT_SUBPIXEL_BITS 0x825C +#define GL_VIEW_CLASS_128_BITS 0x82C4 +#define GL_VIEW_CLASS_16_BITS 0x82CA +#define GL_VIEW_CLASS_24_BITS 0x82C9 +#define GL_VIEW_CLASS_32_BITS 0x82C8 +#define GL_VIEW_CLASS_48_BITS 0x82C7 +#define GL_VIEW_CLASS_64_BITS 0x82C6 +#define GL_VIEW_CLASS_8_BITS 0x82CB +#define GL_VIEW_CLASS_96_BITS 0x82C5 +#define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3 +#define GL_VIEW_CLASS_BPTC_UNORM 0x82D2 +#define GL_VIEW_CLASS_RGTC1_RED 0x82D0 +#define GL_VIEW_CLASS_RGTC2_RG 0x82D1 +#define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC +#define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD +#define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE +#define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF +#define GL_VIEW_COMPATIBILITY_CLASS 0x82B6 +#define GL_WAIT_FAILED 0x911D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING 0x889E +#define GL_WRITE_ONLY 0x88B9 +#define GL_XOR 0x1506 +#define GL_ZERO 0 +#define GL_ZERO_TO_ONE 0x935F +#define GL_ZOOM_X 0x0D16 +#define GL_ZOOM_Y 0x0D17 + + +#include + +typedef unsigned int GLenum; + +typedef unsigned char GLboolean; + +typedef unsigned int GLbitfield; + +typedef void GLvoid; + +typedef khronos_int8_t GLbyte; + +typedef khronos_uint8_t GLubyte; + +typedef khronos_int16_t GLshort; + +typedef khronos_uint16_t GLushort; + +typedef int GLint; + +typedef unsigned int GLuint; + +typedef khronos_int32_t GLclampx; + +typedef int GLsizei; + +typedef khronos_float_t GLfloat; + +typedef khronos_float_t GLclampf; + +typedef double GLdouble; + +typedef double GLclampd; + +typedef void *GLeglClientBufferEXT; + +typedef void *GLeglImageOES; + +typedef char GLchar; + +typedef char GLcharARB; + +#ifdef __APPLE__ +typedef void *GLhandleARB; +#else +typedef unsigned int GLhandleARB; +#endif + +typedef khronos_uint16_t GLhalf; + +typedef khronos_uint16_t GLhalfARB; + +typedef khronos_int32_t GLfixed; + +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_intptr_t GLintptr; +#else +typedef khronos_intptr_t GLintptr; +#endif + +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_intptr_t GLintptrARB; +#else +typedef khronos_intptr_t GLintptrARB; +#endif + +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_ssize_t GLsizeiptr; +#else +typedef khronos_ssize_t GLsizeiptr; +#endif + +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_ssize_t GLsizeiptrARB; +#else +typedef khronos_ssize_t GLsizeiptrARB; +#endif + +typedef khronos_int64_t GLint64; + +typedef khronos_int64_t GLint64EXT; + +typedef khronos_uint64_t GLuint64; + +typedef khronos_uint64_t GLuint64EXT; + +typedef struct __GLsync *GLsync; + +struct _cl_context; + +struct _cl_event; + +typedef void(GLAD_API_PTR *GLDEBUGPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam); + +typedef void(GLAD_API_PTR *GLDEBUGPROCARB)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam); + +typedef void(GLAD_API_PTR *GLDEBUGPROCKHR)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam); + +typedef void(GLAD_API_PTR *GLDEBUGPROCAMD)(GLuint id, GLenum category, GLenum severity, GLsizei length, const GLchar *message, void *userParam); + +typedef unsigned short GLhalfNV; + +typedef GLintptr GLvdpauSurfaceNV; + +typedef void(GLAD_API_PTR *GLVULKANPROCNV)(void); + + +#define GL_VERSION_1_0 1 +#define GL_VERSION_1_1 1 +#define GL_VERSION_1_2 1 +#define GL_VERSION_1_3 1 +#define GL_VERSION_1_4 1 +#define GL_VERSION_1_5 1 +#define GL_VERSION_2_0 1 +#define GL_VERSION_2_1 1 +#define GL_VERSION_3_0 1 +#define GL_VERSION_3_1 1 +#define GL_VERSION_3_2 1 +#define GL_VERSION_3_3 1 +#define GL_VERSION_4_0 1 +#define GL_VERSION_4_1 1 +#define GL_VERSION_4_2 1 +#define GL_VERSION_4_3 1 +#define GL_VERSION_4_4 1 +#define GL_VERSION_4_5 1 +#define GL_VERSION_4_6 1 + +typedef void(GLAD_API_PTR *PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)(GLenum target, GLeglImageOES image); +typedef void(GLAD_API_PTR *PFNGLACCUMPROC)(GLenum op, GLfloat value); +typedef void(GLAD_API_PTR *PFNGLACTIVESHADERPROGRAMPROC)(GLuint pipeline, GLuint program); +typedef void(GLAD_API_PTR *PFNGLACTIVETEXTUREPROC)(GLenum texture); +typedef void(GLAD_API_PTR *PFNGLALPHAFUNCPROC)(GLenum func, GLfloat ref); +typedef GLboolean(GLAD_API_PTR *PFNGLARETEXTURESRESIDENTPROC)(GLsizei n, const GLuint *textures, GLboolean *residences); +typedef void(GLAD_API_PTR *PFNGLARRAYELEMENTPROC)(GLint i); +typedef void(GLAD_API_PTR *PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader); +typedef void(GLAD_API_PTR *PFNGLBEGINPROC)(GLenum mode); +typedef void(GLAD_API_PTR *PFNGLBEGINCONDITIONALRENDERPROC)(GLuint id, GLenum mode); +typedef void(GLAD_API_PTR *PFNGLBEGINQUERYPROC)(GLenum target, GLuint id); +typedef void(GLAD_API_PTR *PFNGLBEGINQUERYINDEXEDPROC)(GLenum target, GLuint index, GLuint id); +typedef void(GLAD_API_PTR *PFNGLBEGINTRANSFORMFEEDBACKPROC)(GLenum primitiveMode); +typedef void(GLAD_API_PTR *PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar *name); +typedef void(GLAD_API_PTR *PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer); +typedef void(GLAD_API_PTR *PFNGLBINDBUFFERBASEPROC)(GLenum target, GLuint index, GLuint buffer); +typedef void(GLAD_API_PTR *PFNGLBINDBUFFERRANGEPROC)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void(GLAD_API_PTR *PFNGLBINDBUFFERSBASEPROC)(GLenum target, GLuint first, GLsizei count, const GLuint *buffers); +typedef void(GLAD_API_PTR *PFNGLBINDBUFFERSRANGEPROC)(GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); +typedef void(GLAD_API_PTR *PFNGLBINDFRAGDATALOCATIONPROC)(GLuint program, GLuint color, const GLchar *name); +typedef void(GLAD_API_PTR *PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)(GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); +typedef void(GLAD_API_PTR *PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer); +typedef void(GLAD_API_PTR *PFNGLBINDIMAGETEXTUREPROC)(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); +typedef void(GLAD_API_PTR *PFNGLBINDIMAGETEXTURESPROC)(GLuint first, GLsizei count, const GLuint *textures); +typedef void(GLAD_API_PTR *PFNGLBINDPROGRAMPIPELINEPROC)(GLuint pipeline); +typedef void(GLAD_API_PTR *PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer); +typedef void(GLAD_API_PTR *PFNGLBINDSAMPLERPROC)(GLuint unit, GLuint sampler); +typedef void(GLAD_API_PTR *PFNGLBINDSAMPLERSPROC)(GLuint first, GLsizei count, const GLuint *samplers); +typedef void(GLAD_API_PTR *PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture); +typedef void(GLAD_API_PTR *PFNGLBINDTEXTUREUNITPROC)(GLuint unit, GLuint texture); +typedef void(GLAD_API_PTR *PFNGLBINDTEXTURESPROC)(GLuint first, GLsizei count, const GLuint *textures); +typedef void(GLAD_API_PTR *PFNGLBINDTRANSFORMFEEDBACKPROC)(GLenum target, GLuint id); +typedef void(GLAD_API_PTR *PFNGLBINDVERTEXARRAYPROC)(GLuint array); +typedef void(GLAD_API_PTR *PFNGLBINDVERTEXBUFFERPROC)(GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void(GLAD_API_PTR *PFNGLBINDVERTEXBUFFERSPROC)(GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +typedef void(GLAD_API_PTR *PFNGLBITMAPPROC)(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap); +typedef void(GLAD_API_PTR *PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void(GLAD_API_PTR *PFNGLBLENDEQUATIONPROC)(GLenum mode); +typedef void(GLAD_API_PTR *PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha); +typedef void(GLAD_API_PTR *PFNGLBLENDEQUATIONSEPARATEIPROC)(GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void(GLAD_API_PTR *PFNGLBLENDEQUATIONIPROC)(GLuint buf, GLenum mode); +typedef void(GLAD_API_PTR *PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor); +typedef void(GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void(GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEIPROC)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void(GLAD_API_PTR *PFNGLBLENDFUNCIPROC)(GLuint buf, GLenum src, GLenum dst); +typedef void(GLAD_API_PTR *PFNGLBLITFRAMEBUFFERPROC)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void(GLAD_API_PTR *PFNGLBLITNAMEDFRAMEBUFFERPROC)(GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void(GLAD_API_PTR *PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void *data, GLenum usage); +typedef void(GLAD_API_PTR *PFNGLBUFFERSTORAGEPROC)(GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); +typedef void(GLAD_API_PTR *PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void *data); +typedef void(GLAD_API_PTR *PFNGLCALLLISTPROC)(GLuint list); +typedef void(GLAD_API_PTR *PFNGLCALLLISTSPROC)(GLsizei n, GLenum type, const void *lists); +typedef GLenum(GLAD_API_PTR *PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target); +typedef GLenum(GLAD_API_PTR *PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC)(GLuint framebuffer, GLenum target); +typedef void(GLAD_API_PTR *PFNGLCLAMPCOLORPROC)(GLenum target, GLenum clamp); +typedef void(GLAD_API_PTR *PFNGLCLEARPROC)(GLbitfield mask); +typedef void(GLAD_API_PTR *PFNGLCLEARACCUMPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void(GLAD_API_PTR *PFNGLCLEARBUFFERDATAPROC)(GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void(GLAD_API_PTR *PFNGLCLEARBUFFERSUBDATAPROC)(GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +typedef void(GLAD_API_PTR *PFNGLCLEARBUFFERFIPROC)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +typedef void(GLAD_API_PTR *PFNGLCLEARBUFFERFVPROC)(GLenum buffer, GLint drawbuffer, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLCLEARBUFFERIVPROC)(GLenum buffer, GLint drawbuffer, const GLint *value); +typedef void(GLAD_API_PTR *PFNGLCLEARBUFFERUIVPROC)(GLenum buffer, GLint drawbuffer, const GLuint *value); +typedef void(GLAD_API_PTR *PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void(GLAD_API_PTR *PFNGLCLEARDEPTHPROC)(GLdouble depth); +typedef void(GLAD_API_PTR *PFNGLCLEARDEPTHFPROC)(GLfloat d); +typedef void(GLAD_API_PTR *PFNGLCLEARINDEXPROC)(GLfloat c); +typedef void(GLAD_API_PTR *PFNGLCLEARNAMEDBUFFERDATAPROC)(GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void(GLAD_API_PTR *PFNGLCLEARNAMEDBUFFERSUBDATAPROC)(GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +typedef void(GLAD_API_PTR *PFNGLCLEARNAMEDFRAMEBUFFERFIPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +typedef void(GLAD_API_PTR *PFNGLCLEARNAMEDFRAMEBUFFERFVPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLCLEARNAMEDFRAMEBUFFERIVPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value); +typedef void(GLAD_API_PTR *PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value); +typedef void(GLAD_API_PTR *PFNGLCLEARSTENCILPROC)(GLint s); +typedef void(GLAD_API_PTR *PFNGLCLEARTEXIMAGEPROC)(GLuint texture, GLint level, GLenum format, GLenum type, const void *data); +typedef void(GLAD_API_PTR *PFNGLCLEARTEXSUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); +typedef void(GLAD_API_PTR *PFNGLCLIENTACTIVETEXTUREPROC)(GLenum texture); +typedef GLenum(GLAD_API_PTR *PFNGLCLIENTWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void(GLAD_API_PTR *PFNGLCLIPCONTROLPROC)(GLenum origin, GLenum depth); +typedef void(GLAD_API_PTR *PFNGLCLIPPLANEPROC)(GLenum plane, const GLdouble *equation); +typedef void(GLAD_API_PTR *PFNGLCOLOR3BPROC)(GLbyte red, GLbyte green, GLbyte blue); +typedef void(GLAD_API_PTR *PFNGLCOLOR3BVPROC)(const GLbyte *v); +typedef void(GLAD_API_PTR *PFNGLCOLOR3DPROC)(GLdouble red, GLdouble green, GLdouble blue); +typedef void(GLAD_API_PTR *PFNGLCOLOR3DVPROC)(const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLCOLOR3FPROC)(GLfloat red, GLfloat green, GLfloat blue); +typedef void(GLAD_API_PTR *PFNGLCOLOR3FVPROC)(const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLCOLOR3IPROC)(GLint red, GLint green, GLint blue); +typedef void(GLAD_API_PTR *PFNGLCOLOR3IVPROC)(const GLint *v); +typedef void(GLAD_API_PTR *PFNGLCOLOR3SPROC)(GLshort red, GLshort green, GLshort blue); +typedef void(GLAD_API_PTR *PFNGLCOLOR3SVPROC)(const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLCOLOR3UBPROC)(GLubyte red, GLubyte green, GLubyte blue); +typedef void(GLAD_API_PTR *PFNGLCOLOR3UBVPROC)(const GLubyte *v); +typedef void(GLAD_API_PTR *PFNGLCOLOR3UIPROC)(GLuint red, GLuint green, GLuint blue); +typedef void(GLAD_API_PTR *PFNGLCOLOR3UIVPROC)(const GLuint *v); +typedef void(GLAD_API_PTR *PFNGLCOLOR3USPROC)(GLushort red, GLushort green, GLushort blue); +typedef void(GLAD_API_PTR *PFNGLCOLOR3USVPROC)(const GLushort *v); +typedef void(GLAD_API_PTR *PFNGLCOLOR4BPROC)(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha); +typedef void(GLAD_API_PTR *PFNGLCOLOR4BVPROC)(const GLbyte *v); +typedef void(GLAD_API_PTR *PFNGLCOLOR4DPROC)(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha); +typedef void(GLAD_API_PTR *PFNGLCOLOR4DVPROC)(const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLCOLOR4FPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void(GLAD_API_PTR *PFNGLCOLOR4FVPROC)(const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLCOLOR4IPROC)(GLint red, GLint green, GLint blue, GLint alpha); +typedef void(GLAD_API_PTR *PFNGLCOLOR4IVPROC)(const GLint *v); +typedef void(GLAD_API_PTR *PFNGLCOLOR4SPROC)(GLshort red, GLshort green, GLshort blue, GLshort alpha); +typedef void(GLAD_API_PTR *PFNGLCOLOR4SVPROC)(const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLCOLOR4UBPROC)(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); +typedef void(GLAD_API_PTR *PFNGLCOLOR4UBVPROC)(const GLubyte *v); +typedef void(GLAD_API_PTR *PFNGLCOLOR4UIPROC)(GLuint red, GLuint green, GLuint blue, GLuint alpha); +typedef void(GLAD_API_PTR *PFNGLCOLOR4UIVPROC)(const GLuint *v); +typedef void(GLAD_API_PTR *PFNGLCOLOR4USPROC)(GLushort red, GLushort green, GLushort blue, GLushort alpha); +typedef void(GLAD_API_PTR *PFNGLCOLOR4USVPROC)(const GLushort *v); +typedef void(GLAD_API_PTR *PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +typedef void(GLAD_API_PTR *PFNGLCOLORMASKIPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +typedef void(GLAD_API_PTR *PFNGLCOLORMATERIALPROC)(GLenum face, GLenum mode); +typedef void(GLAD_API_PTR *PFNGLCOLORP3UIPROC)(GLenum type, GLuint color); +typedef void(GLAD_API_PTR *PFNGLCOLORP3UIVPROC)(GLenum type, const GLuint *color); +typedef void(GLAD_API_PTR *PFNGLCOLORP4UIPROC)(GLenum type, GLuint color); +typedef void(GLAD_API_PTR *PFNGLCOLORP4UIVPROC)(GLenum type, const GLuint *color); +typedef void(GLAD_API_PTR *PFNGLCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void(GLAD_API_PTR *PFNGLCOMPILESHADERPROC)(GLuint shader); +typedef void(GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +typedef void(GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +typedef void(GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE3DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +typedef void(GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +typedef void(GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +typedef void(GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +typedef void(GLAD_API_PTR *PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC)(GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +typedef void(GLAD_API_PTR *PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +typedef void(GLAD_API_PTR *PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +typedef void(GLAD_API_PTR *PFNGLCOPYBUFFERSUBDATAPROC)(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void(GLAD_API_PTR *PFNGLCOPYIMAGESUBDATAPROC)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +typedef void(GLAD_API_PTR *PFNGLCOPYNAMEDBUFFERSUBDATAPROC)(GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void(GLAD_API_PTR *PFNGLCOPYPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type); +typedef void(GLAD_API_PTR *PFNGLCOPYTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void(GLAD_API_PTR *PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void(GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void(GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void(GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void(GLAD_API_PTR *PFNGLCOPYTEXTURESUBIMAGE1DPROC)(GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void(GLAD_API_PTR *PFNGLCOPYTEXTURESUBIMAGE2DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void(GLAD_API_PTR *PFNGLCOPYTEXTURESUBIMAGE3DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void(GLAD_API_PTR *PFNGLCREATEBUFFERSPROC)(GLsizei n, GLuint *buffers); +typedef void(GLAD_API_PTR *PFNGLCREATEFRAMEBUFFERSPROC)(GLsizei n, GLuint *framebuffers); +typedef GLuint(GLAD_API_PTR *PFNGLCREATEPROGRAMPROC)(void); +typedef void(GLAD_API_PTR *PFNGLCREATEPROGRAMPIPELINESPROC)(GLsizei n, GLuint *pipelines); +typedef void(GLAD_API_PTR *PFNGLCREATEQUERIESPROC)(GLenum target, GLsizei n, GLuint *ids); +typedef void(GLAD_API_PTR *PFNGLCREATERENDERBUFFERSPROC)(GLsizei n, GLuint *renderbuffers); +typedef void(GLAD_API_PTR *PFNGLCREATESAMPLERSPROC)(GLsizei n, GLuint *samplers); +typedef GLuint(GLAD_API_PTR *PFNGLCREATESHADERPROC)(GLenum type); +typedef GLuint(GLAD_API_PTR *PFNGLCREATESHADERPROGRAMVPROC)(GLenum type, GLsizei count, const GLchar *const *strings); +typedef void(GLAD_API_PTR *PFNGLCREATETEXTURESPROC)(GLenum target, GLsizei n, GLuint *textures); +typedef void(GLAD_API_PTR *PFNGLCREATETRANSFORMFEEDBACKSPROC)(GLsizei n, GLuint *ids); +typedef void(GLAD_API_PTR *PFNGLCREATEVERTEXARRAYSPROC)(GLsizei n, GLuint *arrays); +typedef void(GLAD_API_PTR *PFNGLCULLFACEPROC)(GLenum mode); +typedef void(GLAD_API_PTR *PFNGLDEBUGMESSAGECALLBACKPROC)(GLDEBUGPROC callback, const void *userParam); +typedef void(GLAD_API_PTR *PFNGLDEBUGMESSAGECONTROLPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void(GLAD_API_PTR *PFNGLDEBUGMESSAGEINSERTPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +typedef void(GLAD_API_PTR *PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint *buffers); +typedef void(GLAD_API_PTR *PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint *framebuffers); +typedef void(GLAD_API_PTR *PFNGLDELETELISTSPROC)(GLuint list, GLsizei range); +typedef void(GLAD_API_PTR *PFNGLDELETEPROGRAMPROC)(GLuint program); +typedef void(GLAD_API_PTR *PFNGLDELETEPROGRAMPIPELINESPROC)(GLsizei n, const GLuint *pipelines); +typedef void(GLAD_API_PTR *PFNGLDELETEQUERIESPROC)(GLsizei n, const GLuint *ids); +typedef void(GLAD_API_PTR *PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint *renderbuffers); +typedef void(GLAD_API_PTR *PFNGLDELETESAMPLERSPROC)(GLsizei count, const GLuint *samplers); +typedef void(GLAD_API_PTR *PFNGLDELETESHADERPROC)(GLuint shader); +typedef void(GLAD_API_PTR *PFNGLDELETESYNCPROC)(GLsync sync); +typedef void(GLAD_API_PTR *PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint *textures); +typedef void(GLAD_API_PTR *PFNGLDELETETRANSFORMFEEDBACKSPROC)(GLsizei n, const GLuint *ids); +typedef void(GLAD_API_PTR *PFNGLDELETEVERTEXARRAYSPROC)(GLsizei n, const GLuint *arrays); +typedef void(GLAD_API_PTR *PFNGLDEPTHFUNCPROC)(GLenum func); +typedef void(GLAD_API_PTR *PFNGLDEPTHMASKPROC)(GLboolean flag); +typedef void(GLAD_API_PTR *PFNGLDEPTHRANGEPROC)(GLdouble n, GLdouble f); +typedef void(GLAD_API_PTR *PFNGLDEPTHRANGEARRAYVPROC)(GLuint first, GLsizei count, const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLDEPTHRANGEINDEXEDPROC)(GLuint index, GLdouble n, GLdouble f); +typedef void(GLAD_API_PTR *PFNGLDEPTHRANGEFPROC)(GLfloat n, GLfloat f); +typedef void(GLAD_API_PTR *PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader); +typedef void(GLAD_API_PTR *PFNGLDISABLEPROC)(GLenum cap); +typedef void(GLAD_API_PTR *PFNGLDISABLECLIENTSTATEPROC)(GLenum array); +typedef void(GLAD_API_PTR *PFNGLDISABLEVERTEXARRAYATTRIBPROC)(GLuint vaobj, GLuint index); +typedef void(GLAD_API_PTR *PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index); +typedef void(GLAD_API_PTR *PFNGLDISABLEIPROC)(GLenum target, GLuint index); +typedef void(GLAD_API_PTR *PFNGLDISPATCHCOMPUTEPROC)(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +typedef void(GLAD_API_PTR *PFNGLDISPATCHCOMPUTEINDIRECTPROC)(GLintptr indirect); +typedef void(GLAD_API_PTR *PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count); +typedef void(GLAD_API_PTR *PFNGLDRAWARRAYSINDIRECTPROC)(GLenum mode, const void *indirect); +typedef void(GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +typedef void(GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +typedef void(GLAD_API_PTR *PFNGLDRAWBUFFERPROC)(GLenum buf); +typedef void(GLAD_API_PTR *PFNGLDRAWBUFFERSPROC)(GLsizei n, const GLenum *bufs); +typedef void(GLAD_API_PTR *PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices); +typedef void(GLAD_API_PTR *PFNGLDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); +typedef void(GLAD_API_PTR *PFNGLDRAWELEMENTSINDIRECTPROC)(GLenum mode, GLenum type, const void *indirect); +typedef void(GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); +typedef void(GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); +typedef void(GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); +typedef void(GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +typedef void(GLAD_API_PTR *PFNGLDRAWPIXELSPROC)(GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +typedef void(GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +typedef void(GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); +typedef void(GLAD_API_PTR *PFNGLDRAWTRANSFORMFEEDBACKPROC)(GLenum mode, GLuint id); +typedef void(GLAD_API_PTR *PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC)(GLenum mode, GLuint id, GLsizei instancecount); +typedef void(GLAD_API_PTR *PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC)(GLenum mode, GLuint id, GLuint stream); +typedef void(GLAD_API_PTR *PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC)(GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +typedef void(GLAD_API_PTR *PFNGLEDGEFLAGPROC)(GLboolean flag); +typedef void(GLAD_API_PTR *PFNGLEDGEFLAGPOINTERPROC)(GLsizei stride, const void *pointer); +typedef void(GLAD_API_PTR *PFNGLEDGEFLAGVPROC)(const GLboolean *flag); +typedef void(GLAD_API_PTR *PFNGLENABLEPROC)(GLenum cap); +typedef void(GLAD_API_PTR *PFNGLENABLECLIENTSTATEPROC)(GLenum array); +typedef void(GLAD_API_PTR *PFNGLENABLEVERTEXARRAYATTRIBPROC)(GLuint vaobj, GLuint index); +typedef void(GLAD_API_PTR *PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index); +typedef void(GLAD_API_PTR *PFNGLENABLEIPROC)(GLenum target, GLuint index); +typedef void(GLAD_API_PTR *PFNGLENDPROC)(void); +typedef void(GLAD_API_PTR *PFNGLENDCONDITIONALRENDERPROC)(void); +typedef void(GLAD_API_PTR *PFNGLENDLISTPROC)(void); +typedef void(GLAD_API_PTR *PFNGLENDQUERYPROC)(GLenum target); +typedef void(GLAD_API_PTR *PFNGLENDQUERYINDEXEDPROC)(GLenum target, GLuint index); +typedef void(GLAD_API_PTR *PFNGLENDTRANSFORMFEEDBACKPROC)(void); +typedef void(GLAD_API_PTR *PFNGLEVALCOORD1DPROC)(GLdouble u); +typedef void(GLAD_API_PTR *PFNGLEVALCOORD1DVPROC)(const GLdouble *u); +typedef void(GLAD_API_PTR *PFNGLEVALCOORD1FPROC)(GLfloat u); +typedef void(GLAD_API_PTR *PFNGLEVALCOORD1FVPROC)(const GLfloat *u); +typedef void(GLAD_API_PTR *PFNGLEVALCOORD2DPROC)(GLdouble u, GLdouble v); +typedef void(GLAD_API_PTR *PFNGLEVALCOORD2DVPROC)(const GLdouble *u); +typedef void(GLAD_API_PTR *PFNGLEVALCOORD2FPROC)(GLfloat u, GLfloat v); +typedef void(GLAD_API_PTR *PFNGLEVALCOORD2FVPROC)(const GLfloat *u); +typedef void(GLAD_API_PTR *PFNGLEVALMESH1PROC)(GLenum mode, GLint i1, GLint i2); +typedef void(GLAD_API_PTR *PFNGLEVALMESH2PROC)(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2); +typedef void(GLAD_API_PTR *PFNGLEVALPOINT1PROC)(GLint i); +typedef void(GLAD_API_PTR *PFNGLEVALPOINT2PROC)(GLint i, GLint j); +typedef void(GLAD_API_PTR *PFNGLFEEDBACKBUFFERPROC)(GLsizei size, GLenum type, GLfloat *buffer); +typedef GLsync(GLAD_API_PTR *PFNGLFENCESYNCPROC)(GLenum condition, GLbitfield flags); +typedef void(GLAD_API_PTR *PFNGLFINISHPROC)(void); +typedef void(GLAD_API_PTR *PFNGLFLUSHPROC)(void); +typedef void(GLAD_API_PTR *PFNGLFLUSHMAPPEDBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length); +typedef void(GLAD_API_PTR *PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC)(GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void(GLAD_API_PTR *PFNGLFOGCOORDPOINTERPROC)(GLenum type, GLsizei stride, const void *pointer); +typedef void(GLAD_API_PTR *PFNGLFOGCOORDDPROC)(GLdouble coord); +typedef void(GLAD_API_PTR *PFNGLFOGCOORDDVPROC)(const GLdouble *coord); +typedef void(GLAD_API_PTR *PFNGLFOGCOORDFPROC)(GLfloat coord); +typedef void(GLAD_API_PTR *PFNGLFOGCOORDFVPROC)(const GLfloat *coord); +typedef void(GLAD_API_PTR *PFNGLFOGFPROC)(GLenum pname, GLfloat param); +typedef void(GLAD_API_PTR *PFNGLFOGFVPROC)(GLenum pname, const GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLFOGIPROC)(GLenum pname, GLint param); +typedef void(GLAD_API_PTR *PFNGLFOGIVPROC)(GLenum pname, const GLint *params); +typedef void(GLAD_API_PTR *PFNGLFRAMEBUFFERPARAMETERIPROC)(GLenum target, GLenum pname, GLint param); +typedef void(GLAD_API_PTR *PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void(GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTUREPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void(GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE1DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void(GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void(GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE3DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void(GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURELAYERPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void(GLAD_API_PTR *PFNGLFRONTFACEPROC)(GLenum mode); +typedef void(GLAD_API_PTR *PFNGLFRUSTUMPROC)(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void(GLAD_API_PTR *PFNGLGENBUFFERSPROC)(GLsizei n, GLuint *buffers); +typedef void(GLAD_API_PTR *PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint *framebuffers); +typedef GLuint(GLAD_API_PTR *PFNGLGENLISTSPROC)(GLsizei range); +typedef void(GLAD_API_PTR *PFNGLGENPROGRAMPIPELINESPROC)(GLsizei n, GLuint *pipelines); +typedef void(GLAD_API_PTR *PFNGLGENQUERIESPROC)(GLsizei n, GLuint *ids); +typedef void(GLAD_API_PTR *PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint *renderbuffers); +typedef void(GLAD_API_PTR *PFNGLGENSAMPLERSPROC)(GLsizei count, GLuint *samplers); +typedef void(GLAD_API_PTR *PFNGLGENTEXTURESPROC)(GLsizei n, GLuint *textures); +typedef void(GLAD_API_PTR *PFNGLGENTRANSFORMFEEDBACKSPROC)(GLsizei n, GLuint *ids); +typedef void(GLAD_API_PTR *PFNGLGENVERTEXARRAYSPROC)(GLsizei n, GLuint *arrays); +typedef void(GLAD_API_PTR *PFNGLGENERATEMIPMAPPROC)(GLenum target); +typedef void(GLAD_API_PTR *PFNGLGENERATETEXTUREMIPMAPPROC)(GLuint texture); +typedef void(GLAD_API_PTR *PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC)(GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void(GLAD_API_PTR *PFNGLGETACTIVESUBROUTINENAMEPROC)(GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +typedef void(GLAD_API_PTR *PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC)(GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +typedef void(GLAD_API_PTR *PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC)(GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); +typedef void(GLAD_API_PTR *PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void(GLAD_API_PTR *PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); +typedef void(GLAD_API_PTR *PFNGLGETACTIVEUNIFORMBLOCKIVPROC)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETACTIVEUNIFORMNAMEPROC)(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); +typedef void(GLAD_API_PTR *PFNGLGETACTIVEUNIFORMSIVPROC)(GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); +typedef GLint(GLAD_API_PTR *PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar *name); +typedef void(GLAD_API_PTR *PFNGLGETBOOLEANI_VPROC)(GLenum target, GLuint index, GLboolean *data); +typedef void(GLAD_API_PTR *PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean *data); +typedef void(GLAD_API_PTR *PFNGLGETBUFFERPARAMETERI64VPROC)(GLenum target, GLenum pname, GLint64 *params); +typedef void(GLAD_API_PTR *PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, void **params); +typedef void(GLAD_API_PTR *PFNGLGETBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, void *data); +typedef void(GLAD_API_PTR *PFNGLGETCLIPPLANEPROC)(GLenum plane, GLdouble *equation); +typedef void(GLAD_API_PTR *PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint level, void *img); +typedef void(GLAD_API_PTR *PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC)(GLuint texture, GLint level, GLsizei bufSize, void *pixels); +typedef void(GLAD_API_PTR *PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels); +typedef GLuint(GLAD_API_PTR *PFNGLGETDEBUGMESSAGELOGPROC)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +typedef void(GLAD_API_PTR *PFNGLGETDOUBLEI_VPROC)(GLenum target, GLuint index, GLdouble *data); +typedef void(GLAD_API_PTR *PFNGLGETDOUBLEVPROC)(GLenum pname, GLdouble *data); +typedef GLenum(GLAD_API_PTR *PFNGLGETERRORPROC)(void); +typedef void(GLAD_API_PTR *PFNGLGETFLOATI_VPROC)(GLenum target, GLuint index, GLfloat *data); +typedef void(GLAD_API_PTR *PFNGLGETFLOATVPROC)(GLenum pname, GLfloat *data); +typedef GLint(GLAD_API_PTR *PFNGLGETFRAGDATAINDEXPROC)(GLuint program, const GLchar *name); +typedef GLint(GLAD_API_PTR *PFNGLGETFRAGDATALOCATIONPROC)(GLuint program, const GLchar *name); +typedef void(GLAD_API_PTR *PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETFRAMEBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); +typedef GLenum(GLAD_API_PTR *PFNGLGETGRAPHICSRESETSTATUSPROC)(void); +typedef void(GLAD_API_PTR *PFNGLGETINTEGER64I_VPROC)(GLenum target, GLuint index, GLint64 *data); +typedef void(GLAD_API_PTR *PFNGLGETINTEGER64VPROC)(GLenum pname, GLint64 *data); +typedef void(GLAD_API_PTR *PFNGLGETINTEGERI_VPROC)(GLenum target, GLuint index, GLint *data); +typedef void(GLAD_API_PTR *PFNGLGETINTEGERVPROC)(GLenum pname, GLint *data); +typedef void(GLAD_API_PTR *PFNGLGETINTERNALFORMATI64VPROC)(GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint64 *params); +typedef void(GLAD_API_PTR *PFNGLGETINTERNALFORMATIVPROC)(GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETLIGHTFVPROC)(GLenum light, GLenum pname, GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLGETLIGHTIVPROC)(GLenum light, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETMAPDVPROC)(GLenum target, GLenum query, GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLGETMAPFVPROC)(GLenum target, GLenum query, GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLGETMAPIVPROC)(GLenum target, GLenum query, GLint *v); +typedef void(GLAD_API_PTR *PFNGLGETMATERIALFVPROC)(GLenum face, GLenum pname, GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLGETMATERIALIVPROC)(GLenum face, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETMULTISAMPLEFVPROC)(GLenum pname, GLuint index, GLfloat *val); +typedef void(GLAD_API_PTR *PFNGLGETNAMEDBUFFERPARAMETERI64VPROC)(GLuint buffer, GLenum pname, GLint64 *params); +typedef void(GLAD_API_PTR *PFNGLGETNAMEDBUFFERPARAMETERIVPROC)(GLuint buffer, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETNAMEDBUFFERPOINTERVPROC)(GLuint buffer, GLenum pname, void **params); +typedef void(GLAD_API_PTR *PFNGLGETNAMEDBUFFERSUBDATAPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); +typedef void(GLAD_API_PTR *PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC)(GLuint framebuffer, GLenum pname, GLint *param); +typedef void(GLAD_API_PTR *PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC)(GLuint renderbuffer, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +typedef void(GLAD_API_PTR *PFNGLGETOBJECTPTRLABELPROC)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +typedef void(GLAD_API_PTR *PFNGLGETPIXELMAPFVPROC)(GLenum map, GLfloat *values); +typedef void(GLAD_API_PTR *PFNGLGETPIXELMAPUIVPROC)(GLenum map, GLuint *values); +typedef void(GLAD_API_PTR *PFNGLGETPIXELMAPUSVPROC)(GLenum map, GLushort *values); +typedef void(GLAD_API_PTR *PFNGLGETPOINTERVPROC)(GLenum pname, void **params); +typedef void(GLAD_API_PTR *PFNGLGETPOLYGONSTIPPLEPROC)(GLubyte *mask); +typedef void(GLAD_API_PTR *PFNGLGETPROGRAMBINARYPROC)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); +typedef void(GLAD_API_PTR *PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void(GLAD_API_PTR *PFNGLGETPROGRAMINTERFACEIVPROC)(GLuint program, GLenum programInterface, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETPROGRAMPIPELINEINFOLOGPROC)(GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void(GLAD_API_PTR *PFNGLGETPROGRAMPIPELINEIVPROC)(GLuint pipeline, GLenum pname, GLint *params); +typedef GLuint(GLAD_API_PTR *PFNGLGETPROGRAMRESOURCEINDEXPROC)(GLuint program, GLenum programInterface, const GLchar *name); +typedef GLint(GLAD_API_PTR *PFNGLGETPROGRAMRESOURCELOCATIONPROC)(GLuint program, GLenum programInterface, const GLchar *name); +typedef GLint(GLAD_API_PTR *PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC)(GLuint program, GLenum programInterface, const GLchar *name); +typedef void(GLAD_API_PTR *PFNGLGETPROGRAMRESOURCENAMEPROC)(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +typedef void(GLAD_API_PTR *PFNGLGETPROGRAMRESOURCEIVPROC)(GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETPROGRAMSTAGEIVPROC)(GLuint program, GLenum shadertype, GLenum pname, GLint *values); +typedef void(GLAD_API_PTR *PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETQUERYBUFFEROBJECTI64VPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void(GLAD_API_PTR *PFNGLGETQUERYBUFFEROBJECTIVPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void(GLAD_API_PTR *PFNGLGETQUERYBUFFEROBJECTUI64VPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void(GLAD_API_PTR *PFNGLGETQUERYBUFFEROBJECTUIVPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void(GLAD_API_PTR *PFNGLGETQUERYINDEXEDIVPROC)(GLenum target, GLuint index, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETQUERYOBJECTI64VPROC)(GLuint id, GLenum pname, GLint64 *params); +typedef void(GLAD_API_PTR *PFNGLGETQUERYOBJECTIVPROC)(GLuint id, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETQUERYOBJECTUI64VPROC)(GLuint id, GLenum pname, GLuint64 *params); +typedef void(GLAD_API_PTR *PFNGLGETQUERYOBJECTUIVPROC)(GLuint id, GLenum pname, GLuint *params); +typedef void(GLAD_API_PTR *PFNGLGETQUERYIVPROC)(GLenum target, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, GLuint *params); +typedef void(GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void(GLAD_API_PTR *PFNGLGETSHADERPRECISIONFORMATPROC)(GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); +typedef void(GLAD_API_PTR *PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +typedef void(GLAD_API_PTR *PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint *params); +typedef const GLubyte *(GLAD_API_PTR *PFNGLGETSTRINGPROC)(GLenum name); +typedef const GLubyte *(GLAD_API_PTR *PFNGLGETSTRINGIPROC)(GLenum name, GLuint index); +typedef GLuint(GLAD_API_PTR *PFNGLGETSUBROUTINEINDEXPROC)(GLuint program, GLenum shadertype, const GLchar *name); +typedef GLint(GLAD_API_PTR *PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC)(GLuint program, GLenum shadertype, const GLchar *name); +typedef void(GLAD_API_PTR *PFNGLGETSYNCIVPROC)(GLsync sync, GLenum pname, GLsizei count, GLsizei *length, GLint *values); +typedef void(GLAD_API_PTR *PFNGLGETTEXENVFVPROC)(GLenum target, GLenum pname, GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLGETTEXENVIVPROC)(GLenum target, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETTEXGENDVPROC)(GLenum coord, GLenum pname, GLdouble *params); +typedef void(GLAD_API_PTR *PFNGLGETTEXGENFVPROC)(GLenum coord, GLenum pname, GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLGETTEXGENIVPROC)(GLenum coord, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +typedef void(GLAD_API_PTR *PFNGLGETTEXLEVELPARAMETERFVPROC)(GLenum target, GLint level, GLenum pname, GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLGETTEXLEVELPARAMETERIVPROC)(GLenum target, GLint level, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, GLuint *params); +typedef void(GLAD_API_PTR *PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETTEXTUREIMAGEPROC)(GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +typedef void(GLAD_API_PTR *PFNGLGETTEXTURELEVELPARAMETERFVPROC)(GLuint texture, GLint level, GLenum pname, GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLGETTEXTURELEVELPARAMETERIVPROC)(GLuint texture, GLint level, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETTEXTUREPARAMETERIIVPROC)(GLuint texture, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETTEXTUREPARAMETERIUIVPROC)(GLuint texture, GLenum pname, GLuint *params); +typedef void(GLAD_API_PTR *PFNGLGETTEXTUREPARAMETERFVPROC)(GLuint texture, GLenum pname, GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLGETTEXTUREPARAMETERIVPROC)(GLuint texture, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETTEXTURESUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +typedef void(GLAD_API_PTR *PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +typedef void(GLAD_API_PTR *PFNGLGETTRANSFORMFEEDBACKI64_VPROC)(GLuint xfb, GLenum pname, GLuint index, GLint64 *param); +typedef void(GLAD_API_PTR *PFNGLGETTRANSFORMFEEDBACKI_VPROC)(GLuint xfb, GLenum pname, GLuint index, GLint *param); +typedef void(GLAD_API_PTR *PFNGLGETTRANSFORMFEEDBACKIVPROC)(GLuint xfb, GLenum pname, GLint *param); +typedef GLuint(GLAD_API_PTR *PFNGLGETUNIFORMBLOCKINDEXPROC)(GLuint program, const GLchar *uniformBlockName); +typedef void(GLAD_API_PTR *PFNGLGETUNIFORMINDICESPROC)(GLuint program, GLsizei uniformCount, const GLchar *const *uniformNames, GLuint *uniformIndices); +typedef GLint(GLAD_API_PTR *PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar *name); +typedef void(GLAD_API_PTR *PFNGLGETUNIFORMSUBROUTINEUIVPROC)(GLenum shadertype, GLint location, GLuint *params); +typedef void(GLAD_API_PTR *PFNGLGETUNIFORMDVPROC)(GLuint program, GLint location, GLdouble *params); +typedef void(GLAD_API_PTR *PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETUNIFORMUIVPROC)(GLuint program, GLint location, GLuint *params); +typedef void(GLAD_API_PTR *PFNGLGETVERTEXARRAYINDEXED64IVPROC)(GLuint vaobj, GLuint index, GLenum pname, GLint64 *param); +typedef void(GLAD_API_PTR *PFNGLGETVERTEXARRAYINDEXEDIVPROC)(GLuint vaobj, GLuint index, GLenum pname, GLint *param); +typedef void(GLAD_API_PTR *PFNGLGETVERTEXARRAYIVPROC)(GLuint vaobj, GLenum pname, GLint *param); +typedef void(GLAD_API_PTR *PFNGLGETVERTEXATTRIBIIVPROC)(GLuint index, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETVERTEXATTRIBIUIVPROC)(GLuint index, GLenum pname, GLuint *params); +typedef void(GLAD_API_PTR *PFNGLGETVERTEXATTRIBLDVPROC)(GLuint index, GLenum pname, GLdouble *params); +typedef void(GLAD_API_PTR *PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void **pointer); +typedef void(GLAD_API_PTR *PFNGLGETVERTEXATTRIBDVPROC)(GLuint index, GLenum pname, GLdouble *params); +typedef void(GLAD_API_PTR *PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETNCOLORTABLEPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); +typedef void(GLAD_API_PTR *PFNGLGETNCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint lod, GLsizei bufSize, void *pixels); +typedef void(GLAD_API_PTR *PFNGLGETNCONVOLUTIONFILTERPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); +typedef void(GLAD_API_PTR *PFNGLGETNHISTOGRAMPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +typedef void(GLAD_API_PTR *PFNGLGETNMAPDVPROC)(GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLGETNMAPFVPROC)(GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLGETNMAPIVPROC)(GLenum target, GLenum query, GLsizei bufSize, GLint *v); +typedef void(GLAD_API_PTR *PFNGLGETNMINMAXPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +typedef void(GLAD_API_PTR *PFNGLGETNPIXELMAPFVPROC)(GLenum map, GLsizei bufSize, GLfloat *values); +typedef void(GLAD_API_PTR *PFNGLGETNPIXELMAPUIVPROC)(GLenum map, GLsizei bufSize, GLuint *values); +typedef void(GLAD_API_PTR *PFNGLGETNPIXELMAPUSVPROC)(GLenum map, GLsizei bufSize, GLushort *values); +typedef void(GLAD_API_PTR *PFNGLGETNPOLYGONSTIPPLEPROC)(GLsizei bufSize, GLubyte *pattern); +typedef void(GLAD_API_PTR *PFNGLGETNSEPARABLEFILTERPROC)(GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); +typedef void(GLAD_API_PTR *PFNGLGETNTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +typedef void(GLAD_API_PTR *PFNGLGETNUNIFORMDVPROC)(GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +typedef void(GLAD_API_PTR *PFNGLGETNUNIFORMFVPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLGETNUNIFORMIVPROC)(GLuint program, GLint location, GLsizei bufSize, GLint *params); +typedef void(GLAD_API_PTR *PFNGLGETNUNIFORMUIVPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint *params); +typedef void(GLAD_API_PTR *PFNGLHINTPROC)(GLenum target, GLenum mode); +typedef void(GLAD_API_PTR *PFNGLINDEXMASKPROC)(GLuint mask); +typedef void(GLAD_API_PTR *PFNGLINDEXPOINTERPROC)(GLenum type, GLsizei stride, const void *pointer); +typedef void(GLAD_API_PTR *PFNGLINDEXDPROC)(GLdouble c); +typedef void(GLAD_API_PTR *PFNGLINDEXDVPROC)(const GLdouble *c); +typedef void(GLAD_API_PTR *PFNGLINDEXFPROC)(GLfloat c); +typedef void(GLAD_API_PTR *PFNGLINDEXFVPROC)(const GLfloat *c); +typedef void(GLAD_API_PTR *PFNGLINDEXIPROC)(GLint c); +typedef void(GLAD_API_PTR *PFNGLINDEXIVPROC)(const GLint *c); +typedef void(GLAD_API_PTR *PFNGLINDEXSPROC)(GLshort c); +typedef void(GLAD_API_PTR *PFNGLINDEXSVPROC)(const GLshort *c); +typedef void(GLAD_API_PTR *PFNGLINDEXUBPROC)(GLubyte c); +typedef void(GLAD_API_PTR *PFNGLINDEXUBVPROC)(const GLubyte *c); +typedef void(GLAD_API_PTR *PFNGLINITNAMESPROC)(void); +typedef void(GLAD_API_PTR *PFNGLINTERLEAVEDARRAYSPROC)(GLenum format, GLsizei stride, const void *pointer); +typedef void(GLAD_API_PTR *PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer); +typedef void(GLAD_API_PTR *PFNGLINVALIDATEBUFFERSUBDATAPROC)(GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void(GLAD_API_PTR *PFNGLINVALIDATEFRAMEBUFFERPROC)(GLenum target, GLsizei numAttachments, const GLenum *attachments); +typedef void(GLAD_API_PTR *PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC)(GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments); +typedef void(GLAD_API_PTR *PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC)(GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void(GLAD_API_PTR *PFNGLINVALIDATESUBFRAMEBUFFERPROC)(GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void(GLAD_API_PTR *PFNGLINVALIDATETEXIMAGEPROC)(GLuint texture, GLint level); +typedef void(GLAD_API_PTR *PFNGLINVALIDATETEXSUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); +typedef GLboolean(GLAD_API_PTR *PFNGLISBUFFERPROC)(GLuint buffer); +typedef GLboolean(GLAD_API_PTR *PFNGLISENABLEDPROC)(GLenum cap); +typedef GLboolean(GLAD_API_PTR *PFNGLISENABLEDIPROC)(GLenum target, GLuint index); +typedef GLboolean(GLAD_API_PTR *PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer); +typedef GLboolean(GLAD_API_PTR *PFNGLISLISTPROC)(GLuint list); +typedef GLboolean(GLAD_API_PTR *PFNGLISPROGRAMPROC)(GLuint program); +typedef GLboolean(GLAD_API_PTR *PFNGLISPROGRAMPIPELINEPROC)(GLuint pipeline); +typedef GLboolean(GLAD_API_PTR *PFNGLISQUERYPROC)(GLuint id); +typedef GLboolean(GLAD_API_PTR *PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer); +typedef GLboolean(GLAD_API_PTR *PFNGLISSAMPLERPROC)(GLuint sampler); +typedef GLboolean(GLAD_API_PTR *PFNGLISSHADERPROC)(GLuint shader); +typedef GLboolean(GLAD_API_PTR *PFNGLISSYNCPROC)(GLsync sync); +typedef GLboolean(GLAD_API_PTR *PFNGLISTEXTUREPROC)(GLuint texture); +typedef GLboolean(GLAD_API_PTR *PFNGLISTRANSFORMFEEDBACKPROC)(GLuint id); +typedef GLboolean(GLAD_API_PTR *PFNGLISVERTEXARRAYPROC)(GLuint array); +typedef void(GLAD_API_PTR *PFNGLLIGHTMODELFPROC)(GLenum pname, GLfloat param); +typedef void(GLAD_API_PTR *PFNGLLIGHTMODELFVPROC)(GLenum pname, const GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLLIGHTMODELIPROC)(GLenum pname, GLint param); +typedef void(GLAD_API_PTR *PFNGLLIGHTMODELIVPROC)(GLenum pname, const GLint *params); +typedef void(GLAD_API_PTR *PFNGLLIGHTFPROC)(GLenum light, GLenum pname, GLfloat param); +typedef void(GLAD_API_PTR *PFNGLLIGHTFVPROC)(GLenum light, GLenum pname, const GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLLIGHTIPROC)(GLenum light, GLenum pname, GLint param); +typedef void(GLAD_API_PTR *PFNGLLIGHTIVPROC)(GLenum light, GLenum pname, const GLint *params); +typedef void(GLAD_API_PTR *PFNGLLINESTIPPLEPROC)(GLint factor, GLushort pattern); +typedef void(GLAD_API_PTR *PFNGLLINEWIDTHPROC)(GLfloat width); +typedef void(GLAD_API_PTR *PFNGLLINKPROGRAMPROC)(GLuint program); +typedef void(GLAD_API_PTR *PFNGLLISTBASEPROC)(GLuint base); +typedef void(GLAD_API_PTR *PFNGLLOADIDENTITYPROC)(void); +typedef void(GLAD_API_PTR *PFNGLLOADMATRIXDPROC)(const GLdouble *m); +typedef void(GLAD_API_PTR *PFNGLLOADMATRIXFPROC)(const GLfloat *m); +typedef void(GLAD_API_PTR *PFNGLLOADNAMEPROC)(GLuint name); +typedef void(GLAD_API_PTR *PFNGLLOADTRANSPOSEMATRIXDPROC)(const GLdouble *m); +typedef void(GLAD_API_PTR *PFNGLLOADTRANSPOSEMATRIXFPROC)(const GLfloat *m); +typedef void(GLAD_API_PTR *PFNGLLOGICOPPROC)(GLenum opcode); +typedef void(GLAD_API_PTR *PFNGLMAP1DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); +typedef void(GLAD_API_PTR *PFNGLMAP1FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); +typedef void(GLAD_API_PTR *PFNGLMAP2DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); +typedef void(GLAD_API_PTR *PFNGLMAP2FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); +typedef void *(GLAD_API_PTR *PFNGLMAPBUFFERPROC)(GLenum target, GLenum access); +typedef void *(GLAD_API_PTR *PFNGLMAPBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void(GLAD_API_PTR *PFNGLMAPGRID1DPROC)(GLint un, GLdouble u1, GLdouble u2); +typedef void(GLAD_API_PTR *PFNGLMAPGRID1FPROC)(GLint un, GLfloat u1, GLfloat u2); +typedef void(GLAD_API_PTR *PFNGLMAPGRID2DPROC)(GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2); +typedef void(GLAD_API_PTR *PFNGLMAPGRID2FPROC)(GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2); +typedef void *(GLAD_API_PTR *PFNGLMAPNAMEDBUFFERPROC)(GLuint buffer, GLenum access); +typedef void *(GLAD_API_PTR *PFNGLMAPNAMEDBUFFERRANGEPROC)(GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void(GLAD_API_PTR *PFNGLMATERIALFPROC)(GLenum face, GLenum pname, GLfloat param); +typedef void(GLAD_API_PTR *PFNGLMATERIALFVPROC)(GLenum face, GLenum pname, const GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLMATERIALIPROC)(GLenum face, GLenum pname, GLint param); +typedef void(GLAD_API_PTR *PFNGLMATERIALIVPROC)(GLenum face, GLenum pname, const GLint *params); +typedef void(GLAD_API_PTR *PFNGLMATRIXMODEPROC)(GLenum mode); +typedef void(GLAD_API_PTR *PFNGLMEMORYBARRIERPROC)(GLbitfield barriers); +typedef void(GLAD_API_PTR *PFNGLMEMORYBARRIERBYREGIONPROC)(GLbitfield barriers); +typedef void(GLAD_API_PTR *PFNGLMINSAMPLESHADINGPROC)(GLfloat value); +typedef void(GLAD_API_PTR *PFNGLMULTMATRIXDPROC)(const GLdouble *m); +typedef void(GLAD_API_PTR *PFNGLMULTMATRIXFPROC)(const GLfloat *m); +typedef void(GLAD_API_PTR *PFNGLMULTTRANSPOSEMATRIXDPROC)(const GLdouble *m); +typedef void(GLAD_API_PTR *PFNGLMULTTRANSPOSEMATRIXFPROC)(const GLfloat *m); +typedef void(GLAD_API_PTR *PFNGLMULTIDRAWARRAYSPROC)(GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); +typedef void(GLAD_API_PTR *PFNGLMULTIDRAWARRAYSINDIRECTPROC)(GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); +typedef void(GLAD_API_PTR *PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC)(GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +typedef void(GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSPROC)(GLenum mode, const GLsizei *count, GLenum type, const void *const *indices, GLsizei drawcount); +typedef void(GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, const GLsizei *count, GLenum type, const void *const *indices, GLsizei drawcount, const GLint *basevertex); +typedef void(GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSINDIRECTPROC)(GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); +typedef void(GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC)(GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD1DPROC)(GLenum target, GLdouble s); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD1DVPROC)(GLenum target, const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD1FPROC)(GLenum target, GLfloat s); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD1FVPROC)(GLenum target, const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD1IPROC)(GLenum target, GLint s); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD1IVPROC)(GLenum target, const GLint *v); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD1SPROC)(GLenum target, GLshort s); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD1SVPROC)(GLenum target, const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD2DPROC)(GLenum target, GLdouble s, GLdouble t); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD2DVPROC)(GLenum target, const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD2FPROC)(GLenum target, GLfloat s, GLfloat t); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD2FVPROC)(GLenum target, const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD2IPROC)(GLenum target, GLint s, GLint t); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD2IVPROC)(GLenum target, const GLint *v); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD2SPROC)(GLenum target, GLshort s, GLshort t); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD2SVPROC)(GLenum target, const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD3DPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD3DVPROC)(GLenum target, const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD3FPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD3FVPROC)(GLenum target, const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD3IPROC)(GLenum target, GLint s, GLint t, GLint r); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD3IVPROC)(GLenum target, const GLint *v); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD3SPROC)(GLenum target, GLshort s, GLshort t, GLshort r); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD3SVPROC)(GLenum target, const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD4DPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD4DVPROC)(GLenum target, const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD4FPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD4FVPROC)(GLenum target, const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD4IPROC)(GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD4IVPROC)(GLenum target, const GLint *v); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD4SPROC)(GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORD4SVPROC)(GLenum target, const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORDP1UIPROC)(GLenum texture, GLenum type, GLuint coords); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORDP1UIVPROC)(GLenum texture, GLenum type, const GLuint *coords); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORDP2UIPROC)(GLenum texture, GLenum type, GLuint coords); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORDP2UIVPROC)(GLenum texture, GLenum type, const GLuint *coords); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORDP3UIPROC)(GLenum texture, GLenum type, GLuint coords); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORDP3UIVPROC)(GLenum texture, GLenum type, const GLuint *coords); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORDP4UIPROC)(GLenum texture, GLenum type, GLuint coords); +typedef void(GLAD_API_PTR *PFNGLMULTITEXCOORDP4UIVPROC)(GLenum texture, GLenum type, const GLuint *coords); +typedef void(GLAD_API_PTR *PFNGLNAMEDBUFFERDATAPROC)(GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); +typedef void(GLAD_API_PTR *PFNGLNAMEDBUFFERSTORAGEPROC)(GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); +typedef void(GLAD_API_PTR *PFNGLNAMEDBUFFERSUBDATAPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +typedef void(GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC)(GLuint framebuffer, GLenum buf); +typedef void(GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC)(GLuint framebuffer, GLsizei n, const GLenum *bufs); +typedef void(GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC)(GLuint framebuffer, GLenum pname, GLint param); +typedef void(GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC)(GLuint framebuffer, GLenum src); +typedef void(GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC)(GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void(GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERTEXTUREPROC)(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +typedef void(GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC)(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void(GLAD_API_PTR *PFNGLNAMEDRENDERBUFFERSTORAGEPROC)(GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +typedef void(GLAD_API_PTR *PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void(GLAD_API_PTR *PFNGLNEWLISTPROC)(GLuint list, GLenum mode); +typedef void(GLAD_API_PTR *PFNGLNORMAL3BPROC)(GLbyte nx, GLbyte ny, GLbyte nz); +typedef void(GLAD_API_PTR *PFNGLNORMAL3BVPROC)(const GLbyte *v); +typedef void(GLAD_API_PTR *PFNGLNORMAL3DPROC)(GLdouble nx, GLdouble ny, GLdouble nz); +typedef void(GLAD_API_PTR *PFNGLNORMAL3DVPROC)(const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLNORMAL3FPROC)(GLfloat nx, GLfloat ny, GLfloat nz); +typedef void(GLAD_API_PTR *PFNGLNORMAL3FVPROC)(const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLNORMAL3IPROC)(GLint nx, GLint ny, GLint nz); +typedef void(GLAD_API_PTR *PFNGLNORMAL3IVPROC)(const GLint *v); +typedef void(GLAD_API_PTR *PFNGLNORMAL3SPROC)(GLshort nx, GLshort ny, GLshort nz); +typedef void(GLAD_API_PTR *PFNGLNORMAL3SVPROC)(const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLNORMALP3UIPROC)(GLenum type, GLuint coords); +typedef void(GLAD_API_PTR *PFNGLNORMALP3UIVPROC)(GLenum type, const GLuint *coords); +typedef void(GLAD_API_PTR *PFNGLNORMALPOINTERPROC)(GLenum type, GLsizei stride, const void *pointer); +typedef void(GLAD_API_PTR *PFNGLOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +typedef void(GLAD_API_PTR *PFNGLOBJECTPTRLABELPROC)(const void *ptr, GLsizei length, const GLchar *label); +typedef void(GLAD_API_PTR *PFNGLORTHOPROC)(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void(GLAD_API_PTR *PFNGLPASSTHROUGHPROC)(GLfloat token); +typedef void(GLAD_API_PTR *PFNGLPATCHPARAMETERFVPROC)(GLenum pname, const GLfloat *values); +typedef void(GLAD_API_PTR *PFNGLPATCHPARAMETERIPROC)(GLenum pname, GLint value); +typedef void(GLAD_API_PTR *PFNGLPAUSETRANSFORMFEEDBACKPROC)(void); +typedef void(GLAD_API_PTR *PFNGLPIXELMAPFVPROC)(GLenum map, GLsizei mapsize, const GLfloat *values); +typedef void(GLAD_API_PTR *PFNGLPIXELMAPUIVPROC)(GLenum map, GLsizei mapsize, const GLuint *values); +typedef void(GLAD_API_PTR *PFNGLPIXELMAPUSVPROC)(GLenum map, GLsizei mapsize, const GLushort *values); +typedef void(GLAD_API_PTR *PFNGLPIXELSTOREFPROC)(GLenum pname, GLfloat param); +typedef void(GLAD_API_PTR *PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param); +typedef void(GLAD_API_PTR *PFNGLPIXELTRANSFERFPROC)(GLenum pname, GLfloat param); +typedef void(GLAD_API_PTR *PFNGLPIXELTRANSFERIPROC)(GLenum pname, GLint param); +typedef void(GLAD_API_PTR *PFNGLPIXELZOOMPROC)(GLfloat xfactor, GLfloat yfactor); +typedef void(GLAD_API_PTR *PFNGLPOINTPARAMETERFPROC)(GLenum pname, GLfloat param); +typedef void(GLAD_API_PTR *PFNGLPOINTPARAMETERFVPROC)(GLenum pname, const GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLPOINTPARAMETERIPROC)(GLenum pname, GLint param); +typedef void(GLAD_API_PTR *PFNGLPOINTPARAMETERIVPROC)(GLenum pname, const GLint *params); +typedef void(GLAD_API_PTR *PFNGLPOINTSIZEPROC)(GLfloat size); +typedef void(GLAD_API_PTR *PFNGLPOLYGONMODEPROC)(GLenum face, GLenum mode); +typedef void(GLAD_API_PTR *PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units); +typedef void(GLAD_API_PTR *PFNGLPOLYGONOFFSETCLAMPPROC)(GLfloat factor, GLfloat units, GLfloat clamp); +typedef void(GLAD_API_PTR *PFNGLPOLYGONSTIPPLEPROC)(const GLubyte *mask); +typedef void(GLAD_API_PTR *PFNGLPOPATTRIBPROC)(void); +typedef void(GLAD_API_PTR *PFNGLPOPCLIENTATTRIBPROC)(void); +typedef void(GLAD_API_PTR *PFNGLPOPDEBUGGROUPPROC)(void); +typedef void(GLAD_API_PTR *PFNGLPOPMATRIXPROC)(void); +typedef void(GLAD_API_PTR *PFNGLPOPNAMEPROC)(void); +typedef void(GLAD_API_PTR *PFNGLPRIMITIVERESTARTINDEXPROC)(GLuint index); +typedef void(GLAD_API_PTR *PFNGLPRIORITIZETEXTURESPROC)(GLsizei n, const GLuint *textures, const GLfloat *priorities); +typedef void(GLAD_API_PTR *PFNGLPROGRAMBINARYPROC)(GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); +typedef void(GLAD_API_PTR *PFNGLPROGRAMPARAMETERIPROC)(GLuint program, GLenum pname, GLint value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM1DPROC)(GLuint program, GLint location, GLdouble v0); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM1DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM1FPROC)(GLuint program, GLint location, GLfloat v0); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM1FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM1IPROC)(GLuint program, GLint location, GLint v0); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM1IVPROC)(GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM1UIPROC)(GLuint program, GLint location, GLuint v0); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM1UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM2DPROC)(GLuint program, GLint location, GLdouble v0, GLdouble v1); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM2DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM2FPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM2FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM2IPROC)(GLuint program, GLint location, GLint v0, GLint v1); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM2IVPROC)(GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM2UIPROC)(GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM2UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM3DPROC)(GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM3DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM3FPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM3FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM3IPROC)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM3IVPROC)(GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM3UIPROC)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM3UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM4DPROC)(GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM4DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM4FPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM4FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM4IPROC)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM4IVPROC)(GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM4UIPROC)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORM4UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLPROVOKINGVERTEXPROC)(GLenum mode); +typedef void(GLAD_API_PTR *PFNGLPUSHATTRIBPROC)(GLbitfield mask); +typedef void(GLAD_API_PTR *PFNGLPUSHCLIENTATTRIBPROC)(GLbitfield mask); +typedef void(GLAD_API_PTR *PFNGLPUSHDEBUGGROUPPROC)(GLenum source, GLuint id, GLsizei length, const GLchar *message); +typedef void(GLAD_API_PTR *PFNGLPUSHMATRIXPROC)(void); +typedef void(GLAD_API_PTR *PFNGLPUSHNAMEPROC)(GLuint name); +typedef void(GLAD_API_PTR *PFNGLQUERYCOUNTERPROC)(GLuint id, GLenum target); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS2DPROC)(GLdouble x, GLdouble y); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS2DVPROC)(const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS2FPROC)(GLfloat x, GLfloat y); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS2FVPROC)(const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS2IPROC)(GLint x, GLint y); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS2IVPROC)(const GLint *v); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS2SPROC)(GLshort x, GLshort y); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS2SVPROC)(const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS3DPROC)(GLdouble x, GLdouble y, GLdouble z); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS3DVPROC)(const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS3FPROC)(GLfloat x, GLfloat y, GLfloat z); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS3FVPROC)(const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS3IPROC)(GLint x, GLint y, GLint z); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS3IVPROC)(const GLint *v); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS3SPROC)(GLshort x, GLshort y, GLshort z); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS3SVPROC)(const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS4DPROC)(GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS4DVPROC)(const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS4FPROC)(GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS4FVPROC)(const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS4IPROC)(GLint x, GLint y, GLint z, GLint w); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS4IVPROC)(const GLint *v); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS4SPROC)(GLshort x, GLshort y, GLshort z, GLshort w); +typedef void(GLAD_API_PTR *PFNGLRASTERPOS4SVPROC)(const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLREADBUFFERPROC)(GLenum src); +typedef void(GLAD_API_PTR *PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); +typedef void(GLAD_API_PTR *PFNGLREADNPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +typedef void(GLAD_API_PTR *PFNGLRECTDPROC)(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2); +typedef void(GLAD_API_PTR *PFNGLRECTDVPROC)(const GLdouble *v1, const GLdouble *v2); +typedef void(GLAD_API_PTR *PFNGLRECTFPROC)(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2); +typedef void(GLAD_API_PTR *PFNGLRECTFVPROC)(const GLfloat *v1, const GLfloat *v2); +typedef void(GLAD_API_PTR *PFNGLRECTIPROC)(GLint x1, GLint y1, GLint x2, GLint y2); +typedef void(GLAD_API_PTR *PFNGLRECTIVPROC)(const GLint *v1, const GLint *v2); +typedef void(GLAD_API_PTR *PFNGLRECTSPROC)(GLshort x1, GLshort y1, GLshort x2, GLshort y2); +typedef void(GLAD_API_PTR *PFNGLRECTSVPROC)(const GLshort *v1, const GLshort *v2); +typedef void(GLAD_API_PTR *PFNGLRELEASESHADERCOMPILERPROC)(void); +typedef GLint(GLAD_API_PTR *PFNGLRENDERMODEPROC)(GLenum mode); +typedef void(GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void(GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void(GLAD_API_PTR *PFNGLRESUMETRANSFORMFEEDBACKPROC)(void); +typedef void(GLAD_API_PTR *PFNGLROTATEDPROC)(GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +typedef void(GLAD_API_PTR *PFNGLROTATEFPROC)(GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +typedef void(GLAD_API_PTR *PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert); +typedef void(GLAD_API_PTR *PFNGLSAMPLEMASKIPROC)(GLuint maskNumber, GLbitfield mask); +typedef void(GLAD_API_PTR *PFNGLSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, const GLint *param); +typedef void(GLAD_API_PTR *PFNGLSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, const GLuint *param); +typedef void(GLAD_API_PTR *PFNGLSAMPLERPARAMETERFPROC)(GLuint sampler, GLenum pname, GLfloat param); +typedef void(GLAD_API_PTR *PFNGLSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, const GLfloat *param); +typedef void(GLAD_API_PTR *PFNGLSAMPLERPARAMETERIPROC)(GLuint sampler, GLenum pname, GLint param); +typedef void(GLAD_API_PTR *PFNGLSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, const GLint *param); +typedef void(GLAD_API_PTR *PFNGLSCALEDPROC)(GLdouble x, GLdouble y, GLdouble z); +typedef void(GLAD_API_PTR *PFNGLSCALEFPROC)(GLfloat x, GLfloat y, GLfloat z); +typedef void(GLAD_API_PTR *PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height); +typedef void(GLAD_API_PTR *PFNGLSCISSORARRAYVPROC)(GLuint first, GLsizei count, const GLint *v); +typedef void(GLAD_API_PTR *PFNGLSCISSORINDEXEDPROC)(GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +typedef void(GLAD_API_PTR *PFNGLSCISSORINDEXEDVPROC)(GLuint index, const GLint *v); +typedef void(GLAD_API_PTR *PFNGLSECONDARYCOLOR3BPROC)(GLbyte red, GLbyte green, GLbyte blue); +typedef void(GLAD_API_PTR *PFNGLSECONDARYCOLOR3BVPROC)(const GLbyte *v); +typedef void(GLAD_API_PTR *PFNGLSECONDARYCOLOR3DPROC)(GLdouble red, GLdouble green, GLdouble blue); +typedef void(GLAD_API_PTR *PFNGLSECONDARYCOLOR3DVPROC)(const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLSECONDARYCOLOR3FPROC)(GLfloat red, GLfloat green, GLfloat blue); +typedef void(GLAD_API_PTR *PFNGLSECONDARYCOLOR3FVPROC)(const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLSECONDARYCOLOR3IPROC)(GLint red, GLint green, GLint blue); +typedef void(GLAD_API_PTR *PFNGLSECONDARYCOLOR3IVPROC)(const GLint *v); +typedef void(GLAD_API_PTR *PFNGLSECONDARYCOLOR3SPROC)(GLshort red, GLshort green, GLshort blue); +typedef void(GLAD_API_PTR *PFNGLSECONDARYCOLOR3SVPROC)(const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLSECONDARYCOLOR3UBPROC)(GLubyte red, GLubyte green, GLubyte blue); +typedef void(GLAD_API_PTR *PFNGLSECONDARYCOLOR3UBVPROC)(const GLubyte *v); +typedef void(GLAD_API_PTR *PFNGLSECONDARYCOLOR3UIPROC)(GLuint red, GLuint green, GLuint blue); +typedef void(GLAD_API_PTR *PFNGLSECONDARYCOLOR3UIVPROC)(const GLuint *v); +typedef void(GLAD_API_PTR *PFNGLSECONDARYCOLOR3USPROC)(GLushort red, GLushort green, GLushort blue); +typedef void(GLAD_API_PTR *PFNGLSECONDARYCOLOR3USVPROC)(const GLushort *v); +typedef void(GLAD_API_PTR *PFNGLSECONDARYCOLORP3UIPROC)(GLenum type, GLuint color); +typedef void(GLAD_API_PTR *PFNGLSECONDARYCOLORP3UIVPROC)(GLenum type, const GLuint *color); +typedef void(GLAD_API_PTR *PFNGLSECONDARYCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void(GLAD_API_PTR *PFNGLSELECTBUFFERPROC)(GLsizei size, GLuint *buffer); +typedef void(GLAD_API_PTR *PFNGLSHADEMODELPROC)(GLenum mode); +typedef void(GLAD_API_PTR *PFNGLSHADERBINARYPROC)(GLsizei count, const GLuint *shaders, GLenum binaryFormat, const void *binary, GLsizei length); +typedef void(GLAD_API_PTR *PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const *string, const GLint *length); +typedef void(GLAD_API_PTR *PFNGLSHADERSTORAGEBLOCKBINDINGPROC)(GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); +typedef void(GLAD_API_PTR *PFNGLSPECIALIZESHADERPROC)(GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); +typedef void(GLAD_API_PTR *PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask); +typedef void(GLAD_API_PTR *PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask); +typedef void(GLAD_API_PTR *PFNGLSTENCILMASKPROC)(GLuint mask); +typedef void(GLAD_API_PTR *PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask); +typedef void(GLAD_API_PTR *PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass); +typedef void(GLAD_API_PTR *PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void(GLAD_API_PTR *PFNGLTEXBUFFERPROC)(GLenum target, GLenum internalformat, GLuint buffer); +typedef void(GLAD_API_PTR *PFNGLTEXBUFFERRANGEPROC)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD1DPROC)(GLdouble s); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD1DVPROC)(const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD1FPROC)(GLfloat s); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD1FVPROC)(const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD1IPROC)(GLint s); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD1IVPROC)(const GLint *v); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD1SPROC)(GLshort s); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD1SVPROC)(const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD2DPROC)(GLdouble s, GLdouble t); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD2DVPROC)(const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD2FPROC)(GLfloat s, GLfloat t); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD2FVPROC)(const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD2IPROC)(GLint s, GLint t); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD2IVPROC)(const GLint *v); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD2SPROC)(GLshort s, GLshort t); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD2SVPROC)(const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD3DPROC)(GLdouble s, GLdouble t, GLdouble r); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD3DVPROC)(const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD3FPROC)(GLfloat s, GLfloat t, GLfloat r); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD3FVPROC)(const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD3IPROC)(GLint s, GLint t, GLint r); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD3IVPROC)(const GLint *v); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD3SPROC)(GLshort s, GLshort t, GLshort r); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD3SVPROC)(const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD4DPROC)(GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD4DVPROC)(const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD4FPROC)(GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD4FVPROC)(const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD4IPROC)(GLint s, GLint t, GLint r, GLint q); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD4IVPROC)(const GLint *v); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD4SPROC)(GLshort s, GLshort t, GLshort r, GLshort q); +typedef void(GLAD_API_PTR *PFNGLTEXCOORD4SVPROC)(const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLTEXCOORDP1UIPROC)(GLenum type, GLuint coords); +typedef void(GLAD_API_PTR *PFNGLTEXCOORDP1UIVPROC)(GLenum type, const GLuint *coords); +typedef void(GLAD_API_PTR *PFNGLTEXCOORDP2UIPROC)(GLenum type, GLuint coords); +typedef void(GLAD_API_PTR *PFNGLTEXCOORDP2UIVPROC)(GLenum type, const GLuint *coords); +typedef void(GLAD_API_PTR *PFNGLTEXCOORDP3UIPROC)(GLenum type, GLuint coords); +typedef void(GLAD_API_PTR *PFNGLTEXCOORDP3UIVPROC)(GLenum type, const GLuint *coords); +typedef void(GLAD_API_PTR *PFNGLTEXCOORDP4UIPROC)(GLenum type, GLuint coords); +typedef void(GLAD_API_PTR *PFNGLTEXCOORDP4UIVPROC)(GLenum type, const GLuint *coords); +typedef void(GLAD_API_PTR *PFNGLTEXCOORDPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void(GLAD_API_PTR *PFNGLTEXENVFPROC)(GLenum target, GLenum pname, GLfloat param); +typedef void(GLAD_API_PTR *PFNGLTEXENVFVPROC)(GLenum target, GLenum pname, const GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLTEXENVIPROC)(GLenum target, GLenum pname, GLint param); +typedef void(GLAD_API_PTR *PFNGLTEXENVIVPROC)(GLenum target, GLenum pname, const GLint *params); +typedef void(GLAD_API_PTR *PFNGLTEXGENDPROC)(GLenum coord, GLenum pname, GLdouble param); +typedef void(GLAD_API_PTR *PFNGLTEXGENDVPROC)(GLenum coord, GLenum pname, const GLdouble *params); +typedef void(GLAD_API_PTR *PFNGLTEXGENFPROC)(GLenum coord, GLenum pname, GLfloat param); +typedef void(GLAD_API_PTR *PFNGLTEXGENFVPROC)(GLenum coord, GLenum pname, const GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLTEXGENIPROC)(GLenum coord, GLenum pname, GLint param); +typedef void(GLAD_API_PTR *PFNGLTEXGENIVPROC)(GLenum coord, GLenum pname, const GLint *params); +typedef void(GLAD_API_PTR *PFNGLTEXIMAGE1DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void(GLAD_API_PTR *PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void(GLAD_API_PTR *PFNGLTEXIMAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void(GLAD_API_PTR *PFNGLTEXIMAGE3DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void(GLAD_API_PTR *PFNGLTEXIMAGE3DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void(GLAD_API_PTR *PFNGLTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, const GLint *params); +typedef void(GLAD_API_PTR *PFNGLTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, const GLuint *params); +typedef void(GLAD_API_PTR *PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param); +typedef void(GLAD_API_PTR *PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat *params); +typedef void(GLAD_API_PTR *PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param); +typedef void(GLAD_API_PTR *PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint *params); +typedef void(GLAD_API_PTR *PFNGLTEXSTORAGE1DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void(GLAD_API_PTR *PFNGLTEXSTORAGE2DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void(GLAD_API_PTR *PFNGLTEXSTORAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void(GLAD_API_PTR *PFNGLTEXSTORAGE3DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void(GLAD_API_PTR *PFNGLTEXSTORAGE3DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void(GLAD_API_PTR *PFNGLTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void(GLAD_API_PTR *PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +typedef void(GLAD_API_PTR *PFNGLTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void(GLAD_API_PTR *PFNGLTEXTUREBARRIERPROC)(void); +typedef void(GLAD_API_PTR *PFNGLTEXTUREBUFFERPROC)(GLuint texture, GLenum internalformat, GLuint buffer); +typedef void(GLAD_API_PTR *PFNGLTEXTUREBUFFERRANGEPROC)(GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void(GLAD_API_PTR *PFNGLTEXTUREPARAMETERIIVPROC)(GLuint texture, GLenum pname, const GLint *params); +typedef void(GLAD_API_PTR *PFNGLTEXTUREPARAMETERIUIVPROC)(GLuint texture, GLenum pname, const GLuint *params); +typedef void(GLAD_API_PTR *PFNGLTEXTUREPARAMETERFPROC)(GLuint texture, GLenum pname, GLfloat param); +typedef void(GLAD_API_PTR *PFNGLTEXTUREPARAMETERFVPROC)(GLuint texture, GLenum pname, const GLfloat *param); +typedef void(GLAD_API_PTR *PFNGLTEXTUREPARAMETERIPROC)(GLuint texture, GLenum pname, GLint param); +typedef void(GLAD_API_PTR *PFNGLTEXTUREPARAMETERIVPROC)(GLuint texture, GLenum pname, const GLint *param); +typedef void(GLAD_API_PTR *PFNGLTEXTURESTORAGE1DPROC)(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void(GLAD_API_PTR *PFNGLTEXTURESTORAGE2DPROC)(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void(GLAD_API_PTR *PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC)(GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void(GLAD_API_PTR *PFNGLTEXTURESTORAGE3DPROC)(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void(GLAD_API_PTR *PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC)(GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void(GLAD_API_PTR *PFNGLTEXTURESUBIMAGE1DPROC)(GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void(GLAD_API_PTR *PFNGLTEXTURESUBIMAGE2DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +typedef void(GLAD_API_PTR *PFNGLTEXTURESUBIMAGE3DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void(GLAD_API_PTR *PFNGLTEXTUREVIEWPROC)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +typedef void(GLAD_API_PTR *PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC)(GLuint xfb, GLuint index, GLuint buffer); +typedef void(GLAD_API_PTR *PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC)(GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void(GLAD_API_PTR *PFNGLTRANSFORMFEEDBACKVARYINGSPROC)(GLuint program, GLsizei count, const GLchar *const *varyings, GLenum bufferMode); +typedef void(GLAD_API_PTR *PFNGLTRANSLATEDPROC)(GLdouble x, GLdouble y, GLdouble z); +typedef void(GLAD_API_PTR *PFNGLTRANSLATEFPROC)(GLfloat x, GLfloat y, GLfloat z); +typedef void(GLAD_API_PTR *PFNGLUNIFORM1DPROC)(GLint location, GLdouble x); +typedef void(GLAD_API_PTR *PFNGLUNIFORM1DVPROC)(GLint location, GLsizei count, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0); +typedef void(GLAD_API_PTR *PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORM1IPROC)(GLint location, GLint v0); +typedef void(GLAD_API_PTR *PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORM1UIPROC)(GLint location, GLuint v0); +typedef void(GLAD_API_PTR *PFNGLUNIFORM1UIVPROC)(GLint location, GLsizei count, const GLuint *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORM2DPROC)(GLint location, GLdouble x, GLdouble y); +typedef void(GLAD_API_PTR *PFNGLUNIFORM2DVPROC)(GLint location, GLsizei count, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1); +typedef void(GLAD_API_PTR *PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1); +typedef void(GLAD_API_PTR *PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORM2UIPROC)(GLint location, GLuint v0, GLuint v1); +typedef void(GLAD_API_PTR *PFNGLUNIFORM2UIVPROC)(GLint location, GLsizei count, const GLuint *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORM3DPROC)(GLint location, GLdouble x, GLdouble y, GLdouble z); +typedef void(GLAD_API_PTR *PFNGLUNIFORM3DVPROC)(GLint location, GLsizei count, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void(GLAD_API_PTR *PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2); +typedef void(GLAD_API_PTR *PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORM3UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void(GLAD_API_PTR *PFNGLUNIFORM3UIVPROC)(GLint location, GLsizei count, const GLuint *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORM4DPROC)(GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void(GLAD_API_PTR *PFNGLUNIFORM4DVPROC)(GLint location, GLsizei count, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void(GLAD_API_PTR *PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void(GLAD_API_PTR *PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORM4UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void(GLAD_API_PTR *PFNGLUNIFORM4UIVPROC)(GLint location, GLsizei count, const GLuint *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORMBLOCKBINDINGPROC)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +typedef void(GLAD_API_PTR *PFNGLUNIFORMMATRIX2DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORMMATRIX2X3DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORMMATRIX2X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORMMATRIX2X4DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORMMATRIX2X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORMMATRIX3DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORMMATRIX3X2DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORMMATRIX3X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORMMATRIX3X4DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORMMATRIX3X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORMMATRIX4DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORMMATRIX4X2DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORMMATRIX4X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORMMATRIX4X3DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORMMATRIX4X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void(GLAD_API_PTR *PFNGLUNIFORMSUBROUTINESUIVPROC)(GLenum shadertype, GLsizei count, const GLuint *indices); +typedef GLboolean(GLAD_API_PTR *PFNGLUNMAPBUFFERPROC)(GLenum target); +typedef GLboolean(GLAD_API_PTR *PFNGLUNMAPNAMEDBUFFERPROC)(GLuint buffer); +typedef void(GLAD_API_PTR *PFNGLUSEPROGRAMPROC)(GLuint program); +typedef void(GLAD_API_PTR *PFNGLUSEPROGRAMSTAGESPROC)(GLuint pipeline, GLbitfield stages, GLuint program); +typedef void(GLAD_API_PTR *PFNGLVALIDATEPROGRAMPROC)(GLuint program); +typedef void(GLAD_API_PTR *PFNGLVALIDATEPROGRAMPIPELINEPROC)(GLuint pipeline); +typedef void(GLAD_API_PTR *PFNGLVERTEX2DPROC)(GLdouble x, GLdouble y); +typedef void(GLAD_API_PTR *PFNGLVERTEX2DVPROC)(const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLVERTEX2FPROC)(GLfloat x, GLfloat y); +typedef void(GLAD_API_PTR *PFNGLVERTEX2FVPROC)(const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLVERTEX2IPROC)(GLint x, GLint y); +typedef void(GLAD_API_PTR *PFNGLVERTEX2IVPROC)(const GLint *v); +typedef void(GLAD_API_PTR *PFNGLVERTEX2SPROC)(GLshort x, GLshort y); +typedef void(GLAD_API_PTR *PFNGLVERTEX2SVPROC)(const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLVERTEX3DPROC)(GLdouble x, GLdouble y, GLdouble z); +typedef void(GLAD_API_PTR *PFNGLVERTEX3DVPROC)(const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLVERTEX3FPROC)(GLfloat x, GLfloat y, GLfloat z); +typedef void(GLAD_API_PTR *PFNGLVERTEX3FVPROC)(const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLVERTEX3IPROC)(GLint x, GLint y, GLint z); +typedef void(GLAD_API_PTR *PFNGLVERTEX3IVPROC)(const GLint *v); +typedef void(GLAD_API_PTR *PFNGLVERTEX3SPROC)(GLshort x, GLshort y, GLshort z); +typedef void(GLAD_API_PTR *PFNGLVERTEX3SVPROC)(const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLVERTEX4DPROC)(GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void(GLAD_API_PTR *PFNGLVERTEX4DVPROC)(const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLVERTEX4FPROC)(GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void(GLAD_API_PTR *PFNGLVERTEX4FVPROC)(const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLVERTEX4IPROC)(GLint x, GLint y, GLint z, GLint w); +typedef void(GLAD_API_PTR *PFNGLVERTEX4IVPROC)(const GLint *v); +typedef void(GLAD_API_PTR *PFNGLVERTEX4SPROC)(GLshort x, GLshort y, GLshort z, GLshort w); +typedef void(GLAD_API_PTR *PFNGLVERTEX4SVPROC)(const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXARRAYATTRIBBINDINGPROC)(GLuint vaobj, GLuint attribindex, GLuint bindingindex); +typedef void(GLAD_API_PTR *PFNGLVERTEXARRAYATTRIBFORMATPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void(GLAD_API_PTR *PFNGLVERTEXARRAYATTRIBIFORMATPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void(GLAD_API_PTR *PFNGLVERTEXARRAYATTRIBLFORMATPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void(GLAD_API_PTR *PFNGLVERTEXARRAYBINDINGDIVISORPROC)(GLuint vaobj, GLuint bindingindex, GLuint divisor); +typedef void(GLAD_API_PTR *PFNGLVERTEXARRAYELEMENTBUFFERPROC)(GLuint vaobj, GLuint buffer); +typedef void(GLAD_API_PTR *PFNGLVERTEXARRAYVERTEXBUFFERPROC)(GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void(GLAD_API_PTR *PFNGLVERTEXARRAYVERTEXBUFFERSPROC)(GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB1DPROC)(GLuint index, GLdouble x); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB1DVPROC)(GLuint index, const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB1SPROC)(GLuint index, GLshort x); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB1SVPROC)(GLuint index, const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB2DPROC)(GLuint index, GLdouble x, GLdouble y); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB2DVPROC)(GLuint index, const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB2SPROC)(GLuint index, GLshort x, GLshort y); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB2SVPROC)(GLuint index, const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB3DVPROC)(GLuint index, const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB3SPROC)(GLuint index, GLshort x, GLshort y, GLshort z); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB3SVPROC)(GLuint index, const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB4NBVPROC)(GLuint index, const GLbyte *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB4NIVPROC)(GLuint index, const GLint *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB4NSVPROC)(GLuint index, const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB4NUBPROC)(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB4NUBVPROC)(GLuint index, const GLubyte *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB4NUIVPROC)(GLuint index, const GLuint *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB4NUSVPROC)(GLuint index, const GLushort *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB4BVPROC)(GLuint index, const GLbyte *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB4DVPROC)(GLuint index, const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB4IVPROC)(GLuint index, const GLint *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB4SPROC)(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB4SVPROC)(GLuint index, const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB4UBVPROC)(GLuint index, const GLubyte *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB4UIVPROC)(GLuint index, const GLuint *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIB4USVPROC)(GLuint index, const GLushort *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBBINDINGPROC)(GLuint attribindex, GLuint bindingindex); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBDIVISORPROC)(GLuint index, GLuint divisor); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBFORMATPROC)(GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI1IPROC)(GLuint index, GLint x); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI1IVPROC)(GLuint index, const GLint *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI1UIPROC)(GLuint index, GLuint x); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI1UIVPROC)(GLuint index, const GLuint *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI2IPROC)(GLuint index, GLint x, GLint y); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI2IVPROC)(GLuint index, const GLint *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI2UIPROC)(GLuint index, GLuint x, GLuint y); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI2UIVPROC)(GLuint index, const GLuint *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI3IPROC)(GLuint index, GLint x, GLint y, GLint z); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI3IVPROC)(GLuint index, const GLint *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI3UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI3UIVPROC)(GLuint index, const GLuint *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI4BVPROC)(GLuint index, const GLbyte *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI4IPROC)(GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI4IVPROC)(GLuint index, const GLint *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI4SVPROC)(GLuint index, const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI4UBVPROC)(GLuint index, const GLubyte *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI4UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI4UIVPROC)(GLuint index, const GLuint *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBI4USVPROC)(GLuint index, const GLushort *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBIFORMATPROC)(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBIPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBL1DPROC)(GLuint index, GLdouble x); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBL1DVPROC)(GLuint index, const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBL2DPROC)(GLuint index, GLdouble x, GLdouble y); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBL2DVPROC)(GLuint index, const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBL3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBL3DVPROC)(GLuint index, const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBL4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBL4DVPROC)(GLuint index, const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBLFORMATPROC)(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBLPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBP1UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBP1UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBP2UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBP2UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBP3UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBP3UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBP4UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBP4UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void(GLAD_API_PTR *PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +typedef void(GLAD_API_PTR *PFNGLVERTEXBINDINGDIVISORPROC)(GLuint bindingindex, GLuint divisor); +typedef void(GLAD_API_PTR *PFNGLVERTEXP2UIPROC)(GLenum type, GLuint value); +typedef void(GLAD_API_PTR *PFNGLVERTEXP2UIVPROC)(GLenum type, const GLuint *value); +typedef void(GLAD_API_PTR *PFNGLVERTEXP3UIPROC)(GLenum type, GLuint value); +typedef void(GLAD_API_PTR *PFNGLVERTEXP3UIVPROC)(GLenum type, const GLuint *value); +typedef void(GLAD_API_PTR *PFNGLVERTEXP4UIPROC)(GLenum type, GLuint value); +typedef void(GLAD_API_PTR *PFNGLVERTEXP4UIVPROC)(GLenum type, const GLuint *value); +typedef void(GLAD_API_PTR *PFNGLVERTEXPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void(GLAD_API_PTR *PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height); +typedef void(GLAD_API_PTR *PFNGLVIEWPORTARRAYVPROC)(GLuint first, GLsizei count, const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLVIEWPORTINDEXEDFPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +typedef void(GLAD_API_PTR *PFNGLVIEWPORTINDEXEDFVPROC)(GLuint index, const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void(GLAD_API_PTR *PFNGLWINDOWPOS2DPROC)(GLdouble x, GLdouble y); +typedef void(GLAD_API_PTR *PFNGLWINDOWPOS2DVPROC)(const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLWINDOWPOS2FPROC)(GLfloat x, GLfloat y); +typedef void(GLAD_API_PTR *PFNGLWINDOWPOS2FVPROC)(const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLWINDOWPOS2IPROC)(GLint x, GLint y); +typedef void(GLAD_API_PTR *PFNGLWINDOWPOS2IVPROC)(const GLint *v); +typedef void(GLAD_API_PTR *PFNGLWINDOWPOS2SPROC)(GLshort x, GLshort y); +typedef void(GLAD_API_PTR *PFNGLWINDOWPOS2SVPROC)(const GLshort *v); +typedef void(GLAD_API_PTR *PFNGLWINDOWPOS3DPROC)(GLdouble x, GLdouble y, GLdouble z); +typedef void(GLAD_API_PTR *PFNGLWINDOWPOS3DVPROC)(const GLdouble *v); +typedef void(GLAD_API_PTR *PFNGLWINDOWPOS3FPROC)(GLfloat x, GLfloat y, GLfloat z); +typedef void(GLAD_API_PTR *PFNGLWINDOWPOS3FVPROC)(const GLfloat *v); +typedef void(GLAD_API_PTR *PFNGLWINDOWPOS3IPROC)(GLint x, GLint y, GLint z); +typedef void(GLAD_API_PTR *PFNGLWINDOWPOS3IVPROC)(const GLint *v); +typedef void(GLAD_API_PTR *PFNGLWINDOWPOS3SPROC)(GLshort x, GLshort y, GLshort z); +typedef void(GLAD_API_PTR *PFNGLWINDOWPOS3SVPROC)(const GLshort *v); + +typedef struct GladGLContext { + void *userptr; + + int VERSION_1_0; + int VERSION_1_1; + int VERSION_1_2; + int VERSION_1_3; + int VERSION_1_4; + int VERSION_1_5; + int VERSION_2_0; + int VERSION_2_1; + int VERSION_3_0; + int VERSION_3_1; + int VERSION_3_2; + int VERSION_3_3; + int VERSION_4_0; + int VERSION_4_1; + int VERSION_4_2; + int VERSION_4_3; + int VERSION_4_4; + int VERSION_4_5; + int VERSION_4_6; + + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC EGLImageTargetTexture2DOES; + PFNGLACCUMPROC Accum; + PFNGLACTIVESHADERPROGRAMPROC ActiveShaderProgram; + PFNGLACTIVETEXTUREPROC ActiveTexture; + PFNGLALPHAFUNCPROC AlphaFunc; + PFNGLARETEXTURESRESIDENTPROC AreTexturesResident; + PFNGLARRAYELEMENTPROC ArrayElement; + PFNGLATTACHSHADERPROC AttachShader; + PFNGLBEGINPROC Begin; + PFNGLBEGINCONDITIONALRENDERPROC BeginConditionalRender; + PFNGLBEGINQUERYPROC BeginQuery; + PFNGLBEGINQUERYINDEXEDPROC BeginQueryIndexed; + PFNGLBEGINTRANSFORMFEEDBACKPROC BeginTransformFeedback; + PFNGLBINDATTRIBLOCATIONPROC BindAttribLocation; + PFNGLBINDBUFFERPROC BindBuffer; + PFNGLBINDBUFFERBASEPROC BindBufferBase; + PFNGLBINDBUFFERRANGEPROC BindBufferRange; + PFNGLBINDBUFFERSBASEPROC BindBuffersBase; + PFNGLBINDBUFFERSRANGEPROC BindBuffersRange; + PFNGLBINDFRAGDATALOCATIONPROC BindFragDataLocation; + PFNGLBINDFRAGDATALOCATIONINDEXEDPROC BindFragDataLocationIndexed; + PFNGLBINDFRAMEBUFFERPROC BindFramebuffer; + PFNGLBINDIMAGETEXTUREPROC BindImageTexture; + PFNGLBINDIMAGETEXTURESPROC BindImageTextures; + PFNGLBINDPROGRAMPIPELINEPROC BindProgramPipeline; + PFNGLBINDRENDERBUFFERPROC BindRenderbuffer; + PFNGLBINDSAMPLERPROC BindSampler; + PFNGLBINDSAMPLERSPROC BindSamplers; + PFNGLBINDTEXTUREPROC BindTexture; + PFNGLBINDTEXTUREUNITPROC BindTextureUnit; + PFNGLBINDTEXTURESPROC BindTextures; + PFNGLBINDTRANSFORMFEEDBACKPROC BindTransformFeedback; + PFNGLBINDVERTEXARRAYPROC BindVertexArray; + PFNGLBINDVERTEXBUFFERPROC BindVertexBuffer; + PFNGLBINDVERTEXBUFFERSPROC BindVertexBuffers; + PFNGLBITMAPPROC Bitmap; + PFNGLBLENDCOLORPROC BlendColor; + PFNGLBLENDEQUATIONPROC BlendEquation; + PFNGLBLENDEQUATIONSEPARATEPROC BlendEquationSeparate; + PFNGLBLENDEQUATIONSEPARATEIPROC BlendEquationSeparatei; + PFNGLBLENDEQUATIONIPROC BlendEquationi; + PFNGLBLENDFUNCPROC BlendFunc; + PFNGLBLENDFUNCSEPARATEPROC BlendFuncSeparate; + PFNGLBLENDFUNCSEPARATEIPROC BlendFuncSeparatei; + PFNGLBLENDFUNCIPROC BlendFunci; + PFNGLBLITFRAMEBUFFERPROC BlitFramebuffer; + PFNGLBLITNAMEDFRAMEBUFFERPROC BlitNamedFramebuffer; + PFNGLBUFFERDATAPROC BufferData; + PFNGLBUFFERSTORAGEPROC BufferStorage; + PFNGLBUFFERSUBDATAPROC BufferSubData; + PFNGLCALLLISTPROC CallList; + PFNGLCALLLISTSPROC CallLists; + PFNGLCHECKFRAMEBUFFERSTATUSPROC CheckFramebufferStatus; + PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC CheckNamedFramebufferStatus; + PFNGLCLAMPCOLORPROC ClampColor; + PFNGLCLEARPROC Clear; + PFNGLCLEARACCUMPROC ClearAccum; + PFNGLCLEARBUFFERDATAPROC ClearBufferData; + PFNGLCLEARBUFFERSUBDATAPROC ClearBufferSubData; + PFNGLCLEARBUFFERFIPROC ClearBufferfi; + PFNGLCLEARBUFFERFVPROC ClearBufferfv; + PFNGLCLEARBUFFERIVPROC ClearBufferiv; + PFNGLCLEARBUFFERUIVPROC ClearBufferuiv; + PFNGLCLEARCOLORPROC ClearColor; + PFNGLCLEARDEPTHPROC ClearDepth; + PFNGLCLEARDEPTHFPROC ClearDepthf; + PFNGLCLEARINDEXPROC ClearIndex; + PFNGLCLEARNAMEDBUFFERDATAPROC ClearNamedBufferData; + PFNGLCLEARNAMEDBUFFERSUBDATAPROC ClearNamedBufferSubData; + PFNGLCLEARNAMEDFRAMEBUFFERFIPROC ClearNamedFramebufferfi; + PFNGLCLEARNAMEDFRAMEBUFFERFVPROC ClearNamedFramebufferfv; + PFNGLCLEARNAMEDFRAMEBUFFERIVPROC ClearNamedFramebufferiv; + PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC ClearNamedFramebufferuiv; + PFNGLCLEARSTENCILPROC ClearStencil; + PFNGLCLEARTEXIMAGEPROC ClearTexImage; + PFNGLCLEARTEXSUBIMAGEPROC ClearTexSubImage; + PFNGLCLIENTACTIVETEXTUREPROC ClientActiveTexture; + PFNGLCLIENTWAITSYNCPROC ClientWaitSync; + PFNGLCLIPCONTROLPROC ClipControl; + PFNGLCLIPPLANEPROC ClipPlane; + PFNGLCOLOR3BPROC Color3b; + PFNGLCOLOR3BVPROC Color3bv; + PFNGLCOLOR3DPROC Color3d; + PFNGLCOLOR3DVPROC Color3dv; + PFNGLCOLOR3FPROC Color3f; + PFNGLCOLOR3FVPROC Color3fv; + PFNGLCOLOR3IPROC Color3i; + PFNGLCOLOR3IVPROC Color3iv; + PFNGLCOLOR3SPROC Color3s; + PFNGLCOLOR3SVPROC Color3sv; + PFNGLCOLOR3UBPROC Color3ub; + PFNGLCOLOR3UBVPROC Color3ubv; + PFNGLCOLOR3UIPROC Color3ui; + PFNGLCOLOR3UIVPROC Color3uiv; + PFNGLCOLOR3USPROC Color3us; + PFNGLCOLOR3USVPROC Color3usv; + PFNGLCOLOR4BPROC Color4b; + PFNGLCOLOR4BVPROC Color4bv; + PFNGLCOLOR4DPROC Color4d; + PFNGLCOLOR4DVPROC Color4dv; + PFNGLCOLOR4FPROC Color4f; + PFNGLCOLOR4FVPROC Color4fv; + PFNGLCOLOR4IPROC Color4i; + PFNGLCOLOR4IVPROC Color4iv; + PFNGLCOLOR4SPROC Color4s; + PFNGLCOLOR4SVPROC Color4sv; + PFNGLCOLOR4UBPROC Color4ub; + PFNGLCOLOR4UBVPROC Color4ubv; + PFNGLCOLOR4UIPROC Color4ui; + PFNGLCOLOR4UIVPROC Color4uiv; + PFNGLCOLOR4USPROC Color4us; + PFNGLCOLOR4USVPROC Color4usv; + PFNGLCOLORMASKPROC ColorMask; + PFNGLCOLORMASKIPROC ColorMaski; + PFNGLCOLORMATERIALPROC ColorMaterial; + PFNGLCOLORP3UIPROC ColorP3ui; + PFNGLCOLORP3UIVPROC ColorP3uiv; + PFNGLCOLORP4UIPROC ColorP4ui; + PFNGLCOLORP4UIVPROC ColorP4uiv; + PFNGLCOLORPOINTERPROC ColorPointer; + PFNGLCOMPILESHADERPROC CompileShader; + PFNGLCOMPRESSEDTEXIMAGE1DPROC CompressedTexImage1D; + PFNGLCOMPRESSEDTEXIMAGE2DPROC CompressedTexImage2D; + PFNGLCOMPRESSEDTEXIMAGE3DPROC CompressedTexImage3D; + PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC CompressedTexSubImage1D; + PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC CompressedTexSubImage2D; + PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC CompressedTexSubImage3D; + PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC CompressedTextureSubImage1D; + PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC CompressedTextureSubImage2D; + PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC CompressedTextureSubImage3D; + PFNGLCOPYBUFFERSUBDATAPROC CopyBufferSubData; + PFNGLCOPYIMAGESUBDATAPROC CopyImageSubData; + PFNGLCOPYNAMEDBUFFERSUBDATAPROC CopyNamedBufferSubData; + PFNGLCOPYPIXELSPROC CopyPixels; + PFNGLCOPYTEXIMAGE1DPROC CopyTexImage1D; + PFNGLCOPYTEXIMAGE2DPROC CopyTexImage2D; + PFNGLCOPYTEXSUBIMAGE1DPROC CopyTexSubImage1D; + PFNGLCOPYTEXSUBIMAGE2DPROC CopyTexSubImage2D; + PFNGLCOPYTEXSUBIMAGE3DPROC CopyTexSubImage3D; + PFNGLCOPYTEXTURESUBIMAGE1DPROC CopyTextureSubImage1D; + PFNGLCOPYTEXTURESUBIMAGE2DPROC CopyTextureSubImage2D; + PFNGLCOPYTEXTURESUBIMAGE3DPROC CopyTextureSubImage3D; + PFNGLCREATEBUFFERSPROC CreateBuffers; + PFNGLCREATEFRAMEBUFFERSPROC CreateFramebuffers; + PFNGLCREATEPROGRAMPROC CreateProgram; + PFNGLCREATEPROGRAMPIPELINESPROC CreateProgramPipelines; + PFNGLCREATEQUERIESPROC CreateQueries; + PFNGLCREATERENDERBUFFERSPROC CreateRenderbuffers; + PFNGLCREATESAMPLERSPROC CreateSamplers; + PFNGLCREATESHADERPROC CreateShader; + PFNGLCREATESHADERPROGRAMVPROC CreateShaderProgramv; + PFNGLCREATETEXTURESPROC CreateTextures; + PFNGLCREATETRANSFORMFEEDBACKSPROC CreateTransformFeedbacks; + PFNGLCREATEVERTEXARRAYSPROC CreateVertexArrays; + PFNGLCULLFACEPROC CullFace; + PFNGLDEBUGMESSAGECALLBACKPROC DebugMessageCallback; + PFNGLDEBUGMESSAGECONTROLPROC DebugMessageControl; + PFNGLDEBUGMESSAGEINSERTPROC DebugMessageInsert; + PFNGLDELETEBUFFERSPROC DeleteBuffers; + PFNGLDELETEFRAMEBUFFERSPROC DeleteFramebuffers; + PFNGLDELETELISTSPROC DeleteLists; + PFNGLDELETEPROGRAMPROC DeleteProgram; + PFNGLDELETEPROGRAMPIPELINESPROC DeleteProgramPipelines; + PFNGLDELETEQUERIESPROC DeleteQueries; + PFNGLDELETERENDERBUFFERSPROC DeleteRenderbuffers; + PFNGLDELETESAMPLERSPROC DeleteSamplers; + PFNGLDELETESHADERPROC DeleteShader; + PFNGLDELETESYNCPROC DeleteSync; + PFNGLDELETETEXTURESPROC DeleteTextures; + PFNGLDELETETRANSFORMFEEDBACKSPROC DeleteTransformFeedbacks; + PFNGLDELETEVERTEXARRAYSPROC DeleteVertexArrays; + PFNGLDEPTHFUNCPROC DepthFunc; + PFNGLDEPTHMASKPROC DepthMask; + PFNGLDEPTHRANGEPROC DepthRange; + PFNGLDEPTHRANGEARRAYVPROC DepthRangeArrayv; + PFNGLDEPTHRANGEINDEXEDPROC DepthRangeIndexed; + PFNGLDEPTHRANGEFPROC DepthRangef; + PFNGLDETACHSHADERPROC DetachShader; + PFNGLDISABLEPROC Disable; + PFNGLDISABLECLIENTSTATEPROC DisableClientState; + PFNGLDISABLEVERTEXARRAYATTRIBPROC DisableVertexArrayAttrib; + PFNGLDISABLEVERTEXATTRIBARRAYPROC DisableVertexAttribArray; + PFNGLDISABLEIPROC Disablei; + PFNGLDISPATCHCOMPUTEPROC DispatchCompute; + PFNGLDISPATCHCOMPUTEINDIRECTPROC DispatchComputeIndirect; + PFNGLDRAWARRAYSPROC DrawArrays; + PFNGLDRAWARRAYSINDIRECTPROC DrawArraysIndirect; + PFNGLDRAWARRAYSINSTANCEDPROC DrawArraysInstanced; + PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC DrawArraysInstancedBaseInstance; + PFNGLDRAWBUFFERPROC DrawBuffer; + PFNGLDRAWBUFFERSPROC DrawBuffers; + PFNGLDRAWELEMENTSPROC DrawElements; + PFNGLDRAWELEMENTSBASEVERTEXPROC DrawElementsBaseVertex; + PFNGLDRAWELEMENTSINDIRECTPROC DrawElementsIndirect; + PFNGLDRAWELEMENTSINSTANCEDPROC DrawElementsInstanced; + PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC DrawElementsInstancedBaseInstance; + PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC DrawElementsInstancedBaseVertex; + PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC DrawElementsInstancedBaseVertexBaseInstance; + PFNGLDRAWPIXELSPROC DrawPixels; + PFNGLDRAWRANGEELEMENTSPROC DrawRangeElements; + PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC DrawRangeElementsBaseVertex; + PFNGLDRAWTRANSFORMFEEDBACKPROC DrawTransformFeedback; + PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC DrawTransformFeedbackInstanced; + PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC DrawTransformFeedbackStream; + PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC DrawTransformFeedbackStreamInstanced; + PFNGLEDGEFLAGPROC EdgeFlag; + PFNGLEDGEFLAGPOINTERPROC EdgeFlagPointer; + PFNGLEDGEFLAGVPROC EdgeFlagv; + PFNGLENABLEPROC Enable; + PFNGLENABLECLIENTSTATEPROC EnableClientState; + PFNGLENABLEVERTEXARRAYATTRIBPROC EnableVertexArrayAttrib; + PFNGLENABLEVERTEXATTRIBARRAYPROC EnableVertexAttribArray; + PFNGLENABLEIPROC Enablei; + PFNGLENDPROC End; + PFNGLENDCONDITIONALRENDERPROC EndConditionalRender; + PFNGLENDLISTPROC EndList; + PFNGLENDQUERYPROC EndQuery; + PFNGLENDQUERYINDEXEDPROC EndQueryIndexed; + PFNGLENDTRANSFORMFEEDBACKPROC EndTransformFeedback; + PFNGLEVALCOORD1DPROC EvalCoord1d; + PFNGLEVALCOORD1DVPROC EvalCoord1dv; + PFNGLEVALCOORD1FPROC EvalCoord1f; + PFNGLEVALCOORD1FVPROC EvalCoord1fv; + PFNGLEVALCOORD2DPROC EvalCoord2d; + PFNGLEVALCOORD2DVPROC EvalCoord2dv; + PFNGLEVALCOORD2FPROC EvalCoord2f; + PFNGLEVALCOORD2FVPROC EvalCoord2fv; + PFNGLEVALMESH1PROC EvalMesh1; + PFNGLEVALMESH2PROC EvalMesh2; + PFNGLEVALPOINT1PROC EvalPoint1; + PFNGLEVALPOINT2PROC EvalPoint2; + PFNGLFEEDBACKBUFFERPROC FeedbackBuffer; + PFNGLFENCESYNCPROC FenceSync; + PFNGLFINISHPROC Finish; + PFNGLFLUSHPROC Flush; + PFNGLFLUSHMAPPEDBUFFERRANGEPROC FlushMappedBufferRange; + PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC FlushMappedNamedBufferRange; + PFNGLFOGCOORDPOINTERPROC FogCoordPointer; + PFNGLFOGCOORDDPROC FogCoordd; + PFNGLFOGCOORDDVPROC FogCoorddv; + PFNGLFOGCOORDFPROC FogCoordf; + PFNGLFOGCOORDFVPROC FogCoordfv; + PFNGLFOGFPROC Fogf; + PFNGLFOGFVPROC Fogfv; + PFNGLFOGIPROC Fogi; + PFNGLFOGIVPROC Fogiv; + PFNGLFRAMEBUFFERPARAMETERIPROC FramebufferParameteri; + PFNGLFRAMEBUFFERRENDERBUFFERPROC FramebufferRenderbuffer; + PFNGLFRAMEBUFFERTEXTUREPROC FramebufferTexture; + PFNGLFRAMEBUFFERTEXTURE1DPROC FramebufferTexture1D; + PFNGLFRAMEBUFFERTEXTURE2DPROC FramebufferTexture2D; + PFNGLFRAMEBUFFERTEXTURE3DPROC FramebufferTexture3D; + PFNGLFRAMEBUFFERTEXTURELAYERPROC FramebufferTextureLayer; + PFNGLFRONTFACEPROC FrontFace; + PFNGLFRUSTUMPROC Frustum; + PFNGLGENBUFFERSPROC GenBuffers; + PFNGLGENFRAMEBUFFERSPROC GenFramebuffers; + PFNGLGENLISTSPROC GenLists; + PFNGLGENPROGRAMPIPELINESPROC GenProgramPipelines; + PFNGLGENQUERIESPROC GenQueries; + PFNGLGENRENDERBUFFERSPROC GenRenderbuffers; + PFNGLGENSAMPLERSPROC GenSamplers; + PFNGLGENTEXTURESPROC GenTextures; + PFNGLGENTRANSFORMFEEDBACKSPROC GenTransformFeedbacks; + PFNGLGENVERTEXARRAYSPROC GenVertexArrays; + PFNGLGENERATEMIPMAPPROC GenerateMipmap; + PFNGLGENERATETEXTUREMIPMAPPROC GenerateTextureMipmap; + PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC GetActiveAtomicCounterBufferiv; + PFNGLGETACTIVEATTRIBPROC GetActiveAttrib; + PFNGLGETACTIVESUBROUTINENAMEPROC GetActiveSubroutineName; + PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC GetActiveSubroutineUniformName; + PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC GetActiveSubroutineUniformiv; + PFNGLGETACTIVEUNIFORMPROC GetActiveUniform; + PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC GetActiveUniformBlockName; + PFNGLGETACTIVEUNIFORMBLOCKIVPROC GetActiveUniformBlockiv; + PFNGLGETACTIVEUNIFORMNAMEPROC GetActiveUniformName; + PFNGLGETACTIVEUNIFORMSIVPROC GetActiveUniformsiv; + PFNGLGETATTACHEDSHADERSPROC GetAttachedShaders; + PFNGLGETATTRIBLOCATIONPROC GetAttribLocation; + PFNGLGETBOOLEANI_VPROC GetBooleani_v; + PFNGLGETBOOLEANVPROC GetBooleanv; + PFNGLGETBUFFERPARAMETERI64VPROC GetBufferParameteri64v; + PFNGLGETBUFFERPARAMETERIVPROC GetBufferParameteriv; + PFNGLGETBUFFERPOINTERVPROC GetBufferPointerv; + PFNGLGETBUFFERSUBDATAPROC GetBufferSubData; + PFNGLGETCLIPPLANEPROC GetClipPlane; + PFNGLGETCOMPRESSEDTEXIMAGEPROC GetCompressedTexImage; + PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC GetCompressedTextureImage; + PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC GetCompressedTextureSubImage; + PFNGLGETDEBUGMESSAGELOGPROC GetDebugMessageLog; + PFNGLGETDOUBLEI_VPROC GetDoublei_v; + PFNGLGETDOUBLEVPROC GetDoublev; + PFNGLGETERRORPROC GetError; + PFNGLGETFLOATI_VPROC GetFloati_v; + PFNGLGETFLOATVPROC GetFloatv; + PFNGLGETFRAGDATAINDEXPROC GetFragDataIndex; + PFNGLGETFRAGDATALOCATIONPROC GetFragDataLocation; + PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC GetFramebufferAttachmentParameteriv; + PFNGLGETFRAMEBUFFERPARAMETERIVPROC GetFramebufferParameteriv; + PFNGLGETGRAPHICSRESETSTATUSPROC GetGraphicsResetStatus; + PFNGLGETINTEGER64I_VPROC GetInteger64i_v; + PFNGLGETINTEGER64VPROC GetInteger64v; + PFNGLGETINTEGERI_VPROC GetIntegeri_v; + PFNGLGETINTEGERVPROC GetIntegerv; + PFNGLGETINTERNALFORMATI64VPROC GetInternalformati64v; + PFNGLGETINTERNALFORMATIVPROC GetInternalformativ; + PFNGLGETLIGHTFVPROC GetLightfv; + PFNGLGETLIGHTIVPROC GetLightiv; + PFNGLGETMAPDVPROC GetMapdv; + PFNGLGETMAPFVPROC GetMapfv; + PFNGLGETMAPIVPROC GetMapiv; + PFNGLGETMATERIALFVPROC GetMaterialfv; + PFNGLGETMATERIALIVPROC GetMaterialiv; + PFNGLGETMULTISAMPLEFVPROC GetMultisamplefv; + PFNGLGETNAMEDBUFFERPARAMETERI64VPROC GetNamedBufferParameteri64v; + PFNGLGETNAMEDBUFFERPARAMETERIVPROC GetNamedBufferParameteriv; + PFNGLGETNAMEDBUFFERPOINTERVPROC GetNamedBufferPointerv; + PFNGLGETNAMEDBUFFERSUBDATAPROC GetNamedBufferSubData; + PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC GetNamedFramebufferAttachmentParameteriv; + PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC GetNamedFramebufferParameteriv; + PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC GetNamedRenderbufferParameteriv; + PFNGLGETOBJECTLABELPROC GetObjectLabel; + PFNGLGETOBJECTPTRLABELPROC GetObjectPtrLabel; + PFNGLGETPIXELMAPFVPROC GetPixelMapfv; + PFNGLGETPIXELMAPUIVPROC GetPixelMapuiv; + PFNGLGETPIXELMAPUSVPROC GetPixelMapusv; + PFNGLGETPOINTERVPROC GetPointerv; + PFNGLGETPOLYGONSTIPPLEPROC GetPolygonStipple; + PFNGLGETPROGRAMBINARYPROC GetProgramBinary; + PFNGLGETPROGRAMINFOLOGPROC GetProgramInfoLog; + PFNGLGETPROGRAMINTERFACEIVPROC GetProgramInterfaceiv; + PFNGLGETPROGRAMPIPELINEINFOLOGPROC GetProgramPipelineInfoLog; + PFNGLGETPROGRAMPIPELINEIVPROC GetProgramPipelineiv; + PFNGLGETPROGRAMRESOURCEINDEXPROC GetProgramResourceIndex; + PFNGLGETPROGRAMRESOURCELOCATIONPROC GetProgramResourceLocation; + PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC GetProgramResourceLocationIndex; + PFNGLGETPROGRAMRESOURCENAMEPROC GetProgramResourceName; + PFNGLGETPROGRAMRESOURCEIVPROC GetProgramResourceiv; + PFNGLGETPROGRAMSTAGEIVPROC GetProgramStageiv; + PFNGLGETPROGRAMIVPROC GetProgramiv; + PFNGLGETQUERYBUFFEROBJECTI64VPROC GetQueryBufferObjecti64v; + PFNGLGETQUERYBUFFEROBJECTIVPROC GetQueryBufferObjectiv; + PFNGLGETQUERYBUFFEROBJECTUI64VPROC GetQueryBufferObjectui64v; + PFNGLGETQUERYBUFFEROBJECTUIVPROC GetQueryBufferObjectuiv; + PFNGLGETQUERYINDEXEDIVPROC GetQueryIndexediv; + PFNGLGETQUERYOBJECTI64VPROC GetQueryObjecti64v; + PFNGLGETQUERYOBJECTIVPROC GetQueryObjectiv; + PFNGLGETQUERYOBJECTUI64VPROC GetQueryObjectui64v; + PFNGLGETQUERYOBJECTUIVPROC GetQueryObjectuiv; + PFNGLGETQUERYIVPROC GetQueryiv; + PFNGLGETRENDERBUFFERPARAMETERIVPROC GetRenderbufferParameteriv; + PFNGLGETSAMPLERPARAMETERIIVPROC GetSamplerParameterIiv; + PFNGLGETSAMPLERPARAMETERIUIVPROC GetSamplerParameterIuiv; + PFNGLGETSAMPLERPARAMETERFVPROC GetSamplerParameterfv; + PFNGLGETSAMPLERPARAMETERIVPROC GetSamplerParameteriv; + PFNGLGETSHADERINFOLOGPROC GetShaderInfoLog; + PFNGLGETSHADERPRECISIONFORMATPROC GetShaderPrecisionFormat; + PFNGLGETSHADERSOURCEPROC GetShaderSource; + PFNGLGETSHADERIVPROC GetShaderiv; + PFNGLGETSTRINGPROC GetString; + PFNGLGETSTRINGIPROC GetStringi; + PFNGLGETSUBROUTINEINDEXPROC GetSubroutineIndex; + PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC GetSubroutineUniformLocation; + PFNGLGETSYNCIVPROC GetSynciv; + PFNGLGETTEXENVFVPROC GetTexEnvfv; + PFNGLGETTEXENVIVPROC GetTexEnviv; + PFNGLGETTEXGENDVPROC GetTexGendv; + PFNGLGETTEXGENFVPROC GetTexGenfv; + PFNGLGETTEXGENIVPROC GetTexGeniv; + PFNGLGETTEXIMAGEPROC GetTexImage; + PFNGLGETTEXLEVELPARAMETERFVPROC GetTexLevelParameterfv; + PFNGLGETTEXLEVELPARAMETERIVPROC GetTexLevelParameteriv; + PFNGLGETTEXPARAMETERIIVPROC GetTexParameterIiv; + PFNGLGETTEXPARAMETERIUIVPROC GetTexParameterIuiv; + PFNGLGETTEXPARAMETERFVPROC GetTexParameterfv; + PFNGLGETTEXPARAMETERIVPROC GetTexParameteriv; + PFNGLGETTEXTUREIMAGEPROC GetTextureImage; + PFNGLGETTEXTURELEVELPARAMETERFVPROC GetTextureLevelParameterfv; + PFNGLGETTEXTURELEVELPARAMETERIVPROC GetTextureLevelParameteriv; + PFNGLGETTEXTUREPARAMETERIIVPROC GetTextureParameterIiv; + PFNGLGETTEXTUREPARAMETERIUIVPROC GetTextureParameterIuiv; + PFNGLGETTEXTUREPARAMETERFVPROC GetTextureParameterfv; + PFNGLGETTEXTUREPARAMETERIVPROC GetTextureParameteriv; + PFNGLGETTEXTURESUBIMAGEPROC GetTextureSubImage; + PFNGLGETTRANSFORMFEEDBACKVARYINGPROC GetTransformFeedbackVarying; + PFNGLGETTRANSFORMFEEDBACKI64_VPROC GetTransformFeedbacki64_v; + PFNGLGETTRANSFORMFEEDBACKI_VPROC GetTransformFeedbacki_v; + PFNGLGETTRANSFORMFEEDBACKIVPROC GetTransformFeedbackiv; + PFNGLGETUNIFORMBLOCKINDEXPROC GetUniformBlockIndex; + PFNGLGETUNIFORMINDICESPROC GetUniformIndices; + PFNGLGETUNIFORMLOCATIONPROC GetUniformLocation; + PFNGLGETUNIFORMSUBROUTINEUIVPROC GetUniformSubroutineuiv; + PFNGLGETUNIFORMDVPROC GetUniformdv; + PFNGLGETUNIFORMFVPROC GetUniformfv; + PFNGLGETUNIFORMIVPROC GetUniformiv; + PFNGLGETUNIFORMUIVPROC GetUniformuiv; + PFNGLGETVERTEXARRAYINDEXED64IVPROC GetVertexArrayIndexed64iv; + PFNGLGETVERTEXARRAYINDEXEDIVPROC GetVertexArrayIndexediv; + PFNGLGETVERTEXARRAYIVPROC GetVertexArrayiv; + PFNGLGETVERTEXATTRIBIIVPROC GetVertexAttribIiv; + PFNGLGETVERTEXATTRIBIUIVPROC GetVertexAttribIuiv; + PFNGLGETVERTEXATTRIBLDVPROC GetVertexAttribLdv; + PFNGLGETVERTEXATTRIBPOINTERVPROC GetVertexAttribPointerv; + PFNGLGETVERTEXATTRIBDVPROC GetVertexAttribdv; + PFNGLGETVERTEXATTRIBFVPROC GetVertexAttribfv; + PFNGLGETVERTEXATTRIBIVPROC GetVertexAttribiv; + PFNGLGETNCOLORTABLEPROC GetnColorTable; + PFNGLGETNCOMPRESSEDTEXIMAGEPROC GetnCompressedTexImage; + PFNGLGETNCONVOLUTIONFILTERPROC GetnConvolutionFilter; + PFNGLGETNHISTOGRAMPROC GetnHistogram; + PFNGLGETNMAPDVPROC GetnMapdv; + PFNGLGETNMAPFVPROC GetnMapfv; + PFNGLGETNMAPIVPROC GetnMapiv; + PFNGLGETNMINMAXPROC GetnMinmax; + PFNGLGETNPIXELMAPFVPROC GetnPixelMapfv; + PFNGLGETNPIXELMAPUIVPROC GetnPixelMapuiv; + PFNGLGETNPIXELMAPUSVPROC GetnPixelMapusv; + PFNGLGETNPOLYGONSTIPPLEPROC GetnPolygonStipple; + PFNGLGETNSEPARABLEFILTERPROC GetnSeparableFilter; + PFNGLGETNTEXIMAGEPROC GetnTexImage; + PFNGLGETNUNIFORMDVPROC GetnUniformdv; + PFNGLGETNUNIFORMFVPROC GetnUniformfv; + PFNGLGETNUNIFORMIVPROC GetnUniformiv; + PFNGLGETNUNIFORMUIVPROC GetnUniformuiv; + PFNGLHINTPROC Hint; + PFNGLINDEXMASKPROC IndexMask; + PFNGLINDEXPOINTERPROC IndexPointer; + PFNGLINDEXDPROC Indexd; + PFNGLINDEXDVPROC Indexdv; + PFNGLINDEXFPROC Indexf; + PFNGLINDEXFVPROC Indexfv; + PFNGLINDEXIPROC Indexi; + PFNGLINDEXIVPROC Indexiv; + PFNGLINDEXSPROC Indexs; + PFNGLINDEXSVPROC Indexsv; + PFNGLINDEXUBPROC Indexub; + PFNGLINDEXUBVPROC Indexubv; + PFNGLINITNAMESPROC InitNames; + PFNGLINTERLEAVEDARRAYSPROC InterleavedArrays; + PFNGLINVALIDATEBUFFERDATAPROC InvalidateBufferData; + PFNGLINVALIDATEBUFFERSUBDATAPROC InvalidateBufferSubData; + PFNGLINVALIDATEFRAMEBUFFERPROC InvalidateFramebuffer; + PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC InvalidateNamedFramebufferData; + PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC InvalidateNamedFramebufferSubData; + PFNGLINVALIDATESUBFRAMEBUFFERPROC InvalidateSubFramebuffer; + PFNGLINVALIDATETEXIMAGEPROC InvalidateTexImage; + PFNGLINVALIDATETEXSUBIMAGEPROC InvalidateTexSubImage; + PFNGLISBUFFERPROC IsBuffer; + PFNGLISENABLEDPROC IsEnabled; + PFNGLISENABLEDIPROC IsEnabledi; + PFNGLISFRAMEBUFFERPROC IsFramebuffer; + PFNGLISLISTPROC IsList; + PFNGLISPROGRAMPROC IsProgram; + PFNGLISPROGRAMPIPELINEPROC IsProgramPipeline; + PFNGLISQUERYPROC IsQuery; + PFNGLISRENDERBUFFERPROC IsRenderbuffer; + PFNGLISSAMPLERPROC IsSampler; + PFNGLISSHADERPROC IsShader; + PFNGLISSYNCPROC IsSync; + PFNGLISTEXTUREPROC IsTexture; + PFNGLISTRANSFORMFEEDBACKPROC IsTransformFeedback; + PFNGLISVERTEXARRAYPROC IsVertexArray; + PFNGLLIGHTMODELFPROC LightModelf; + PFNGLLIGHTMODELFVPROC LightModelfv; + PFNGLLIGHTMODELIPROC LightModeli; + PFNGLLIGHTMODELIVPROC LightModeliv; + PFNGLLIGHTFPROC Lightf; + PFNGLLIGHTFVPROC Lightfv; + PFNGLLIGHTIPROC Lighti; + PFNGLLIGHTIVPROC Lightiv; + PFNGLLINESTIPPLEPROC LineStipple; + PFNGLLINEWIDTHPROC LineWidth; + PFNGLLINKPROGRAMPROC LinkProgram; + PFNGLLISTBASEPROC ListBase; + PFNGLLOADIDENTITYPROC LoadIdentity; + PFNGLLOADMATRIXDPROC LoadMatrixd; + PFNGLLOADMATRIXFPROC LoadMatrixf; + PFNGLLOADNAMEPROC LoadName; + PFNGLLOADTRANSPOSEMATRIXDPROC LoadTransposeMatrixd; + PFNGLLOADTRANSPOSEMATRIXFPROC LoadTransposeMatrixf; + PFNGLLOGICOPPROC LogicOp; + PFNGLMAP1DPROC Map1d; + PFNGLMAP1FPROC Map1f; + PFNGLMAP2DPROC Map2d; + PFNGLMAP2FPROC Map2f; + PFNGLMAPBUFFERPROC MapBuffer; + PFNGLMAPBUFFERRANGEPROC MapBufferRange; + PFNGLMAPGRID1DPROC MapGrid1d; + PFNGLMAPGRID1FPROC MapGrid1f; + PFNGLMAPGRID2DPROC MapGrid2d; + PFNGLMAPGRID2FPROC MapGrid2f; + PFNGLMAPNAMEDBUFFERPROC MapNamedBuffer; + PFNGLMAPNAMEDBUFFERRANGEPROC MapNamedBufferRange; + PFNGLMATERIALFPROC Materialf; + PFNGLMATERIALFVPROC Materialfv; + PFNGLMATERIALIPROC Materiali; + PFNGLMATERIALIVPROC Materialiv; + PFNGLMATRIXMODEPROC MatrixMode; + PFNGLMEMORYBARRIERPROC MemoryBarrier; + PFNGLMEMORYBARRIERBYREGIONPROC MemoryBarrierByRegion; + PFNGLMINSAMPLESHADINGPROC MinSampleShading; + PFNGLMULTMATRIXDPROC MultMatrixd; + PFNGLMULTMATRIXFPROC MultMatrixf; + PFNGLMULTTRANSPOSEMATRIXDPROC MultTransposeMatrixd; + PFNGLMULTTRANSPOSEMATRIXFPROC MultTransposeMatrixf; + PFNGLMULTIDRAWARRAYSPROC MultiDrawArrays; + PFNGLMULTIDRAWARRAYSINDIRECTPROC MultiDrawArraysIndirect; + PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC MultiDrawArraysIndirectCount; + PFNGLMULTIDRAWELEMENTSPROC MultiDrawElements; + PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC MultiDrawElementsBaseVertex; + PFNGLMULTIDRAWELEMENTSINDIRECTPROC MultiDrawElementsIndirect; + PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC MultiDrawElementsIndirectCount; + PFNGLMULTITEXCOORD1DPROC MultiTexCoord1d; + PFNGLMULTITEXCOORD1DVPROC MultiTexCoord1dv; + PFNGLMULTITEXCOORD1FPROC MultiTexCoord1f; + PFNGLMULTITEXCOORD1FVPROC MultiTexCoord1fv; + PFNGLMULTITEXCOORD1IPROC MultiTexCoord1i; + PFNGLMULTITEXCOORD1IVPROC MultiTexCoord1iv; + PFNGLMULTITEXCOORD1SPROC MultiTexCoord1s; + PFNGLMULTITEXCOORD1SVPROC MultiTexCoord1sv; + PFNGLMULTITEXCOORD2DPROC MultiTexCoord2d; + PFNGLMULTITEXCOORD2DVPROC MultiTexCoord2dv; + PFNGLMULTITEXCOORD2FPROC MultiTexCoord2f; + PFNGLMULTITEXCOORD2FVPROC MultiTexCoord2fv; + PFNGLMULTITEXCOORD2IPROC MultiTexCoord2i; + PFNGLMULTITEXCOORD2IVPROC MultiTexCoord2iv; + PFNGLMULTITEXCOORD2SPROC MultiTexCoord2s; + PFNGLMULTITEXCOORD2SVPROC MultiTexCoord2sv; + PFNGLMULTITEXCOORD3DPROC MultiTexCoord3d; + PFNGLMULTITEXCOORD3DVPROC MultiTexCoord3dv; + PFNGLMULTITEXCOORD3FPROC MultiTexCoord3f; + PFNGLMULTITEXCOORD3FVPROC MultiTexCoord3fv; + PFNGLMULTITEXCOORD3IPROC MultiTexCoord3i; + PFNGLMULTITEXCOORD3IVPROC MultiTexCoord3iv; + PFNGLMULTITEXCOORD3SPROC MultiTexCoord3s; + PFNGLMULTITEXCOORD3SVPROC MultiTexCoord3sv; + PFNGLMULTITEXCOORD4DPROC MultiTexCoord4d; + PFNGLMULTITEXCOORD4DVPROC MultiTexCoord4dv; + PFNGLMULTITEXCOORD4FPROC MultiTexCoord4f; + PFNGLMULTITEXCOORD4FVPROC MultiTexCoord4fv; + PFNGLMULTITEXCOORD4IPROC MultiTexCoord4i; + PFNGLMULTITEXCOORD4IVPROC MultiTexCoord4iv; + PFNGLMULTITEXCOORD4SPROC MultiTexCoord4s; + PFNGLMULTITEXCOORD4SVPROC MultiTexCoord4sv; + PFNGLMULTITEXCOORDP1UIPROC MultiTexCoordP1ui; + PFNGLMULTITEXCOORDP1UIVPROC MultiTexCoordP1uiv; + PFNGLMULTITEXCOORDP2UIPROC MultiTexCoordP2ui; + PFNGLMULTITEXCOORDP2UIVPROC MultiTexCoordP2uiv; + PFNGLMULTITEXCOORDP3UIPROC MultiTexCoordP3ui; + PFNGLMULTITEXCOORDP3UIVPROC MultiTexCoordP3uiv; + PFNGLMULTITEXCOORDP4UIPROC MultiTexCoordP4ui; + PFNGLMULTITEXCOORDP4UIVPROC MultiTexCoordP4uiv; + PFNGLNAMEDBUFFERDATAPROC NamedBufferData; + PFNGLNAMEDBUFFERSTORAGEPROC NamedBufferStorage; + PFNGLNAMEDBUFFERSUBDATAPROC NamedBufferSubData; + PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC NamedFramebufferDrawBuffer; + PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC NamedFramebufferDrawBuffers; + PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC NamedFramebufferParameteri; + PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC NamedFramebufferReadBuffer; + PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC NamedFramebufferRenderbuffer; + PFNGLNAMEDFRAMEBUFFERTEXTUREPROC NamedFramebufferTexture; + PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC NamedFramebufferTextureLayer; + PFNGLNAMEDRENDERBUFFERSTORAGEPROC NamedRenderbufferStorage; + PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC NamedRenderbufferStorageMultisample; + PFNGLNEWLISTPROC NewList; + PFNGLNORMAL3BPROC Normal3b; + PFNGLNORMAL3BVPROC Normal3bv; + PFNGLNORMAL3DPROC Normal3d; + PFNGLNORMAL3DVPROC Normal3dv; + PFNGLNORMAL3FPROC Normal3f; + PFNGLNORMAL3FVPROC Normal3fv; + PFNGLNORMAL3IPROC Normal3i; + PFNGLNORMAL3IVPROC Normal3iv; + PFNGLNORMAL3SPROC Normal3s; + PFNGLNORMAL3SVPROC Normal3sv; + PFNGLNORMALP3UIPROC NormalP3ui; + PFNGLNORMALP3UIVPROC NormalP3uiv; + PFNGLNORMALPOINTERPROC NormalPointer; + PFNGLOBJECTLABELPROC ObjectLabel; + PFNGLOBJECTPTRLABELPROC ObjectPtrLabel; + PFNGLORTHOPROC Ortho; + PFNGLPASSTHROUGHPROC PassThrough; + PFNGLPATCHPARAMETERFVPROC PatchParameterfv; + PFNGLPATCHPARAMETERIPROC PatchParameteri; + PFNGLPAUSETRANSFORMFEEDBACKPROC PauseTransformFeedback; + PFNGLPIXELMAPFVPROC PixelMapfv; + PFNGLPIXELMAPUIVPROC PixelMapuiv; + PFNGLPIXELMAPUSVPROC PixelMapusv; + PFNGLPIXELSTOREFPROC PixelStoref; + PFNGLPIXELSTOREIPROC PixelStorei; + PFNGLPIXELTRANSFERFPROC PixelTransferf; + PFNGLPIXELTRANSFERIPROC PixelTransferi; + PFNGLPIXELZOOMPROC PixelZoom; + PFNGLPOINTPARAMETERFPROC PointParameterf; + PFNGLPOINTPARAMETERFVPROC PointParameterfv; + PFNGLPOINTPARAMETERIPROC PointParameteri; + PFNGLPOINTPARAMETERIVPROC PointParameteriv; + PFNGLPOINTSIZEPROC PointSize; + PFNGLPOLYGONMODEPROC PolygonMode; + PFNGLPOLYGONOFFSETPROC PolygonOffset; + PFNGLPOLYGONOFFSETCLAMPPROC PolygonOffsetClamp; + PFNGLPOLYGONSTIPPLEPROC PolygonStipple; + PFNGLPOPATTRIBPROC PopAttrib; + PFNGLPOPCLIENTATTRIBPROC PopClientAttrib; + PFNGLPOPDEBUGGROUPPROC PopDebugGroup; + PFNGLPOPMATRIXPROC PopMatrix; + PFNGLPOPNAMEPROC PopName; + PFNGLPRIMITIVERESTARTINDEXPROC PrimitiveRestartIndex; + PFNGLPRIORITIZETEXTURESPROC PrioritizeTextures; + PFNGLPROGRAMBINARYPROC ProgramBinary; + PFNGLPROGRAMPARAMETERIPROC ProgramParameteri; + PFNGLPROGRAMUNIFORM1DPROC ProgramUniform1d; + PFNGLPROGRAMUNIFORM1DVPROC ProgramUniform1dv; + PFNGLPROGRAMUNIFORM1FPROC ProgramUniform1f; + PFNGLPROGRAMUNIFORM1FVPROC ProgramUniform1fv; + PFNGLPROGRAMUNIFORM1IPROC ProgramUniform1i; + PFNGLPROGRAMUNIFORM1IVPROC ProgramUniform1iv; + PFNGLPROGRAMUNIFORM1UIPROC ProgramUniform1ui; + PFNGLPROGRAMUNIFORM1UIVPROC ProgramUniform1uiv; + PFNGLPROGRAMUNIFORM2DPROC ProgramUniform2d; + PFNGLPROGRAMUNIFORM2DVPROC ProgramUniform2dv; + PFNGLPROGRAMUNIFORM2FPROC ProgramUniform2f; + PFNGLPROGRAMUNIFORM2FVPROC ProgramUniform2fv; + PFNGLPROGRAMUNIFORM2IPROC ProgramUniform2i; + PFNGLPROGRAMUNIFORM2IVPROC ProgramUniform2iv; + PFNGLPROGRAMUNIFORM2UIPROC ProgramUniform2ui; + PFNGLPROGRAMUNIFORM2UIVPROC ProgramUniform2uiv; + PFNGLPROGRAMUNIFORM3DPROC ProgramUniform3d; + PFNGLPROGRAMUNIFORM3DVPROC ProgramUniform3dv; + PFNGLPROGRAMUNIFORM3FPROC ProgramUniform3f; + PFNGLPROGRAMUNIFORM3FVPROC ProgramUniform3fv; + PFNGLPROGRAMUNIFORM3IPROC ProgramUniform3i; + PFNGLPROGRAMUNIFORM3IVPROC ProgramUniform3iv; + PFNGLPROGRAMUNIFORM3UIPROC ProgramUniform3ui; + PFNGLPROGRAMUNIFORM3UIVPROC ProgramUniform3uiv; + PFNGLPROGRAMUNIFORM4DPROC ProgramUniform4d; + PFNGLPROGRAMUNIFORM4DVPROC ProgramUniform4dv; + PFNGLPROGRAMUNIFORM4FPROC ProgramUniform4f; + PFNGLPROGRAMUNIFORM4FVPROC ProgramUniform4fv; + PFNGLPROGRAMUNIFORM4IPROC ProgramUniform4i; + PFNGLPROGRAMUNIFORM4IVPROC ProgramUniform4iv; + PFNGLPROGRAMUNIFORM4UIPROC ProgramUniform4ui; + PFNGLPROGRAMUNIFORM4UIVPROC ProgramUniform4uiv; + PFNGLPROGRAMUNIFORMMATRIX2DVPROC ProgramUniformMatrix2dv; + PFNGLPROGRAMUNIFORMMATRIX2FVPROC ProgramUniformMatrix2fv; + PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC ProgramUniformMatrix2x3dv; + PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC ProgramUniformMatrix2x3fv; + PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC ProgramUniformMatrix2x4dv; + PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC ProgramUniformMatrix2x4fv; + PFNGLPROGRAMUNIFORMMATRIX3DVPROC ProgramUniformMatrix3dv; + PFNGLPROGRAMUNIFORMMATRIX3FVPROC ProgramUniformMatrix3fv; + PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC ProgramUniformMatrix3x2dv; + PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC ProgramUniformMatrix3x2fv; + PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC ProgramUniformMatrix3x4dv; + PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC ProgramUniformMatrix3x4fv; + PFNGLPROGRAMUNIFORMMATRIX4DVPROC ProgramUniformMatrix4dv; + PFNGLPROGRAMUNIFORMMATRIX4FVPROC ProgramUniformMatrix4fv; + PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC ProgramUniformMatrix4x2dv; + PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC ProgramUniformMatrix4x2fv; + PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC ProgramUniformMatrix4x3dv; + PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC ProgramUniformMatrix4x3fv; + PFNGLPROVOKINGVERTEXPROC ProvokingVertex; + PFNGLPUSHATTRIBPROC PushAttrib; + PFNGLPUSHCLIENTATTRIBPROC PushClientAttrib; + PFNGLPUSHDEBUGGROUPPROC PushDebugGroup; + PFNGLPUSHMATRIXPROC PushMatrix; + PFNGLPUSHNAMEPROC PushName; + PFNGLQUERYCOUNTERPROC QueryCounter; + PFNGLRASTERPOS2DPROC RasterPos2d; + PFNGLRASTERPOS2DVPROC RasterPos2dv; + PFNGLRASTERPOS2FPROC RasterPos2f; + PFNGLRASTERPOS2FVPROC RasterPos2fv; + PFNGLRASTERPOS2IPROC RasterPos2i; + PFNGLRASTERPOS2IVPROC RasterPos2iv; + PFNGLRASTERPOS2SPROC RasterPos2s; + PFNGLRASTERPOS2SVPROC RasterPos2sv; + PFNGLRASTERPOS3DPROC RasterPos3d; + PFNGLRASTERPOS3DVPROC RasterPos3dv; + PFNGLRASTERPOS3FPROC RasterPos3f; + PFNGLRASTERPOS3FVPROC RasterPos3fv; + PFNGLRASTERPOS3IPROC RasterPos3i; + PFNGLRASTERPOS3IVPROC RasterPos3iv; + PFNGLRASTERPOS3SPROC RasterPos3s; + PFNGLRASTERPOS3SVPROC RasterPos3sv; + PFNGLRASTERPOS4DPROC RasterPos4d; + PFNGLRASTERPOS4DVPROC RasterPos4dv; + PFNGLRASTERPOS4FPROC RasterPos4f; + PFNGLRASTERPOS4FVPROC RasterPos4fv; + PFNGLRASTERPOS4IPROC RasterPos4i; + PFNGLRASTERPOS4IVPROC RasterPos4iv; + PFNGLRASTERPOS4SPROC RasterPos4s; + PFNGLRASTERPOS4SVPROC RasterPos4sv; + PFNGLREADBUFFERPROC ReadBuffer; + PFNGLREADPIXELSPROC ReadPixels; + PFNGLREADNPIXELSPROC ReadnPixels; + PFNGLRECTDPROC Rectd; + PFNGLRECTDVPROC Rectdv; + PFNGLRECTFPROC Rectf; + PFNGLRECTFVPROC Rectfv; + PFNGLRECTIPROC Recti; + PFNGLRECTIVPROC Rectiv; + PFNGLRECTSPROC Rects; + PFNGLRECTSVPROC Rectsv; + PFNGLRELEASESHADERCOMPILERPROC ReleaseShaderCompiler; + PFNGLRENDERMODEPROC RenderMode; + PFNGLRENDERBUFFERSTORAGEPROC RenderbufferStorage; + PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC RenderbufferStorageMultisample; + PFNGLRESUMETRANSFORMFEEDBACKPROC ResumeTransformFeedback; + PFNGLROTATEDPROC Rotated; + PFNGLROTATEFPROC Rotatef; + PFNGLSAMPLECOVERAGEPROC SampleCoverage; + PFNGLSAMPLEMASKIPROC SampleMaski; + PFNGLSAMPLERPARAMETERIIVPROC SamplerParameterIiv; + PFNGLSAMPLERPARAMETERIUIVPROC SamplerParameterIuiv; + PFNGLSAMPLERPARAMETERFPROC SamplerParameterf; + PFNGLSAMPLERPARAMETERFVPROC SamplerParameterfv; + PFNGLSAMPLERPARAMETERIPROC SamplerParameteri; + PFNGLSAMPLERPARAMETERIVPROC SamplerParameteriv; + PFNGLSCALEDPROC Scaled; + PFNGLSCALEFPROC Scalef; + PFNGLSCISSORPROC Scissor; + PFNGLSCISSORARRAYVPROC ScissorArrayv; + PFNGLSCISSORINDEXEDPROC ScissorIndexed; + PFNGLSCISSORINDEXEDVPROC ScissorIndexedv; + PFNGLSECONDARYCOLOR3BPROC SecondaryColor3b; + PFNGLSECONDARYCOLOR3BVPROC SecondaryColor3bv; + PFNGLSECONDARYCOLOR3DPROC SecondaryColor3d; + PFNGLSECONDARYCOLOR3DVPROC SecondaryColor3dv; + PFNGLSECONDARYCOLOR3FPROC SecondaryColor3f; + PFNGLSECONDARYCOLOR3FVPROC SecondaryColor3fv; + PFNGLSECONDARYCOLOR3IPROC SecondaryColor3i; + PFNGLSECONDARYCOLOR3IVPROC SecondaryColor3iv; + PFNGLSECONDARYCOLOR3SPROC SecondaryColor3s; + PFNGLSECONDARYCOLOR3SVPROC SecondaryColor3sv; + PFNGLSECONDARYCOLOR3UBPROC SecondaryColor3ub; + PFNGLSECONDARYCOLOR3UBVPROC SecondaryColor3ubv; + PFNGLSECONDARYCOLOR3UIPROC SecondaryColor3ui; + PFNGLSECONDARYCOLOR3UIVPROC SecondaryColor3uiv; + PFNGLSECONDARYCOLOR3USPROC SecondaryColor3us; + PFNGLSECONDARYCOLOR3USVPROC SecondaryColor3usv; + PFNGLSECONDARYCOLORP3UIPROC SecondaryColorP3ui; + PFNGLSECONDARYCOLORP3UIVPROC SecondaryColorP3uiv; + PFNGLSECONDARYCOLORPOINTERPROC SecondaryColorPointer; + PFNGLSELECTBUFFERPROC SelectBuffer; + PFNGLSHADEMODELPROC ShadeModel; + PFNGLSHADERBINARYPROC ShaderBinary; + PFNGLSHADERSOURCEPROC ShaderSource; + PFNGLSHADERSTORAGEBLOCKBINDINGPROC ShaderStorageBlockBinding; + PFNGLSPECIALIZESHADERPROC SpecializeShader; + PFNGLSTENCILFUNCPROC StencilFunc; + PFNGLSTENCILFUNCSEPARATEPROC StencilFuncSeparate; + PFNGLSTENCILMASKPROC StencilMask; + PFNGLSTENCILMASKSEPARATEPROC StencilMaskSeparate; + PFNGLSTENCILOPPROC StencilOp; + PFNGLSTENCILOPSEPARATEPROC StencilOpSeparate; + PFNGLTEXBUFFERPROC TexBuffer; + PFNGLTEXBUFFERRANGEPROC TexBufferRange; + PFNGLTEXCOORD1DPROC TexCoord1d; + PFNGLTEXCOORD1DVPROC TexCoord1dv; + PFNGLTEXCOORD1FPROC TexCoord1f; + PFNGLTEXCOORD1FVPROC TexCoord1fv; + PFNGLTEXCOORD1IPROC TexCoord1i; + PFNGLTEXCOORD1IVPROC TexCoord1iv; + PFNGLTEXCOORD1SPROC TexCoord1s; + PFNGLTEXCOORD1SVPROC TexCoord1sv; + PFNGLTEXCOORD2DPROC TexCoord2d; + PFNGLTEXCOORD2DVPROC TexCoord2dv; + PFNGLTEXCOORD2FPROC TexCoord2f; + PFNGLTEXCOORD2FVPROC TexCoord2fv; + PFNGLTEXCOORD2IPROC TexCoord2i; + PFNGLTEXCOORD2IVPROC TexCoord2iv; + PFNGLTEXCOORD2SPROC TexCoord2s; + PFNGLTEXCOORD2SVPROC TexCoord2sv; + PFNGLTEXCOORD3DPROC TexCoord3d; + PFNGLTEXCOORD3DVPROC TexCoord3dv; + PFNGLTEXCOORD3FPROC TexCoord3f; + PFNGLTEXCOORD3FVPROC TexCoord3fv; + PFNGLTEXCOORD3IPROC TexCoord3i; + PFNGLTEXCOORD3IVPROC TexCoord3iv; + PFNGLTEXCOORD3SPROC TexCoord3s; + PFNGLTEXCOORD3SVPROC TexCoord3sv; + PFNGLTEXCOORD4DPROC TexCoord4d; + PFNGLTEXCOORD4DVPROC TexCoord4dv; + PFNGLTEXCOORD4FPROC TexCoord4f; + PFNGLTEXCOORD4FVPROC TexCoord4fv; + PFNGLTEXCOORD4IPROC TexCoord4i; + PFNGLTEXCOORD4IVPROC TexCoord4iv; + PFNGLTEXCOORD4SPROC TexCoord4s; + PFNGLTEXCOORD4SVPROC TexCoord4sv; + PFNGLTEXCOORDP1UIPROC TexCoordP1ui; + PFNGLTEXCOORDP1UIVPROC TexCoordP1uiv; + PFNGLTEXCOORDP2UIPROC TexCoordP2ui; + PFNGLTEXCOORDP2UIVPROC TexCoordP2uiv; + PFNGLTEXCOORDP3UIPROC TexCoordP3ui; + PFNGLTEXCOORDP3UIVPROC TexCoordP3uiv; + PFNGLTEXCOORDP4UIPROC TexCoordP4ui; + PFNGLTEXCOORDP4UIVPROC TexCoordP4uiv; + PFNGLTEXCOORDPOINTERPROC TexCoordPointer; + PFNGLTEXENVFPROC TexEnvf; + PFNGLTEXENVFVPROC TexEnvfv; + PFNGLTEXENVIPROC TexEnvi; + PFNGLTEXENVIVPROC TexEnviv; + PFNGLTEXGENDPROC TexGend; + PFNGLTEXGENDVPROC TexGendv; + PFNGLTEXGENFPROC TexGenf; + PFNGLTEXGENFVPROC TexGenfv; + PFNGLTEXGENIPROC TexGeni; + PFNGLTEXGENIVPROC TexGeniv; + PFNGLTEXIMAGE1DPROC TexImage1D; + PFNGLTEXIMAGE2DPROC TexImage2D; + PFNGLTEXIMAGE2DMULTISAMPLEPROC TexImage2DMultisample; + PFNGLTEXIMAGE3DPROC TexImage3D; + PFNGLTEXIMAGE3DMULTISAMPLEPROC TexImage3DMultisample; + PFNGLTEXPARAMETERIIVPROC TexParameterIiv; + PFNGLTEXPARAMETERIUIVPROC TexParameterIuiv; + PFNGLTEXPARAMETERFPROC TexParameterf; + PFNGLTEXPARAMETERFVPROC TexParameterfv; + PFNGLTEXPARAMETERIPROC TexParameteri; + PFNGLTEXPARAMETERIVPROC TexParameteriv; + PFNGLTEXSTORAGE1DPROC TexStorage1D; + PFNGLTEXSTORAGE2DPROC TexStorage2D; + PFNGLTEXSTORAGE2DMULTISAMPLEPROC TexStorage2DMultisample; + PFNGLTEXSTORAGE3DPROC TexStorage3D; + PFNGLTEXSTORAGE3DMULTISAMPLEPROC TexStorage3DMultisample; + PFNGLTEXSUBIMAGE1DPROC TexSubImage1D; + PFNGLTEXSUBIMAGE2DPROC TexSubImage2D; + PFNGLTEXSUBIMAGE3DPROC TexSubImage3D; + PFNGLTEXTUREBARRIERPROC TextureBarrier; + PFNGLTEXTUREBUFFERPROC TextureBuffer; + PFNGLTEXTUREBUFFERRANGEPROC TextureBufferRange; + PFNGLTEXTUREPARAMETERIIVPROC TextureParameterIiv; + PFNGLTEXTUREPARAMETERIUIVPROC TextureParameterIuiv; + PFNGLTEXTUREPARAMETERFPROC TextureParameterf; + PFNGLTEXTUREPARAMETERFVPROC TextureParameterfv; + PFNGLTEXTUREPARAMETERIPROC TextureParameteri; + PFNGLTEXTUREPARAMETERIVPROC TextureParameteriv; + PFNGLTEXTURESTORAGE1DPROC TextureStorage1D; + PFNGLTEXTURESTORAGE2DPROC TextureStorage2D; + PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC TextureStorage2DMultisample; + PFNGLTEXTURESTORAGE3DPROC TextureStorage3D; + PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC TextureStorage3DMultisample; + PFNGLTEXTURESUBIMAGE1DPROC TextureSubImage1D; + PFNGLTEXTURESUBIMAGE2DPROC TextureSubImage2D; + PFNGLTEXTURESUBIMAGE3DPROC TextureSubImage3D; + PFNGLTEXTUREVIEWPROC TextureView; + PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC TransformFeedbackBufferBase; + PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC TransformFeedbackBufferRange; + PFNGLTRANSFORMFEEDBACKVARYINGSPROC TransformFeedbackVaryings; + PFNGLTRANSLATEDPROC Translated; + PFNGLTRANSLATEFPROC Translatef; + PFNGLUNIFORM1DPROC Uniform1d; + PFNGLUNIFORM1DVPROC Uniform1dv; + PFNGLUNIFORM1FPROC Uniform1f; + PFNGLUNIFORM1FVPROC Uniform1fv; + PFNGLUNIFORM1IPROC Uniform1i; + PFNGLUNIFORM1IVPROC Uniform1iv; + PFNGLUNIFORM1UIPROC Uniform1ui; + PFNGLUNIFORM1UIVPROC Uniform1uiv; + PFNGLUNIFORM2DPROC Uniform2d; + PFNGLUNIFORM2DVPROC Uniform2dv; + PFNGLUNIFORM2FPROC Uniform2f; + PFNGLUNIFORM2FVPROC Uniform2fv; + PFNGLUNIFORM2IPROC Uniform2i; + PFNGLUNIFORM2IVPROC Uniform2iv; + PFNGLUNIFORM2UIPROC Uniform2ui; + PFNGLUNIFORM2UIVPROC Uniform2uiv; + PFNGLUNIFORM3DPROC Uniform3d; + PFNGLUNIFORM3DVPROC Uniform3dv; + PFNGLUNIFORM3FPROC Uniform3f; + PFNGLUNIFORM3FVPROC Uniform3fv; + PFNGLUNIFORM3IPROC Uniform3i; + PFNGLUNIFORM3IVPROC Uniform3iv; + PFNGLUNIFORM3UIPROC Uniform3ui; + PFNGLUNIFORM3UIVPROC Uniform3uiv; + PFNGLUNIFORM4DPROC Uniform4d; + PFNGLUNIFORM4DVPROC Uniform4dv; + PFNGLUNIFORM4FPROC Uniform4f; + PFNGLUNIFORM4FVPROC Uniform4fv; + PFNGLUNIFORM4IPROC Uniform4i; + PFNGLUNIFORM4IVPROC Uniform4iv; + PFNGLUNIFORM4UIPROC Uniform4ui; + PFNGLUNIFORM4UIVPROC Uniform4uiv; + PFNGLUNIFORMBLOCKBINDINGPROC UniformBlockBinding; + PFNGLUNIFORMMATRIX2DVPROC UniformMatrix2dv; + PFNGLUNIFORMMATRIX2FVPROC UniformMatrix2fv; + PFNGLUNIFORMMATRIX2X3DVPROC UniformMatrix2x3dv; + PFNGLUNIFORMMATRIX2X3FVPROC UniformMatrix2x3fv; + PFNGLUNIFORMMATRIX2X4DVPROC UniformMatrix2x4dv; + PFNGLUNIFORMMATRIX2X4FVPROC UniformMatrix2x4fv; + PFNGLUNIFORMMATRIX3DVPROC UniformMatrix3dv; + PFNGLUNIFORMMATRIX3FVPROC UniformMatrix3fv; + PFNGLUNIFORMMATRIX3X2DVPROC UniformMatrix3x2dv; + PFNGLUNIFORMMATRIX3X2FVPROC UniformMatrix3x2fv; + PFNGLUNIFORMMATRIX3X4DVPROC UniformMatrix3x4dv; + PFNGLUNIFORMMATRIX3X4FVPROC UniformMatrix3x4fv; + PFNGLUNIFORMMATRIX4DVPROC UniformMatrix4dv; + PFNGLUNIFORMMATRIX4FVPROC UniformMatrix4fv; + PFNGLUNIFORMMATRIX4X2DVPROC UniformMatrix4x2dv; + PFNGLUNIFORMMATRIX4X2FVPROC UniformMatrix4x2fv; + PFNGLUNIFORMMATRIX4X3DVPROC UniformMatrix4x3dv; + PFNGLUNIFORMMATRIX4X3FVPROC UniformMatrix4x3fv; + PFNGLUNIFORMSUBROUTINESUIVPROC UniformSubroutinesuiv; + PFNGLUNMAPBUFFERPROC UnmapBuffer; + PFNGLUNMAPNAMEDBUFFERPROC UnmapNamedBuffer; + PFNGLUSEPROGRAMPROC UseProgram; + PFNGLUSEPROGRAMSTAGESPROC UseProgramStages; + PFNGLVALIDATEPROGRAMPROC ValidateProgram; + PFNGLVALIDATEPROGRAMPIPELINEPROC ValidateProgramPipeline; + PFNGLVERTEX2DPROC Vertex2d; + PFNGLVERTEX2DVPROC Vertex2dv; + PFNGLVERTEX2FPROC Vertex2f; + PFNGLVERTEX2FVPROC Vertex2fv; + PFNGLVERTEX2IPROC Vertex2i; + PFNGLVERTEX2IVPROC Vertex2iv; + PFNGLVERTEX2SPROC Vertex2s; + PFNGLVERTEX2SVPROC Vertex2sv; + PFNGLVERTEX3DPROC Vertex3d; + PFNGLVERTEX3DVPROC Vertex3dv; + PFNGLVERTEX3FPROC Vertex3f; + PFNGLVERTEX3FVPROC Vertex3fv; + PFNGLVERTEX3IPROC Vertex3i; + PFNGLVERTEX3IVPROC Vertex3iv; + PFNGLVERTEX3SPROC Vertex3s; + PFNGLVERTEX3SVPROC Vertex3sv; + PFNGLVERTEX4DPROC Vertex4d; + PFNGLVERTEX4DVPROC Vertex4dv; + PFNGLVERTEX4FPROC Vertex4f; + PFNGLVERTEX4FVPROC Vertex4fv; + PFNGLVERTEX4IPROC Vertex4i; + PFNGLVERTEX4IVPROC Vertex4iv; + PFNGLVERTEX4SPROC Vertex4s; + PFNGLVERTEX4SVPROC Vertex4sv; + PFNGLVERTEXARRAYATTRIBBINDINGPROC VertexArrayAttribBinding; + PFNGLVERTEXARRAYATTRIBFORMATPROC VertexArrayAttribFormat; + PFNGLVERTEXARRAYATTRIBIFORMATPROC VertexArrayAttribIFormat; + PFNGLVERTEXARRAYATTRIBLFORMATPROC VertexArrayAttribLFormat; + PFNGLVERTEXARRAYBINDINGDIVISORPROC VertexArrayBindingDivisor; + PFNGLVERTEXARRAYELEMENTBUFFERPROC VertexArrayElementBuffer; + PFNGLVERTEXARRAYVERTEXBUFFERPROC VertexArrayVertexBuffer; + PFNGLVERTEXARRAYVERTEXBUFFERSPROC VertexArrayVertexBuffers; + PFNGLVERTEXATTRIB1DPROC VertexAttrib1d; + PFNGLVERTEXATTRIB1DVPROC VertexAttrib1dv; + PFNGLVERTEXATTRIB1FPROC VertexAttrib1f; + PFNGLVERTEXATTRIB1FVPROC VertexAttrib1fv; + PFNGLVERTEXATTRIB1SPROC VertexAttrib1s; + PFNGLVERTEXATTRIB1SVPROC VertexAttrib1sv; + PFNGLVERTEXATTRIB2DPROC VertexAttrib2d; + PFNGLVERTEXATTRIB2DVPROC VertexAttrib2dv; + PFNGLVERTEXATTRIB2FPROC VertexAttrib2f; + PFNGLVERTEXATTRIB2FVPROC VertexAttrib2fv; + PFNGLVERTEXATTRIB2SPROC VertexAttrib2s; + PFNGLVERTEXATTRIB2SVPROC VertexAttrib2sv; + PFNGLVERTEXATTRIB3DPROC VertexAttrib3d; + PFNGLVERTEXATTRIB3DVPROC VertexAttrib3dv; + PFNGLVERTEXATTRIB3FPROC VertexAttrib3f; + PFNGLVERTEXATTRIB3FVPROC VertexAttrib3fv; + PFNGLVERTEXATTRIB3SPROC VertexAttrib3s; + PFNGLVERTEXATTRIB3SVPROC VertexAttrib3sv; + PFNGLVERTEXATTRIB4NBVPROC VertexAttrib4Nbv; + PFNGLVERTEXATTRIB4NIVPROC VertexAttrib4Niv; + PFNGLVERTEXATTRIB4NSVPROC VertexAttrib4Nsv; + PFNGLVERTEXATTRIB4NUBPROC VertexAttrib4Nub; + PFNGLVERTEXATTRIB4NUBVPROC VertexAttrib4Nubv; + PFNGLVERTEXATTRIB4NUIVPROC VertexAttrib4Nuiv; + PFNGLVERTEXATTRIB4NUSVPROC VertexAttrib4Nusv; + PFNGLVERTEXATTRIB4BVPROC VertexAttrib4bv; + PFNGLVERTEXATTRIB4DPROC VertexAttrib4d; + PFNGLVERTEXATTRIB4DVPROC VertexAttrib4dv; + PFNGLVERTEXATTRIB4FPROC VertexAttrib4f; + PFNGLVERTEXATTRIB4FVPROC VertexAttrib4fv; + PFNGLVERTEXATTRIB4IVPROC VertexAttrib4iv; + PFNGLVERTEXATTRIB4SPROC VertexAttrib4s; + PFNGLVERTEXATTRIB4SVPROC VertexAttrib4sv; + PFNGLVERTEXATTRIB4UBVPROC VertexAttrib4ubv; + PFNGLVERTEXATTRIB4UIVPROC VertexAttrib4uiv; + PFNGLVERTEXATTRIB4USVPROC VertexAttrib4usv; + PFNGLVERTEXATTRIBBINDINGPROC VertexAttribBinding; + PFNGLVERTEXATTRIBDIVISORPROC VertexAttribDivisor; + PFNGLVERTEXATTRIBFORMATPROC VertexAttribFormat; + PFNGLVERTEXATTRIBI1IPROC VertexAttribI1i; + PFNGLVERTEXATTRIBI1IVPROC VertexAttribI1iv; + PFNGLVERTEXATTRIBI1UIPROC VertexAttribI1ui; + PFNGLVERTEXATTRIBI1UIVPROC VertexAttribI1uiv; + PFNGLVERTEXATTRIBI2IPROC VertexAttribI2i; + PFNGLVERTEXATTRIBI2IVPROC VertexAttribI2iv; + PFNGLVERTEXATTRIBI2UIPROC VertexAttribI2ui; + PFNGLVERTEXATTRIBI2UIVPROC VertexAttribI2uiv; + PFNGLVERTEXATTRIBI3IPROC VertexAttribI3i; + PFNGLVERTEXATTRIBI3IVPROC VertexAttribI3iv; + PFNGLVERTEXATTRIBI3UIPROC VertexAttribI3ui; + PFNGLVERTEXATTRIBI3UIVPROC VertexAttribI3uiv; + PFNGLVERTEXATTRIBI4BVPROC VertexAttribI4bv; + PFNGLVERTEXATTRIBI4IPROC VertexAttribI4i; + PFNGLVERTEXATTRIBI4IVPROC VertexAttribI4iv; + PFNGLVERTEXATTRIBI4SVPROC VertexAttribI4sv; + PFNGLVERTEXATTRIBI4UBVPROC VertexAttribI4ubv; + PFNGLVERTEXATTRIBI4UIPROC VertexAttribI4ui; + PFNGLVERTEXATTRIBI4UIVPROC VertexAttribI4uiv; + PFNGLVERTEXATTRIBI4USVPROC VertexAttribI4usv; + PFNGLVERTEXATTRIBIFORMATPROC VertexAttribIFormat; + PFNGLVERTEXATTRIBIPOINTERPROC VertexAttribIPointer; + PFNGLVERTEXATTRIBL1DPROC VertexAttribL1d; + PFNGLVERTEXATTRIBL1DVPROC VertexAttribL1dv; + PFNGLVERTEXATTRIBL2DPROC VertexAttribL2d; + PFNGLVERTEXATTRIBL2DVPROC VertexAttribL2dv; + PFNGLVERTEXATTRIBL3DPROC VertexAttribL3d; + PFNGLVERTEXATTRIBL3DVPROC VertexAttribL3dv; + PFNGLVERTEXATTRIBL4DPROC VertexAttribL4d; + PFNGLVERTEXATTRIBL4DVPROC VertexAttribL4dv; + PFNGLVERTEXATTRIBLFORMATPROC VertexAttribLFormat; + PFNGLVERTEXATTRIBLPOINTERPROC VertexAttribLPointer; + PFNGLVERTEXATTRIBP1UIPROC VertexAttribP1ui; + PFNGLVERTEXATTRIBP1UIVPROC VertexAttribP1uiv; + PFNGLVERTEXATTRIBP2UIPROC VertexAttribP2ui; + PFNGLVERTEXATTRIBP2UIVPROC VertexAttribP2uiv; + PFNGLVERTEXATTRIBP3UIPROC VertexAttribP3ui; + PFNGLVERTEXATTRIBP3UIVPROC VertexAttribP3uiv; + PFNGLVERTEXATTRIBP4UIPROC VertexAttribP4ui; + PFNGLVERTEXATTRIBP4UIVPROC VertexAttribP4uiv; + PFNGLVERTEXATTRIBPOINTERPROC VertexAttribPointer; + PFNGLVERTEXBINDINGDIVISORPROC VertexBindingDivisor; + PFNGLVERTEXP2UIPROC VertexP2ui; + PFNGLVERTEXP2UIVPROC VertexP2uiv; + PFNGLVERTEXP3UIPROC VertexP3ui; + PFNGLVERTEXP3UIVPROC VertexP3uiv; + PFNGLVERTEXP4UIPROC VertexP4ui; + PFNGLVERTEXP4UIVPROC VertexP4uiv; + PFNGLVERTEXPOINTERPROC VertexPointer; + PFNGLVIEWPORTPROC Viewport; + PFNGLVIEWPORTARRAYVPROC ViewportArrayv; + PFNGLVIEWPORTINDEXEDFPROC ViewportIndexedf; + PFNGLVIEWPORTINDEXEDFVPROC ViewportIndexedfv; + PFNGLWAITSYNCPROC WaitSync; + PFNGLWINDOWPOS2DPROC WindowPos2d; + PFNGLWINDOWPOS2DVPROC WindowPos2dv; + PFNGLWINDOWPOS2FPROC WindowPos2f; + PFNGLWINDOWPOS2FVPROC WindowPos2fv; + PFNGLWINDOWPOS2IPROC WindowPos2i; + PFNGLWINDOWPOS2IVPROC WindowPos2iv; + PFNGLWINDOWPOS2SPROC WindowPos2s; + PFNGLWINDOWPOS2SVPROC WindowPos2sv; + PFNGLWINDOWPOS3DPROC WindowPos3d; + PFNGLWINDOWPOS3DVPROC WindowPos3dv; + PFNGLWINDOWPOS3FPROC WindowPos3f; + PFNGLWINDOWPOS3FVPROC WindowPos3fv; + PFNGLWINDOWPOS3IPROC WindowPos3i; + PFNGLWINDOWPOS3IVPROC WindowPos3iv; + PFNGLWINDOWPOS3SPROC WindowPos3s; + PFNGLWINDOWPOS3SVPROC WindowPos3sv; +} GladGLContext; + + +GLAD_API_CALL int gladLoadGLContextUserPtr(GladGLContext *context, GLADuserptrloadfunc load, void *userptr); +GLAD_API_CALL int gladLoadGLContext(GladGLContext *context, GLADloadfunc load); + + +#ifdef GLAD_GL + +GLAD_API_CALL int gladLoaderLoadGLContext(GladGLContext *context); +GLAD_API_CALL void gladLoaderUnloadGL(void); + +#endif + +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/third-party/glad/src/egl.c b/third-party/glad/src/egl.c new file mode 100644 index 00000000..fa4ffba9 --- /dev/null +++ b/third-party/glad/src/egl.c @@ -0,0 +1,397 @@ +#include +#include +#include +#include + +#ifndef GLAD_IMPL_UTIL_C_ +#define GLAD_IMPL_UTIL_C_ + +#ifdef _MSC_VER +#define GLAD_IMPL_UTIL_SSCANF sscanf_s +#else +#define GLAD_IMPL_UTIL_SSCANF sscanf +#endif + +#endif /* GLAD_IMPL_UTIL_C_ */ + +#ifdef __cplusplus +extern "C" { +#endif + + + +int GLAD_EGL_VERSION_1_0 = 0; +int GLAD_EGL_VERSION_1_1 = 0; +int GLAD_EGL_VERSION_1_2 = 0; +int GLAD_EGL_VERSION_1_3 = 0; +int GLAD_EGL_VERSION_1_4 = 0; +int GLAD_EGL_VERSION_1_5 = 0; + + + +PFNEGLBINDAPIPROC glad_eglBindAPI = NULL; +PFNEGLBINDTEXIMAGEPROC glad_eglBindTexImage = NULL; +PFNEGLCHOOSECONFIGPROC glad_eglChooseConfig = NULL; +PFNEGLCLIENTWAITSYNCPROC glad_eglClientWaitSync = NULL; +PFNEGLCOPYBUFFERSPROC glad_eglCopyBuffers = NULL; +PFNEGLCREATECONTEXTPROC glad_eglCreateContext = NULL; +PFNEGLCREATEIMAGEPROC glad_eglCreateImage = NULL; +PFNEGLCREATEPBUFFERFROMCLIENTBUFFERPROC glad_eglCreatePbufferFromClientBuffer = NULL; +PFNEGLCREATEPBUFFERSURFACEPROC glad_eglCreatePbufferSurface = NULL; +PFNEGLCREATEPIXMAPSURFACEPROC glad_eglCreatePixmapSurface = NULL; +PFNEGLCREATEPLATFORMPIXMAPSURFACEPROC glad_eglCreatePlatformPixmapSurface = NULL; +PFNEGLCREATEPLATFORMWINDOWSURFACEPROC glad_eglCreatePlatformWindowSurface = NULL; +PFNEGLCREATESYNCPROC glad_eglCreateSync = NULL; +PFNEGLCREATEWINDOWSURFACEPROC glad_eglCreateWindowSurface = NULL; +PFNEGLDESTROYCONTEXTPROC glad_eglDestroyContext = NULL; +PFNEGLDESTROYIMAGEPROC glad_eglDestroyImage = NULL; +PFNEGLDESTROYSURFACEPROC glad_eglDestroySurface = NULL; +PFNEGLDESTROYSYNCPROC glad_eglDestroySync = NULL; +PFNEGLGETCONFIGATTRIBPROC glad_eglGetConfigAttrib = NULL; +PFNEGLGETCONFIGSPROC glad_eglGetConfigs = NULL; +PFNEGLGETCURRENTCONTEXTPROC glad_eglGetCurrentContext = NULL; +PFNEGLGETCURRENTDISPLAYPROC glad_eglGetCurrentDisplay = NULL; +PFNEGLGETCURRENTSURFACEPROC glad_eglGetCurrentSurface = NULL; +PFNEGLGETDISPLAYPROC glad_eglGetDisplay = NULL; +PFNEGLGETERRORPROC glad_eglGetError = NULL; +PFNEGLGETPLATFORMDISPLAYPROC glad_eglGetPlatformDisplay = NULL; +PFNEGLGETPROCADDRESSPROC glad_eglGetProcAddress = NULL; +PFNEGLGETSYNCATTRIBPROC glad_eglGetSyncAttrib = NULL; +PFNEGLINITIALIZEPROC glad_eglInitialize = NULL; +PFNEGLMAKECURRENTPROC glad_eglMakeCurrent = NULL; +PFNEGLQUERYAPIPROC glad_eglQueryAPI = NULL; +PFNEGLQUERYCONTEXTPROC glad_eglQueryContext = NULL; +PFNEGLQUERYSTRINGPROC glad_eglQueryString = NULL; +PFNEGLQUERYSURFACEPROC glad_eglQuerySurface = NULL; +PFNEGLRELEASETEXIMAGEPROC glad_eglReleaseTexImage = NULL; +PFNEGLRELEASETHREADPROC glad_eglReleaseThread = NULL; +PFNEGLSURFACEATTRIBPROC glad_eglSurfaceAttrib = NULL; +PFNEGLSWAPBUFFERSPROC glad_eglSwapBuffers = NULL; +PFNEGLSWAPINTERVALPROC glad_eglSwapInterval = NULL; +PFNEGLTERMINATEPROC glad_eglTerminate = NULL; +PFNEGLWAITCLIENTPROC glad_eglWaitClient = NULL; +PFNEGLWAITGLPROC glad_eglWaitGL = NULL; +PFNEGLWAITNATIVEPROC glad_eglWaitNative = NULL; +PFNEGLWAITSYNCPROC glad_eglWaitSync = NULL; +PFNEGLCREATEIMAGEKHRPROC glad_eglCreateImageKHR = NULL; +PFNEGLDESTROYIMAGEKHRPROC glad_eglDestroyImageKHR = NULL; + + +static void glad_egl_load_EGL_VERSION_1_0( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_EGL_VERSION_1_0) return; + glad_eglChooseConfig = (PFNEGLCHOOSECONFIGPROC) load(userptr, "eglChooseConfig"); + glad_eglCopyBuffers = (PFNEGLCOPYBUFFERSPROC) load(userptr, "eglCopyBuffers"); + glad_eglCreateContext = (PFNEGLCREATECONTEXTPROC) load(userptr, "eglCreateContext"); + glad_eglCreatePbufferSurface = (PFNEGLCREATEPBUFFERSURFACEPROC) load(userptr, "eglCreatePbufferSurface"); + glad_eglCreatePixmapSurface = (PFNEGLCREATEPIXMAPSURFACEPROC) load(userptr, "eglCreatePixmapSurface"); + glad_eglCreateWindowSurface = (PFNEGLCREATEWINDOWSURFACEPROC) load(userptr, "eglCreateWindowSurface"); + glad_eglDestroyContext = (PFNEGLDESTROYCONTEXTPROC) load(userptr, "eglDestroyContext"); + glad_eglDestroySurface = (PFNEGLDESTROYSURFACEPROC) load(userptr, "eglDestroySurface"); + glad_eglGetConfigAttrib = (PFNEGLGETCONFIGATTRIBPROC) load(userptr, "eglGetConfigAttrib"); + glad_eglGetConfigs = (PFNEGLGETCONFIGSPROC) load(userptr, "eglGetConfigs"); + glad_eglGetCurrentDisplay = (PFNEGLGETCURRENTDISPLAYPROC) load(userptr, "eglGetCurrentDisplay"); + glad_eglGetCurrentSurface = (PFNEGLGETCURRENTSURFACEPROC) load(userptr, "eglGetCurrentSurface"); + glad_eglGetDisplay = (PFNEGLGETDISPLAYPROC) load(userptr, "eglGetDisplay"); + glad_eglGetError = (PFNEGLGETERRORPROC) load(userptr, "eglGetError"); + glad_eglGetProcAddress = (PFNEGLGETPROCADDRESSPROC) load(userptr, "eglGetProcAddress"); + glad_eglInitialize = (PFNEGLINITIALIZEPROC) load(userptr, "eglInitialize"); + glad_eglMakeCurrent = (PFNEGLMAKECURRENTPROC) load(userptr, "eglMakeCurrent"); + glad_eglQueryContext = (PFNEGLQUERYCONTEXTPROC) load(userptr, "eglQueryContext"); + glad_eglQueryString = (PFNEGLQUERYSTRINGPROC) load(userptr, "eglQueryString"); + glad_eglQuerySurface = (PFNEGLQUERYSURFACEPROC) load(userptr, "eglQuerySurface"); + glad_eglSwapBuffers = (PFNEGLSWAPBUFFERSPROC) load(userptr, "eglSwapBuffers"); + glad_eglTerminate = (PFNEGLTERMINATEPROC) load(userptr, "eglTerminate"); + glad_eglWaitGL = (PFNEGLWAITGLPROC) load(userptr, "eglWaitGL"); + glad_eglWaitNative = (PFNEGLWAITNATIVEPROC) load(userptr, "eglWaitNative"); +} +static void glad_egl_load_EGL_VERSION_1_1( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_EGL_VERSION_1_1) return; + glad_eglBindTexImage = (PFNEGLBINDTEXIMAGEPROC) load(userptr, "eglBindTexImage"); + glad_eglReleaseTexImage = (PFNEGLRELEASETEXIMAGEPROC) load(userptr, "eglReleaseTexImage"); + glad_eglSurfaceAttrib = (PFNEGLSURFACEATTRIBPROC) load(userptr, "eglSurfaceAttrib"); + glad_eglSwapInterval = (PFNEGLSWAPINTERVALPROC) load(userptr, "eglSwapInterval"); +} +static void glad_egl_load_EGL_VERSION_1_2( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_EGL_VERSION_1_2) return; + glad_eglBindAPI = (PFNEGLBINDAPIPROC) load(userptr, "eglBindAPI"); + glad_eglCreatePbufferFromClientBuffer = (PFNEGLCREATEPBUFFERFROMCLIENTBUFFERPROC) load(userptr, "eglCreatePbufferFromClientBuffer"); + glad_eglQueryAPI = (PFNEGLQUERYAPIPROC) load(userptr, "eglQueryAPI"); + glad_eglReleaseThread = (PFNEGLRELEASETHREADPROC) load(userptr, "eglReleaseThread"); + glad_eglWaitClient = (PFNEGLWAITCLIENTPROC) load(userptr, "eglWaitClient"); + glad_eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC) load(userptr, "eglCreateImageKHR"); + glad_eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC) load(userptr, "eglDestroyImageKHR"); +} +static void glad_egl_load_EGL_VERSION_1_4( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_EGL_VERSION_1_4) return; + glad_eglGetCurrentContext = (PFNEGLGETCURRENTCONTEXTPROC) load(userptr, "eglGetCurrentContext"); +} +static void glad_egl_load_EGL_VERSION_1_5( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_EGL_VERSION_1_5) return; + glad_eglClientWaitSync = (PFNEGLCLIENTWAITSYNCPROC) load(userptr, "eglClientWaitSync"); + glad_eglCreateImage = (PFNEGLCREATEIMAGEPROC) load(userptr, "eglCreateImage"); + glad_eglCreatePlatformPixmapSurface = (PFNEGLCREATEPLATFORMPIXMAPSURFACEPROC) load(userptr, "eglCreatePlatformPixmapSurface"); + glad_eglCreatePlatformWindowSurface = (PFNEGLCREATEPLATFORMWINDOWSURFACEPROC) load(userptr, "eglCreatePlatformWindowSurface"); + glad_eglCreateSync = (PFNEGLCREATESYNCPROC) load(userptr, "eglCreateSync"); + glad_eglDestroyImage = (PFNEGLDESTROYIMAGEPROC) load(userptr, "eglDestroyImage"); + glad_eglDestroySync = (PFNEGLDESTROYSYNCPROC) load(userptr, "eglDestroySync"); + glad_eglGetPlatformDisplay = (PFNEGLGETPLATFORMDISPLAYPROC) load(userptr, "eglGetPlatformDisplay"); + glad_eglGetSyncAttrib = (PFNEGLGETSYNCATTRIBPROC) load(userptr, "eglGetSyncAttrib"); + glad_eglWaitSync = (PFNEGLWAITSYNCPROC) load(userptr, "eglWaitSync"); +} + + + +static int glad_egl_get_extensions(EGLDisplay display, const char **extensions) { + *extensions = eglQueryString(display, EGL_EXTENSIONS); + + return extensions != NULL; +} + +static int glad_egl_has_extension(const char *extensions, const char *ext) { + const char *loc; + const char *terminator; + if(extensions == NULL) { + return 0; + } + while(1) { + loc = strstr(extensions, ext); + if(loc == NULL) { + return 0; + } + terminator = loc + strlen(ext); + if((loc == extensions || *(loc - 1) == ' ') && + (*terminator == ' ' || *terminator == '\0')) { + return 1; + } + extensions = terminator; + } +} + +static GLADapiproc glad_egl_get_proc_from_userptr(void *userptr, const char *name) { + return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name); +} + +static int glad_egl_find_extensions_egl(EGLDisplay display) { + const char *extensions; + if (!glad_egl_get_extensions(display, &extensions)) return 0; + + (void) glad_egl_has_extension; + + return 1; +} + +static int glad_egl_find_core_egl(EGLDisplay display) { + int major, minor; + const char *version; + + if (display == NULL) { + display = EGL_NO_DISPLAY; /* this is usually NULL, better safe than sorry */ + } + if (display == EGL_NO_DISPLAY) { + display = eglGetCurrentDisplay(); + } +#ifdef EGL_VERSION_1_4 + if (display == EGL_NO_DISPLAY) { + display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + } +#endif +#ifndef EGL_VERSION_1_5 + if (display == EGL_NO_DISPLAY) { + return 0; + } +#endif + + version = eglQueryString(display, EGL_VERSION); + (void) eglGetError(); + + if (version == NULL) { + major = 1; + minor = 5; // We need version 1.5 anyway + } else { + GLAD_IMPL_UTIL_SSCANF(version, "%d.%d", &major, &minor); + } + + GLAD_EGL_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1; + GLAD_EGL_VERSION_1_1 = (major == 1 && minor >= 1) || major > 1; + GLAD_EGL_VERSION_1_2 = (major == 1 && minor >= 2) || major > 1; + GLAD_EGL_VERSION_1_3 = (major == 1 && minor >= 3) || major > 1; + GLAD_EGL_VERSION_1_4 = (major == 1 && minor >= 4) || major > 1; + GLAD_EGL_VERSION_1_5 = (major == 1 && minor >= 5) || major > 1; + + return GLAD_MAKE_VERSION(major, minor); +} + +int gladLoadEGLUserPtr(EGLDisplay display, GLADuserptrloadfunc load, void* userptr) { + int version; + eglGetDisplay = (PFNEGLGETDISPLAYPROC) load(userptr, "eglGetDisplay"); + eglGetCurrentDisplay = (PFNEGLGETCURRENTDISPLAYPROC) load(userptr, "eglGetCurrentDisplay"); + eglQueryString = (PFNEGLQUERYSTRINGPROC) load(userptr, "eglQueryString"); + eglGetError = (PFNEGLGETERRORPROC) load(userptr, "eglGetError"); + if (eglGetDisplay == NULL || eglGetCurrentDisplay == NULL || eglQueryString == NULL || eglGetError == NULL) return 0; + + version = glad_egl_find_core_egl(display); + if (!version) return 0; + glad_egl_load_EGL_VERSION_1_0(load, userptr); + glad_egl_load_EGL_VERSION_1_1(load, userptr); + glad_egl_load_EGL_VERSION_1_2(load, userptr); + glad_egl_load_EGL_VERSION_1_4(load, userptr); + glad_egl_load_EGL_VERSION_1_5(load, userptr); + + if (!glad_egl_find_extensions_egl(display)) return 0; + + return version; +} + +int gladLoadEGL(EGLDisplay display, GLADloadfunc load) { + return gladLoadEGLUserPtr(display, glad_egl_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load); +} + + + +#ifdef GLAD_EGL + +#ifndef GLAD_LOADER_LIBRARY_C_ +#define GLAD_LOADER_LIBRARY_C_ + +#include +#include + +#if GLAD_PLATFORM_WIN32 +#include +#else +#include +#endif + + +static void* glad_get_dlopen_handle(const char *lib_names[], int length) { + void *handle = NULL; + int i; + + for (i = 0; i < length; ++i) { +#if GLAD_PLATFORM_WIN32 + #if GLAD_PLATFORM_UWP + size_t buffer_size = (strlen(lib_names[i]) + 1) * sizeof(WCHAR); + LPWSTR buffer = (LPWSTR) malloc(buffer_size); + if (buffer != NULL) { + int ret = MultiByteToWideChar(CP_ACP, 0, lib_names[i], -1, buffer, buffer_size); + if (ret != 0) { + handle = (void*) LoadPackagedLibrary(buffer, 0); + } + free((void*) buffer); + } + #else + handle = (void*) LoadLibraryA(lib_names[i]); + #endif +#else + handle = dlopen(lib_names[i], RTLD_LAZY | RTLD_LOCAL); +#endif + if (handle != NULL) { + return handle; + } + } + + return NULL; +} + +static void glad_close_dlopen_handle(void* handle) { + if (handle != NULL) { +#if GLAD_PLATFORM_WIN32 + FreeLibrary((HMODULE) handle); +#else + dlclose(handle); +#endif + } +} + +static GLADapiproc glad_dlsym_handle(void* handle, const char *name) { + if (handle == NULL) { + return NULL; + } + +#if GLAD_PLATFORM_WIN32 + return (GLADapiproc) GetProcAddress((HMODULE) handle, name); +#else + return GLAD_GNUC_EXTENSION (GLADapiproc) dlsym(handle, name); +#endif +} + +#endif /* GLAD_LOADER_LIBRARY_C_ */ + +struct _glad_egl_userptr { + void *handle; + PFNEGLGETPROCADDRESSPROC get_proc_address_ptr; +}; + +static GLADapiproc glad_egl_get_proc(void *vuserptr, const char* name) { + struct _glad_egl_userptr userptr = *(struct _glad_egl_userptr*) vuserptr; + GLADapiproc result = NULL; + + result = glad_dlsym_handle(userptr.handle, name); + if (result == NULL) { + result = GLAD_GNUC_EXTENSION (GLADapiproc) userptr.get_proc_address_ptr(name); + } + + return result; +} + +static void* _egl_handle = NULL; + +static void* glad_egl_dlopen_handle(void) { +#if GLAD_PLATFORM_APPLE + static const char *NAMES[] = {"libEGL.dylib"}; +#elif GLAD_PLATFORM_WIN32 + static const char *NAMES[] = {"libEGL.dll", "EGL.dll"}; +#else + static const char *NAMES[] = {"libEGL.so.1", "libEGL.so"}; +#endif + + if (_egl_handle == NULL) { + _egl_handle = glad_get_dlopen_handle(NAMES, sizeof(NAMES) / sizeof(NAMES[0])); + } + + return _egl_handle; +} + +static struct _glad_egl_userptr glad_egl_build_userptr(void *handle) { + struct _glad_egl_userptr userptr; + userptr.handle = handle; + userptr.get_proc_address_ptr = (PFNEGLGETPROCADDRESSPROC) glad_dlsym_handle(handle, "eglGetProcAddress"); + return userptr; +} + +int gladLoaderLoadEGL(EGLDisplay display) { + int version = 0; + void *handle = NULL; + int did_load = 0; + struct _glad_egl_userptr userptr; + + did_load = _egl_handle == NULL; + handle = glad_egl_dlopen_handle(); + if (handle != NULL) { + userptr = glad_egl_build_userptr(handle); + + if (userptr.get_proc_address_ptr != NULL) { + version = gladLoadEGLUserPtr(display, glad_egl_get_proc, &userptr); + } + + if (!version && did_load) { + gladLoaderUnloadEGL(); + } + } + + return version; +} + + +void gladLoaderUnloadEGL() { + if (_egl_handle != NULL) { + glad_close_dlopen_handle(_egl_handle); + _egl_handle = NULL; + } +} + +#endif /* GLAD_EGL */ + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/third-party/glad/src/gl.c b/third-party/glad/src/gl.c new file mode 100644 index 00000000..a3989cf6 --- /dev/null +++ b/third-party/glad/src/gl.c @@ -0,0 +1,1514 @@ +#include +#include +#include +#include + +#ifndef GLAD_IMPL_UTIL_C_ +#define GLAD_IMPL_UTIL_C_ + +#ifdef _MSC_VER +#define GLAD_IMPL_UTIL_SSCANF sscanf_s +#else +#define GLAD_IMPL_UTIL_SSCANF sscanf +#endif + +#endif /* GLAD_IMPL_UTIL_C_ */ + +#ifdef __cplusplus +extern "C" { +#endif + + + + + + + + +static void glad_gl_load_GL_VERSION_1_0(GladGLContext *context, GLADuserptrloadfunc load, void* userptr) { + if(!context->VERSION_1_0) return; + context->Accum = (PFNGLACCUMPROC) load(userptr, "glAccum"); + context->AlphaFunc = (PFNGLALPHAFUNCPROC) load(userptr, "glAlphaFunc"); + context->Begin = (PFNGLBEGINPROC) load(userptr, "glBegin"); + context->Bitmap = (PFNGLBITMAPPROC) load(userptr, "glBitmap"); + context->BlendFunc = (PFNGLBLENDFUNCPROC) load(userptr, "glBlendFunc"); + context->CallList = (PFNGLCALLLISTPROC) load(userptr, "glCallList"); + context->CallLists = (PFNGLCALLLISTSPROC) load(userptr, "glCallLists"); + context->Clear = (PFNGLCLEARPROC) load(userptr, "glClear"); + context->ClearAccum = (PFNGLCLEARACCUMPROC) load(userptr, "glClearAccum"); + context->ClearColor = (PFNGLCLEARCOLORPROC) load(userptr, "glClearColor"); + context->ClearDepth = (PFNGLCLEARDEPTHPROC) load(userptr, "glClearDepth"); + context->ClearIndex = (PFNGLCLEARINDEXPROC) load(userptr, "glClearIndex"); + context->ClearStencil = (PFNGLCLEARSTENCILPROC) load(userptr, "glClearStencil"); + context->ClipPlane = (PFNGLCLIPPLANEPROC) load(userptr, "glClipPlane"); + context->Color3b = (PFNGLCOLOR3BPROC) load(userptr, "glColor3b"); + context->Color3bv = (PFNGLCOLOR3BVPROC) load(userptr, "glColor3bv"); + context->Color3d = (PFNGLCOLOR3DPROC) load(userptr, "glColor3d"); + context->Color3dv = (PFNGLCOLOR3DVPROC) load(userptr, "glColor3dv"); + context->Color3f = (PFNGLCOLOR3FPROC) load(userptr, "glColor3f"); + context->Color3fv = (PFNGLCOLOR3FVPROC) load(userptr, "glColor3fv"); + context->Color3i = (PFNGLCOLOR3IPROC) load(userptr, "glColor3i"); + context->Color3iv = (PFNGLCOLOR3IVPROC) load(userptr, "glColor3iv"); + context->Color3s = (PFNGLCOLOR3SPROC) load(userptr, "glColor3s"); + context->Color3sv = (PFNGLCOLOR3SVPROC) load(userptr, "glColor3sv"); + context->Color3ub = (PFNGLCOLOR3UBPROC) load(userptr, "glColor3ub"); + context->Color3ubv = (PFNGLCOLOR3UBVPROC) load(userptr, "glColor3ubv"); + context->Color3ui = (PFNGLCOLOR3UIPROC) load(userptr, "glColor3ui"); + context->Color3uiv = (PFNGLCOLOR3UIVPROC) load(userptr, "glColor3uiv"); + context->Color3us = (PFNGLCOLOR3USPROC) load(userptr, "glColor3us"); + context->Color3usv = (PFNGLCOLOR3USVPROC) load(userptr, "glColor3usv"); + context->Color4b = (PFNGLCOLOR4BPROC) load(userptr, "glColor4b"); + context->Color4bv = (PFNGLCOLOR4BVPROC) load(userptr, "glColor4bv"); + context->Color4d = (PFNGLCOLOR4DPROC) load(userptr, "glColor4d"); + context->Color4dv = (PFNGLCOLOR4DVPROC) load(userptr, "glColor4dv"); + context->Color4f = (PFNGLCOLOR4FPROC) load(userptr, "glColor4f"); + context->Color4fv = (PFNGLCOLOR4FVPROC) load(userptr, "glColor4fv"); + context->Color4i = (PFNGLCOLOR4IPROC) load(userptr, "glColor4i"); + context->Color4iv = (PFNGLCOLOR4IVPROC) load(userptr, "glColor4iv"); + context->Color4s = (PFNGLCOLOR4SPROC) load(userptr, "glColor4s"); + context->Color4sv = (PFNGLCOLOR4SVPROC) load(userptr, "glColor4sv"); + context->Color4ub = (PFNGLCOLOR4UBPROC) load(userptr, "glColor4ub"); + context->Color4ubv = (PFNGLCOLOR4UBVPROC) load(userptr, "glColor4ubv"); + context->Color4ui = (PFNGLCOLOR4UIPROC) load(userptr, "glColor4ui"); + context->Color4uiv = (PFNGLCOLOR4UIVPROC) load(userptr, "glColor4uiv"); + context->Color4us = (PFNGLCOLOR4USPROC) load(userptr, "glColor4us"); + context->Color4usv = (PFNGLCOLOR4USVPROC) load(userptr, "glColor4usv"); + context->ColorMask = (PFNGLCOLORMASKPROC) load(userptr, "glColorMask"); + context->ColorMaterial = (PFNGLCOLORMATERIALPROC) load(userptr, "glColorMaterial"); + context->CopyPixels = (PFNGLCOPYPIXELSPROC) load(userptr, "glCopyPixels"); + context->CullFace = (PFNGLCULLFACEPROC) load(userptr, "glCullFace"); + context->DeleteLists = (PFNGLDELETELISTSPROC) load(userptr, "glDeleteLists"); + context->DepthFunc = (PFNGLDEPTHFUNCPROC) load(userptr, "glDepthFunc"); + context->DepthMask = (PFNGLDEPTHMASKPROC) load(userptr, "glDepthMask"); + context->DepthRange = (PFNGLDEPTHRANGEPROC) load(userptr, "glDepthRange"); + context->Disable = (PFNGLDISABLEPROC) load(userptr, "glDisable"); + context->DrawBuffer = (PFNGLDRAWBUFFERPROC) load(userptr, "glDrawBuffer"); + context->DrawPixels = (PFNGLDRAWPIXELSPROC) load(userptr, "glDrawPixels"); + context->EdgeFlag = (PFNGLEDGEFLAGPROC) load(userptr, "glEdgeFlag"); + context->EdgeFlagv = (PFNGLEDGEFLAGVPROC) load(userptr, "glEdgeFlagv"); + context->Enable = (PFNGLENABLEPROC) load(userptr, "glEnable"); + context->End = (PFNGLENDPROC) load(userptr, "glEnd"); + context->EndList = (PFNGLENDLISTPROC) load(userptr, "glEndList"); + context->EvalCoord1d = (PFNGLEVALCOORD1DPROC) load(userptr, "glEvalCoord1d"); + context->EvalCoord1dv = (PFNGLEVALCOORD1DVPROC) load(userptr, "glEvalCoord1dv"); + context->EvalCoord1f = (PFNGLEVALCOORD1FPROC) load(userptr, "glEvalCoord1f"); + context->EvalCoord1fv = (PFNGLEVALCOORD1FVPROC) load(userptr, "glEvalCoord1fv"); + context->EvalCoord2d = (PFNGLEVALCOORD2DPROC) load(userptr, "glEvalCoord2d"); + context->EvalCoord2dv = (PFNGLEVALCOORD2DVPROC) load(userptr, "glEvalCoord2dv"); + context->EvalCoord2f = (PFNGLEVALCOORD2FPROC) load(userptr, "glEvalCoord2f"); + context->EvalCoord2fv = (PFNGLEVALCOORD2FVPROC) load(userptr, "glEvalCoord2fv"); + context->EvalMesh1 = (PFNGLEVALMESH1PROC) load(userptr, "glEvalMesh1"); + context->EvalMesh2 = (PFNGLEVALMESH2PROC) load(userptr, "glEvalMesh2"); + context->EvalPoint1 = (PFNGLEVALPOINT1PROC) load(userptr, "glEvalPoint1"); + context->EvalPoint2 = (PFNGLEVALPOINT2PROC) load(userptr, "glEvalPoint2"); + context->FeedbackBuffer = (PFNGLFEEDBACKBUFFERPROC) load(userptr, "glFeedbackBuffer"); + context->Finish = (PFNGLFINISHPROC) load(userptr, "glFinish"); + context->Flush = (PFNGLFLUSHPROC) load(userptr, "glFlush"); + context->Fogf = (PFNGLFOGFPROC) load(userptr, "glFogf"); + context->Fogfv = (PFNGLFOGFVPROC) load(userptr, "glFogfv"); + context->Fogi = (PFNGLFOGIPROC) load(userptr, "glFogi"); + context->Fogiv = (PFNGLFOGIVPROC) load(userptr, "glFogiv"); + context->FrontFace = (PFNGLFRONTFACEPROC) load(userptr, "glFrontFace"); + context->Frustum = (PFNGLFRUSTUMPROC) load(userptr, "glFrustum"); + context->GenLists = (PFNGLGENLISTSPROC) load(userptr, "glGenLists"); + context->GetBooleanv = (PFNGLGETBOOLEANVPROC) load(userptr, "glGetBooleanv"); + context->GetClipPlane = (PFNGLGETCLIPPLANEPROC) load(userptr, "glGetClipPlane"); + context->GetDoublev = (PFNGLGETDOUBLEVPROC) load(userptr, "glGetDoublev"); + context->GetError = (PFNGLGETERRORPROC) load(userptr, "glGetError"); + context->GetFloatv = (PFNGLGETFLOATVPROC) load(userptr, "glGetFloatv"); + context->GetIntegerv = (PFNGLGETINTEGERVPROC) load(userptr, "glGetIntegerv"); + context->GetLightfv = (PFNGLGETLIGHTFVPROC) load(userptr, "glGetLightfv"); + context->GetLightiv = (PFNGLGETLIGHTIVPROC) load(userptr, "glGetLightiv"); + context->GetMapdv = (PFNGLGETMAPDVPROC) load(userptr, "glGetMapdv"); + context->GetMapfv = (PFNGLGETMAPFVPROC) load(userptr, "glGetMapfv"); + context->GetMapiv = (PFNGLGETMAPIVPROC) load(userptr, "glGetMapiv"); + context->GetMaterialfv = (PFNGLGETMATERIALFVPROC) load(userptr, "glGetMaterialfv"); + context->GetMaterialiv = (PFNGLGETMATERIALIVPROC) load(userptr, "glGetMaterialiv"); + context->GetPixelMapfv = (PFNGLGETPIXELMAPFVPROC) load(userptr, "glGetPixelMapfv"); + context->GetPixelMapuiv = (PFNGLGETPIXELMAPUIVPROC) load(userptr, "glGetPixelMapuiv"); + context->GetPixelMapusv = (PFNGLGETPIXELMAPUSVPROC) load(userptr, "glGetPixelMapusv"); + context->GetPolygonStipple = (PFNGLGETPOLYGONSTIPPLEPROC) load(userptr, "glGetPolygonStipple"); + context->GetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString"); + context->GetTexEnvfv = (PFNGLGETTEXENVFVPROC) load(userptr, "glGetTexEnvfv"); + context->GetTexEnviv = (PFNGLGETTEXENVIVPROC) load(userptr, "glGetTexEnviv"); + context->GetTexGendv = (PFNGLGETTEXGENDVPROC) load(userptr, "glGetTexGendv"); + context->GetTexGenfv = (PFNGLGETTEXGENFVPROC) load(userptr, "glGetTexGenfv"); + context->GetTexGeniv = (PFNGLGETTEXGENIVPROC) load(userptr, "glGetTexGeniv"); + context->GetTexImage = (PFNGLGETTEXIMAGEPROC) load(userptr, "glGetTexImage"); + context->GetTexLevelParameterfv = (PFNGLGETTEXLEVELPARAMETERFVPROC) load(userptr, "glGetTexLevelParameterfv"); + context->GetTexLevelParameteriv = (PFNGLGETTEXLEVELPARAMETERIVPROC) load(userptr, "glGetTexLevelParameteriv"); + context->GetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC) load(userptr, "glGetTexParameterfv"); + context->GetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC) load(userptr, "glGetTexParameteriv"); + context->Hint = (PFNGLHINTPROC) load(userptr, "glHint"); + context->IndexMask = (PFNGLINDEXMASKPROC) load(userptr, "glIndexMask"); + context->Indexd = (PFNGLINDEXDPROC) load(userptr, "glIndexd"); + context->Indexdv = (PFNGLINDEXDVPROC) load(userptr, "glIndexdv"); + context->Indexf = (PFNGLINDEXFPROC) load(userptr, "glIndexf"); + context->Indexfv = (PFNGLINDEXFVPROC) load(userptr, "glIndexfv"); + context->Indexi = (PFNGLINDEXIPROC) load(userptr, "glIndexi"); + context->Indexiv = (PFNGLINDEXIVPROC) load(userptr, "glIndexiv"); + context->Indexs = (PFNGLINDEXSPROC) load(userptr, "glIndexs"); + context->Indexsv = (PFNGLINDEXSVPROC) load(userptr, "glIndexsv"); + context->InitNames = (PFNGLINITNAMESPROC) load(userptr, "glInitNames"); + context->IsEnabled = (PFNGLISENABLEDPROC) load(userptr, "glIsEnabled"); + context->IsList = (PFNGLISLISTPROC) load(userptr, "glIsList"); + context->LightModelf = (PFNGLLIGHTMODELFPROC) load(userptr, "glLightModelf"); + context->LightModelfv = (PFNGLLIGHTMODELFVPROC) load(userptr, "glLightModelfv"); + context->LightModeli = (PFNGLLIGHTMODELIPROC) load(userptr, "glLightModeli"); + context->LightModeliv = (PFNGLLIGHTMODELIVPROC) load(userptr, "glLightModeliv"); + context->Lightf = (PFNGLLIGHTFPROC) load(userptr, "glLightf"); + context->Lightfv = (PFNGLLIGHTFVPROC) load(userptr, "glLightfv"); + context->Lighti = (PFNGLLIGHTIPROC) load(userptr, "glLighti"); + context->Lightiv = (PFNGLLIGHTIVPROC) load(userptr, "glLightiv"); + context->LineStipple = (PFNGLLINESTIPPLEPROC) load(userptr, "glLineStipple"); + context->LineWidth = (PFNGLLINEWIDTHPROC) load(userptr, "glLineWidth"); + context->ListBase = (PFNGLLISTBASEPROC) load(userptr, "glListBase"); + context->LoadIdentity = (PFNGLLOADIDENTITYPROC) load(userptr, "glLoadIdentity"); + context->LoadMatrixd = (PFNGLLOADMATRIXDPROC) load(userptr, "glLoadMatrixd"); + context->LoadMatrixf = (PFNGLLOADMATRIXFPROC) load(userptr, "glLoadMatrixf"); + context->LoadName = (PFNGLLOADNAMEPROC) load(userptr, "glLoadName"); + context->LogicOp = (PFNGLLOGICOPPROC) load(userptr, "glLogicOp"); + context->Map1d = (PFNGLMAP1DPROC) load(userptr, "glMap1d"); + context->Map1f = (PFNGLMAP1FPROC) load(userptr, "glMap1f"); + context->Map2d = (PFNGLMAP2DPROC) load(userptr, "glMap2d"); + context->Map2f = (PFNGLMAP2FPROC) load(userptr, "glMap2f"); + context->MapGrid1d = (PFNGLMAPGRID1DPROC) load(userptr, "glMapGrid1d"); + context->MapGrid1f = (PFNGLMAPGRID1FPROC) load(userptr, "glMapGrid1f"); + context->MapGrid2d = (PFNGLMAPGRID2DPROC) load(userptr, "glMapGrid2d"); + context->MapGrid2f = (PFNGLMAPGRID2FPROC) load(userptr, "glMapGrid2f"); + context->Materialf = (PFNGLMATERIALFPROC) load(userptr, "glMaterialf"); + context->Materialfv = (PFNGLMATERIALFVPROC) load(userptr, "glMaterialfv"); + context->Materiali = (PFNGLMATERIALIPROC) load(userptr, "glMateriali"); + context->Materialiv = (PFNGLMATERIALIVPROC) load(userptr, "glMaterialiv"); + context->MatrixMode = (PFNGLMATRIXMODEPROC) load(userptr, "glMatrixMode"); + context->MultMatrixd = (PFNGLMULTMATRIXDPROC) load(userptr, "glMultMatrixd"); + context->MultMatrixf = (PFNGLMULTMATRIXFPROC) load(userptr, "glMultMatrixf"); + context->NewList = (PFNGLNEWLISTPROC) load(userptr, "glNewList"); + context->Normal3b = (PFNGLNORMAL3BPROC) load(userptr, "glNormal3b"); + context->Normal3bv = (PFNGLNORMAL3BVPROC) load(userptr, "glNormal3bv"); + context->Normal3d = (PFNGLNORMAL3DPROC) load(userptr, "glNormal3d"); + context->Normal3dv = (PFNGLNORMAL3DVPROC) load(userptr, "glNormal3dv"); + context->Normal3f = (PFNGLNORMAL3FPROC) load(userptr, "glNormal3f"); + context->Normal3fv = (PFNGLNORMAL3FVPROC) load(userptr, "glNormal3fv"); + context->Normal3i = (PFNGLNORMAL3IPROC) load(userptr, "glNormal3i"); + context->Normal3iv = (PFNGLNORMAL3IVPROC) load(userptr, "glNormal3iv"); + context->Normal3s = (PFNGLNORMAL3SPROC) load(userptr, "glNormal3s"); + context->Normal3sv = (PFNGLNORMAL3SVPROC) load(userptr, "glNormal3sv"); + context->Ortho = (PFNGLORTHOPROC) load(userptr, "glOrtho"); + context->PassThrough = (PFNGLPASSTHROUGHPROC) load(userptr, "glPassThrough"); + context->PixelMapfv = (PFNGLPIXELMAPFVPROC) load(userptr, "glPixelMapfv"); + context->PixelMapuiv = (PFNGLPIXELMAPUIVPROC) load(userptr, "glPixelMapuiv"); + context->PixelMapusv = (PFNGLPIXELMAPUSVPROC) load(userptr, "glPixelMapusv"); + context->PixelStoref = (PFNGLPIXELSTOREFPROC) load(userptr, "glPixelStoref"); + context->PixelStorei = (PFNGLPIXELSTOREIPROC) load(userptr, "glPixelStorei"); + context->PixelTransferf = (PFNGLPIXELTRANSFERFPROC) load(userptr, "glPixelTransferf"); + context->PixelTransferi = (PFNGLPIXELTRANSFERIPROC) load(userptr, "glPixelTransferi"); + context->PixelZoom = (PFNGLPIXELZOOMPROC) load(userptr, "glPixelZoom"); + context->PointSize = (PFNGLPOINTSIZEPROC) load(userptr, "glPointSize"); + context->PolygonMode = (PFNGLPOLYGONMODEPROC) load(userptr, "glPolygonMode"); + context->PolygonStipple = (PFNGLPOLYGONSTIPPLEPROC) load(userptr, "glPolygonStipple"); + context->PopAttrib = (PFNGLPOPATTRIBPROC) load(userptr, "glPopAttrib"); + context->PopMatrix = (PFNGLPOPMATRIXPROC) load(userptr, "glPopMatrix"); + context->PopName = (PFNGLPOPNAMEPROC) load(userptr, "glPopName"); + context->PushAttrib = (PFNGLPUSHATTRIBPROC) load(userptr, "glPushAttrib"); + context->PushMatrix = (PFNGLPUSHMATRIXPROC) load(userptr, "glPushMatrix"); + context->PushName = (PFNGLPUSHNAMEPROC) load(userptr, "glPushName"); + context->RasterPos2d = (PFNGLRASTERPOS2DPROC) load(userptr, "glRasterPos2d"); + context->RasterPos2dv = (PFNGLRASTERPOS2DVPROC) load(userptr, "glRasterPos2dv"); + context->RasterPos2f = (PFNGLRASTERPOS2FPROC) load(userptr, "glRasterPos2f"); + context->RasterPos2fv = (PFNGLRASTERPOS2FVPROC) load(userptr, "glRasterPos2fv"); + context->RasterPos2i = (PFNGLRASTERPOS2IPROC) load(userptr, "glRasterPos2i"); + context->RasterPos2iv = (PFNGLRASTERPOS2IVPROC) load(userptr, "glRasterPos2iv"); + context->RasterPos2s = (PFNGLRASTERPOS2SPROC) load(userptr, "glRasterPos2s"); + context->RasterPos2sv = (PFNGLRASTERPOS2SVPROC) load(userptr, "glRasterPos2sv"); + context->RasterPos3d = (PFNGLRASTERPOS3DPROC) load(userptr, "glRasterPos3d"); + context->RasterPos3dv = (PFNGLRASTERPOS3DVPROC) load(userptr, "glRasterPos3dv"); + context->RasterPos3f = (PFNGLRASTERPOS3FPROC) load(userptr, "glRasterPos3f"); + context->RasterPos3fv = (PFNGLRASTERPOS3FVPROC) load(userptr, "glRasterPos3fv"); + context->RasterPos3i = (PFNGLRASTERPOS3IPROC) load(userptr, "glRasterPos3i"); + context->RasterPos3iv = (PFNGLRASTERPOS3IVPROC) load(userptr, "glRasterPos3iv"); + context->RasterPos3s = (PFNGLRASTERPOS3SPROC) load(userptr, "glRasterPos3s"); + context->RasterPos3sv = (PFNGLRASTERPOS3SVPROC) load(userptr, "glRasterPos3sv"); + context->RasterPos4d = (PFNGLRASTERPOS4DPROC) load(userptr, "glRasterPos4d"); + context->RasterPos4dv = (PFNGLRASTERPOS4DVPROC) load(userptr, "glRasterPos4dv"); + context->RasterPos4f = (PFNGLRASTERPOS4FPROC) load(userptr, "glRasterPos4f"); + context->RasterPos4fv = (PFNGLRASTERPOS4FVPROC) load(userptr, "glRasterPos4fv"); + context->RasterPos4i = (PFNGLRASTERPOS4IPROC) load(userptr, "glRasterPos4i"); + context->RasterPos4iv = (PFNGLRASTERPOS4IVPROC) load(userptr, "glRasterPos4iv"); + context->RasterPos4s = (PFNGLRASTERPOS4SPROC) load(userptr, "glRasterPos4s"); + context->RasterPos4sv = (PFNGLRASTERPOS4SVPROC) load(userptr, "glRasterPos4sv"); + context->ReadBuffer = (PFNGLREADBUFFERPROC) load(userptr, "glReadBuffer"); + context->ReadPixels = (PFNGLREADPIXELSPROC) load(userptr, "glReadPixels"); + context->Rectd = (PFNGLRECTDPROC) load(userptr, "glRectd"); + context->Rectdv = (PFNGLRECTDVPROC) load(userptr, "glRectdv"); + context->Rectf = (PFNGLRECTFPROC) load(userptr, "glRectf"); + context->Rectfv = (PFNGLRECTFVPROC) load(userptr, "glRectfv"); + context->Recti = (PFNGLRECTIPROC) load(userptr, "glRecti"); + context->Rectiv = (PFNGLRECTIVPROC) load(userptr, "glRectiv"); + context->Rects = (PFNGLRECTSPROC) load(userptr, "glRects"); + context->Rectsv = (PFNGLRECTSVPROC) load(userptr, "glRectsv"); + context->RenderMode = (PFNGLRENDERMODEPROC) load(userptr, "glRenderMode"); + context->Rotated = (PFNGLROTATEDPROC) load(userptr, "glRotated"); + context->Rotatef = (PFNGLROTATEFPROC) load(userptr, "glRotatef"); + context->Scaled = (PFNGLSCALEDPROC) load(userptr, "glScaled"); + context->Scalef = (PFNGLSCALEFPROC) load(userptr, "glScalef"); + context->Scissor = (PFNGLSCISSORPROC) load(userptr, "glScissor"); + context->SelectBuffer = (PFNGLSELECTBUFFERPROC) load(userptr, "glSelectBuffer"); + context->ShadeModel = (PFNGLSHADEMODELPROC) load(userptr, "glShadeModel"); + context->StencilFunc = (PFNGLSTENCILFUNCPROC) load(userptr, "glStencilFunc"); + context->StencilMask = (PFNGLSTENCILMASKPROC) load(userptr, "glStencilMask"); + context->StencilOp = (PFNGLSTENCILOPPROC) load(userptr, "glStencilOp"); + context->TexCoord1d = (PFNGLTEXCOORD1DPROC) load(userptr, "glTexCoord1d"); + context->TexCoord1dv = (PFNGLTEXCOORD1DVPROC) load(userptr, "glTexCoord1dv"); + context->TexCoord1f = (PFNGLTEXCOORD1FPROC) load(userptr, "glTexCoord1f"); + context->TexCoord1fv = (PFNGLTEXCOORD1FVPROC) load(userptr, "glTexCoord1fv"); + context->TexCoord1i = (PFNGLTEXCOORD1IPROC) load(userptr, "glTexCoord1i"); + context->TexCoord1iv = (PFNGLTEXCOORD1IVPROC) load(userptr, "glTexCoord1iv"); + context->TexCoord1s = (PFNGLTEXCOORD1SPROC) load(userptr, "glTexCoord1s"); + context->TexCoord1sv = (PFNGLTEXCOORD1SVPROC) load(userptr, "glTexCoord1sv"); + context->TexCoord2d = (PFNGLTEXCOORD2DPROC) load(userptr, "glTexCoord2d"); + context->TexCoord2dv = (PFNGLTEXCOORD2DVPROC) load(userptr, "glTexCoord2dv"); + context->TexCoord2f = (PFNGLTEXCOORD2FPROC) load(userptr, "glTexCoord2f"); + context->TexCoord2fv = (PFNGLTEXCOORD2FVPROC) load(userptr, "glTexCoord2fv"); + context->TexCoord2i = (PFNGLTEXCOORD2IPROC) load(userptr, "glTexCoord2i"); + context->TexCoord2iv = (PFNGLTEXCOORD2IVPROC) load(userptr, "glTexCoord2iv"); + context->TexCoord2s = (PFNGLTEXCOORD2SPROC) load(userptr, "glTexCoord2s"); + context->TexCoord2sv = (PFNGLTEXCOORD2SVPROC) load(userptr, "glTexCoord2sv"); + context->TexCoord3d = (PFNGLTEXCOORD3DPROC) load(userptr, "glTexCoord3d"); + context->TexCoord3dv = (PFNGLTEXCOORD3DVPROC) load(userptr, "glTexCoord3dv"); + context->TexCoord3f = (PFNGLTEXCOORD3FPROC) load(userptr, "glTexCoord3f"); + context->TexCoord3fv = (PFNGLTEXCOORD3FVPROC) load(userptr, "glTexCoord3fv"); + context->TexCoord3i = (PFNGLTEXCOORD3IPROC) load(userptr, "glTexCoord3i"); + context->TexCoord3iv = (PFNGLTEXCOORD3IVPROC) load(userptr, "glTexCoord3iv"); + context->TexCoord3s = (PFNGLTEXCOORD3SPROC) load(userptr, "glTexCoord3s"); + context->TexCoord3sv = (PFNGLTEXCOORD3SVPROC) load(userptr, "glTexCoord3sv"); + context->TexCoord4d = (PFNGLTEXCOORD4DPROC) load(userptr, "glTexCoord4d"); + context->TexCoord4dv = (PFNGLTEXCOORD4DVPROC) load(userptr, "glTexCoord4dv"); + context->TexCoord4f = (PFNGLTEXCOORD4FPROC) load(userptr, "glTexCoord4f"); + context->TexCoord4fv = (PFNGLTEXCOORD4FVPROC) load(userptr, "glTexCoord4fv"); + context->TexCoord4i = (PFNGLTEXCOORD4IPROC) load(userptr, "glTexCoord4i"); + context->TexCoord4iv = (PFNGLTEXCOORD4IVPROC) load(userptr, "glTexCoord4iv"); + context->TexCoord4s = (PFNGLTEXCOORD4SPROC) load(userptr, "glTexCoord4s"); + context->TexCoord4sv = (PFNGLTEXCOORD4SVPROC) load(userptr, "glTexCoord4sv"); + context->TexEnvf = (PFNGLTEXENVFPROC) load(userptr, "glTexEnvf"); + context->TexEnvfv = (PFNGLTEXENVFVPROC) load(userptr, "glTexEnvfv"); + context->TexEnvi = (PFNGLTEXENVIPROC) load(userptr, "glTexEnvi"); + context->TexEnviv = (PFNGLTEXENVIVPROC) load(userptr, "glTexEnviv"); + context->TexGend = (PFNGLTEXGENDPROC) load(userptr, "glTexGend"); + context->TexGendv = (PFNGLTEXGENDVPROC) load(userptr, "glTexGendv"); + context->TexGenf = (PFNGLTEXGENFPROC) load(userptr, "glTexGenf"); + context->TexGenfv = (PFNGLTEXGENFVPROC) load(userptr, "glTexGenfv"); + context->TexGeni = (PFNGLTEXGENIPROC) load(userptr, "glTexGeni"); + context->TexGeniv = (PFNGLTEXGENIVPROC) load(userptr, "glTexGeniv"); + context->TexImage1D = (PFNGLTEXIMAGE1DPROC) load(userptr, "glTexImage1D"); + context->TexImage2D = (PFNGLTEXIMAGE2DPROC) load(userptr, "glTexImage2D"); + context->TexParameterf = (PFNGLTEXPARAMETERFPROC) load(userptr, "glTexParameterf"); + context->TexParameterfv = (PFNGLTEXPARAMETERFVPROC) load(userptr, "glTexParameterfv"); + context->TexParameteri = (PFNGLTEXPARAMETERIPROC) load(userptr, "glTexParameteri"); + context->TexParameteriv = (PFNGLTEXPARAMETERIVPROC) load(userptr, "glTexParameteriv"); + context->Translated = (PFNGLTRANSLATEDPROC) load(userptr, "glTranslated"); + context->Translatef = (PFNGLTRANSLATEFPROC) load(userptr, "glTranslatef"); + context->Vertex2d = (PFNGLVERTEX2DPROC) load(userptr, "glVertex2d"); + context->Vertex2dv = (PFNGLVERTEX2DVPROC) load(userptr, "glVertex2dv"); + context->Vertex2f = (PFNGLVERTEX2FPROC) load(userptr, "glVertex2f"); + context->Vertex2fv = (PFNGLVERTEX2FVPROC) load(userptr, "glVertex2fv"); + context->Vertex2i = (PFNGLVERTEX2IPROC) load(userptr, "glVertex2i"); + context->Vertex2iv = (PFNGLVERTEX2IVPROC) load(userptr, "glVertex2iv"); + context->Vertex2s = (PFNGLVERTEX2SPROC) load(userptr, "glVertex2s"); + context->Vertex2sv = (PFNGLVERTEX2SVPROC) load(userptr, "glVertex2sv"); + context->Vertex3d = (PFNGLVERTEX3DPROC) load(userptr, "glVertex3d"); + context->Vertex3dv = (PFNGLVERTEX3DVPROC) load(userptr, "glVertex3dv"); + context->Vertex3f = (PFNGLVERTEX3FPROC) load(userptr, "glVertex3f"); + context->Vertex3fv = (PFNGLVERTEX3FVPROC) load(userptr, "glVertex3fv"); + context->Vertex3i = (PFNGLVERTEX3IPROC) load(userptr, "glVertex3i"); + context->Vertex3iv = (PFNGLVERTEX3IVPROC) load(userptr, "glVertex3iv"); + context->Vertex3s = (PFNGLVERTEX3SPROC) load(userptr, "glVertex3s"); + context->Vertex3sv = (PFNGLVERTEX3SVPROC) load(userptr, "glVertex3sv"); + context->Vertex4d = (PFNGLVERTEX4DPROC) load(userptr, "glVertex4d"); + context->Vertex4dv = (PFNGLVERTEX4DVPROC) load(userptr, "glVertex4dv"); + context->Vertex4f = (PFNGLVERTEX4FPROC) load(userptr, "glVertex4f"); + context->Vertex4fv = (PFNGLVERTEX4FVPROC) load(userptr, "glVertex4fv"); + context->Vertex4i = (PFNGLVERTEX4IPROC) load(userptr, "glVertex4i"); + context->Vertex4iv = (PFNGLVERTEX4IVPROC) load(userptr, "glVertex4iv"); + context->Vertex4s = (PFNGLVERTEX4SPROC) load(userptr, "glVertex4s"); + context->Vertex4sv = (PFNGLVERTEX4SVPROC) load(userptr, "glVertex4sv"); + context->Viewport = (PFNGLVIEWPORTPROC) load(userptr, "glViewport"); +} +static void glad_gl_load_GL_VERSION_1_1(GladGLContext *context, GLADuserptrloadfunc load, void* userptr) { + if(!context->VERSION_1_1) return; + context->AreTexturesResident = (PFNGLARETEXTURESRESIDENTPROC) load(userptr, "glAreTexturesResident"); + context->ArrayElement = (PFNGLARRAYELEMENTPROC) load(userptr, "glArrayElement"); + context->BindTexture = (PFNGLBINDTEXTUREPROC) load(userptr, "glBindTexture"); + context->ColorPointer = (PFNGLCOLORPOINTERPROC) load(userptr, "glColorPointer"); + context->CopyTexImage1D = (PFNGLCOPYTEXIMAGE1DPROC) load(userptr, "glCopyTexImage1D"); + context->CopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC) load(userptr, "glCopyTexImage2D"); + context->CopyTexSubImage1D = (PFNGLCOPYTEXSUBIMAGE1DPROC) load(userptr, "glCopyTexSubImage1D"); + context->CopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC) load(userptr, "glCopyTexSubImage2D"); + context->DeleteTextures = (PFNGLDELETETEXTURESPROC) load(userptr, "glDeleteTextures"); + context->DisableClientState = (PFNGLDISABLECLIENTSTATEPROC) load(userptr, "glDisableClientState"); + context->DrawArrays = (PFNGLDRAWARRAYSPROC) load(userptr, "glDrawArrays"); + context->DrawElements = (PFNGLDRAWELEMENTSPROC) load(userptr, "glDrawElements"); + context->EdgeFlagPointer = (PFNGLEDGEFLAGPOINTERPROC) load(userptr, "glEdgeFlagPointer"); + context->EnableClientState = (PFNGLENABLECLIENTSTATEPROC) load(userptr, "glEnableClientState"); + context->GenTextures = (PFNGLGENTEXTURESPROC) load(userptr, "glGenTextures"); + context->GetPointerv = (PFNGLGETPOINTERVPROC) load(userptr, "glGetPointerv"); + context->IndexPointer = (PFNGLINDEXPOINTERPROC) load(userptr, "glIndexPointer"); + context->Indexub = (PFNGLINDEXUBPROC) load(userptr, "glIndexub"); + context->Indexubv = (PFNGLINDEXUBVPROC) load(userptr, "glIndexubv"); + context->InterleavedArrays = (PFNGLINTERLEAVEDARRAYSPROC) load(userptr, "glInterleavedArrays"); + context->IsTexture = (PFNGLISTEXTUREPROC) load(userptr, "glIsTexture"); + context->NormalPointer = (PFNGLNORMALPOINTERPROC) load(userptr, "glNormalPointer"); + context->PolygonOffset = (PFNGLPOLYGONOFFSETPROC) load(userptr, "glPolygonOffset"); + context->PopClientAttrib = (PFNGLPOPCLIENTATTRIBPROC) load(userptr, "glPopClientAttrib"); + context->PrioritizeTextures = (PFNGLPRIORITIZETEXTURESPROC) load(userptr, "glPrioritizeTextures"); + context->PushClientAttrib = (PFNGLPUSHCLIENTATTRIBPROC) load(userptr, "glPushClientAttrib"); + context->TexCoordPointer = (PFNGLTEXCOORDPOINTERPROC) load(userptr, "glTexCoordPointer"); + context->TexSubImage1D = (PFNGLTEXSUBIMAGE1DPROC) load(userptr, "glTexSubImage1D"); + context->TexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC) load(userptr, "glTexSubImage2D"); + context->VertexPointer = (PFNGLVERTEXPOINTERPROC) load(userptr, "glVertexPointer"); +} +static void glad_gl_load_GL_VERSION_1_2(GladGLContext *context, GLADuserptrloadfunc load, void* userptr) { + if(!context->VERSION_1_2) return; + context->CopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC) load(userptr, "glCopyTexSubImage3D"); + context->DrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC) load(userptr, "glDrawRangeElements"); + context->TexImage3D = (PFNGLTEXIMAGE3DPROC) load(userptr, "glTexImage3D"); + context->TexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC) load(userptr, "glTexSubImage3D"); +} +static void glad_gl_load_GL_VERSION_1_3(GladGLContext *context, GLADuserptrloadfunc load, void* userptr) { + if(!context->VERSION_1_3) return; + context->ActiveTexture = (PFNGLACTIVETEXTUREPROC) load(userptr, "glActiveTexture"); + context->ClientActiveTexture = (PFNGLCLIENTACTIVETEXTUREPROC) load(userptr, "glClientActiveTexture"); + context->CompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC) load(userptr, "glCompressedTexImage1D"); + context->CompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC) load(userptr, "glCompressedTexImage2D"); + context->CompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC) load(userptr, "glCompressedTexImage3D"); + context->CompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) load(userptr, "glCompressedTexSubImage1D"); + context->CompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) load(userptr, "glCompressedTexSubImage2D"); + context->CompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) load(userptr, "glCompressedTexSubImage3D"); + context->GetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC) load(userptr, "glGetCompressedTexImage"); + context->LoadTransposeMatrixd = (PFNGLLOADTRANSPOSEMATRIXDPROC) load(userptr, "glLoadTransposeMatrixd"); + context->LoadTransposeMatrixf = (PFNGLLOADTRANSPOSEMATRIXFPROC) load(userptr, "glLoadTransposeMatrixf"); + context->MultTransposeMatrixd = (PFNGLMULTTRANSPOSEMATRIXDPROC) load(userptr, "glMultTransposeMatrixd"); + context->MultTransposeMatrixf = (PFNGLMULTTRANSPOSEMATRIXFPROC) load(userptr, "glMultTransposeMatrixf"); + context->MultiTexCoord1d = (PFNGLMULTITEXCOORD1DPROC) load(userptr, "glMultiTexCoord1d"); + context->MultiTexCoord1dv = (PFNGLMULTITEXCOORD1DVPROC) load(userptr, "glMultiTexCoord1dv"); + context->MultiTexCoord1f = (PFNGLMULTITEXCOORD1FPROC) load(userptr, "glMultiTexCoord1f"); + context->MultiTexCoord1fv = (PFNGLMULTITEXCOORD1FVPROC) load(userptr, "glMultiTexCoord1fv"); + context->MultiTexCoord1i = (PFNGLMULTITEXCOORD1IPROC) load(userptr, "glMultiTexCoord1i"); + context->MultiTexCoord1iv = (PFNGLMULTITEXCOORD1IVPROC) load(userptr, "glMultiTexCoord1iv"); + context->MultiTexCoord1s = (PFNGLMULTITEXCOORD1SPROC) load(userptr, "glMultiTexCoord1s"); + context->MultiTexCoord1sv = (PFNGLMULTITEXCOORD1SVPROC) load(userptr, "glMultiTexCoord1sv"); + context->MultiTexCoord2d = (PFNGLMULTITEXCOORD2DPROC) load(userptr, "glMultiTexCoord2d"); + context->MultiTexCoord2dv = (PFNGLMULTITEXCOORD2DVPROC) load(userptr, "glMultiTexCoord2dv"); + context->MultiTexCoord2f = (PFNGLMULTITEXCOORD2FPROC) load(userptr, "glMultiTexCoord2f"); + context->MultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC) load(userptr, "glMultiTexCoord2fv"); + context->MultiTexCoord2i = (PFNGLMULTITEXCOORD2IPROC) load(userptr, "glMultiTexCoord2i"); + context->MultiTexCoord2iv = (PFNGLMULTITEXCOORD2IVPROC) load(userptr, "glMultiTexCoord2iv"); + context->MultiTexCoord2s = (PFNGLMULTITEXCOORD2SPROC) load(userptr, "glMultiTexCoord2s"); + context->MultiTexCoord2sv = (PFNGLMULTITEXCOORD2SVPROC) load(userptr, "glMultiTexCoord2sv"); + context->MultiTexCoord3d = (PFNGLMULTITEXCOORD3DPROC) load(userptr, "glMultiTexCoord3d"); + context->MultiTexCoord3dv = (PFNGLMULTITEXCOORD3DVPROC) load(userptr, "glMultiTexCoord3dv"); + context->MultiTexCoord3f = (PFNGLMULTITEXCOORD3FPROC) load(userptr, "glMultiTexCoord3f"); + context->MultiTexCoord3fv = (PFNGLMULTITEXCOORD3FVPROC) load(userptr, "glMultiTexCoord3fv"); + context->MultiTexCoord3i = (PFNGLMULTITEXCOORD3IPROC) load(userptr, "glMultiTexCoord3i"); + context->MultiTexCoord3iv = (PFNGLMULTITEXCOORD3IVPROC) load(userptr, "glMultiTexCoord3iv"); + context->MultiTexCoord3s = (PFNGLMULTITEXCOORD3SPROC) load(userptr, "glMultiTexCoord3s"); + context->MultiTexCoord3sv = (PFNGLMULTITEXCOORD3SVPROC) load(userptr, "glMultiTexCoord3sv"); + context->MultiTexCoord4d = (PFNGLMULTITEXCOORD4DPROC) load(userptr, "glMultiTexCoord4d"); + context->MultiTexCoord4dv = (PFNGLMULTITEXCOORD4DVPROC) load(userptr, "glMultiTexCoord4dv"); + context->MultiTexCoord4f = (PFNGLMULTITEXCOORD4FPROC) load(userptr, "glMultiTexCoord4f"); + context->MultiTexCoord4fv = (PFNGLMULTITEXCOORD4FVPROC) load(userptr, "glMultiTexCoord4fv"); + context->MultiTexCoord4i = (PFNGLMULTITEXCOORD4IPROC) load(userptr, "glMultiTexCoord4i"); + context->MultiTexCoord4iv = (PFNGLMULTITEXCOORD4IVPROC) load(userptr, "glMultiTexCoord4iv"); + context->MultiTexCoord4s = (PFNGLMULTITEXCOORD4SPROC) load(userptr, "glMultiTexCoord4s"); + context->MultiTexCoord4sv = (PFNGLMULTITEXCOORD4SVPROC) load(userptr, "glMultiTexCoord4sv"); + context->SampleCoverage = (PFNGLSAMPLECOVERAGEPROC) load(userptr, "glSampleCoverage"); +} +static void glad_gl_load_GL_VERSION_1_4(GladGLContext *context, GLADuserptrloadfunc load, void* userptr) { + if(!context->VERSION_1_4) return; + context->BlendColor = (PFNGLBLENDCOLORPROC) load(userptr, "glBlendColor"); + context->BlendEquation = (PFNGLBLENDEQUATIONPROC) load(userptr, "glBlendEquation"); + context->BlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC) load(userptr, "glBlendFuncSeparate"); + context->FogCoordPointer = (PFNGLFOGCOORDPOINTERPROC) load(userptr, "glFogCoordPointer"); + context->FogCoordd = (PFNGLFOGCOORDDPROC) load(userptr, "glFogCoordd"); + context->FogCoorddv = (PFNGLFOGCOORDDVPROC) load(userptr, "glFogCoorddv"); + context->FogCoordf = (PFNGLFOGCOORDFPROC) load(userptr, "glFogCoordf"); + context->FogCoordfv = (PFNGLFOGCOORDFVPROC) load(userptr, "glFogCoordfv"); + context->MultiDrawArrays = (PFNGLMULTIDRAWARRAYSPROC) load(userptr, "glMultiDrawArrays"); + context->MultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC) load(userptr, "glMultiDrawElements"); + context->PointParameterf = (PFNGLPOINTPARAMETERFPROC) load(userptr, "glPointParameterf"); + context->PointParameterfv = (PFNGLPOINTPARAMETERFVPROC) load(userptr, "glPointParameterfv"); + context->PointParameteri = (PFNGLPOINTPARAMETERIPROC) load(userptr, "glPointParameteri"); + context->PointParameteriv = (PFNGLPOINTPARAMETERIVPROC) load(userptr, "glPointParameteriv"); + context->SecondaryColor3b = (PFNGLSECONDARYCOLOR3BPROC) load(userptr, "glSecondaryColor3b"); + context->SecondaryColor3bv = (PFNGLSECONDARYCOLOR3BVPROC) load(userptr, "glSecondaryColor3bv"); + context->SecondaryColor3d = (PFNGLSECONDARYCOLOR3DPROC) load(userptr, "glSecondaryColor3d"); + context->SecondaryColor3dv = (PFNGLSECONDARYCOLOR3DVPROC) load(userptr, "glSecondaryColor3dv"); + context->SecondaryColor3f = (PFNGLSECONDARYCOLOR3FPROC) load(userptr, "glSecondaryColor3f"); + context->SecondaryColor3fv = (PFNGLSECONDARYCOLOR3FVPROC) load(userptr, "glSecondaryColor3fv"); + context->SecondaryColor3i = (PFNGLSECONDARYCOLOR3IPROC) load(userptr, "glSecondaryColor3i"); + context->SecondaryColor3iv = (PFNGLSECONDARYCOLOR3IVPROC) load(userptr, "glSecondaryColor3iv"); + context->SecondaryColor3s = (PFNGLSECONDARYCOLOR3SPROC) load(userptr, "glSecondaryColor3s"); + context->SecondaryColor3sv = (PFNGLSECONDARYCOLOR3SVPROC) load(userptr, "glSecondaryColor3sv"); + context->SecondaryColor3ub = (PFNGLSECONDARYCOLOR3UBPROC) load(userptr, "glSecondaryColor3ub"); + context->SecondaryColor3ubv = (PFNGLSECONDARYCOLOR3UBVPROC) load(userptr, "glSecondaryColor3ubv"); + context->SecondaryColor3ui = (PFNGLSECONDARYCOLOR3UIPROC) load(userptr, "glSecondaryColor3ui"); + context->SecondaryColor3uiv = (PFNGLSECONDARYCOLOR3UIVPROC) load(userptr, "glSecondaryColor3uiv"); + context->SecondaryColor3us = (PFNGLSECONDARYCOLOR3USPROC) load(userptr, "glSecondaryColor3us"); + context->SecondaryColor3usv = (PFNGLSECONDARYCOLOR3USVPROC) load(userptr, "glSecondaryColor3usv"); + context->SecondaryColorPointer = (PFNGLSECONDARYCOLORPOINTERPROC) load(userptr, "glSecondaryColorPointer"); + context->WindowPos2d = (PFNGLWINDOWPOS2DPROC) load(userptr, "glWindowPos2d"); + context->WindowPos2dv = (PFNGLWINDOWPOS2DVPROC) load(userptr, "glWindowPos2dv"); + context->WindowPos2f = (PFNGLWINDOWPOS2FPROC) load(userptr, "glWindowPos2f"); + context->WindowPos2fv = (PFNGLWINDOWPOS2FVPROC) load(userptr, "glWindowPos2fv"); + context->WindowPos2i = (PFNGLWINDOWPOS2IPROC) load(userptr, "glWindowPos2i"); + context->WindowPos2iv = (PFNGLWINDOWPOS2IVPROC) load(userptr, "glWindowPos2iv"); + context->WindowPos2s = (PFNGLWINDOWPOS2SPROC) load(userptr, "glWindowPos2s"); + context->WindowPos2sv = (PFNGLWINDOWPOS2SVPROC) load(userptr, "glWindowPos2sv"); + context->WindowPos3d = (PFNGLWINDOWPOS3DPROC) load(userptr, "glWindowPos3d"); + context->WindowPos3dv = (PFNGLWINDOWPOS3DVPROC) load(userptr, "glWindowPos3dv"); + context->WindowPos3f = (PFNGLWINDOWPOS3FPROC) load(userptr, "glWindowPos3f"); + context->WindowPos3fv = (PFNGLWINDOWPOS3FVPROC) load(userptr, "glWindowPos3fv"); + context->WindowPos3i = (PFNGLWINDOWPOS3IPROC) load(userptr, "glWindowPos3i"); + context->WindowPos3iv = (PFNGLWINDOWPOS3IVPROC) load(userptr, "glWindowPos3iv"); + context->WindowPos3s = (PFNGLWINDOWPOS3SPROC) load(userptr, "glWindowPos3s"); + context->WindowPos3sv = (PFNGLWINDOWPOS3SVPROC) load(userptr, "glWindowPos3sv"); +} +static void glad_gl_load_GL_VERSION_1_5(GladGLContext *context, GLADuserptrloadfunc load, void* userptr) { + if(!context->VERSION_1_5) return; + context->BeginQuery = (PFNGLBEGINQUERYPROC) load(userptr, "glBeginQuery"); + context->BindBuffer = (PFNGLBINDBUFFERPROC) load(userptr, "glBindBuffer"); + context->BufferData = (PFNGLBUFFERDATAPROC) load(userptr, "glBufferData"); + context->BufferSubData = (PFNGLBUFFERSUBDATAPROC) load(userptr, "glBufferSubData"); + context->DeleteBuffers = (PFNGLDELETEBUFFERSPROC) load(userptr, "glDeleteBuffers"); + context->DeleteQueries = (PFNGLDELETEQUERIESPROC) load(userptr, "glDeleteQueries"); + context->EndQuery = (PFNGLENDQUERYPROC) load(userptr, "glEndQuery"); + context->GenBuffers = (PFNGLGENBUFFERSPROC) load(userptr, "glGenBuffers"); + context->GenQueries = (PFNGLGENQUERIESPROC) load(userptr, "glGenQueries"); + context->GetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC) load(userptr, "glGetBufferParameteriv"); + context->GetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC) load(userptr, "glGetBufferPointerv"); + context->GetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC) load(userptr, "glGetBufferSubData"); + context->GetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC) load(userptr, "glGetQueryObjectiv"); + context->GetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC) load(userptr, "glGetQueryObjectuiv"); + context->GetQueryiv = (PFNGLGETQUERYIVPROC) load(userptr, "glGetQueryiv"); + context->IsBuffer = (PFNGLISBUFFERPROC) load(userptr, "glIsBuffer"); + context->IsQuery = (PFNGLISQUERYPROC) load(userptr, "glIsQuery"); + context->MapBuffer = (PFNGLMAPBUFFERPROC) load(userptr, "glMapBuffer"); + context->UnmapBuffer = (PFNGLUNMAPBUFFERPROC) load(userptr, "glUnmapBuffer"); +} +static void glad_gl_load_GL_VERSION_2_0(GladGLContext *context, GLADuserptrloadfunc load, void* userptr) { + if(!context->VERSION_2_0) return; + context->EGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) load(userptr, "glEGLImageTargetTexture2DOES"); + context->AttachShader = (PFNGLATTACHSHADERPROC) load(userptr, "glAttachShader"); + context->BindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC) load(userptr, "glBindAttribLocation"); + context->BlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC) load(userptr, "glBlendEquationSeparate"); + context->CompileShader = (PFNGLCOMPILESHADERPROC) load(userptr, "glCompileShader"); + context->CreateProgram = (PFNGLCREATEPROGRAMPROC) load(userptr, "glCreateProgram"); + context->CreateShader = (PFNGLCREATESHADERPROC) load(userptr, "glCreateShader"); + context->DeleteProgram = (PFNGLDELETEPROGRAMPROC) load(userptr, "glDeleteProgram"); + context->DeleteShader = (PFNGLDELETESHADERPROC) load(userptr, "glDeleteShader"); + context->DetachShader = (PFNGLDETACHSHADERPROC) load(userptr, "glDetachShader"); + context->DisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC) load(userptr, "glDisableVertexAttribArray"); + context->DrawBuffers = (PFNGLDRAWBUFFERSPROC) load(userptr, "glDrawBuffers"); + context->EnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC) load(userptr, "glEnableVertexAttribArray"); + context->GetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC) load(userptr, "glGetActiveAttrib"); + context->GetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC) load(userptr, "glGetActiveUniform"); + context->GetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC) load(userptr, "glGetAttachedShaders"); + context->GetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC) load(userptr, "glGetAttribLocation"); + context->GetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) load(userptr, "glGetProgramInfoLog"); + context->GetProgramiv = (PFNGLGETPROGRAMIVPROC) load(userptr, "glGetProgramiv"); + context->GetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) load(userptr, "glGetShaderInfoLog"); + context->GetShaderSource = (PFNGLGETSHADERSOURCEPROC) load(userptr, "glGetShaderSource"); + context->GetShaderiv = (PFNGLGETSHADERIVPROC) load(userptr, "glGetShaderiv"); + context->GetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) load(userptr, "glGetUniformLocation"); + context->GetUniformfv = (PFNGLGETUNIFORMFVPROC) load(userptr, "glGetUniformfv"); + context->GetUniformiv = (PFNGLGETUNIFORMIVPROC) load(userptr, "glGetUniformiv"); + context->GetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC) load(userptr, "glGetVertexAttribPointerv"); + context->GetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC) load(userptr, "glGetVertexAttribdv"); + context->GetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC) load(userptr, "glGetVertexAttribfv"); + context->GetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC) load(userptr, "glGetVertexAttribiv"); + context->IsProgram = (PFNGLISPROGRAMPROC) load(userptr, "glIsProgram"); + context->IsShader = (PFNGLISSHADERPROC) load(userptr, "glIsShader"); + context->LinkProgram = (PFNGLLINKPROGRAMPROC) load(userptr, "glLinkProgram"); + context->ShaderSource = (PFNGLSHADERSOURCEPROC) load(userptr, "glShaderSource"); + context->StencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC) load(userptr, "glStencilFuncSeparate"); + context->StencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC) load(userptr, "glStencilMaskSeparate"); + context->StencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC) load(userptr, "glStencilOpSeparate"); + context->Uniform1f = (PFNGLUNIFORM1FPROC) load(userptr, "glUniform1f"); + context->Uniform1fv = (PFNGLUNIFORM1FVPROC) load(userptr, "glUniform1fv"); + context->Uniform1i = (PFNGLUNIFORM1IPROC) load(userptr, "glUniform1i"); + context->Uniform1iv = (PFNGLUNIFORM1IVPROC) load(userptr, "glUniform1iv"); + context->Uniform2f = (PFNGLUNIFORM2FPROC) load(userptr, "glUniform2f"); + context->Uniform2fv = (PFNGLUNIFORM2FVPROC) load(userptr, "glUniform2fv"); + context->Uniform2i = (PFNGLUNIFORM2IPROC) load(userptr, "glUniform2i"); + context->Uniform2iv = (PFNGLUNIFORM2IVPROC) load(userptr, "glUniform2iv"); + context->Uniform3f = (PFNGLUNIFORM3FPROC) load(userptr, "glUniform3f"); + context->Uniform3fv = (PFNGLUNIFORM3FVPROC) load(userptr, "glUniform3fv"); + context->Uniform3i = (PFNGLUNIFORM3IPROC) load(userptr, "glUniform3i"); + context->Uniform3iv = (PFNGLUNIFORM3IVPROC) load(userptr, "glUniform3iv"); + context->Uniform4f = (PFNGLUNIFORM4FPROC) load(userptr, "glUniform4f"); + context->Uniform4fv = (PFNGLUNIFORM4FVPROC) load(userptr, "glUniform4fv"); + context->Uniform4i = (PFNGLUNIFORM4IPROC) load(userptr, "glUniform4i"); + context->Uniform4iv = (PFNGLUNIFORM4IVPROC) load(userptr, "glUniform4iv"); + context->UniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC) load(userptr, "glUniformMatrix2fv"); + context->UniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC) load(userptr, "glUniformMatrix3fv"); + context->UniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC) load(userptr, "glUniformMatrix4fv"); + context->UseProgram = (PFNGLUSEPROGRAMPROC) load(userptr, "glUseProgram"); + context->ValidateProgram = (PFNGLVALIDATEPROGRAMPROC) load(userptr, "glValidateProgram"); + context->VertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC) load(userptr, "glVertexAttrib1d"); + context->VertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC) load(userptr, "glVertexAttrib1dv"); + context->VertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC) load(userptr, "glVertexAttrib1f"); + context->VertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC) load(userptr, "glVertexAttrib1fv"); + context->VertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC) load(userptr, "glVertexAttrib1s"); + context->VertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC) load(userptr, "glVertexAttrib1sv"); + context->VertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC) load(userptr, "glVertexAttrib2d"); + context->VertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC) load(userptr, "glVertexAttrib2dv"); + context->VertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC) load(userptr, "glVertexAttrib2f"); + context->VertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC) load(userptr, "glVertexAttrib2fv"); + context->VertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC) load(userptr, "glVertexAttrib2s"); + context->VertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC) load(userptr, "glVertexAttrib2sv"); + context->VertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC) load(userptr, "glVertexAttrib3d"); + context->VertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC) load(userptr, "glVertexAttrib3dv"); + context->VertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC) load(userptr, "glVertexAttrib3f"); + context->VertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC) load(userptr, "glVertexAttrib3fv"); + context->VertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC) load(userptr, "glVertexAttrib3s"); + context->VertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC) load(userptr, "glVertexAttrib3sv"); + context->VertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC) load(userptr, "glVertexAttrib4Nbv"); + context->VertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC) load(userptr, "glVertexAttrib4Niv"); + context->VertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC) load(userptr, "glVertexAttrib4Nsv"); + context->VertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC) load(userptr, "glVertexAttrib4Nub"); + context->VertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC) load(userptr, "glVertexAttrib4Nubv"); + context->VertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC) load(userptr, "glVertexAttrib4Nuiv"); + context->VertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC) load(userptr, "glVertexAttrib4Nusv"); + context->VertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC) load(userptr, "glVertexAttrib4bv"); + context->VertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC) load(userptr, "glVertexAttrib4d"); + context->VertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC) load(userptr, "glVertexAttrib4dv"); + context->VertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC) load(userptr, "glVertexAttrib4f"); + context->VertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC) load(userptr, "glVertexAttrib4fv"); + context->VertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC) load(userptr, "glVertexAttrib4iv"); + context->VertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC) load(userptr, "glVertexAttrib4s"); + context->VertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC) load(userptr, "glVertexAttrib4sv"); + context->VertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC) load(userptr, "glVertexAttrib4ubv"); + context->VertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC) load(userptr, "glVertexAttrib4uiv"); + context->VertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC) load(userptr, "glVertexAttrib4usv"); + context->VertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC) load(userptr, "glVertexAttribPointer"); +} +static void glad_gl_load_GL_VERSION_2_1(GladGLContext *context, GLADuserptrloadfunc load, void* userptr) { + if(!context->VERSION_2_1) return; + context->UniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC) load(userptr, "glUniformMatrix2x3fv"); + context->UniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC) load(userptr, "glUniformMatrix2x4fv"); + context->UniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC) load(userptr, "glUniformMatrix3x2fv"); + context->UniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC) load(userptr, "glUniformMatrix3x4fv"); + context->UniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC) load(userptr, "glUniformMatrix4x2fv"); + context->UniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC) load(userptr, "glUniformMatrix4x3fv"); +} +static void glad_gl_load_GL_VERSION_3_0(GladGLContext *context, GLADuserptrloadfunc load, void* userptr) { + if(!context->VERSION_3_0) return; + context->BeginConditionalRender = (PFNGLBEGINCONDITIONALRENDERPROC) load(userptr, "glBeginConditionalRender"); + context->BeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC) load(userptr, "glBeginTransformFeedback"); + context->BindBufferBase = (PFNGLBINDBUFFERBASEPROC) load(userptr, "glBindBufferBase"); + context->BindBufferRange = (PFNGLBINDBUFFERRANGEPROC) load(userptr, "glBindBufferRange"); + context->BindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC) load(userptr, "glBindFragDataLocation"); + context->BindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) load(userptr, "glBindFramebuffer"); + context->BindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC) load(userptr, "glBindRenderbuffer"); + context->BindVertexArray = (PFNGLBINDVERTEXARRAYPROC) load(userptr, "glBindVertexArray"); + context->BlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC) load(userptr, "glBlitFramebuffer"); + context->CheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) load(userptr, "glCheckFramebufferStatus"); + context->ClampColor = (PFNGLCLAMPCOLORPROC) load(userptr, "glClampColor"); + context->ClearBufferfi = (PFNGLCLEARBUFFERFIPROC) load(userptr, "glClearBufferfi"); + context->ClearBufferfv = (PFNGLCLEARBUFFERFVPROC) load(userptr, "glClearBufferfv"); + context->ClearBufferiv = (PFNGLCLEARBUFFERIVPROC) load(userptr, "glClearBufferiv"); + context->ClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC) load(userptr, "glClearBufferuiv"); + context->ColorMaski = (PFNGLCOLORMASKIPROC) load(userptr, "glColorMaski"); + context->DeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC) load(userptr, "glDeleteFramebuffers"); + context->DeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC) load(userptr, "glDeleteRenderbuffers"); + context->DeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC) load(userptr, "glDeleteVertexArrays"); + context->Disablei = (PFNGLDISABLEIPROC) load(userptr, "glDisablei"); + context->Enablei = (PFNGLENABLEIPROC) load(userptr, "glEnablei"); + context->EndConditionalRender = (PFNGLENDCONDITIONALRENDERPROC) load(userptr, "glEndConditionalRender"); + context->EndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC) load(userptr, "glEndTransformFeedback"); + context->FlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC) load(userptr, "glFlushMappedBufferRange"); + context->FramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC) load(userptr, "glFramebufferRenderbuffer"); + context->FramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC) load(userptr, "glFramebufferTexture1D"); + context->FramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC) load(userptr, "glFramebufferTexture2D"); + context->FramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC) load(userptr, "glFramebufferTexture3D"); + context->FramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC) load(userptr, "glFramebufferTextureLayer"); + context->GenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC) load(userptr, "glGenFramebuffers"); + context->GenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC) load(userptr, "glGenRenderbuffers"); + context->GenVertexArrays = (PFNGLGENVERTEXARRAYSPROC) load(userptr, "glGenVertexArrays"); + context->GenerateMipmap = (PFNGLGENERATEMIPMAPPROC) load(userptr, "glGenerateMipmap"); + context->GetBooleani_v = (PFNGLGETBOOLEANI_VPROC) load(userptr, "glGetBooleani_v"); + context->GetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC) load(userptr, "glGetFragDataLocation"); + context->GetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) load(userptr, "glGetFramebufferAttachmentParameteriv"); + context->GetIntegeri_v = (PFNGLGETINTEGERI_VPROC) load(userptr, "glGetIntegeri_v"); + context->GetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC) load(userptr, "glGetRenderbufferParameteriv"); + context->GetStringi = (PFNGLGETSTRINGIPROC) load(userptr, "glGetStringi"); + context->GetTexParameterIiv = (PFNGLGETTEXPARAMETERIIVPROC) load(userptr, "glGetTexParameterIiv"); + context->GetTexParameterIuiv = (PFNGLGETTEXPARAMETERIUIVPROC) load(userptr, "glGetTexParameterIuiv"); + context->GetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) load(userptr, "glGetTransformFeedbackVarying"); + context->GetUniformuiv = (PFNGLGETUNIFORMUIVPROC) load(userptr, "glGetUniformuiv"); + context->GetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC) load(userptr, "glGetVertexAttribIiv"); + context->GetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC) load(userptr, "glGetVertexAttribIuiv"); + context->IsEnabledi = (PFNGLISENABLEDIPROC) load(userptr, "glIsEnabledi"); + context->IsFramebuffer = (PFNGLISFRAMEBUFFERPROC) load(userptr, "glIsFramebuffer"); + context->IsRenderbuffer = (PFNGLISRENDERBUFFERPROC) load(userptr, "glIsRenderbuffer"); + context->IsVertexArray = (PFNGLISVERTEXARRAYPROC) load(userptr, "glIsVertexArray"); + context->MapBufferRange = (PFNGLMAPBUFFERRANGEPROC) load(userptr, "glMapBufferRange"); + context->RenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC) load(userptr, "glRenderbufferStorage"); + context->RenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) load(userptr, "glRenderbufferStorageMultisample"); + context->TexParameterIiv = (PFNGLTEXPARAMETERIIVPROC) load(userptr, "glTexParameterIiv"); + context->TexParameterIuiv = (PFNGLTEXPARAMETERIUIVPROC) load(userptr, "glTexParameterIuiv"); + context->TransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC) load(userptr, "glTransformFeedbackVaryings"); + context->Uniform1ui = (PFNGLUNIFORM1UIPROC) load(userptr, "glUniform1ui"); + context->Uniform1uiv = (PFNGLUNIFORM1UIVPROC) load(userptr, "glUniform1uiv"); + context->Uniform2ui = (PFNGLUNIFORM2UIPROC) load(userptr, "glUniform2ui"); + context->Uniform2uiv = (PFNGLUNIFORM2UIVPROC) load(userptr, "glUniform2uiv"); + context->Uniform3ui = (PFNGLUNIFORM3UIPROC) load(userptr, "glUniform3ui"); + context->Uniform3uiv = (PFNGLUNIFORM3UIVPROC) load(userptr, "glUniform3uiv"); + context->Uniform4ui = (PFNGLUNIFORM4UIPROC) load(userptr, "glUniform4ui"); + context->Uniform4uiv = (PFNGLUNIFORM4UIVPROC) load(userptr, "glUniform4uiv"); + context->VertexAttribI1i = (PFNGLVERTEXATTRIBI1IPROC) load(userptr, "glVertexAttribI1i"); + context->VertexAttribI1iv = (PFNGLVERTEXATTRIBI1IVPROC) load(userptr, "glVertexAttribI1iv"); + context->VertexAttribI1ui = (PFNGLVERTEXATTRIBI1UIPROC) load(userptr, "glVertexAttribI1ui"); + context->VertexAttribI1uiv = (PFNGLVERTEXATTRIBI1UIVPROC) load(userptr, "glVertexAttribI1uiv"); + context->VertexAttribI2i = (PFNGLVERTEXATTRIBI2IPROC) load(userptr, "glVertexAttribI2i"); + context->VertexAttribI2iv = (PFNGLVERTEXATTRIBI2IVPROC) load(userptr, "glVertexAttribI2iv"); + context->VertexAttribI2ui = (PFNGLVERTEXATTRIBI2UIPROC) load(userptr, "glVertexAttribI2ui"); + context->VertexAttribI2uiv = (PFNGLVERTEXATTRIBI2UIVPROC) load(userptr, "glVertexAttribI2uiv"); + context->VertexAttribI3i = (PFNGLVERTEXATTRIBI3IPROC) load(userptr, "glVertexAttribI3i"); + context->VertexAttribI3iv = (PFNGLVERTEXATTRIBI3IVPROC) load(userptr, "glVertexAttribI3iv"); + context->VertexAttribI3ui = (PFNGLVERTEXATTRIBI3UIPROC) load(userptr, "glVertexAttribI3ui"); + context->VertexAttribI3uiv = (PFNGLVERTEXATTRIBI3UIVPROC) load(userptr, "glVertexAttribI3uiv"); + context->VertexAttribI4bv = (PFNGLVERTEXATTRIBI4BVPROC) load(userptr, "glVertexAttribI4bv"); + context->VertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC) load(userptr, "glVertexAttribI4i"); + context->VertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC) load(userptr, "glVertexAttribI4iv"); + context->VertexAttribI4sv = (PFNGLVERTEXATTRIBI4SVPROC) load(userptr, "glVertexAttribI4sv"); + context->VertexAttribI4ubv = (PFNGLVERTEXATTRIBI4UBVPROC) load(userptr, "glVertexAttribI4ubv"); + context->VertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC) load(userptr, "glVertexAttribI4ui"); + context->VertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC) load(userptr, "glVertexAttribI4uiv"); + context->VertexAttribI4usv = (PFNGLVERTEXATTRIBI4USVPROC) load(userptr, "glVertexAttribI4usv"); + context->VertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC) load(userptr, "glVertexAttribIPointer"); +} +static void glad_gl_load_GL_VERSION_3_1(GladGLContext *context, GLADuserptrloadfunc load, void* userptr) { + if(!context->VERSION_3_1) return; + context->BindBufferBase = (PFNGLBINDBUFFERBASEPROC) load(userptr, "glBindBufferBase"); + context->BindBufferRange = (PFNGLBINDBUFFERRANGEPROC) load(userptr, "glBindBufferRange"); + context->CopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC) load(userptr, "glCopyBufferSubData"); + context->DrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC) load(userptr, "glDrawArraysInstanced"); + context->DrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC) load(userptr, "glDrawElementsInstanced"); + context->GetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) load(userptr, "glGetActiveUniformBlockName"); + context->GetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC) load(userptr, "glGetActiveUniformBlockiv"); + context->GetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC) load(userptr, "glGetActiveUniformName"); + context->GetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC) load(userptr, "glGetActiveUniformsiv"); + context->GetIntegeri_v = (PFNGLGETINTEGERI_VPROC) load(userptr, "glGetIntegeri_v"); + context->GetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC) load(userptr, "glGetUniformBlockIndex"); + context->GetUniformIndices = (PFNGLGETUNIFORMINDICESPROC) load(userptr, "glGetUniformIndices"); + context->PrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC) load(userptr, "glPrimitiveRestartIndex"); + context->TexBuffer = (PFNGLTEXBUFFERPROC) load(userptr, "glTexBuffer"); + context->UniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC) load(userptr, "glUniformBlockBinding"); +} +static void glad_gl_load_GL_VERSION_3_2(GladGLContext *context, GLADuserptrloadfunc load, void* userptr) { + if(!context->VERSION_3_2) return; + context->ClientWaitSync = (PFNGLCLIENTWAITSYNCPROC) load(userptr, "glClientWaitSync"); + context->DeleteSync = (PFNGLDELETESYNCPROC) load(userptr, "glDeleteSync"); + context->DrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC) load(userptr, "glDrawElementsBaseVertex"); + context->DrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) load(userptr, "glDrawElementsInstancedBaseVertex"); + context->DrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) load(userptr, "glDrawRangeElementsBaseVertex"); + context->FenceSync = (PFNGLFENCESYNCPROC) load(userptr, "glFenceSync"); + context->FramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREPROC) load(userptr, "glFramebufferTexture"); + context->GetBufferParameteri64v = (PFNGLGETBUFFERPARAMETERI64VPROC) load(userptr, "glGetBufferParameteri64v"); + context->GetInteger64i_v = (PFNGLGETINTEGER64I_VPROC) load(userptr, "glGetInteger64i_v"); + context->GetInteger64v = (PFNGLGETINTEGER64VPROC) load(userptr, "glGetInteger64v"); + context->GetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC) load(userptr, "glGetMultisamplefv"); + context->GetSynciv = (PFNGLGETSYNCIVPROC) load(userptr, "glGetSynciv"); + context->IsSync = (PFNGLISSYNCPROC) load(userptr, "glIsSync"); + context->MultiDrawElementsBaseVertex = (PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) load(userptr, "glMultiDrawElementsBaseVertex"); + context->ProvokingVertex = (PFNGLPROVOKINGVERTEXPROC) load(userptr, "glProvokingVertex"); + context->SampleMaski = (PFNGLSAMPLEMASKIPROC) load(userptr, "glSampleMaski"); + context->TexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC) load(userptr, "glTexImage2DMultisample"); + context->TexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC) load(userptr, "glTexImage3DMultisample"); + context->WaitSync = (PFNGLWAITSYNCPROC) load(userptr, "glWaitSync"); +} +static void glad_gl_load_GL_VERSION_3_3(GladGLContext *context, GLADuserptrloadfunc load, void* userptr) { + if(!context->VERSION_3_3) return; + context->BindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) load(userptr, "glBindFragDataLocationIndexed"); + context->BindSampler = (PFNGLBINDSAMPLERPROC) load(userptr, "glBindSampler"); + context->ColorP3ui = (PFNGLCOLORP3UIPROC) load(userptr, "glColorP3ui"); + context->ColorP3uiv = (PFNGLCOLORP3UIVPROC) load(userptr, "glColorP3uiv"); + context->ColorP4ui = (PFNGLCOLORP4UIPROC) load(userptr, "glColorP4ui"); + context->ColorP4uiv = (PFNGLCOLORP4UIVPROC) load(userptr, "glColorP4uiv"); + context->DeleteSamplers = (PFNGLDELETESAMPLERSPROC) load(userptr, "glDeleteSamplers"); + context->GenSamplers = (PFNGLGENSAMPLERSPROC) load(userptr, "glGenSamplers"); + context->GetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC) load(userptr, "glGetFragDataIndex"); + context->GetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC) load(userptr, "glGetQueryObjecti64v"); + context->GetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC) load(userptr, "glGetQueryObjectui64v"); + context->GetSamplerParameterIiv = (PFNGLGETSAMPLERPARAMETERIIVPROC) load(userptr, "glGetSamplerParameterIiv"); + context->GetSamplerParameterIuiv = (PFNGLGETSAMPLERPARAMETERIUIVPROC) load(userptr, "glGetSamplerParameterIuiv"); + context->GetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC) load(userptr, "glGetSamplerParameterfv"); + context->GetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC) load(userptr, "glGetSamplerParameteriv"); + context->IsSampler = (PFNGLISSAMPLERPROC) load(userptr, "glIsSampler"); + context->MultiTexCoordP1ui = (PFNGLMULTITEXCOORDP1UIPROC) load(userptr, "glMultiTexCoordP1ui"); + context->MultiTexCoordP1uiv = (PFNGLMULTITEXCOORDP1UIVPROC) load(userptr, "glMultiTexCoordP1uiv"); + context->MultiTexCoordP2ui = (PFNGLMULTITEXCOORDP2UIPROC) load(userptr, "glMultiTexCoordP2ui"); + context->MultiTexCoordP2uiv = (PFNGLMULTITEXCOORDP2UIVPROC) load(userptr, "glMultiTexCoordP2uiv"); + context->MultiTexCoordP3ui = (PFNGLMULTITEXCOORDP3UIPROC) load(userptr, "glMultiTexCoordP3ui"); + context->MultiTexCoordP3uiv = (PFNGLMULTITEXCOORDP3UIVPROC) load(userptr, "glMultiTexCoordP3uiv"); + context->MultiTexCoordP4ui = (PFNGLMULTITEXCOORDP4UIPROC) load(userptr, "glMultiTexCoordP4ui"); + context->MultiTexCoordP4uiv = (PFNGLMULTITEXCOORDP4UIVPROC) load(userptr, "glMultiTexCoordP4uiv"); + context->NormalP3ui = (PFNGLNORMALP3UIPROC) load(userptr, "glNormalP3ui"); + context->NormalP3uiv = (PFNGLNORMALP3UIVPROC) load(userptr, "glNormalP3uiv"); + context->QueryCounter = (PFNGLQUERYCOUNTERPROC) load(userptr, "glQueryCounter"); + context->SamplerParameterIiv = (PFNGLSAMPLERPARAMETERIIVPROC) load(userptr, "glSamplerParameterIiv"); + context->SamplerParameterIuiv = (PFNGLSAMPLERPARAMETERIUIVPROC) load(userptr, "glSamplerParameterIuiv"); + context->SamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC) load(userptr, "glSamplerParameterf"); + context->SamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC) load(userptr, "glSamplerParameterfv"); + context->SamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC) load(userptr, "glSamplerParameteri"); + context->SamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC) load(userptr, "glSamplerParameteriv"); + context->SecondaryColorP3ui = (PFNGLSECONDARYCOLORP3UIPROC) load(userptr, "glSecondaryColorP3ui"); + context->SecondaryColorP3uiv = (PFNGLSECONDARYCOLORP3UIVPROC) load(userptr, "glSecondaryColorP3uiv"); + context->TexCoordP1ui = (PFNGLTEXCOORDP1UIPROC) load(userptr, "glTexCoordP1ui"); + context->TexCoordP1uiv = (PFNGLTEXCOORDP1UIVPROC) load(userptr, "glTexCoordP1uiv"); + context->TexCoordP2ui = (PFNGLTEXCOORDP2UIPROC) load(userptr, "glTexCoordP2ui"); + context->TexCoordP2uiv = (PFNGLTEXCOORDP2UIVPROC) load(userptr, "glTexCoordP2uiv"); + context->TexCoordP3ui = (PFNGLTEXCOORDP3UIPROC) load(userptr, "glTexCoordP3ui"); + context->TexCoordP3uiv = (PFNGLTEXCOORDP3UIVPROC) load(userptr, "glTexCoordP3uiv"); + context->TexCoordP4ui = (PFNGLTEXCOORDP4UIPROC) load(userptr, "glTexCoordP4ui"); + context->TexCoordP4uiv = (PFNGLTEXCOORDP4UIVPROC) load(userptr, "glTexCoordP4uiv"); + context->VertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC) load(userptr, "glVertexAttribDivisor"); + context->VertexAttribP1ui = (PFNGLVERTEXATTRIBP1UIPROC) load(userptr, "glVertexAttribP1ui"); + context->VertexAttribP1uiv = (PFNGLVERTEXATTRIBP1UIVPROC) load(userptr, "glVertexAttribP1uiv"); + context->VertexAttribP2ui = (PFNGLVERTEXATTRIBP2UIPROC) load(userptr, "glVertexAttribP2ui"); + context->VertexAttribP2uiv = (PFNGLVERTEXATTRIBP2UIVPROC) load(userptr, "glVertexAttribP2uiv"); + context->VertexAttribP3ui = (PFNGLVERTEXATTRIBP3UIPROC) load(userptr, "glVertexAttribP3ui"); + context->VertexAttribP3uiv = (PFNGLVERTEXATTRIBP3UIVPROC) load(userptr, "glVertexAttribP3uiv"); + context->VertexAttribP4ui = (PFNGLVERTEXATTRIBP4UIPROC) load(userptr, "glVertexAttribP4ui"); + context->VertexAttribP4uiv = (PFNGLVERTEXATTRIBP4UIVPROC) load(userptr, "glVertexAttribP4uiv"); + context->VertexP2ui = (PFNGLVERTEXP2UIPROC) load(userptr, "glVertexP2ui"); + context->VertexP2uiv = (PFNGLVERTEXP2UIVPROC) load(userptr, "glVertexP2uiv"); + context->VertexP3ui = (PFNGLVERTEXP3UIPROC) load(userptr, "glVertexP3ui"); + context->VertexP3uiv = (PFNGLVERTEXP3UIVPROC) load(userptr, "glVertexP3uiv"); + context->VertexP4ui = (PFNGLVERTEXP4UIPROC) load(userptr, "glVertexP4ui"); + context->VertexP4uiv = (PFNGLVERTEXP4UIVPROC) load(userptr, "glVertexP4uiv"); +} +static void glad_gl_load_GL_VERSION_4_0(GladGLContext *context, GLADuserptrloadfunc load, void* userptr) { + if(!context->VERSION_4_0) return; + context->BeginQueryIndexed = (PFNGLBEGINQUERYINDEXEDPROC) load(userptr, "glBeginQueryIndexed"); + context->BindTransformFeedback = (PFNGLBINDTRANSFORMFEEDBACKPROC) load(userptr, "glBindTransformFeedback"); + context->BlendEquationSeparatei = (PFNGLBLENDEQUATIONSEPARATEIPROC) load(userptr, "glBlendEquationSeparatei"); + context->BlendEquationi = (PFNGLBLENDEQUATIONIPROC) load(userptr, "glBlendEquationi"); + context->BlendFuncSeparatei = (PFNGLBLENDFUNCSEPARATEIPROC) load(userptr, "glBlendFuncSeparatei"); + context->BlendFunci = (PFNGLBLENDFUNCIPROC) load(userptr, "glBlendFunci"); + context->DeleteTransformFeedbacks = (PFNGLDELETETRANSFORMFEEDBACKSPROC) load(userptr, "glDeleteTransformFeedbacks"); + context->DrawArraysIndirect = (PFNGLDRAWARRAYSINDIRECTPROC) load(userptr, "glDrawArraysIndirect"); + context->DrawElementsIndirect = (PFNGLDRAWELEMENTSINDIRECTPROC) load(userptr, "glDrawElementsIndirect"); + context->DrawTransformFeedback = (PFNGLDRAWTRANSFORMFEEDBACKPROC) load(userptr, "glDrawTransformFeedback"); + context->DrawTransformFeedbackStream = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC) load(userptr, "glDrawTransformFeedbackStream"); + context->EndQueryIndexed = (PFNGLENDQUERYINDEXEDPROC) load(userptr, "glEndQueryIndexed"); + context->GenTransformFeedbacks = (PFNGLGENTRANSFORMFEEDBACKSPROC) load(userptr, "glGenTransformFeedbacks"); + context->GetActiveSubroutineName = (PFNGLGETACTIVESUBROUTINENAMEPROC) load(userptr, "glGetActiveSubroutineName"); + context->GetActiveSubroutineUniformName = (PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC) load(userptr, "glGetActiveSubroutineUniformName"); + context->GetActiveSubroutineUniformiv = (PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC) load(userptr, "glGetActiveSubroutineUniformiv"); + context->GetProgramStageiv = (PFNGLGETPROGRAMSTAGEIVPROC) load(userptr, "glGetProgramStageiv"); + context->GetQueryIndexediv = (PFNGLGETQUERYINDEXEDIVPROC) load(userptr, "glGetQueryIndexediv"); + context->GetSubroutineIndex = (PFNGLGETSUBROUTINEINDEXPROC) load(userptr, "glGetSubroutineIndex"); + context->GetSubroutineUniformLocation = (PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC) load(userptr, "glGetSubroutineUniformLocation"); + context->GetUniformSubroutineuiv = (PFNGLGETUNIFORMSUBROUTINEUIVPROC) load(userptr, "glGetUniformSubroutineuiv"); + context->GetUniformdv = (PFNGLGETUNIFORMDVPROC) load(userptr, "glGetUniformdv"); + context->IsTransformFeedback = (PFNGLISTRANSFORMFEEDBACKPROC) load(userptr, "glIsTransformFeedback"); + context->MinSampleShading = (PFNGLMINSAMPLESHADINGPROC) load(userptr, "glMinSampleShading"); + context->PatchParameterfv = (PFNGLPATCHPARAMETERFVPROC) load(userptr, "glPatchParameterfv"); + context->PatchParameteri = (PFNGLPATCHPARAMETERIPROC) load(userptr, "glPatchParameteri"); + context->PauseTransformFeedback = (PFNGLPAUSETRANSFORMFEEDBACKPROC) load(userptr, "glPauseTransformFeedback"); + context->ResumeTransformFeedback = (PFNGLRESUMETRANSFORMFEEDBACKPROC) load(userptr, "glResumeTransformFeedback"); + context->Uniform1d = (PFNGLUNIFORM1DPROC) load(userptr, "glUniform1d"); + context->Uniform1dv = (PFNGLUNIFORM1DVPROC) load(userptr, "glUniform1dv"); + context->Uniform2d = (PFNGLUNIFORM2DPROC) load(userptr, "glUniform2d"); + context->Uniform2dv = (PFNGLUNIFORM2DVPROC) load(userptr, "glUniform2dv"); + context->Uniform3d = (PFNGLUNIFORM3DPROC) load(userptr, "glUniform3d"); + context->Uniform3dv = (PFNGLUNIFORM3DVPROC) load(userptr, "glUniform3dv"); + context->Uniform4d = (PFNGLUNIFORM4DPROC) load(userptr, "glUniform4d"); + context->Uniform4dv = (PFNGLUNIFORM4DVPROC) load(userptr, "glUniform4dv"); + context->UniformMatrix2dv = (PFNGLUNIFORMMATRIX2DVPROC) load(userptr, "glUniformMatrix2dv"); + context->UniformMatrix2x3dv = (PFNGLUNIFORMMATRIX2X3DVPROC) load(userptr, "glUniformMatrix2x3dv"); + context->UniformMatrix2x4dv = (PFNGLUNIFORMMATRIX2X4DVPROC) load(userptr, "glUniformMatrix2x4dv"); + context->UniformMatrix3dv = (PFNGLUNIFORMMATRIX3DVPROC) load(userptr, "glUniformMatrix3dv"); + context->UniformMatrix3x2dv = (PFNGLUNIFORMMATRIX3X2DVPROC) load(userptr, "glUniformMatrix3x2dv"); + context->UniformMatrix3x4dv = (PFNGLUNIFORMMATRIX3X4DVPROC) load(userptr, "glUniformMatrix3x4dv"); + context->UniformMatrix4dv = (PFNGLUNIFORMMATRIX4DVPROC) load(userptr, "glUniformMatrix4dv"); + context->UniformMatrix4x2dv = (PFNGLUNIFORMMATRIX4X2DVPROC) load(userptr, "glUniformMatrix4x2dv"); + context->UniformMatrix4x3dv = (PFNGLUNIFORMMATRIX4X3DVPROC) load(userptr, "glUniformMatrix4x3dv"); + context->UniformSubroutinesuiv = (PFNGLUNIFORMSUBROUTINESUIVPROC) load(userptr, "glUniformSubroutinesuiv"); +} +static void glad_gl_load_GL_VERSION_4_1(GladGLContext *context, GLADuserptrloadfunc load, void* userptr) { + if(!context->VERSION_4_1) return; + context->ActiveShaderProgram = (PFNGLACTIVESHADERPROGRAMPROC) load(userptr, "glActiveShaderProgram"); + context->BindProgramPipeline = (PFNGLBINDPROGRAMPIPELINEPROC) load(userptr, "glBindProgramPipeline"); + context->ClearDepthf = (PFNGLCLEARDEPTHFPROC) load(userptr, "glClearDepthf"); + context->CreateShaderProgramv = (PFNGLCREATESHADERPROGRAMVPROC) load(userptr, "glCreateShaderProgramv"); + context->DeleteProgramPipelines = (PFNGLDELETEPROGRAMPIPELINESPROC) load(userptr, "glDeleteProgramPipelines"); + context->DepthRangeArrayv = (PFNGLDEPTHRANGEARRAYVPROC) load(userptr, "glDepthRangeArrayv"); + context->DepthRangeIndexed = (PFNGLDEPTHRANGEINDEXEDPROC) load(userptr, "glDepthRangeIndexed"); + context->DepthRangef = (PFNGLDEPTHRANGEFPROC) load(userptr, "glDepthRangef"); + context->GenProgramPipelines = (PFNGLGENPROGRAMPIPELINESPROC) load(userptr, "glGenProgramPipelines"); + context->GetDoublei_v = (PFNGLGETDOUBLEI_VPROC) load(userptr, "glGetDoublei_v"); + context->GetFloati_v = (PFNGLGETFLOATI_VPROC) load(userptr, "glGetFloati_v"); + context->GetProgramBinary = (PFNGLGETPROGRAMBINARYPROC) load(userptr, "glGetProgramBinary"); + context->GetProgramPipelineInfoLog = (PFNGLGETPROGRAMPIPELINEINFOLOGPROC) load(userptr, "glGetProgramPipelineInfoLog"); + context->GetProgramPipelineiv = (PFNGLGETPROGRAMPIPELINEIVPROC) load(userptr, "glGetProgramPipelineiv"); + context->GetShaderPrecisionFormat = (PFNGLGETSHADERPRECISIONFORMATPROC) load(userptr, "glGetShaderPrecisionFormat"); + context->GetVertexAttribLdv = (PFNGLGETVERTEXATTRIBLDVPROC) load(userptr, "glGetVertexAttribLdv"); + context->IsProgramPipeline = (PFNGLISPROGRAMPIPELINEPROC) load(userptr, "glIsProgramPipeline"); + context->ProgramBinary = (PFNGLPROGRAMBINARYPROC) load(userptr, "glProgramBinary"); + context->ProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC) load(userptr, "glProgramParameteri"); + context->ProgramUniform1d = (PFNGLPROGRAMUNIFORM1DPROC) load(userptr, "glProgramUniform1d"); + context->ProgramUniform1dv = (PFNGLPROGRAMUNIFORM1DVPROC) load(userptr, "glProgramUniform1dv"); + context->ProgramUniform1f = (PFNGLPROGRAMUNIFORM1FPROC) load(userptr, "glProgramUniform1f"); + context->ProgramUniform1fv = (PFNGLPROGRAMUNIFORM1FVPROC) load(userptr, "glProgramUniform1fv"); + context->ProgramUniform1i = (PFNGLPROGRAMUNIFORM1IPROC) load(userptr, "glProgramUniform1i"); + context->ProgramUniform1iv = (PFNGLPROGRAMUNIFORM1IVPROC) load(userptr, "glProgramUniform1iv"); + context->ProgramUniform1ui = (PFNGLPROGRAMUNIFORM1UIPROC) load(userptr, "glProgramUniform1ui"); + context->ProgramUniform1uiv = (PFNGLPROGRAMUNIFORM1UIVPROC) load(userptr, "glProgramUniform1uiv"); + context->ProgramUniform2d = (PFNGLPROGRAMUNIFORM2DPROC) load(userptr, "glProgramUniform2d"); + context->ProgramUniform2dv = (PFNGLPROGRAMUNIFORM2DVPROC) load(userptr, "glProgramUniform2dv"); + context->ProgramUniform2f = (PFNGLPROGRAMUNIFORM2FPROC) load(userptr, "glProgramUniform2f"); + context->ProgramUniform2fv = (PFNGLPROGRAMUNIFORM2FVPROC) load(userptr, "glProgramUniform2fv"); + context->ProgramUniform2i = (PFNGLPROGRAMUNIFORM2IPROC) load(userptr, "glProgramUniform2i"); + context->ProgramUniform2iv = (PFNGLPROGRAMUNIFORM2IVPROC) load(userptr, "glProgramUniform2iv"); + context->ProgramUniform2ui = (PFNGLPROGRAMUNIFORM2UIPROC) load(userptr, "glProgramUniform2ui"); + context->ProgramUniform2uiv = (PFNGLPROGRAMUNIFORM2UIVPROC) load(userptr, "glProgramUniform2uiv"); + context->ProgramUniform3d = (PFNGLPROGRAMUNIFORM3DPROC) load(userptr, "glProgramUniform3d"); + context->ProgramUniform3dv = (PFNGLPROGRAMUNIFORM3DVPROC) load(userptr, "glProgramUniform3dv"); + context->ProgramUniform3f = (PFNGLPROGRAMUNIFORM3FPROC) load(userptr, "glProgramUniform3f"); + context->ProgramUniform3fv = (PFNGLPROGRAMUNIFORM3FVPROC) load(userptr, "glProgramUniform3fv"); + context->ProgramUniform3i = (PFNGLPROGRAMUNIFORM3IPROC) load(userptr, "glProgramUniform3i"); + context->ProgramUniform3iv = (PFNGLPROGRAMUNIFORM3IVPROC) load(userptr, "glProgramUniform3iv"); + context->ProgramUniform3ui = (PFNGLPROGRAMUNIFORM3UIPROC) load(userptr, "glProgramUniform3ui"); + context->ProgramUniform3uiv = (PFNGLPROGRAMUNIFORM3UIVPROC) load(userptr, "glProgramUniform3uiv"); + context->ProgramUniform4d = (PFNGLPROGRAMUNIFORM4DPROC) load(userptr, "glProgramUniform4d"); + context->ProgramUniform4dv = (PFNGLPROGRAMUNIFORM4DVPROC) load(userptr, "glProgramUniform4dv"); + context->ProgramUniform4f = (PFNGLPROGRAMUNIFORM4FPROC) load(userptr, "glProgramUniform4f"); + context->ProgramUniform4fv = (PFNGLPROGRAMUNIFORM4FVPROC) load(userptr, "glProgramUniform4fv"); + context->ProgramUniform4i = (PFNGLPROGRAMUNIFORM4IPROC) load(userptr, "glProgramUniform4i"); + context->ProgramUniform4iv = (PFNGLPROGRAMUNIFORM4IVPROC) load(userptr, "glProgramUniform4iv"); + context->ProgramUniform4ui = (PFNGLPROGRAMUNIFORM4UIPROC) load(userptr, "glProgramUniform4ui"); + context->ProgramUniform4uiv = (PFNGLPROGRAMUNIFORM4UIVPROC) load(userptr, "glProgramUniform4uiv"); + context->ProgramUniformMatrix2dv = (PFNGLPROGRAMUNIFORMMATRIX2DVPROC) load(userptr, "glProgramUniformMatrix2dv"); + context->ProgramUniformMatrix2fv = (PFNGLPROGRAMUNIFORMMATRIX2FVPROC) load(userptr, "glProgramUniformMatrix2fv"); + context->ProgramUniformMatrix2x3dv = (PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC) load(userptr, "glProgramUniformMatrix2x3dv"); + context->ProgramUniformMatrix2x3fv = (PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC) load(userptr, "glProgramUniformMatrix2x3fv"); + context->ProgramUniformMatrix2x4dv = (PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC) load(userptr, "glProgramUniformMatrix2x4dv"); + context->ProgramUniformMatrix2x4fv = (PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC) load(userptr, "glProgramUniformMatrix2x4fv"); + context->ProgramUniformMatrix3dv = (PFNGLPROGRAMUNIFORMMATRIX3DVPROC) load(userptr, "glProgramUniformMatrix3dv"); + context->ProgramUniformMatrix3fv = (PFNGLPROGRAMUNIFORMMATRIX3FVPROC) load(userptr, "glProgramUniformMatrix3fv"); + context->ProgramUniformMatrix3x2dv = (PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC) load(userptr, "glProgramUniformMatrix3x2dv"); + context->ProgramUniformMatrix3x2fv = (PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC) load(userptr, "glProgramUniformMatrix3x2fv"); + context->ProgramUniformMatrix3x4dv = (PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC) load(userptr, "glProgramUniformMatrix3x4dv"); + context->ProgramUniformMatrix3x4fv = (PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC) load(userptr, "glProgramUniformMatrix3x4fv"); + context->ProgramUniformMatrix4dv = (PFNGLPROGRAMUNIFORMMATRIX4DVPROC) load(userptr, "glProgramUniformMatrix4dv"); + context->ProgramUniformMatrix4fv = (PFNGLPROGRAMUNIFORMMATRIX4FVPROC) load(userptr, "glProgramUniformMatrix4fv"); + context->ProgramUniformMatrix4x2dv = (PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC) load(userptr, "glProgramUniformMatrix4x2dv"); + context->ProgramUniformMatrix4x2fv = (PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC) load(userptr, "glProgramUniformMatrix4x2fv"); + context->ProgramUniformMatrix4x3dv = (PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC) load(userptr, "glProgramUniformMatrix4x3dv"); + context->ProgramUniformMatrix4x3fv = (PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC) load(userptr, "glProgramUniformMatrix4x3fv"); + context->ReleaseShaderCompiler = (PFNGLRELEASESHADERCOMPILERPROC) load(userptr, "glReleaseShaderCompiler"); + context->ScissorArrayv = (PFNGLSCISSORARRAYVPROC) load(userptr, "glScissorArrayv"); + context->ScissorIndexed = (PFNGLSCISSORINDEXEDPROC) load(userptr, "glScissorIndexed"); + context->ScissorIndexedv = (PFNGLSCISSORINDEXEDVPROC) load(userptr, "glScissorIndexedv"); + context->ShaderBinary = (PFNGLSHADERBINARYPROC) load(userptr, "glShaderBinary"); + context->UseProgramStages = (PFNGLUSEPROGRAMSTAGESPROC) load(userptr, "glUseProgramStages"); + context->ValidateProgramPipeline = (PFNGLVALIDATEPROGRAMPIPELINEPROC) load(userptr, "glValidateProgramPipeline"); + context->VertexAttribL1d = (PFNGLVERTEXATTRIBL1DPROC) load(userptr, "glVertexAttribL1d"); + context->VertexAttribL1dv = (PFNGLVERTEXATTRIBL1DVPROC) load(userptr, "glVertexAttribL1dv"); + context->VertexAttribL2d = (PFNGLVERTEXATTRIBL2DPROC) load(userptr, "glVertexAttribL2d"); + context->VertexAttribL2dv = (PFNGLVERTEXATTRIBL2DVPROC) load(userptr, "glVertexAttribL2dv"); + context->VertexAttribL3d = (PFNGLVERTEXATTRIBL3DPROC) load(userptr, "glVertexAttribL3d"); + context->VertexAttribL3dv = (PFNGLVERTEXATTRIBL3DVPROC) load(userptr, "glVertexAttribL3dv"); + context->VertexAttribL4d = (PFNGLVERTEXATTRIBL4DPROC) load(userptr, "glVertexAttribL4d"); + context->VertexAttribL4dv = (PFNGLVERTEXATTRIBL4DVPROC) load(userptr, "glVertexAttribL4dv"); + context->VertexAttribLPointer = (PFNGLVERTEXATTRIBLPOINTERPROC) load(userptr, "glVertexAttribLPointer"); + context->ViewportArrayv = (PFNGLVIEWPORTARRAYVPROC) load(userptr, "glViewportArrayv"); + context->ViewportIndexedf = (PFNGLVIEWPORTINDEXEDFPROC) load(userptr, "glViewportIndexedf"); + context->ViewportIndexedfv = (PFNGLVIEWPORTINDEXEDFVPROC) load(userptr, "glViewportIndexedfv"); +} +static void glad_gl_load_GL_VERSION_4_2(GladGLContext *context, GLADuserptrloadfunc load, void* userptr) { + if(!context->VERSION_4_2) return; + context->BindImageTexture = (PFNGLBINDIMAGETEXTUREPROC) load(userptr, "glBindImageTexture"); + context->DrawArraysInstancedBaseInstance = (PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC) load(userptr, "glDrawArraysInstancedBaseInstance"); + context->DrawElementsInstancedBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC) load(userptr, "glDrawElementsInstancedBaseInstance"); + context->DrawElementsInstancedBaseVertexBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC) load(userptr, "glDrawElementsInstancedBaseVertexBaseInstance"); + context->DrawTransformFeedbackInstanced = (PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC) load(userptr, "glDrawTransformFeedbackInstanced"); + context->DrawTransformFeedbackStreamInstanced = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC) load(userptr, "glDrawTransformFeedbackStreamInstanced"); + context->GetActiveAtomicCounterBufferiv = (PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC) load(userptr, "glGetActiveAtomicCounterBufferiv"); + context->GetInternalformativ = (PFNGLGETINTERNALFORMATIVPROC) load(userptr, "glGetInternalformativ"); + context->MemoryBarrier = (PFNGLMEMORYBARRIERPROC) load(userptr, "glMemoryBarrier"); + context->TexStorage1D = (PFNGLTEXSTORAGE1DPROC) load(userptr, "glTexStorage1D"); + context->TexStorage2D = (PFNGLTEXSTORAGE2DPROC) load(userptr, "glTexStorage2D"); + context->TexStorage3D = (PFNGLTEXSTORAGE3DPROC) load(userptr, "glTexStorage3D"); +} +static void glad_gl_load_GL_VERSION_4_3(GladGLContext *context, GLADuserptrloadfunc load, void* userptr) { + if(!context->VERSION_4_3) return; + context->BindVertexBuffer = (PFNGLBINDVERTEXBUFFERPROC) load(userptr, "glBindVertexBuffer"); + context->ClearBufferData = (PFNGLCLEARBUFFERDATAPROC) load(userptr, "glClearBufferData"); + context->ClearBufferSubData = (PFNGLCLEARBUFFERSUBDATAPROC) load(userptr, "glClearBufferSubData"); + context->CopyImageSubData = (PFNGLCOPYIMAGESUBDATAPROC) load(userptr, "glCopyImageSubData"); + context->DebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC) load(userptr, "glDebugMessageCallback"); + context->DebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC) load(userptr, "glDebugMessageControl"); + context->DebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC) load(userptr, "glDebugMessageInsert"); + context->DispatchCompute = (PFNGLDISPATCHCOMPUTEPROC) load(userptr, "glDispatchCompute"); + context->DispatchComputeIndirect = (PFNGLDISPATCHCOMPUTEINDIRECTPROC) load(userptr, "glDispatchComputeIndirect"); + context->FramebufferParameteri = (PFNGLFRAMEBUFFERPARAMETERIPROC) load(userptr, "glFramebufferParameteri"); + context->GetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC) load(userptr, "glGetDebugMessageLog"); + context->GetFramebufferParameteriv = (PFNGLGETFRAMEBUFFERPARAMETERIVPROC) load(userptr, "glGetFramebufferParameteriv"); + context->GetInternalformati64v = (PFNGLGETINTERNALFORMATI64VPROC) load(userptr, "glGetInternalformati64v"); + context->GetObjectLabel = (PFNGLGETOBJECTLABELPROC) load(userptr, "glGetObjectLabel"); + context->GetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC) load(userptr, "glGetObjectPtrLabel"); + context->GetPointerv = (PFNGLGETPOINTERVPROC) load(userptr, "glGetPointerv"); + context->GetProgramInterfaceiv = (PFNGLGETPROGRAMINTERFACEIVPROC) load(userptr, "glGetProgramInterfaceiv"); + context->GetProgramResourceIndex = (PFNGLGETPROGRAMRESOURCEINDEXPROC) load(userptr, "glGetProgramResourceIndex"); + context->GetProgramResourceLocation = (PFNGLGETPROGRAMRESOURCELOCATIONPROC) load(userptr, "glGetProgramResourceLocation"); + context->GetProgramResourceLocationIndex = (PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC) load(userptr, "glGetProgramResourceLocationIndex"); + context->GetProgramResourceName = (PFNGLGETPROGRAMRESOURCENAMEPROC) load(userptr, "glGetProgramResourceName"); + context->GetProgramResourceiv = (PFNGLGETPROGRAMRESOURCEIVPROC) load(userptr, "glGetProgramResourceiv"); + context->InvalidateBufferData = (PFNGLINVALIDATEBUFFERDATAPROC) load(userptr, "glInvalidateBufferData"); + context->InvalidateBufferSubData = (PFNGLINVALIDATEBUFFERSUBDATAPROC) load(userptr, "glInvalidateBufferSubData"); + context->InvalidateFramebuffer = (PFNGLINVALIDATEFRAMEBUFFERPROC) load(userptr, "glInvalidateFramebuffer"); + context->InvalidateSubFramebuffer = (PFNGLINVALIDATESUBFRAMEBUFFERPROC) load(userptr, "glInvalidateSubFramebuffer"); + context->InvalidateTexImage = (PFNGLINVALIDATETEXIMAGEPROC) load(userptr, "glInvalidateTexImage"); + context->InvalidateTexSubImage = (PFNGLINVALIDATETEXSUBIMAGEPROC) load(userptr, "glInvalidateTexSubImage"); + context->MultiDrawArraysIndirect = (PFNGLMULTIDRAWARRAYSINDIRECTPROC) load(userptr, "glMultiDrawArraysIndirect"); + context->MultiDrawElementsIndirect = (PFNGLMULTIDRAWELEMENTSINDIRECTPROC) load(userptr, "glMultiDrawElementsIndirect"); + context->ObjectLabel = (PFNGLOBJECTLABELPROC) load(userptr, "glObjectLabel"); + context->ObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC) load(userptr, "glObjectPtrLabel"); + context->PopDebugGroup = (PFNGLPOPDEBUGGROUPPROC) load(userptr, "glPopDebugGroup"); + context->PushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC) load(userptr, "glPushDebugGroup"); + context->ShaderStorageBlockBinding = (PFNGLSHADERSTORAGEBLOCKBINDINGPROC) load(userptr, "glShaderStorageBlockBinding"); + context->TexBufferRange = (PFNGLTEXBUFFERRANGEPROC) load(userptr, "glTexBufferRange"); + context->TexStorage2DMultisample = (PFNGLTEXSTORAGE2DMULTISAMPLEPROC) load(userptr, "glTexStorage2DMultisample"); + context->TexStorage3DMultisample = (PFNGLTEXSTORAGE3DMULTISAMPLEPROC) load(userptr, "glTexStorage3DMultisample"); + context->TextureView = (PFNGLTEXTUREVIEWPROC) load(userptr, "glTextureView"); + context->VertexAttribBinding = (PFNGLVERTEXATTRIBBINDINGPROC) load(userptr, "glVertexAttribBinding"); + context->VertexAttribFormat = (PFNGLVERTEXATTRIBFORMATPROC) load(userptr, "glVertexAttribFormat"); + context->VertexAttribIFormat = (PFNGLVERTEXATTRIBIFORMATPROC) load(userptr, "glVertexAttribIFormat"); + context->VertexAttribLFormat = (PFNGLVERTEXATTRIBLFORMATPROC) load(userptr, "glVertexAttribLFormat"); + context->VertexBindingDivisor = (PFNGLVERTEXBINDINGDIVISORPROC) load(userptr, "glVertexBindingDivisor"); +} +static void glad_gl_load_GL_VERSION_4_4(GladGLContext *context, GLADuserptrloadfunc load, void* userptr) { + if(!context->VERSION_4_4) return; + context->BindBuffersBase = (PFNGLBINDBUFFERSBASEPROC) load(userptr, "glBindBuffersBase"); + context->BindBuffersRange = (PFNGLBINDBUFFERSRANGEPROC) load(userptr, "glBindBuffersRange"); + context->BindImageTextures = (PFNGLBINDIMAGETEXTURESPROC) load(userptr, "glBindImageTextures"); + context->BindSamplers = (PFNGLBINDSAMPLERSPROC) load(userptr, "glBindSamplers"); + context->BindTextures = (PFNGLBINDTEXTURESPROC) load(userptr, "glBindTextures"); + context->BindVertexBuffers = (PFNGLBINDVERTEXBUFFERSPROC) load(userptr, "glBindVertexBuffers"); + context->BufferStorage = (PFNGLBUFFERSTORAGEPROC) load(userptr, "glBufferStorage"); + context->ClearTexImage = (PFNGLCLEARTEXIMAGEPROC) load(userptr, "glClearTexImage"); + context->ClearTexSubImage = (PFNGLCLEARTEXSUBIMAGEPROC) load(userptr, "glClearTexSubImage"); +} +static void glad_gl_load_GL_VERSION_4_5(GladGLContext *context, GLADuserptrloadfunc load, void* userptr) { + if(!context->VERSION_4_5) return; + context->BindTextureUnit = (PFNGLBINDTEXTUREUNITPROC) load(userptr, "glBindTextureUnit"); + context->BlitNamedFramebuffer = (PFNGLBLITNAMEDFRAMEBUFFERPROC) load(userptr, "glBlitNamedFramebuffer"); + context->CheckNamedFramebufferStatus = (PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC) load(userptr, "glCheckNamedFramebufferStatus"); + context->ClearNamedBufferData = (PFNGLCLEARNAMEDBUFFERDATAPROC) load(userptr, "glClearNamedBufferData"); + context->ClearNamedBufferSubData = (PFNGLCLEARNAMEDBUFFERSUBDATAPROC) load(userptr, "glClearNamedBufferSubData"); + context->ClearNamedFramebufferfi = (PFNGLCLEARNAMEDFRAMEBUFFERFIPROC) load(userptr, "glClearNamedFramebufferfi"); + context->ClearNamedFramebufferfv = (PFNGLCLEARNAMEDFRAMEBUFFERFVPROC) load(userptr, "glClearNamedFramebufferfv"); + context->ClearNamedFramebufferiv = (PFNGLCLEARNAMEDFRAMEBUFFERIVPROC) load(userptr, "glClearNamedFramebufferiv"); + context->ClearNamedFramebufferuiv = (PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC) load(userptr, "glClearNamedFramebufferuiv"); + context->ClipControl = (PFNGLCLIPCONTROLPROC) load(userptr, "glClipControl"); + context->CompressedTextureSubImage1D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC) load(userptr, "glCompressedTextureSubImage1D"); + context->CompressedTextureSubImage2D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC) load(userptr, "glCompressedTextureSubImage2D"); + context->CompressedTextureSubImage3D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC) load(userptr, "glCompressedTextureSubImage3D"); + context->CopyNamedBufferSubData = (PFNGLCOPYNAMEDBUFFERSUBDATAPROC) load(userptr, "glCopyNamedBufferSubData"); + context->CopyTextureSubImage1D = (PFNGLCOPYTEXTURESUBIMAGE1DPROC) load(userptr, "glCopyTextureSubImage1D"); + context->CopyTextureSubImage2D = (PFNGLCOPYTEXTURESUBIMAGE2DPROC) load(userptr, "glCopyTextureSubImage2D"); + context->CopyTextureSubImage3D = (PFNGLCOPYTEXTURESUBIMAGE3DPROC) load(userptr, "glCopyTextureSubImage3D"); + context->CreateBuffers = (PFNGLCREATEBUFFERSPROC) load(userptr, "glCreateBuffers"); + context->CreateFramebuffers = (PFNGLCREATEFRAMEBUFFERSPROC) load(userptr, "glCreateFramebuffers"); + context->CreateProgramPipelines = (PFNGLCREATEPROGRAMPIPELINESPROC) load(userptr, "glCreateProgramPipelines"); + context->CreateQueries = (PFNGLCREATEQUERIESPROC) load(userptr, "glCreateQueries"); + context->CreateRenderbuffers = (PFNGLCREATERENDERBUFFERSPROC) load(userptr, "glCreateRenderbuffers"); + context->CreateSamplers = (PFNGLCREATESAMPLERSPROC) load(userptr, "glCreateSamplers"); + context->CreateTextures = (PFNGLCREATETEXTURESPROC) load(userptr, "glCreateTextures"); + context->CreateTransformFeedbacks = (PFNGLCREATETRANSFORMFEEDBACKSPROC) load(userptr, "glCreateTransformFeedbacks"); + context->CreateVertexArrays = (PFNGLCREATEVERTEXARRAYSPROC) load(userptr, "glCreateVertexArrays"); + context->DisableVertexArrayAttrib = (PFNGLDISABLEVERTEXARRAYATTRIBPROC) load(userptr, "glDisableVertexArrayAttrib"); + context->EnableVertexArrayAttrib = (PFNGLENABLEVERTEXARRAYATTRIBPROC) load(userptr, "glEnableVertexArrayAttrib"); + context->FlushMappedNamedBufferRange = (PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC) load(userptr, "glFlushMappedNamedBufferRange"); + context->GenerateTextureMipmap = (PFNGLGENERATETEXTUREMIPMAPPROC) load(userptr, "glGenerateTextureMipmap"); + context->GetCompressedTextureImage = (PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC) load(userptr, "glGetCompressedTextureImage"); + context->GetCompressedTextureSubImage = (PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC) load(userptr, "glGetCompressedTextureSubImage"); + context->GetGraphicsResetStatus = (PFNGLGETGRAPHICSRESETSTATUSPROC) load(userptr, "glGetGraphicsResetStatus"); + context->GetNamedBufferParameteri64v = (PFNGLGETNAMEDBUFFERPARAMETERI64VPROC) load(userptr, "glGetNamedBufferParameteri64v"); + context->GetNamedBufferParameteriv = (PFNGLGETNAMEDBUFFERPARAMETERIVPROC) load(userptr, "glGetNamedBufferParameteriv"); + context->GetNamedBufferPointerv = (PFNGLGETNAMEDBUFFERPOINTERVPROC) load(userptr, "glGetNamedBufferPointerv"); + context->GetNamedBufferSubData = (PFNGLGETNAMEDBUFFERSUBDATAPROC) load(userptr, "glGetNamedBufferSubData"); + context->GetNamedFramebufferAttachmentParameteriv = (PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC) load(userptr, "glGetNamedFramebufferAttachmentParameteriv"); + context->GetNamedFramebufferParameteriv = (PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC) load(userptr, "glGetNamedFramebufferParameteriv"); + context->GetNamedRenderbufferParameteriv = (PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC) load(userptr, "glGetNamedRenderbufferParameteriv"); + context->GetQueryBufferObjecti64v = (PFNGLGETQUERYBUFFEROBJECTI64VPROC) load(userptr, "glGetQueryBufferObjecti64v"); + context->GetQueryBufferObjectiv = (PFNGLGETQUERYBUFFEROBJECTIVPROC) load(userptr, "glGetQueryBufferObjectiv"); + context->GetQueryBufferObjectui64v = (PFNGLGETQUERYBUFFEROBJECTUI64VPROC) load(userptr, "glGetQueryBufferObjectui64v"); + context->GetQueryBufferObjectuiv = (PFNGLGETQUERYBUFFEROBJECTUIVPROC) load(userptr, "glGetQueryBufferObjectuiv"); + context->GetTextureImage = (PFNGLGETTEXTUREIMAGEPROC) load(userptr, "glGetTextureImage"); + context->GetTextureLevelParameterfv = (PFNGLGETTEXTURELEVELPARAMETERFVPROC) load(userptr, "glGetTextureLevelParameterfv"); + context->GetTextureLevelParameteriv = (PFNGLGETTEXTURELEVELPARAMETERIVPROC) load(userptr, "glGetTextureLevelParameteriv"); + context->GetTextureParameterIiv = (PFNGLGETTEXTUREPARAMETERIIVPROC) load(userptr, "glGetTextureParameterIiv"); + context->GetTextureParameterIuiv = (PFNGLGETTEXTUREPARAMETERIUIVPROC) load(userptr, "glGetTextureParameterIuiv"); + context->GetTextureParameterfv = (PFNGLGETTEXTUREPARAMETERFVPROC) load(userptr, "glGetTextureParameterfv"); + context->GetTextureParameteriv = (PFNGLGETTEXTUREPARAMETERIVPROC) load(userptr, "glGetTextureParameteriv"); + context->GetTextureSubImage = (PFNGLGETTEXTURESUBIMAGEPROC) load(userptr, "glGetTextureSubImage"); + context->GetTransformFeedbacki64_v = (PFNGLGETTRANSFORMFEEDBACKI64_VPROC) load(userptr, "glGetTransformFeedbacki64_v"); + context->GetTransformFeedbacki_v = (PFNGLGETTRANSFORMFEEDBACKI_VPROC) load(userptr, "glGetTransformFeedbacki_v"); + context->GetTransformFeedbackiv = (PFNGLGETTRANSFORMFEEDBACKIVPROC) load(userptr, "glGetTransformFeedbackiv"); + context->GetVertexArrayIndexed64iv = (PFNGLGETVERTEXARRAYINDEXED64IVPROC) load(userptr, "glGetVertexArrayIndexed64iv"); + context->GetVertexArrayIndexediv = (PFNGLGETVERTEXARRAYINDEXEDIVPROC) load(userptr, "glGetVertexArrayIndexediv"); + context->GetVertexArrayiv = (PFNGLGETVERTEXARRAYIVPROC) load(userptr, "glGetVertexArrayiv"); + context->GetnColorTable = (PFNGLGETNCOLORTABLEPROC) load(userptr, "glGetnColorTable"); + context->GetnCompressedTexImage = (PFNGLGETNCOMPRESSEDTEXIMAGEPROC) load(userptr, "glGetnCompressedTexImage"); + context->GetnConvolutionFilter = (PFNGLGETNCONVOLUTIONFILTERPROC) load(userptr, "glGetnConvolutionFilter"); + context->GetnHistogram = (PFNGLGETNHISTOGRAMPROC) load(userptr, "glGetnHistogram"); + context->GetnMapdv = (PFNGLGETNMAPDVPROC) load(userptr, "glGetnMapdv"); + context->GetnMapfv = (PFNGLGETNMAPFVPROC) load(userptr, "glGetnMapfv"); + context->GetnMapiv = (PFNGLGETNMAPIVPROC) load(userptr, "glGetnMapiv"); + context->GetnMinmax = (PFNGLGETNMINMAXPROC) load(userptr, "glGetnMinmax"); + context->GetnPixelMapfv = (PFNGLGETNPIXELMAPFVPROC) load(userptr, "glGetnPixelMapfv"); + context->GetnPixelMapuiv = (PFNGLGETNPIXELMAPUIVPROC) load(userptr, "glGetnPixelMapuiv"); + context->GetnPixelMapusv = (PFNGLGETNPIXELMAPUSVPROC) load(userptr, "glGetnPixelMapusv"); + context->GetnPolygonStipple = (PFNGLGETNPOLYGONSTIPPLEPROC) load(userptr, "glGetnPolygonStipple"); + context->GetnSeparableFilter = (PFNGLGETNSEPARABLEFILTERPROC) load(userptr, "glGetnSeparableFilter"); + context->GetnTexImage = (PFNGLGETNTEXIMAGEPROC) load(userptr, "glGetnTexImage"); + context->GetnUniformdv = (PFNGLGETNUNIFORMDVPROC) load(userptr, "glGetnUniformdv"); + context->GetnUniformfv = (PFNGLGETNUNIFORMFVPROC) load(userptr, "glGetnUniformfv"); + context->GetnUniformiv = (PFNGLGETNUNIFORMIVPROC) load(userptr, "glGetnUniformiv"); + context->GetnUniformuiv = (PFNGLGETNUNIFORMUIVPROC) load(userptr, "glGetnUniformuiv"); + context->InvalidateNamedFramebufferData = (PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC) load(userptr, "glInvalidateNamedFramebufferData"); + context->InvalidateNamedFramebufferSubData = (PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC) load(userptr, "glInvalidateNamedFramebufferSubData"); + context->MapNamedBuffer = (PFNGLMAPNAMEDBUFFERPROC) load(userptr, "glMapNamedBuffer"); + context->MapNamedBufferRange = (PFNGLMAPNAMEDBUFFERRANGEPROC) load(userptr, "glMapNamedBufferRange"); + context->MemoryBarrierByRegion = (PFNGLMEMORYBARRIERBYREGIONPROC) load(userptr, "glMemoryBarrierByRegion"); + context->NamedBufferData = (PFNGLNAMEDBUFFERDATAPROC) load(userptr, "glNamedBufferData"); + context->NamedBufferStorage = (PFNGLNAMEDBUFFERSTORAGEPROC) load(userptr, "glNamedBufferStorage"); + context->NamedBufferSubData = (PFNGLNAMEDBUFFERSUBDATAPROC) load(userptr, "glNamedBufferSubData"); + context->NamedFramebufferDrawBuffer = (PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC) load(userptr, "glNamedFramebufferDrawBuffer"); + context->NamedFramebufferDrawBuffers = (PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC) load(userptr, "glNamedFramebufferDrawBuffers"); + context->NamedFramebufferParameteri = (PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC) load(userptr, "glNamedFramebufferParameteri"); + context->NamedFramebufferReadBuffer = (PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC) load(userptr, "glNamedFramebufferReadBuffer"); + context->NamedFramebufferRenderbuffer = (PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC) load(userptr, "glNamedFramebufferRenderbuffer"); + context->NamedFramebufferTexture = (PFNGLNAMEDFRAMEBUFFERTEXTUREPROC) load(userptr, "glNamedFramebufferTexture"); + context->NamedFramebufferTextureLayer = (PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC) load(userptr, "glNamedFramebufferTextureLayer"); + context->NamedRenderbufferStorage = (PFNGLNAMEDRENDERBUFFERSTORAGEPROC) load(userptr, "glNamedRenderbufferStorage"); + context->NamedRenderbufferStorageMultisample = (PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC) load(userptr, "glNamedRenderbufferStorageMultisample"); + context->ReadnPixels = (PFNGLREADNPIXELSPROC) load(userptr, "glReadnPixels"); + context->TextureBarrier = (PFNGLTEXTUREBARRIERPROC) load(userptr, "glTextureBarrier"); + context->TextureBuffer = (PFNGLTEXTUREBUFFERPROC) load(userptr, "glTextureBuffer"); + context->TextureBufferRange = (PFNGLTEXTUREBUFFERRANGEPROC) load(userptr, "glTextureBufferRange"); + context->TextureParameterIiv = (PFNGLTEXTUREPARAMETERIIVPROC) load(userptr, "glTextureParameterIiv"); + context->TextureParameterIuiv = (PFNGLTEXTUREPARAMETERIUIVPROC) load(userptr, "glTextureParameterIuiv"); + context->TextureParameterf = (PFNGLTEXTUREPARAMETERFPROC) load(userptr, "glTextureParameterf"); + context->TextureParameterfv = (PFNGLTEXTUREPARAMETERFVPROC) load(userptr, "glTextureParameterfv"); + context->TextureParameteri = (PFNGLTEXTUREPARAMETERIPROC) load(userptr, "glTextureParameteri"); + context->TextureParameteriv = (PFNGLTEXTUREPARAMETERIVPROC) load(userptr, "glTextureParameteriv"); + context->TextureStorage1D = (PFNGLTEXTURESTORAGE1DPROC) load(userptr, "glTextureStorage1D"); + context->TextureStorage2D = (PFNGLTEXTURESTORAGE2DPROC) load(userptr, "glTextureStorage2D"); + context->TextureStorage2DMultisample = (PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC) load(userptr, "glTextureStorage2DMultisample"); + context->TextureStorage3D = (PFNGLTEXTURESTORAGE3DPROC) load(userptr, "glTextureStorage3D"); + context->TextureStorage3DMultisample = (PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC) load(userptr, "glTextureStorage3DMultisample"); + context->TextureSubImage1D = (PFNGLTEXTURESUBIMAGE1DPROC) load(userptr, "glTextureSubImage1D"); + context->TextureSubImage2D = (PFNGLTEXTURESUBIMAGE2DPROC) load(userptr, "glTextureSubImage2D"); + context->TextureSubImage3D = (PFNGLTEXTURESUBIMAGE3DPROC) load(userptr, "glTextureSubImage3D"); + context->TransformFeedbackBufferBase = (PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC) load(userptr, "glTransformFeedbackBufferBase"); + context->TransformFeedbackBufferRange = (PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC) load(userptr, "glTransformFeedbackBufferRange"); + context->UnmapNamedBuffer = (PFNGLUNMAPNAMEDBUFFERPROC) load(userptr, "glUnmapNamedBuffer"); + context->VertexArrayAttribBinding = (PFNGLVERTEXARRAYATTRIBBINDINGPROC) load(userptr, "glVertexArrayAttribBinding"); + context->VertexArrayAttribFormat = (PFNGLVERTEXARRAYATTRIBFORMATPROC) load(userptr, "glVertexArrayAttribFormat"); + context->VertexArrayAttribIFormat = (PFNGLVERTEXARRAYATTRIBIFORMATPROC) load(userptr, "glVertexArrayAttribIFormat"); + context->VertexArrayAttribLFormat = (PFNGLVERTEXARRAYATTRIBLFORMATPROC) load(userptr, "glVertexArrayAttribLFormat"); + context->VertexArrayBindingDivisor = (PFNGLVERTEXARRAYBINDINGDIVISORPROC) load(userptr, "glVertexArrayBindingDivisor"); + context->VertexArrayElementBuffer = (PFNGLVERTEXARRAYELEMENTBUFFERPROC) load(userptr, "glVertexArrayElementBuffer"); + context->VertexArrayVertexBuffer = (PFNGLVERTEXARRAYVERTEXBUFFERPROC) load(userptr, "glVertexArrayVertexBuffer"); + context->VertexArrayVertexBuffers = (PFNGLVERTEXARRAYVERTEXBUFFERSPROC) load(userptr, "glVertexArrayVertexBuffers"); +} +static void glad_gl_load_GL_VERSION_4_6(GladGLContext *context, GLADuserptrloadfunc load, void* userptr) { + if(!context->VERSION_4_6) return; + context->MultiDrawArraysIndirectCount = (PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC) load(userptr, "glMultiDrawArraysIndirectCount"); + context->MultiDrawElementsIndirectCount = (PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC) load(userptr, "glMultiDrawElementsIndirectCount"); + context->PolygonOffsetClamp = (PFNGLPOLYGONOFFSETCLAMPPROC) load(userptr, "glPolygonOffsetClamp"); + context->SpecializeShader = (PFNGLSPECIALIZESHADERPROC) load(userptr, "glSpecializeShader"); +} + + + +#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0) +#define GLAD_GL_IS_SOME_NEW_VERSION 1 +#else +#define GLAD_GL_IS_SOME_NEW_VERSION 0 +#endif + +static int glad_gl_get_extensions(GladGLContext *context, int version, const char **out_exts, unsigned int *out_num_exts_i, char ***out_exts_i) { +#if GLAD_GL_IS_SOME_NEW_VERSION + if(GLAD_VERSION_MAJOR(version) < 3) { +#else + (void) version; + (void) out_num_exts_i; + (void) out_exts_i; +#endif + if (context->GetString == NULL) { + return 0; + } + *out_exts = (const char *)context->GetString(GL_EXTENSIONS); +#if GLAD_GL_IS_SOME_NEW_VERSION + } else { + unsigned int index = 0; + unsigned int num_exts_i = 0; + char **exts_i = NULL; + if (context->GetStringi == NULL || context->GetIntegerv == NULL) { + return 0; + } + context->GetIntegerv(GL_NUM_EXTENSIONS, (int*) &num_exts_i); + if (num_exts_i > 0) { + exts_i = (char **) malloc(num_exts_i * (sizeof *exts_i)); + } + if (exts_i == NULL) { + return 0; + } + for(index = 0; index < num_exts_i; index++) { + const char *gl_str_tmp = (const char*) context->GetStringi(GL_EXTENSIONS, index); + size_t len = strlen(gl_str_tmp) + 1; + + char *local_str = (char*) malloc(len * sizeof(char)); + if(local_str != NULL) { + memcpy(local_str, gl_str_tmp, len * sizeof(char)); + } + + exts_i[index] = local_str; + } + + *out_num_exts_i = num_exts_i; + *out_exts_i = exts_i; + } +#endif + return 1; +} +static void glad_gl_free_extensions(char **exts_i, unsigned int num_exts_i) { + if (exts_i != NULL) { + unsigned int index; + for(index = 0; index < num_exts_i; index++) { + free((void *) (exts_i[index])); + } + free((void *)exts_i); + exts_i = NULL; + } +} +static int glad_gl_has_extension(int version, const char *exts, unsigned int num_exts_i, char **exts_i, const char *ext) { + if(GLAD_VERSION_MAJOR(version) < 3 || !GLAD_GL_IS_SOME_NEW_VERSION) { + const char *extensions; + const char *loc; + const char *terminator; + extensions = exts; + if(extensions == NULL || ext == NULL) { + return 0; + } + while(1) { + loc = strstr(extensions, ext); + if(loc == NULL) { + return 0; + } + terminator = loc + strlen(ext); + if((loc == extensions || *(loc - 1) == ' ') && + (*terminator == ' ' || *terminator == '\0')) { + return 1; + } + extensions = terminator; + } + } else { + unsigned int index; + for(index = 0; index < num_exts_i; index++) { + const char *e = exts_i[index]; + if(strcmp(e, ext) == 0) { + return 1; + } + } + } + return 0; +} + +static GLADapiproc glad_gl_get_proc_from_userptr(void *userptr, const char* name) { + return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name); +} + +static int glad_gl_find_extensions_gl(GladGLContext *context, int version) { + const char *exts = NULL; + unsigned int num_exts_i = 0; + char **exts_i = NULL; + if (!glad_gl_get_extensions(context, version, &exts, &num_exts_i, &exts_i)) return 0; + + (void) glad_gl_has_extension; + + glad_gl_free_extensions(exts_i, num_exts_i); + + return 1; +} + +static int glad_gl_find_core_gl(GladGLContext *context) { + int i, major, minor; + const char* version; + const char* prefixes[] = { + "OpenGL ES-CM ", + "OpenGL ES-CL ", + "OpenGL ES ", + NULL + }; + version = (const char*) context->GetString(GL_VERSION); + if (!version) return 0; + for (i = 0; prefixes[i]; i++) { + const size_t length = strlen(prefixes[i]); + if (strncmp(version, prefixes[i], length) == 0) { + version += length; + break; + } + } + + GLAD_IMPL_UTIL_SSCANF(version, "%d.%d", &major, &minor); + + // attempt to grab whatever we can + int temp = major; + major = 5; + + context->VERSION_1_0 = (major == 1 && minor >= 0) || major > 1; + context->VERSION_1_1 = (major == 1 && minor >= 1) || major > 1; + context->VERSION_1_2 = (major == 1 && minor >= 2) || major > 1; + context->VERSION_1_3 = (major == 1 && minor >= 3) || major > 1; + context->VERSION_1_4 = (major == 1 && minor >= 4) || major > 1; + context->VERSION_1_5 = (major == 1 && minor >= 5) || major > 1; + context->VERSION_2_0 = (major == 2 && minor >= 0) || major > 2; + context->VERSION_2_1 = (major == 2 && minor >= 1) || major > 2; + context->VERSION_3_0 = (major == 3 && minor >= 0) || major > 3; + context->VERSION_3_1 = (major == 3 && minor >= 1) || major > 3; + context->VERSION_3_2 = (major == 3 && minor >= 2) || major > 3; + context->VERSION_3_3 = (major == 3 && minor >= 3) || major > 3; + context->VERSION_4_0 = (major == 4 && minor >= 0) || major > 4; + context->VERSION_4_1 = (major == 4 && minor >= 1) || major > 4; + context->VERSION_4_2 = (major == 4 && minor >= 2) || major > 4; + context->VERSION_4_3 = (major == 4 && minor >= 3) || major > 4; + context->VERSION_4_4 = (major == 4 && minor >= 4) || major > 4; + context->VERSION_4_5 = (major == 4 && minor >= 5) || major > 4; + context->VERSION_4_6 = (major == 4 && minor >= 6) || major > 4; + + major = temp; + return GLAD_MAKE_VERSION(major, minor); +} + +int gladLoadGLContextUserPtr(GladGLContext *context, GLADuserptrloadfunc load, void *userptr) { + int version; + + context->GetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString"); + if(context->GetString == NULL) return 0; + if(context->GetString(GL_VERSION) == NULL) return 0; + version = glad_gl_find_core_gl(context); + + glad_gl_load_GL_VERSION_1_0(context, load, userptr); + glad_gl_load_GL_VERSION_1_1(context, load, userptr); + glad_gl_load_GL_VERSION_1_2(context, load, userptr); + glad_gl_load_GL_VERSION_1_3(context, load, userptr); + glad_gl_load_GL_VERSION_1_4(context, load, userptr); + glad_gl_load_GL_VERSION_1_5(context, load, userptr); + glad_gl_load_GL_VERSION_2_0(context, load, userptr); + glad_gl_load_GL_VERSION_2_1(context, load, userptr); + glad_gl_load_GL_VERSION_3_0(context, load, userptr); + glad_gl_load_GL_VERSION_3_1(context, load, userptr); + glad_gl_load_GL_VERSION_3_2(context, load, userptr); + glad_gl_load_GL_VERSION_3_3(context, load, userptr); + glad_gl_load_GL_VERSION_4_0(context, load, userptr); + glad_gl_load_GL_VERSION_4_1(context, load, userptr); + glad_gl_load_GL_VERSION_4_2(context, load, userptr); + glad_gl_load_GL_VERSION_4_3(context, load, userptr); + glad_gl_load_GL_VERSION_4_4(context, load, userptr); + glad_gl_load_GL_VERSION_4_5(context, load, userptr); + glad_gl_load_GL_VERSION_4_6(context, load, userptr); + + if (!glad_gl_find_extensions_gl(context, version)) return 0; + + + + return version; +} + + +int gladLoadGLContext(GladGLContext *context, GLADloadfunc load) { + return gladLoadGLContextUserPtr(context, glad_gl_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load); +} + + + + + +#ifdef GLAD_GL + +#ifndef GLAD_LOADER_LIBRARY_C_ +#define GLAD_LOADER_LIBRARY_C_ + +#include +#include + +#if GLAD_PLATFORM_WIN32 +#include +#else +#include +#endif + + +static void* glad_get_dlopen_handle(const char *lib_names[], int length) { + void *handle = NULL; + int i; + + for (i = 0; i < length; ++i) { +#if GLAD_PLATFORM_WIN32 + #if GLAD_PLATFORM_UWP + size_t buffer_size = (strlen(lib_names[i]) + 1) * sizeof(WCHAR); + LPWSTR buffer = (LPWSTR) malloc(buffer_size); + if (buffer != NULL) { + int ret = MultiByteToWideChar(CP_ACP, 0, lib_names[i], -1, buffer, buffer_size); + if (ret != 0) { + handle = (void*) LoadPackagedLibrary(buffer, 0); + } + free((void*) buffer); + } + #else + handle = (void*) LoadLibraryA(lib_names[i]); + #endif +#else + handle = dlopen(lib_names[i], RTLD_LAZY | RTLD_LOCAL); +#endif + if (handle != NULL) { + return handle; + } + } + + return NULL; +} + +static void glad_close_dlopen_handle(void* handle) { + if (handle != NULL) { +#if GLAD_PLATFORM_WIN32 + FreeLibrary((HMODULE) handle); +#else + dlclose(handle); +#endif + } +} + +static GLADapiproc glad_dlsym_handle(void* handle, const char *name) { + if (handle == NULL) { + return NULL; + } + +#if GLAD_PLATFORM_WIN32 + return (GLADapiproc) GetProcAddress((HMODULE) handle, name); +#else + return GLAD_GNUC_EXTENSION (GLADapiproc) dlsym(handle, name); +#endif +} + +#endif /* GLAD_LOADER_LIBRARY_C_ */ + +typedef void* (GLAD_API_PTR *GLADglprocaddrfunc)(const char*); +struct _glad_gl_userptr { + void *handle; + GLADglprocaddrfunc gl_get_proc_address_ptr; +}; + +static GLADapiproc glad_gl_get_proc(void *vuserptr, const char *name) { + struct _glad_gl_userptr userptr = *(struct _glad_gl_userptr*) vuserptr; + GLADapiproc result = NULL; + + if(userptr.gl_get_proc_address_ptr != NULL) { + result = GLAD_GNUC_EXTENSION (GLADapiproc) userptr.gl_get_proc_address_ptr(name); + } + if(result == NULL) { + result = glad_dlsym_handle(userptr.handle, name); + } + + return result; +} + +static void* _gl_handle = NULL; + +static void* glad_gl_dlopen_handle(void) { +#if GLAD_PLATFORM_APPLE + static const char *NAMES[] = { + "../Frameworks/OpenGL.framework/OpenGL", + "/Library/Frameworks/OpenGL.framework/OpenGL", + "/System/Library/Frameworks/OpenGL.framework/OpenGL", + "/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL" + }; +#elif GLAD_PLATFORM_WIN32 + static const char *NAMES[] = {"opengl32.dll"}; +#else + static const char *NAMES[] = { + #if defined(__CYGWIN__) + "libGL-1.so", + #endif + "libGL.so.1", + "libGL.so" + }; +#endif + + if (_gl_handle == NULL) { + _gl_handle = glad_get_dlopen_handle(NAMES, sizeof(NAMES) / sizeof(NAMES[0])); + } + + return _gl_handle; +} + +static struct _glad_gl_userptr glad_gl_build_userptr(void *handle) { + struct _glad_gl_userptr userptr; + + userptr.handle = handle; +#if GLAD_PLATFORM_APPLE || defined(__HAIKU__) + userptr.gl_get_proc_address_ptr = NULL; +#elif GLAD_PLATFORM_WIN32 + userptr.gl_get_proc_address_ptr = + (GLADglprocaddrfunc) glad_dlsym_handle(handle, "wglGetProcAddress"); +#else + userptr.gl_get_proc_address_ptr = + (GLADglprocaddrfunc) glad_dlsym_handle(handle, "glXGetProcAddressARB"); +#endif + + return userptr; +} + +int gladLoaderLoadGLContext(GladGLContext *context) { + int version = 0; + void *handle; + int did_load = 0; + struct _glad_gl_userptr userptr; + + did_load = _gl_handle == NULL; + handle = glad_gl_dlopen_handle(); + if (handle) { + userptr = glad_gl_build_userptr(handle); + + version = gladLoadGLContextUserPtr(context,glad_gl_get_proc, &userptr); + + if (did_load) { + gladLoaderUnloadGL(); + } + } + + return version; +} + + + +void gladLoaderUnloadGL(void) { + if (_gl_handle != NULL) { + glad_close_dlopen_handle(_gl_handle); + _gl_handle = NULL; + } +} + +#endif /* GLAD_GL */ + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/third-party/moonlight-common-c b/third-party/moonlight-common-c new file mode 160000 index 00000000..a13eedda --- /dev/null +++ b/third-party/moonlight-common-c @@ -0,0 +1 @@ +Subproject commit a13eeddacfdb4325bd7d140d635a8cb87e61a61d diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 8d59af33..e3e79969 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -9,7 +9,7 @@ set_target_properties(dxgi-info PROPERTIES CXX_STANDARD 17) target_link_libraries(dxgi-info ${CMAKE_THREAD_LIBS_INIT} dxgi - ${PLATFORM_LIBRARIES}) + ${PLATFORM_LIBRARIES}) target_compile_options(dxgi-info PRIVATE ${SUNSHINE_COMPILE_OPTIONS}) add_executable(audio-info audio.cpp) @@ -17,5 +17,5 @@ set_target_properties(audio-info PROPERTIES CXX_STANDARD 17) target_link_libraries(audio-info ${CMAKE_THREAD_LIBS_INIT} ksuser - ${PLATFORM_LIBRARIES}) + ${PLATFORM_LIBRARIES}) target_compile_options(audio-info PRIVATE ${SUNSHINE_COMPILE_OPTIONS}) diff --git a/tools/audio.cpp b/tools/audio.cpp index 05717b23..a130d384 100644 --- a/tools/audio.cpp +++ b/tools/audio.cpp @@ -2,9 +2,9 @@ // Created by loki on 1/24/20. // -#include -#include #include +#include +#include #include @@ -16,8 +16,8 @@ #include "sunshine/utility.h" -DEFINE_PROPERTYKEY(PKEY_Device_DeviceDesc, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 2); // DEVPROP_TYPE_STRING -DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); // DEVPROP_TYPE_STRING +DEFINE_PROPERTYKEY(PKEY_Device_DeviceDesc, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 2); // DEVPROP_TYPE_STRING +DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); // DEVPROP_TYPE_STRING DEFINE_PROPERTYKEY(PKEY_DeviceInterface_FriendlyName, 0x026e516e, 0xb814, 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22, 2); using namespace std::literals; @@ -26,7 +26,9 @@ const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); const IID IID_IAudioClient = __uuidof(IAudioClient); const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); -int device_state_filter = DEVICE_STATE_ACTIVE; +constexpr auto SAMPLE_RATE = 48000; +int device_state_filter = DEVICE_STATE_ACTIVE; + namespace audio { template void Release(T *p) { @@ -66,19 +68,118 @@ public: const wchar_t *no_null(const wchar_t *str) { return str ? str : L"Unknown"; } -void print_device(device_t &device) { - HRESULT status; - audio::wstring_t::pointer wstring_p {}; +struct format_t { + std::string_view name; + int channels; + int channel_mask; +} formats[] { + { "Mono"sv, + 1, + SPEAKER_FRONT_CENTER }, + { "Stereo"sv, + 2, + SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT }, + { "Surround 5.1"sv, + 6, + SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT }, + { "Surround 7.1"sv, + 8, + SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT | + SPEAKER_SIDE_LEFT | + SPEAKER_SIDE_RIGHT } +}; + +void set_wave_format(audio::wave_format_t &wave_format, const format_t &format) { + wave_format->nChannels = format.channels; + wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8; + wave_format->nAvgBytesPerSec = wave_format->nSamplesPerSec * wave_format->nBlockAlign; + + if(wave_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + ((PWAVEFORMATEXTENSIBLE)wave_format.get())->dwChannelMask = format.channel_mask; + } +} + +audio_client_t make_audio_client(device_t &device, const format_t &format) { + audio_client_t audio_client; + auto status = device->Activate( + IID_IAudioClient, + CLSCTX_ALL, + nullptr, + (void **)&audio_client); + + if(FAILED(status)) { + std::cout << "Couldn't activate Device: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; + + return nullptr; + } + + wave_format_t wave_format; + status = audio_client->GetMixFormat(&wave_format); + + if(FAILED(status)) { + std::cout << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; + + return nullptr; + } + + wave_format->wBitsPerSample = 16; + wave_format->nSamplesPerSec = SAMPLE_RATE; + switch(wave_format->wFormatTag) { + case WAVE_FORMAT_PCM: + break; + case WAVE_FORMAT_IEEE_FLOAT: + break; + case WAVE_FORMAT_EXTENSIBLE: { + auto wave_ex = (PWAVEFORMATEXTENSIBLE)wave_format.get(); + if(IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) { + wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + wave_ex->Samples.wValidBitsPerSample = 16; + break; + } + + std::cout << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']' << std::endl; + } + default: + std::cout << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']' << std::endl; + return nullptr; + }; + + set_wave_format(wave_format, format); + + status = audio_client->Initialize( + AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + 0, 0, + wave_format.get(), + nullptr); + + if(status) { + return nullptr; + } + + return audio_client; +} + +void print_device(device_t &device) { + audio::wstring_t wstring; DWORD device_state; device->GetState(&device_state); - device->GetId(&wstring_p); - audio::wstring_t wstring { wstring_p }; + device->GetId(&wstring); - audio::prop_t::pointer prop_p {}; - device->OpenPropertyStore(STGM_READ, &prop_p); - audio::prop_t prop { prop_p }; + audio::prop_t prop; + device->OpenPropertyStore(STGM_READ, &prop); prop_var_t adapter_friendly_name; prop_var_t device_friendly_name; @@ -94,18 +195,18 @@ void print_device(device_t &device) { std::wstring device_state_string = L"Unknown"s; switch(device_state) { - case DEVICE_STATE_ACTIVE: - device_state_string = L"Active"s; - break; - case DEVICE_STATE_DISABLED: - device_state_string = L"Disabled"s; - break; - case DEVICE_STATE_UNPLUGGED: - device_state_string = L"Unplugged"s; - break; - case DEVICE_STATE_NOTPRESENT: - device_state_string = L"Not present"s; - break; + case DEVICE_STATE_ACTIVE: + device_state_string = L"Active"s; + break; + case DEVICE_STATE_DISABLED: + device_state_string = L"Disabled"s; + break; + case DEVICE_STATE_UNPLUGGED: + device_state_string = L"Unplugged"s; + break; + case DEVICE_STATE_NOTPRESENT: + device_state_string = L"Not present"s; + break; } std::wcout @@ -114,55 +215,21 @@ void print_device(device_t &device) { << L"Device name : "sv << no_null((LPWSTR)device_friendly_name.prop.pszVal) << std::endl << L"Adapter name : "sv << no_null((LPWSTR)adapter_friendly_name.prop.pszVal) << std::endl << L"Device description : "sv << no_null((LPWSTR)device_desc.prop.pszVal) << std::endl - << L"Device state : "sv << device_state_string << std::endl << std::endl; + << L"Device state : "sv << device_state_string << std::endl + << std::endl; if(device_state != DEVICE_STATE_ACTIVE) { return; } - // Ensure WaveFromat is compatible - audio_client_t::pointer audio_client_p{}; - status = device->Activate( - IID_IAudioClient, - CLSCTX_ALL, - nullptr, - (void **) &audio_client_p); - audio_client_t audio_client { audio_client_p }; + for(const auto &format : formats) { + // Ensure WaveFromat is compatible + auto audio_client = make_audio_client(device, format); - if (FAILED(status)) { - std::cout << "Couldn't activate Device: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; - - return; + std::cout << format.name << ": "sv << (!audio_client ? "unsupported"sv : "supported"sv) << std::endl; } - - wave_format_t::pointer wave_format_p{}; - status = audio_client->GetMixFormat(&wave_format_p); - wave_format_t wave_format { wave_format_p }; - - if (FAILED(status)) { - std::cout << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; - - return; - } - - switch(wave_format->wFormatTag) { - case WAVE_FORMAT_PCM: - break; - case WAVE_FORMAT_IEEE_FLOAT: - break; - case WAVE_FORMAT_EXTENSIBLE: { - auto wave_ex = (PWAVEFORMATEXTENSIBLE) wave_format.get(); - if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) { - break; - } - - std::cout << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']' << std::endl; - } - default: - std::cout << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']' << std::endl; - }; -} } +} // namespace audio void print_help() { std::cout @@ -213,26 +280,24 @@ int main(int argc, char *argv[]) { HRESULT status; - audio::device_enum_t::pointer device_enum_p{}; + audio::device_enum_t device_enum; status = CoCreateInstance( CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, - (void **) &device_enum_p); - audio::device_enum_t device_enum { device_enum_p }; + (void **)&device_enum); - if (FAILED(status)) { + if(FAILED(status)) { std::cout << "Couldn't create Device Enumerator: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; return -1; } - audio::collection_t::pointer collection_p {}; - status = device_enum->EnumAudioEndpoints(eRender, DEVICE_STATEMASK_ALL, &collection_p); - audio::collection_t collection { collection_p }; + audio::collection_t collection; + status = device_enum->EnumAudioEndpoints(eRender, device_state_filter, &collection); - if (FAILED(status)) { + if(FAILED(status)) { std::cout << "Couldn't enumerate: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; return -1; @@ -241,11 +306,10 @@ int main(int argc, char *argv[]) { UINT count; collection->GetCount(&count); - std::cout << "====== Found "sv << count << " potential audio devices ======"sv << std::endl; + std::cout << "====== Found "sv << count << " audio devices ======"sv << std::endl; for(auto x = 0; x < count; ++x) { - audio::device_t::pointer device_p {}; - collection->Item(x, &device_p); - audio::device_t device { device_p }; + audio::device_t device; + collection->Item(x, &device); audio::print_device(device); } diff --git a/tools/dxgi.cpp b/tools/dxgi.cpp index 3b3f9c88..9c5f7307 100644 --- a/tools/dxgi.cpp +++ b/tools/dxgi.cpp @@ -2,8 +2,8 @@ // Created by loki on 1/23/20. // -#include #include +#include #include @@ -16,17 +16,17 @@ void Release(T *dxgi) { dxgi->Release(); } -using factory1_t = util::safe_ptr>; -using adapter_t = util::safe_ptr>; -using output_t = util::safe_ptr>; +using factory1_t = util::safe_ptr>; +using adapter_t = util::safe_ptr>; +using output_t = util::safe_ptr>; -} +} // namespace dxgi int main(int argc, char *argv[]) { HRESULT status; dxgi::factory1_t::pointer factory_p {}; - status = CreateDXGIFactory1(IID_IDXGIFactory1, (void**)&factory_p); + status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **)&factory_p); dxgi::factory1_t factory { factory_p }; if(FAILED(status)) { std::cout << "Failed to create DXGIFactory1 [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; @@ -49,12 +49,13 @@ int main(int argc, char *argv[]) { << "Device Device ID : 0x"sv << util::hex(adapter_desc.DeviceId).to_string_view() << std::endl << "Device Video Mem : "sv << adapter_desc.DedicatedVideoMemory / 1048576 << " MiB"sv << std::endl << "Device Sys Mem : "sv << adapter_desc.DedicatedSystemMemory / 1048576 << " MiB"sv << std::endl - << "Share Sys Mem : "sv << adapter_desc.SharedSystemMemory / 1048576 << " MiB"sv << std::endl << std::endl + << "Share Sys Mem : "sv << adapter_desc.SharedSystemMemory / 1048576 << " MiB"sv << std::endl + << std::endl << " ====== OUTPUT ======"sv << std::endl; dxgi::output_t::pointer output_p {}; for(int y = 0; adapter->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) { - dxgi::output_t output {output_p }; + dxgi::output_t output { output_p }; DXGI_OUTPUT_DESC desc; output->GetDesc(&desc); @@ -66,7 +67,8 @@ int main(int argc, char *argv[]) { << L" Output Name : "sv << desc.DeviceName << std::endl; std::cout << " AttachedToDesktop : "sv << (desc.AttachedToDesktop ? "yes"sv : "no"sv) << std::endl - << " Resolution : "sv << width << 'x' << height << std::endl << std::endl; + << " Resolution : "sv << width << 'x' << height << std::endl + << std::endl; } }