From 5e7be6b480929b8e63a76eccbae441254c5e878b Mon Sep 17 00:00:00 2001 From: midchildan Date: Sat, 12 Feb 2022 19:58:22 +0900 Subject: nixos/epgstation: changes for EPGStation v2 --- .../modules/services/video/epgstation/default.nix | 348 +++++++++++---------- .../services/video/epgstation/streaming.json | 239 +++++++------- 2 files changed, 310 insertions(+), 277 deletions(-) (limited to 'nixos/modules/services/video') diff --git a/nixos/modules/services/video/epgstation/default.nix b/nixos/modules/services/video/epgstation/default.nix index 41613dcbb3b..71428a00e4d 100644 --- a/nixos/modules/services/video/epgstation/default.nix +++ b/nixos/modules/services/video/epgstation/default.nix @@ -1,30 +1,40 @@ { config, lib, options, pkgs, ... }: -with lib; - let cfg = config.services.epgstation; opt = options.services.epgstation; + description = "EPGStation: DVR system for Mirakurun-managed TV tuners"; + username = config.users.users.epgstation.name; groupname = config.users.users.epgstation.group; + mirakurun = { + sock = config.services.mirakurun.unixSocket; + option = options.services.mirakurun.unixSocket; + }; - settingsFmt = pkgs.formats.json {}; - settingsTemplate = settingsFmt.generate "config.json" cfg.settings; + yaml = pkgs.formats.yaml { }; + settingsTemplate = yaml.generate "config.yml" cfg.settings; preStartScript = pkgs.writeScript "epgstation-prestart" '' #!${pkgs.runtimeShell} - PASSWORD="$(head -n1 "${cfg.basicAuth.passwordFile}")" - DB_PASSWORD="$(head -n1 "${cfg.database.passwordFile}")" + DB_PASSWORD_FILE=${lib.escapeShellArg cfg.database.passwordFile} + + if [[ ! -f "$DB_PASSWORD_FILE" ]]; then + printf "[FATAL] File containing the DB password was not found in '%s'. Double check the NixOS option '%s'." \ + "$DB_PASSWORD_FILE" ${lib.escapeShellArg opt.database.passwordFile} >&2 + exit 1 + fi + + DB_PASSWORD="$(head -n1 ${lib.escapeShellArg cfg.database.passwordFile})" # setup configuration - touch /etc/epgstation/config.json - chmod 640 /etc/epgstation/config.json + touch /etc/epgstation/config.yml + chmod 640 /etc/epgstation/config.yml sed \ - -e "s,@password@,$PASSWORD,g" \ -e "s,@dbPassword@,$DB_PASSWORD,g" \ - ${settingsTemplate} > /etc/epgstation/config.json - chown "${username}:${groupname}" /etc/epgstation/config.json + ${settingsTemplate} > /etc/epgstation/config.yml + chown "${username}:${groupname}" /etc/epgstation/config.yml # NOTE: Use password authentication, since mysqljs does not yet support auth_socket if [ ! -e /var/lib/epgstation/db-created ]; then @@ -35,7 +45,7 @@ let ''; streamingConfig = lib.importJSON ./streaming.json; - logConfig = { + logConfig = yaml.generate "logConfig.yml" { appenders.stdout.type = "stdout"; categories = { default = { appenders = [ "stdout" ]; level = "info"; }; @@ -45,53 +55,51 @@ let }; }; - defaultPassword = "INSECURE_GO_CHECK_CONFIGURATION_NIX\n"; + # Deprecate top level options that are redundant. + deprecateTopLevelOption = config: + lib.mkRenamedOptionModule + ([ "services" "epgstation" ] ++ config) + ([ "services" "epgstation" "settings" ] ++ config); + + removeOption = config: instruction: + lib.mkRemovedOptionModule + ([ "services" "epgstation" ] ++ config) + instruction; in { - options.services.epgstation = { - enable = mkEnableOption "EPGStation: DTV Software in Japan"; + meta.maintainers = with lib.maintainers; [ midchildan ]; - usePreconfiguredStreaming = mkOption { - type = types.bool; - default = true; - description = '' - Use preconfigured default streaming options. + imports = [ + (deprecateTopLevelOption [ "port" ]) + (deprecateTopLevelOption [ "socketioPort" ]) + (deprecateTopLevelOption [ "clientSocketioPort" ]) + (removeOption [ "basicAuth" ] + "Use a TLS-terminated reverse proxy with authentication instead.") + ]; - Upstream defaults: - - ''; - }; + options.services.epgstation = { + enable = lib.mkEnableOption description; - port = mkOption { - type = types.port; - default = 20772; - description = '' - HTTP port for EPGStation to listen on. - ''; + package = lib.mkOption { + default = pkgs.epgstation; + type = lib.types.package; + defaultText = lib.literalExpression "pkgs.epgstation"; + description = "epgstation package to use"; }; - socketioPort = mkOption { - type = types.port; - default = cfg.port + 1; - defaultText = literalExpression "config.${opt.port} + 1"; + usePreconfiguredStreaming = lib.mkOption { + type = lib.types.bool; + default = true; description = '' - Socket.io port for EPGStation to listen on. - ''; - }; + Use preconfigured default streaming options. - clientSocketioPort = mkOption { - type = types.port; - default = cfg.socketioPort; - defaultText = literalExpression "config.${opt.socketioPort}"; - description = '' - Socket.io port that the web client is going to connect to. This may be - different from if EPGStation is hidden - behind a reverse proxy. + Upstream defaults: + ''; }; - openFirewall = mkOption { - type = types.bool; + openFirewall = lib.mkOption { + type = lib.types.bool; default = false; description = '' Open ports in the firewall for the EPGStation web interface. @@ -106,50 +114,17 @@ in ''; }; - basicAuth = { - user = mkOption { - type = with types; nullOr str; - default = null; - example = "epgstation"; - description = '' - Basic auth username for EPGStation. If null, basic - auth will be disabled. - - - - Basic authentication has known weaknesses, the most critical being - that it sends passwords over the network in clear text. Use this - feature to control access to EPGStation within your family and - friends, but don't rely on it for security. - - - ''; - }; - - passwordFile = mkOption { - type = types.path; - default = pkgs.writeText "epgstation-password" defaultPassword; - defaultText = literalDocBook ''a file containing ${defaultPassword}''; - example = "/run/keys/epgstation-password"; - description = '' - A file containing the password for . - ''; - }; - }; - - database = { - name = mkOption { - type = types.str; + database = { + name = lib.mkOption { + type = lib.types.str; default = "epgstation"; description = '' Name of the MySQL database that holds EPGStation's data. ''; }; - passwordFile = mkOption { - type = types.path; - default = pkgs.writeText "epgstation-db-password" defaultPassword; - defaultText = literalDocBook ''a file containing ${defaultPassword}''; + passwordFile = lib.mkOption { + type = lib.types.path; example = "/run/keys/epgstation-db-password"; description = '' A file containing the password for the database named @@ -158,69 +133,106 @@ in }; }; - settings = mkOption { + # The defaults for some options come from the upstream template + # configuration, which is the one that users would get if they follow the + # upstream instructions. This is, in some cases, different from the + # application defaults. Some options like encodeProcessNum and + # concurrentEncodeNum doesn't have an optimal default value that works for + # all hardware setups and/or performance requirements. For those kind of + # options, the application default wouldn't always result in the expected + # out-of-the-box behavior because it's the responsibility of the user to + # configure them according to their needs. In these cases, the value in the + # upstream template configuration should serve as a "good enough" default. + settings = lib.mkOption { description = '' - Options to add to config.json. + Options to add to config.yml. Documentation: ''; - default = {}; + default = { }; example = { recPriority = 20; conflictPriority = 10; }; - type = types.submodule { - freeformType = settingsFmt.type; + type = lib.types.submodule { + freeformType = yaml.type; + + options.port = lib.mkOption { + type = lib.types.port; + default = 20772; + description = '' + HTTP port for EPGStation to listen on. + ''; + }; - options.readOnlyOnce = mkOption { - type = types.bool; - default = false; - description = "Don't reload configuration files at runtime."; + options.socketioPort = lib.mkOption { + type = lib.types.port; + default = cfg.settings.port + 1; + defaultText = lib.literalExpression "config.${opt.settings.port} + 1"; + description = '' + Socket.io port for EPGStation to listen on. It is valid to share + ports with . + ''; }; - options.mirakurunPath = mkOption (let - sockPath = config.services.mirakurun.unixSocket; - in { - type = types.str; - default = "http+unix://${replaceStrings ["/"] ["%2F"] sockPath}"; - defaultText = literalExpression '' - "http+unix://''${replaceStrings ["/"] ["%2F"] config.${options.services.mirakurun.unixSocket}}" + options.clientSocketioPort = lib.mkOption { + type = lib.types.port; + default = cfg.settings.socketioPort; + defaultText = lib.literalExpression "config.${opt.settings.socketioPort}"; + description = '' + Socket.io port that the web client is going to connect to. This may + be different from if + EPGStation is hidden behind a reverse proxy. + ''; + }; + + options.mirakurunPath = with mirakurun; lib.mkOption { + type = lib.types.str; + default = "http+unix://${lib.replaceStrings ["/"] ["%2F"] sock}"; + defaultText = lib.literalExpression '' + "http+unix://''${lib.replaceStrings ["/"] ["%2F"] config.${option}}" ''; example = "http://localhost:40772"; description = "URL to connect to Mirakurun."; - }); + }; + + options.encodeProcessNum = lib.mkOption { + type = lib.types.ints.positive; + default = 4; + description = '' + The maximum number of processes that EPGStation would allow to run + at the same time for encoding or streaming videos. + ''; + }; + + options.concurrentEncodeNum = lib.mkOption { + type = lib.types.ints.positive; + default = 1; + description = '' + The maximum number of encoding jobs that EPGStation would run at the + same time. + ''; + }; - options.encode = mkOption { - type = with types; listOf attrs; + options.encode = lib.mkOption { + type = with lib.types; listOf attrs; description = "Encoding presets for recorded videos."; default = [ { - name = "H264"; - cmd = "${pkgs.epgstation}/libexec/enc.sh main"; + name = "H.264"; + cmd = "%NODE% ${cfg.package}/libexec/enc.js"; suffix = ".mp4"; - default = true; - } - { - name = "H264-sub"; - cmd = "${pkgs.epgstation}/libexec/enc.sh sub"; - suffix = "-sub.mp4"; } ]; - defaultText = literalExpression '' + defaultText = lib.literalExpression '' [ { - name = "H264"; - cmd = "''${pkgs.epgstation}/libexec/enc.sh main"; + name = "H.264"; + cmd = "%NODE% config.${opt.package}/libexec/enc.js"; suffix = ".mp4"; - default = true; - } - { - name = "H264-sub"; - cmd = "''${pkgs.epgstation}/libexec/enc.sh sub"; - suffix = "-sub.mp4"; } ] ''; @@ -229,14 +241,25 @@ in }; }; - config = mkIf cfg.enable { + config = lib.mkIf cfg.enable { + assertions = [ + { + assertion = !(lib.hasAttr "readOnlyOnce" cfg.settings); + message = '' + The option config.${opt.settings}.readOnlyOnce can no longer be used + since it's been removed. No replacements are available. + ''; + } + ]; + environment.etc = { - "epgstation/operatorLogConfig.json".text = builtins.toJSON logConfig; - "epgstation/serviceLogConfig.json".text = builtins.toJSON logConfig; + "epgstation/epgUpdaterLogConfig.yml".source = logConfig; + "epgstation/operatorLogConfig.yml".source = logConfig; + "epgstation/serviceLogConfig.yml".source = logConfig; }; - networking.firewall = mkIf cfg.openFirewall { - allowedTCPPorts = with cfg; [ port socketioPort ]; + networking.firewall = lib.mkIf cfg.openFirewall { + allowedTCPPorts = with cfg.settings; [ port socketioPort ]; }; users.users.epgstation = { @@ -245,13 +268,13 @@ in isSystemUser = true; }; - users.groups.epgstation = {}; + users.groups.epgstation = { }; - services.mirakurun.enable = mkDefault true; + services.mirakurun.enable = lib.mkDefault true; services.mysql = { - enable = mkDefault true; - package = mkDefault pkgs.mariadb; + enable = lib.mkDefault true; + package = lib.mkDefault pkgs.mariadb; ensureDatabases = [ cfg.database.name ]; # FIXME: enable once mysqljs supports auth_socket # ensureUsers = [ { @@ -260,39 +283,28 @@ in # } ]; }; - services.epgstation.settings = let - defaultSettings = { - serverPort = cfg.port; - socketioPort = cfg.socketioPort; - clientSocketioPort = cfg.clientSocketioPort; - - dbType = mkDefault "mysql"; - mysql = { - user = username; - database = cfg.database.name; - socketPath = mkDefault "/run/mysqld/mysqld.sock"; - password = mkDefault "@dbPassword@"; - connectTimeout = mkDefault 1000; - connectionLimit = mkDefault 10; + services.epgstation.settings = + let + defaultSettings = { + dbtype = lib.mkDefault "mysql"; + mysql = { + socketPath = lib.mkDefault "/run/mysqld/mysqld.sock"; + user = username; + password = lib.mkDefault "@dbPassword@"; + database = cfg.database.name; + }; + + ffmpeg = lib.mkDefault "${pkgs.ffmpeg-full}/bin/ffmpeg"; + ffprobe = lib.mkDefault "${pkgs.ffmpeg-full}/bin/ffprobe"; + + # for disambiguation with TypeScript files + recordedFileExtension = lib.mkDefault ".m2ts"; }; - - basicAuth = mkIf (cfg.basicAuth.user != null) { - user = mkDefault cfg.basicAuth.user; - password = mkDefault "@password@"; - }; - - ffmpeg = mkDefault "${pkgs.ffmpeg-full}/bin/ffmpeg"; - ffprobe = mkDefault "${pkgs.ffmpeg-full}/bin/ffprobe"; - - fileExtension = mkDefault ".m2ts"; - maxEncode = mkDefault 2; - maxStreaming = mkDefault 2; - }; - in - mkMerge [ - defaultSettings - (mkIf cfg.usePreconfiguredStreaming streamingConfig) - ]; + in + lib.mkMerge [ + defaultSettings + (lib.mkIf cfg.usePreconfiguredStreaming streamingConfig) + ]; systemd.tmpfiles.rules = [ "d '/var/lib/epgstation/streamfiles' - ${username} ${groupname} - -" @@ -301,15 +313,15 @@ in ]; systemd.services.epgstation = { - description = pkgs.epgstation.meta.description; + inherit description; + wantedBy = [ "multi-user.target" ]; - after = [ - "network.target" - ] ++ optional config.services.mirakurun.enable "mirakurun.service" - ++ optional config.services.mysql.enable "mysql.service"; + after = [ "network.target" ] + ++ lib.optional config.services.mirakurun.enable "mirakurun.service" + ++ lib.optional config.services.mysql.enable "mysql.service"; serviceConfig = { - ExecStart = "${pkgs.epgstation}/bin/epgstation start"; + ExecStart = "${cfg.package}/bin/epgstation start"; ExecStartPre = "+${preStartScript}"; User = username; Group = groupname; diff --git a/nixos/modules/services/video/epgstation/streaming.json b/nixos/modules/services/video/epgstation/streaming.json index 8eb99cf8558..6c6f9b261b2 100644 --- a/nixos/modules/services/video/epgstation/streaming.json +++ b/nixos/modules/services/video/epgstation/streaming.json @@ -1,119 +1,140 @@ { - "liveHLS": [ - { - "name": "720p", - "cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -map 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 17 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast -flags +loop-global_header %OUTPUT%" + "urlscheme": { + "m2ts": { + "ios": "vlc-x-callback://x-callback-url/stream?url=PROTOCOL://ADDRESS", + "android": "intent://ADDRESS#Intent;package=org.videolan.vlc;type=video;scheme=PROTOCOL;end" }, - { - "name": "480p", - "cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -map 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 17 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast -flags +loop-global_header %OUTPUT%" + "video": { + "ios": "infuse://x-callback-url/play?url=PROTOCOL://ADDRESS", + "android": "intent://ADDRESS#Intent;package=com.mxtech.videoplayer.ad;type=video;scheme=PROTOCOL;end" }, - { - "name": "180p", - "cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -map 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 17 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -c:a aac -ar 48000 -b:a 48k -ac 2 -c:v libx264 -vf yadif,scale=-2:180 -b:v 100k -preset veryfast -maxrate 110k -bufsize 1000k -flags +loop-global_header %OUTPUT%" + "download": { + "ios": "vlc-x-callback://x-callback-url/download?url=PROTOCOL://ADDRESS&filename=FILENAME" } - ], - "liveMP4": [ - { - "name": "720p", - "cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1" - }, - { - "name": "480p", - "cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1" - } - ], - "liveWebM": [ - { - "name": "720p", - "cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 3 -c:a libvorbis -ar 48000 -b:a 192k -ac 2 -c:v libvpx-vp9 -vf yadif,scale=-2:720 -b:v 3000k -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1" - }, - { - "name": "480p", - "cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 2 -c:a libvorbis -ar 48000 -b:a 128k -ac 2 -c:v libvpx-vp9 -vf yadif,scale=-2:480 -b:v 1500k -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1" - } - ], - "mpegTsStreaming": [ - { - "name": "720p", - "cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast -y -f mpegts pipe:1" - }, - { - "name": "480p", - "cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast -y -f mpegts pipe:1" - }, - { - "name": "Original" - } - ], - "mpegTsViewer": { - "ios": "vlc-x-callback://x-callback-url/stream?url=http://ADDRESS", - "android": "intent://ADDRESS#Intent;package=com.mxtech.videoplayer.ad;type=video;scheme=http;end" - }, - "recordedDownloader": { - "ios": "vlc-x-callback://x-callback-url/download?url=http://ADDRESS&filename=FILENAME", - "android": "intent://ADDRESS#Intent;package=com.dv.adm;type=video;scheme=http;end" }, - "recordedStreaming": { - "webm": [ - { - "name": "720p", - "cmd": "%FFMPEG% -dual_mono_mode main %RE% -i pipe:0 -sn -threads 3 -c:a libvorbis -ar 48000 -ac 2 -c:v libvpx-vp9 -vf yadif,scale=-2:720 %VB% %VBUFFER% %AB% %ABUFFER% -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1", - "vb": "3000k", - "ab": "192k" - }, - { - "name": "360p", - "cmd": "%FFMPEG% -dual_mono_mode main %RE% -i pipe:0 -sn -threads 2 -c:a libvorbis -ar 48000 -ac 2 -c:v libvpx-vp9 -vf yadif,scale=-2:360 %VB% %VBUFFER% %AB% %ABUFFER% -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1", - "vb": "1500k", - "ab": "128k" - } - ], - "mp4": [ - { - "name": "720p", - "cmd": "%FFMPEG% -dual_mono_mode main %RE% -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -ac 2 -c:v libx264 -vf yadif,scale=-2:720 %VB% %VBUFFER% %AB% %ABUFFER% -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1", - "vb": "3000k", - "ab": "192k" - }, - { - "name": "360p", - "cmd": "%FFMPEG% -dual_mono_mode main %RE% -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -ac 2 -c:v libx264 -vf yadif,scale=-2:360 %VB% %VBUFFER% %AB% %ABUFFER% -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1", - "vb": "1500k", - "ab": "128k" + "stream": { + "live": { + "ts": { + "m2ts": [ + { + "name": "720p", + "cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast -y -f mpegts pipe:1" + }, + { + "name": "480p", + "cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast -y -f mpegts pipe:1" + }, + { + "name": "無変換" + } + ], + "m2tsll": [ + { + "name": "720p", + "cmd": "%FFMPEG% -dual_mono_mode main -f mpegts -analyzeduration 500000 -i pipe:0 -map 0 -c:s copy -c:d copy -ignore_unknown -fflags nobuffer -flags low_delay -max_delay 250000 -max_interleave_delta 1 -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -flags +cgop -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast -y -f mpegts pipe:1" + }, + { + "name": "480p", + "cmd": "%FFMPEG% -dual_mono_mode main -f mpegts -analyzeduration 500000 -i pipe:0 -map 0 -c:s copy -c:d copy -ignore_unknown -fflags nobuffer -flags low_delay -max_delay 250000 -max_interleave_delta 1 -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -flags +cgop -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast -y -f mpegts pipe:1" + } + ], + "webm": [ + { + "name": "720p", + "cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 3 -c:a libvorbis -ar 48000 -b:a 192k -ac 2 -c:v libvpx-vp9 -vf yadif,scale=-2:720 -b:v 3000k -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1" + }, + { + "name": "480p", + "cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 2 -c:a libvorbis -ar 48000 -b:a 128k -ac 2 -c:v libvpx-vp9 -vf yadif,scale=-2:480 -b:v 1500k -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1" + } + ], + "mp4": [ + { + "name": "720p", + "cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1" + }, + { + "name": "480p", + "cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1" + } + ], + "hls": [ + { + "name": "720p", + "cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -map 0 -threads 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 17 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast -flags +loop-global_header %OUTPUT%" + }, + { + "name": "480p", + "cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -map 0 -threads 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 17 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast -flags +loop-global_header %OUTPUT%" + } + ] } - ], - "mpegTs": [ - { - "name": "720p (H.264)", - "cmd": "%FFMPEG% -dual_mono_mode main %RE% -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -ac 2 -c:v libx264 -vf yadif,scale=-2:720 %VB% %VBUFFER% %AB% %ABUFFER% -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -y -f mpegts pipe:1", - "vb": "3000k", - "ab": "192k" + }, + "recorded": { + "ts": { + "webm": [ + { + "name": "720p", + "cmd": "%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -threads 3 -c:a libvorbis -ar 48000 -b:a 192k -ac 2 -c:v libvpx-vp9 -vf yadif,scale=-2:720 -b:v 3000k -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1" + }, + { + "name": "480p", + "cmd": "%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -threads 3 -c:a libvorbis -ar 48000 -b:a 128k -ac 2 -c:v libvpx-vp9 -vf yadif,scale=-2:480 -b:v 1500k -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1" + } + ], + "mp4": [ + { + "name": "720p", + "cmd": "%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1" + }, + { + "name": "480p", + "cmd": "%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1" + } + ], + "hls": [ + { + "name": "720p", + "cmd": "%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -map 0 -threads 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast -flags +loop-global_header %OUTPUT%" + }, + { + "name": "480p", + "cmd": "%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -map 0 -threads 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast -flags +loop-global_header %OUTPUT%" + } + ] }, - { - "name": "360p (H.264)", - "cmd": "%FFMPEG% -dual_mono_mode main %RE% -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -ac 2 -c:v libx264 -vf yadif,scale=-2:360 %VB% %VBUFFER% %AB% %ABUFFER% -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -y -f mpegts pipe:1", - "vb": "1500k", - "ab": "128k" + "encoded": { + "webm": [ + { + "name": "720p", + "cmd": "%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 3 -c:a libvorbis -ar 48000 -b:a 192k -ac 2 -c:v libvpx-vp9 -vf scale=-2:720 -b:v 3000k -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1" + }, + { + "name": "480p", + "cmd": "%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 3 -c:a libvorbis -ar 48000 -b:a 128k -ac 2 -c:v libvpx-vp9 -vf scale=-2:480 -b:v 1500k -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1" + } + ], + "mp4": [ + { + "name": "720p", + "cmd": "%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf scale=-2:720 -b:v 3000k -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1" + }, + { + "name": "480p", + "cmd": "%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf scale=-2:480 -b:v 1500k -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1" + } + ], + "hls": [ + { + "name": "720p", + "cmd": "%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf scale=-2:720 -b:v 3000k -preset veryfast -flags +loop-global_header %OUTPUT%" + }, + { + "name": "480p", + "cmd": "%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf scale=-2:480 -b:v 3000k -preset veryfast -flags +loop-global_header %OUTPUT%" + } + ] } - ] - }, - "recordedHLS": [ - { - "name": "720p", - "cmd": "%FFMPEG% -dual_mono_mode main -i %INPUT% -sn -threads 0 -map 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast -flags +loop-global_header %OUTPUT%" - }, - { - "name": "480p", - "cmd": "%FFMPEG% -dual_mono_mode main -i %INPUT% -sn -threads 0 -map 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast -flags +loop-global_header %OUTPUT%" - }, - { - "name": "480p(h265)", - "cmd": "%FFMPEG% -dual_mono_mode main -i %INPUT% -sn -map 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1 -hls_segment_type fmp4 -hls_fmp4_init_filename stream%streamNum%-init.mp4 -hls_segment_filename stream%streamNum%-%09d.m4s -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx265 -vf yadif,scale=-2:480 -b:v 350k -preset veryfast -tag:v hvc1 %OUTPUT%" } - ], - "recordedViewer": { - "ios": "infuse://x-callback-url/play?url=http://ADDRESS", - "android": "intent://ADDRESS#Intent;package=com.mxtech.videoplayer.ad;type=video;scheme=http;end" } -} +} \ No newline at end of file -- cgit 1.4.1