From 9c42f1cc597579b383e5929f6dfd2db96339b625 Mon Sep 17 00:00:00 2001 From: sergystepanov Date: Thu, 11 Jun 2026 21:44:33 +0300 Subject: [PATCH] Retune VP8/VP9 codecs Add new params cpuUsed: 0 tileColumns: 0 tune: screen In VP9 it will use multithreaded encoding now. Keyframe interval was not set properly wasting bitrate. By default. It is changed to 120 frames instead of 5. --- pkg/config/config.yaml | 798 +++++++++++++++++++------------------- pkg/config/worker.go | 3 + pkg/encoder/vpx/libvpx.go | 101 ++++- 3 files changed, 501 insertions(+), 401 deletions(-) diff --git a/pkg/config/config.yaml b/pkg/config/config.yaml index dfb14a0b..e5c2641c 100644 --- a/pkg/config/config.yaml +++ b/pkg/config/config.yaml @@ -20,360 +20,366 @@ version: 3 # new decentralized library of games library: - # optional alias file for overriding game names from the basePath path - aliasFile: alias.txt - # root folder for the library (where games are stored) - basePath: assets/games - # a list of ignored words in the ROM filenames - ignored: - - neogeo - - pgm - # DOSBox filesystem state - - .pure - # an explicit list of supported file extensions - # which overrides Libretro emulator ROMs configs - supported: - # print some additional info - verbose: true - # enable library directory live reload - # (experimental) - watchMode: false - -coordinator: - # debugging switch - # - shows debug logs - # - allows selecting worker instances - debug: false - # selects free workers: - # - empty value (default, any free) - # - ping (with the lowest ping) - selector: - monitoring: - port: 6601 - # enable Go profiler HTTP server - profilingEnabled: false - metricEnabled: false - urlPrefix: /coordinator - # a custom Origins for incoming Websocket connections: - # "" -- checks same origin policy - # "*" -- allows all - # "your address" -- checks for that address - origin: - userWs: - workerWs: - # max websocket message size in bytes - maxWsSize: 32000000 - # HTTP(S) server config - server: - address: :8000 - cacheControl: "max-age=259200, must-revalidate" - frameOptions: "" - https: false - # Letsencrypt or self cert config - tls: - address: :443 - # allowed host name - domain: - # if both are set then will use certs - # and Letsencryt instead - httpsCert: - httpsKey: - analytics: - inject: false - gtag: - -worker: - # show more logs - debug: false - library: + # optional alias file for overriding game names from the basePath path + aliasFile: alias.txt # root folder for the library (where games are stored) basePath: assets/games - network: - # a coordinator address to connect to - coordinatorAddress: localhost:8000 - # where to connect - endpoint: /wso - # ping endpoint - pingEndpoint: /echo - # set public ping address (IP or hostname) - publicAddress: - # make coordinator connection secure (wss) - secure: false - # ISO Alpha-2 country code to group workers by zones - zone: - monitoring: - # monitoring server port - port: 6602 - profilingEnabled: false - # monitoring server URL prefix - metricEnabled: false - urlPrefix: /worker - server: - address: :9000 - https: false - tls: - address: :444 - # LetsEncrypt config - # allowed host name - domain: - # Own certs config - httpsCert: - httpsKey: - # optional server tag - tag: + # a list of ignored words in the ROM filenames + ignored: + - neogeo + - pgm + # DOSBox filesystem state + - .pure + # an explicit list of supported file extensions + # which overrides Libretro emulator ROMs configs + supported: + # print some additional info + verbose: true + # enable library directory live reload + # (experimental) + watchMode: false + +coordinator: + # debugging switch + # - shows debug logs + # - allows selecting worker instances + debug: false + # selects free workers: + # - empty value (default, any free) + # - ping (with the lowest ping) + selector: + monitoring: + port: 6601 + # enable Go profiler HTTP server + profilingEnabled: false + metricEnabled: false + urlPrefix: /coordinator + # a custom Origins for incoming Websocket connections: + # "" -- checks same origin policy + # "*" -- allows all + # "your address" -- checks for that address + origin: + userWs: + workerWs: + # max websocket message size in bytes + maxWsSize: 32000000 + # HTTP(S) server config + server: + address: :8000 + cacheControl: "max-age=259200, must-revalidate" + frameOptions: "" + https: false + # Letsencrypt or self cert config + tls: + address: :443 + # allowed host name + domain: + # if both are set then will use certs + # and Letsencryt instead + httpsCert: + httpsKey: + analytics: + inject: false + gtag: + +worker: + # show more logs + debug: false + library: + # root folder for the library (where games are stored) + basePath: assets/games + network: + # a coordinator address to connect to + coordinatorAddress: localhost:8000 + # where to connect + endpoint: /wso + # ping endpoint + pingEndpoint: /echo + # set public ping address (IP or hostname) + publicAddress: + # make coordinator connection secure (wss) + secure: false + # ISO Alpha-2 country code to group workers by zones + zone: + monitoring: + # monitoring server port + port: 6602 + profilingEnabled: false + # monitoring server URL prefix + metricEnabled: false + urlPrefix: /worker + server: + address: :9000 + https: false + tls: + address: :444 + # LetsEncrypt config + # allowed host name + domain: + # Own certs config + httpsCert: + httpsKey: + # optional server tag + tag: emulator: - # set the total number of threads for the image processing - # (removed) - threads: 0 + # set the total number of threads for the image processing + # (removed) + threads: 0 - # enable autosave for emulator states if set to a non-zero value of seconds - autosaveSec: 0 + # enable autosave for emulator states if set to a non-zero value of seconds + autosaveSec: 0 - # save directory for emulator states - # special tag {user} will be replaced with current user's home dir - storage: "{user}/.cr/save" + # save directory for emulator states + # special tag {user} will be replaced with current user's home dir + storage: "{user}/.cr/save" - # path for storing emulator generated files - localPath: "./libretro" + # path for storing emulator generated files + localPath: "./libretro" - # checks if the system supports running an emulator at startup - failFast: true + # checks if the system supports running an emulator at startup + failFast: true - # do not send late video frames - skipLateFrames: false + # do not send late video frames + skipLateFrames: false - # log dropped frames (temp) - logDroppedFrames: false + # log dropped frames (temp) + logDroppedFrames: false - libretro: - # use zip compression for emulator save states - saveCompression: true - # Sets a limiter function for some spammy core callbacks. - # 0 - disabled, otherwise -- time in milliseconds for ignoring repeated calls except the last. - debounceMs: 0 - # Allow duplicate frames - dup: true - # Libretro cores logging level: DEBUG = 0, INFO, WARN, ERROR, DUMMY = INT_MAX - logLevel: 1 - cores: - paths: - libs: assets/cores - # Config params for Libretro cores repository, - # available types are: - # - buildbot (the default Libretro nightly repository) - # - github (GitHub raw repository with a similar structure to buildbot) - # - raw (just a link to a zip file extracted as is) - repo: - # enable auto-download for the list of cores (list->lib) - sync: true - # external cross-process mutex lock - extLock: "{user}/.cr/cloud-game.lock" - map: - darwin: - amd64: - arch: x86_64 - ext: .dylib - os: osx - vendor: apple - arm64: - arch: arm64 - ext: .dylib - os: osx - vendor: apple - linux: - amd64: - arch: x86_64 - ext: .so - os: linux - arm: - arch: armv7-neon-hf - ext: .so - os: linux - windows: - amd64: - arch: x86_64 - ext: .dll - os: windows - main: - type: buildbot - url: https://buildbot.libretro.com/nightly - # if repo has file compression - compression: zip - # a secondary repo to use i.e. for not found in the main cores - secondary: - type: github - url: https://github.com/sergystepanov/libretro-spiegel/raw/main - compression: zip - # Libretro core configuration - # - # The emulator selection will happen in this order: - # - based on the folder name in the folder param - # - based on the folder name (core name) in the list (i.e. nes, snes) - # - based on the rom names in the roms param - # - # Available config params: - # - altRepo (bool) prioritize secondary repo as the download source - # - lib (string) - # - roms ([]string) - # - scale (int) scales the output video frames by this factor. - # - folder (string) - # By default emulator selection is based on the folder named as cores - # in the list (i.e. nes, snes) but if you specify folder param, - # then it will try to load the ROM file from that folder first. - # - width (int) -- broken - # - height (int) -- broken - # - ratio (float) - # - isGlAllowed (bool) - # - usesLibCo (bool) - # - hasMultitap (bool) -- (removed) - # - coreAspectRatio (bool) -- (deprecated) correct the aspect ratio on the client with the info from the core. - # - hid (map[int][]int) - # A list of device IDs to bind to the input ports. - # Can be seen in human readable form in the console when worker.debug is enabled. - # Some cores allow binding multiple devices to a single port (DosBox), but typically, - # you should bind just one device to one port. - # - kbMouseSupport (bool) -- (temp) a flag if the core needs the keyboard and mouse on the client - # - nonBlockingSave (bool) -- write save file in a non-blocking way, needed for huge save files - # - vfr (bool) - # (experimental) - # Enable variable frame rate only for cores that can't produce a constant frame rate. - # By default, we assume that cores output frames at a constant rate which equals - # their tick rate (1/system FPS), but OpenGL cores like N64 may have significant - # frame rendering time inconsistencies. In general, VFR for CFR cores leads to - # noticeable video stutter (with the current frame rendering time calculations). - # - options ([]string) a list of Libretro core options for tweaking. - # All keys of the options should be in the double quotes in order to preserve upper-case symbols. - # - options4rom (rom[[]string]) - # A list of core options to override for a specific core depending on the current ROM name. - # - hacks ([]string) a list of hacks. - # Available: - # - skip_hw_context_destroy -- don't destroy OpenGL context during Libretro core deinit. - # May help with crashes, for example, with PPSSPP. - # - skip_same_thread_save -- skip thread lock save (used with PPSSPP). - # - uniqueSaveDir (bool) -- needed only for cores (like DosBox) that persist their state into one shared file. - # This will allow for concurrent reading and saving of current states. - # - saveStateFs (string) -- the name of the file that will be initially copied into the save folder. - # All * symbols will be replaced to the name of the ROM. - list: - gba: - lib: mgba_libretro - roms: [ "gba", "gbc" ] - options: - mgba_audio_low_pass_filter: disabled - mgba_audio_low_pass_range: 50 - pcsx: - lib: pcsx_rearmed_libretro - roms: [ "cue", "chd" ] - # example of folder override - folder: psx - # see: https://github.com/libretro/pcsx_rearmed/blob/master/frontend/libretro_core_options.h - options: - "pcsx_rearmed_show_bios_bootlogo": enabled - "pcsx_rearmed_drc": enabled - "pcsx_rearmed_display_internal_fps": disabled - # MAME core requires additional manual setup, please read: - # https://docs.libretro.com/library/fbneo/ - mame: - lib: fbneo_libretro - folder: mame - roms: [ "zip" ] - nes: - lib: nestopia_libretro - roms: [ "nes" ] - options: - nestopia_aspect: "uncorrected" - snes: - lib: snes9x_libretro - roms: [ "smc", "sfc", "swc", "fig", "bs" ] - hid: - # set the 2nd port to RETRO_DEVICE_JOYPAD_MULTITAP ((1<<8) | 1) as SNES9x requires it - # in order to support up to 5-player games - # see: https://nintendo.fandom.com/wiki/Super_Multitap - 1: 257 - n64: - lib: mupen64plus_next_libretro - roms: [ "n64", "v64", "z64" ] - isGlAllowed: true - usesLibCo: true - vfr: true - # see: https://github.com/libretro/mupen64plus-libretro-nx/blob/master/libretro/libretro_core_options.h - options: - "mupen64plus-169screensize": 640x360 - "mupen64plus-43screensize": 320x240 - "mupen64plus-EnableCopyColorToRDRAM": Off - "mupen64plus-EnableCopyDepthToRDRAM": Off - "mupen64plus-EnableEnhancedTextureStorage": True - "mupen64plus-EnableFBEmulation": True - "mupen64plus-EnableLegacyBlending": True - "mupen64plus-FrameDuping": True - "mupen64plus-MaxTxCacheSize": 8000 - "mupen64plus-ThreadedRenderer": False - "mupen64plus-cpucore": dynamic_recompiler - "mupen64plus-pak1": memory - "mupen64plus-rdp-plugin": gliden64 - "mupen64plus-rsp-plugin": hle - "mupen64plus-astick-sensitivity": 100 - dos: - lib: dosbox_pure_libretro - roms: [ "zip", "cue" ] - folder: dos - kbMouseSupport: true - nonBlockingSave: true - saveStateFs: "*.pure.zip" - hid: - 0: [ 257, 513 ] - 1: [ 257, 513 ] - 2: [ 257, 513 ] - 3: [ 257, 513 ] - options: - "dosbox_pure_conf": "outside" - "dosbox_pure_force60fps": "true" + libretro: + # use zip compression for emulator save states + saveCompression: true + # Sets a limiter function for some spammy core callbacks. + # 0 - disabled, otherwise -- time in milliseconds for ignoring repeated calls except the last. + debounceMs: 0 + # Allow duplicate frames + dup: true + # Libretro cores logging level: DEBUG = 0, INFO, WARN, ERROR, DUMMY = INT_MAX + logLevel: 1 + cores: + paths: + libs: assets/cores + # Config params for Libretro cores repository, + # available types are: + # - buildbot (the default Libretro nightly repository) + # - github (GitHub raw repository with a similar structure to buildbot) + # - raw (just a link to a zip file extracted as is) + repo: + # enable auto-download for the list of cores (list->lib) + sync: true + # external cross-process mutex lock + extLock: "{user}/.cr/cloud-game.lock" + map: + darwin: + amd64: + arch: x86_64 + ext: .dylib + os: osx + vendor: apple + arm64: + arch: arm64 + ext: .dylib + os: osx + vendor: apple + linux: + amd64: + arch: x86_64 + ext: .so + os: linux + arm: + arch: armv7-neon-hf + ext: .so + os: linux + windows: + amd64: + arch: x86_64 + ext: .dll + os: windows + main: + type: buildbot + url: https://buildbot.libretro.com/nightly + # if repo has file compression + compression: zip + # a secondary repo to use i.e. for not found in the main cores + secondary: + type: github + url: https://github.com/sergystepanov/libretro-spiegel/raw/main + compression: zip + # Libretro core configuration + # + # The emulator selection will happen in this order: + # - based on the folder name in the folder param + # - based on the folder name (core name) in the list (i.e. nes, snes) + # - based on the rom names in the roms param + # + # Available config params: + # - altRepo (bool) prioritize secondary repo as the download source + # - lib (string) + # - roms ([]string) + # - scale (int) scales the output video frames by this factor. + # - folder (string) + # By default emulator selection is based on the folder named as cores + # in the list (i.e. nes, snes) but if you specify folder param, + # then it will try to load the ROM file from that folder first. + # - width (int) -- broken + # - height (int) -- broken + # - ratio (float) + # - isGlAllowed (bool) + # - usesLibCo (bool) + # - hasMultitap (bool) -- (removed) + # - coreAspectRatio (bool) -- (deprecated) correct the aspect ratio on the client with the info from the core. + # - hid (map[int][]int) + # A list of device IDs to bind to the input ports. + # Can be seen in human readable form in the console when worker.debug is enabled. + # Some cores allow binding multiple devices to a single port (DosBox), but typically, + # you should bind just one device to one port. + # - kbMouseSupport (bool) -- (temp) a flag if the core needs the keyboard and mouse on the client + # - nonBlockingSave (bool) -- write save file in a non-blocking way, needed for huge save files + # - vfr (bool) + # (experimental) + # Enable variable frame rate only for cores that can't produce a constant frame rate. + # By default, we assume that cores output frames at a constant rate which equals + # their tick rate (1/system FPS), but OpenGL cores like N64 may have significant + # frame rendering time inconsistencies. In general, VFR for CFR cores leads to + # noticeable video stutter (with the current frame rendering time calculations). + # - options ([]string) a list of Libretro core options for tweaking. + # All keys of the options should be in the double quotes in order to preserve upper-case symbols. + # - options4rom (rom[[]string]) + # A list of core options to override for a specific core depending on the current ROM name. + # - hacks ([]string) a list of hacks. + # Available: + # - skip_hw_context_destroy -- don't destroy OpenGL context during Libretro core deinit. + # May help with crashes, for example, with PPSSPP. + # - skip_same_thread_save -- skip thread lock save (used with PPSSPP). + # - uniqueSaveDir (bool) -- needed only for cores (like DosBox) that persist their state into one shared file. + # This will allow for concurrent reading and saving of current states. + # - saveStateFs (string) -- the name of the file that will be initially copied into the save folder. + # All * symbols will be replaced to the name of the ROM. + list: + gba: + lib: mgba_libretro + roms: ["gba", "gbc"] + options: + mgba_audio_low_pass_filter: disabled + mgba_audio_low_pass_range: 50 + pcsx: + lib: pcsx_rearmed_libretro + roms: ["cue", "chd"] + # example of folder override + folder: psx + # see: https://github.com/libretro/pcsx_rearmed/blob/master/frontend/libretro_core_options.h + options: + "pcsx_rearmed_show_bios_bootlogo": enabled + "pcsx_rearmed_drc": enabled + "pcsx_rearmed_display_internal_fps": disabled + # MAME core requires additional manual setup, please read: + # https://docs.libretro.com/library/fbneo/ + mame: + lib: fbneo_libretro + folder: mame + roms: ["zip"] + nes: + lib: nestopia_libretro + roms: ["nes"] + options: + nestopia_aspect: "uncorrected" + snes: + lib: snes9x_libretro + roms: ["smc", "sfc", "swc", "fig", "bs"] + hid: + # set the 2nd port to RETRO_DEVICE_JOYPAD_MULTITAP ((1<<8) | 1) as SNES9x requires it + # in order to support up to 5-player games + # see: https://nintendo.fandom.com/wiki/Super_Multitap + 1: 257 + n64: + lib: mupen64plus_next_libretro + roms: ["n64", "v64", "z64"] + isGlAllowed: true + usesLibCo: true + vfr: true + # see: https://github.com/libretro/mupen64plus-libretro-nx/blob/master/libretro/libretro_core_options.h + options: + "mupen64plus-169screensize": 640x360 + "mupen64plus-43screensize": 320x240 + "mupen64plus-EnableCopyColorToRDRAM": Off + "mupen64plus-EnableCopyDepthToRDRAM": Off + "mupen64plus-EnableEnhancedTextureStorage": True + "mupen64plus-EnableFBEmulation": True + "mupen64plus-EnableLegacyBlending": True + "mupen64plus-FrameDuping": True + "mupen64plus-MaxTxCacheSize": 8000 + "mupen64plus-ThreadedRenderer": False + "mupen64plus-cpucore": dynamic_recompiler + "mupen64plus-pak1": memory + "mupen64plus-rdp-plugin": gliden64 + "mupen64plus-rsp-plugin": hle + "mupen64plus-astick-sensitivity": 100 + dos: + lib: dosbox_pure_libretro + roms: ["zip", "cue"] + folder: dos + kbMouseSupport: true + nonBlockingSave: true + saveStateFs: "*.pure.zip" + hid: + 0: [257, 513] + 1: [257, 513] + 2: [257, 513] + 3: [257, 513] + options: + "dosbox_pure_conf": "outside" + "dosbox_pure_force60fps": "true" encoder: - audio: - # audio frame duration needed for WebRTC (Opus) - # most of the emulators have ~1400 samples per a video frame, - # so we keep the frame buffer roughly half of that size or 2 RTC packets per frame - # (deprecated) due to frames - frame: 10 - # dynamic frames for Opus encoder - frames: - - 10 - - 5 - # speex (2), linear (1) or nearest neighbour (0) audio resampler - # linear should sound slightly better than 0 - resampler: 2 - video: - # h264, vpx (vp8) or vp9 - codec: h264 - # Threaded encoder if supported, 0 - auto, 1 - nope, >1 - multi-threaded - threads: 0 - # see: https://trac.ffmpeg.org/wiki/Encode/H.264 - h264: - # crf, cbr - mode: crf - # Constant Rate Factor (CRF) 0-51 (default: 23) - crf: 23 - # Rate control options - # set the maximum bitrate - maxRate: 0 - # set the expected client buffer size - bufSize: 0 - # ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo - preset: superfast - # baseline, main, high, high10, high422, high444 - profile: baseline - # film, animation, grain, stillimage, psnr, ssim, fastdecode, zerolatency - tune: zerolatency - # 0-3 - logLevel: 0 - # see: https://www.webmproject.org/docs/encoder-parameters - vpx: - # target bitrate (KBit/s) - bitrate: 1200 - # force keyframe interval - keyframeInterval: 5 + audio: + # audio frame duration needed for WebRTC (Opus) + # most of the emulators have ~1400 samples per a video frame, + # so we keep the frame buffer roughly half of that size or 2 RTC packets per frame + # (deprecated) due to frames + frame: 10 + # dynamic frames for Opus encoder + frames: + - 10 + - 5 + # speex (2), linear (1) or nearest neighbour (0) audio resampler + # linear should sound slightly better than 0 + resampler: 2 + video: + # h264, vpx (vp8) or vp9 + codec: h264 + # Threaded encoder if supported, 0 - auto, 1 - nope, >1 - multi-threaded + threads: 0 + # see: https://trac.ffmpeg.org/wiki/Encode/H.264 + h264: + # crf, cbr + mode: crf + # Constant Rate Factor (CRF) 0-51 (default: 23) + crf: 23 + # Rate control options + # set the maximum bitrate + maxRate: 0 + # set the expected client buffer size + bufSize: 0 + # ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo + preset: superfast + # baseline, main, high, high10, high422, high444 + profile: baseline + # film, animation, grain, stillimage, psnr, ssim, fastdecode, zerolatency + tune: zerolatency + # 0-3 + logLevel: 0 + # see: https://www.webmproject.org/docs/encoder-parameters + vpx: + # target bitrate (KBit/s) + bitrate: 1200 + # force keyframe interval + keyframeInterval: 120 + # speed/quality tradeoff (0=auto, 1=slow, 8=fast) + cpuUsed: 0 + # log2 of tile columns (0=auto, 0-6). VP9 only. + tileColumns: 0 + # content tuning: "screen" for games. VP9 only. + tune: screen # game recording # (experimental) @@ -382,66 +388,66 @@ encoder: # video as a list of PNG-encoded images, and # one additional FFMPEG concat demux file recording: - enabled: false - # name contains the name of the recording dir (or zip) - # format: - # %date:go_time_format% -- refer: https://go.dev/src/time/format.go - # %user% -- user name who started the recording - # %game% -- game name (game ROM name) - # %rand:len% -- a random string of given length - # as example: 20210101101010_yeE_user1_badApple - name: "%date:20060102150405%_%rand:3%_%user%_%game%" - # zip and remove recording dir on completion - zip: true - # save directory - folder: ./recording + enabled: false + # name contains the name of the recording dir (or zip) + # format: + # %date:go_time_format% -- refer: https://go.dev/src/time/format.go + # %user% -- user name who started the recording + # %game% -- game name (game ROM name) + # %rand:len% -- a random string of given length + # as example: 20210101101010_yeE_user1_badApple + name: "%date:20060102150405%_%rand:3%_%user%_%game%" + # zip and remove recording dir on completion + zip: true + # save directory + folder: ./recording # cloud storage options # it is mandatory to use a cloud storage when running # a distributed multi-server configuration in order to # share save states between nodes (resume games on a different worker) storage: - # cloud storage provider: - # - empty (No op storage stub) - # - s3 (S3 API compatible object storage) - provider: - s3Endpoint: - s3BucketName: - s3AccessKeyId: - s3SecretAccessKey: + # cloud storage provider: + # - empty (No op storage stub) + # - s3 (S3 API compatible object storage) + provider: + s3Endpoint: + s3BucketName: + s3AccessKeyId: + s3SecretAccessKey: webrtc: - # turn off default Pion interceptors (see: https://github.com/pion/interceptor) - # (performance) - disableDefaultInterceptors: false - # indicates the role of the DTLS transport (see: https://github.com/pion/webrtc/blob/master/dtlsrole.go) - # (debug) - # - (default) - # - 1 (auto) - # - 2 (client) - # - 3 (server) - dtlsRole: - # a list of STUN/TURN servers to use - iceServers: - - urls: stun:stun.l.google.com:19302 - # configures whether the ice agent should be a lite agent (true/false) - # (performance) - # don't use iceServers when enabled - iceLite: false - # ICE configuration - # by default, ICE ports are random and unlimited - # alternatives: - # 1. instead of random unlimited port range for - # WebRTC connections, these params limit port range of ICE connections - icePorts: - min: - max: - includeLoopbackCandidate: false - # 2. select a single port to forward all ICE connections there - singlePort: - # override ICE candidate IP, see: https://github.com/pion/webrtc/issues/835, - # can be used for Docker bridged network internal IP override - iceIpMap: - # set additional log level for WebRTC separately - # -1 - trace, 6 - nothing, ..., debug - 0 - logLevel: 6 + # turn off default Pion interceptors (see: https://github.com/pion/interceptor) + # (performance) + disableDefaultInterceptors: false + # indicates the role of the DTLS transport (see: https://github.com/pion/webrtc/blob/master/dtlsrole.go) + # (debug) + # - (default) + # - 1 (auto) + # - 2 (client) + # - 3 (server) + dtlsRole: + # a list of STUN/TURN servers to use + iceServers: + - urls: stun:stun.l.google.com:19302 + # configures whether the ice agent should be a lite agent (true/false) + # (performance) + # don't use iceServers when enabled + iceLite: false + # ICE configuration + # by default, ICE ports are random and unlimited + # alternatives: + # 1. instead of random unlimited port range for + # WebRTC connections, these params limit port range of ICE connections + icePorts: + min: + max: + includeLoopbackCandidate: false + # 2. select a single port to forward all ICE connections there + singlePort: + # override ICE candidate IP, see: https://github.com/pion/webrtc/issues/835, + # can be used for Docker bridged network internal IP override + iceIpMap: + # set additional log level for WebRTC separately + # -1 - trace, 6 - nothing, ..., debug - 0 + logLevel: 6 diff --git a/pkg/config/worker.go b/pkg/config/worker.go index 014ce644..f0dc80d8 100644 --- a/pkg/config/worker.go +++ b/pkg/config/worker.go @@ -71,6 +71,9 @@ type Video struct { Vpx struct { Bitrate uint KeyframeInterval uint + CpuUsed int + TileColumns int + Tune string } } diff --git a/pkg/encoder/vpx/libvpx.go b/pkg/encoder/vpx/libvpx.go index c175c14f..33b3b573 100644 --- a/pkg/encoder/vpx/libvpx.go +++ b/pkg/encoder/vpx/libvpx.go @@ -14,6 +14,10 @@ package vpx #define VP8_FOURCC 0x30385056 #define VP9_FOURCC 0x30395056 +#ifndef VP9E_TUNE_SCREEN +#define VP9E_TUNE_SCREEN 1 +#endif + typedef struct VpxInterface { const char *const name; const uint32_t fourcc; @@ -43,6 +47,30 @@ FrameBuffer get_frame_buffer(vpx_codec_ctx_t *codec, vpx_codec_iter_t *iter) { return fb; } +vpx_codec_err_t set_vpx_cpu_used(vpx_codec_ctx_t *ctx, int value) { + return vpx_codec_control_(ctx, VP8E_SET_CPUUSED, value); +} + +vpx_codec_err_t set_vp9_tile_columns(vpx_codec_ctx_t *ctx, int value) { + return vpx_codec_control_(ctx, VP9E_SET_TILE_COLUMNS, value); +} + +vpx_codec_err_t set_vp9_tune_content(vpx_codec_ctx_t *ctx, int value) { + return vpx_codec_control_(ctx, VP9E_SET_TUNE_CONTENT, value); +} + +vpx_codec_err_t set_vp9_row_mt(vpx_codec_ctx_t *ctx, int value) { + return vpx_codec_control_(ctx, VP9E_SET_ROW_MT, value); +} + +vpx_codec_err_t set_vp8_auto_alt_ref(vpx_codec_ctx_t *ctx, int value) { + return vpx_codec_control_(ctx, VP8E_SET_ENABLEAUTOALTREF, value); +} + +vpx_codec_err_t set_vp8_noise_sensitivity(vpx_codec_ctx_t *ctx, int value) { + return vpx_codec_control_(ctx, VP8E_SET_NOISE_SENSITIVITY, value); +} + const VpxInterface vpx_encoders[] = { { "vp8", VP8_FOURCC, &vpx_codec_vp8_cx }, { "vp9", VP9_FOURCC, &vpx_codec_vp9_cx }, @@ -80,6 +108,7 @@ void vpx_img_read(vpx_image_t *dst, void *src) { import "C" import ( "fmt" + "runtime" "unsafe" ) @@ -94,11 +123,32 @@ type Vpx struct { func (vpx *Vpx) SetFlip(b bool) { vpx.flipped = b } +func autoThreads(configured, cpus int) int { + if configured != 0 { + return configured + } + reserve := cpus / 4 + if reserve < 1 { + reserve = 1 + } + if t := cpus - reserve; t > 0 { + return t + } + return 1 +} + type Options struct { // Target bandwidth to use for this stream, in kilobits per second. Bitrate uint // Force keyframe interval. KeyframeInterval uint + // Speed/quality tradeoff (0=auto, 1–8 manual). Defaults to 0. + // Applies to both VP8 and VP9. + CpuUsed int + // Log2 of tile columns for VP9 frame-level parallelism (0–6). VP9 only. + TileColumns int + // Content tuning: "screen" for game/screen content. VP9 only. + Tune string } func NewEncoder(w, h int, th int, version int, opts *Options) (*Vpx, error) { @@ -114,7 +164,9 @@ func NewEncoder(w, h int, th int, version int, opts *Options) (*Vpx, error) { if opts == nil { opts = &Options{ Bitrate: 1200, - KeyframeInterval: 5, + KeyframeInterval: 120, + TileColumns: 2, + Tune: "screen", } } @@ -135,17 +187,56 @@ func NewEncoder(w, h int, th int, version int, opts *Options) (*Vpx, error) { cfg.g_w = C.uint(w) cfg.g_h = C.uint(h) - if th != 0 { - cfg.g_threads = C.uint(th) - } + cfg.g_threads = C.uint(autoThreads(th, runtime.NumCPU())) cfg.g_lag_in_frames = 0 cfg.rc_target_bitrate = C.uint(opts.Bitrate) - cfg.g_error_resilient = C.VPX_ERROR_RESILIENT_DEFAULT + cfg.g_error_resilient = 0 if C.call_vpx_codec_enc_init(&vpx.codecCtx, encoder, &cfg) != 0 { return nil, fmt.Errorf("failed to initialize encoder") } + // Speed/quality tradeoff: higher = faster, lower = better quality. + if opts.CpuUsed == 0 { + thr := autoThreads(th, runtime.NumCPU()) + switch { + case thr <= 2: + opts.CpuUsed = 8 + case thr <= 4: + opts.CpuUsed = 7 + default: + opts.CpuUsed = 6 + } + } + C.set_vpx_cpu_used(&vpx.codecCtx, C.int(opts.CpuUsed)) + + // VP9-specific tuning. + if version == 9 { + if opts.TileColumns == 0 { + thr := autoThreads(th, runtime.NumCPU()) + if thr >= 4 { + opts.TileColumns = 2 + } else if thr >= 2 { + opts.TileColumns = 1 + } + } + if opts.TileColumns > 0 { + C.set_vp9_tile_columns(&vpx.codecCtx, C.int(opts.TileColumns)) + } + if opts.Tune == "screen" { + C.set_vp9_tune_content(&vpx.codecCtx, C.VP9E_TUNE_SCREEN) + } + C.set_vp9_row_mt(&vpx.codecCtx, 1) + } + + // VP8 real-time tuning: disable features that add latency. + if version == 8 { + // No alt-ref frames for zero-latency. + C.set_vp8_auto_alt_ref(&vpx.codecCtx, 0) + // Reduce noise sensitivity — game content is low-noise. + C.set_vp8_noise_sensitivity(&vpx.codecCtx, 0) + } + return &vpx, nil }