diff --git a/.gitignore b/.gitignore index 61ead86..8b7ef35 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /vendor +composer.lock diff --git a/PRESETS.md b/PRESETS.md new file mode 100644 index 0000000..162fee4 --- /dev/null +++ b/PRESETS.md @@ -0,0 +1,114 @@ +VfxApply Presets Documentation +============================== + + +## Preset keys + +**Common keys** + +| Key | Type | Description +|------.------------|----------------|------------------------------------------- +| `preset/name` | *String* | A description string for this preset +| `preset/group` | *String|Null* | Logical grouping +| `preset/plugin` | *String* | The id of the plugin this preset is for +| `preset/props` | *Array* | Properties used by the plugin +| `preset/params` | *Array|Null* | Parameters for tweaking + +## Properties + +**ffmpeg** + +| Property | Type | Description +|-------------------|----------------|------------------------------------------- +| `filter` | *String* | The filter string, with optional param placeholders +| `extra` | *String|Null* | Extra options to pass on the ffmpeg command line +| `type` | *Enum|Null* | ffmpeg filter type; One of `complex` (default), `audio` or `video` + + +**natron** + +| Property | Type | Description +|------.------------|--------------------|------------------------------------------- +| `project` | *String* | Natron project file name +| `input` | *String|Int|Null* | Extra options to pass on the ffmpeg command line +| `output` | *String|Int|Null* | ffmpeg filter type; One of `complex`, `audio` or `video` +| `framescale` | *Float* | Ratio of output frames to input files, 2.0=twice as many out as in=half speed + +**executor** + +| Property | Type | Description +|------.------------|--------------------|------------------------------------------- +| `set` | *Array* | Define variables +| `script` | *Array* | Define commands +| `set/%/value` | *String* | Set variable % to value. +| `set/%/escape` | *Bool* | If true, the resulting vriable will be escaped +| `script/%/info` | *String* | Info about what this step is doing +| `script/%/exec` | *String* | Command to execute +| `script/%/parse` | *Map* | Parse output from command, see docs for plugin + + +## Parameters + +All parameters must specify `label` and `type`. The remaining keys are depending on the +type of parameter. + +**float** + +Float parameters need to specify `min`, `max`, `default` and optionally `step`. + + +## Examples + +### ffmpeg + + preset: + name: Normalize audio level + group: ffmpeg/audio + plugin: ffmpeg + props: + filter: "dynaudnorm=m={maxgain}:s={compress}" + extra: "-c:v copy" + type: complex + params: + compress: { label:"Compression factor", type:float, min:0.0, max:30.0, default:0.0 } + maxgain: { label:"Max gain factor", type:float, min:0.0, max:100.0, default:10.0 } + +### natron + + preset: + name: Apply a CCTV look to the video + group: natron + plugin: natron + props: + project: natron-cctv.ntp + reader: Reader1 + writer: Writer1 + +### executor + + preset: + name: Stabilize video clip (two-pass) + group: video + plugin: executor + props: + set: + inputtrf: { value:"{%uinput}.trf", escape:true } + script: + analyze: + info: Getting stabilization vectors + exec: "transcode -i {%input} -J stabilize" + parse: + from: 'stderr' + regex: '/encoding frames \[0-([0-9]+?)\],\s+([0-9\.]+?) fps/' + frame:1 + eta:2 + stabilize: + info: Stabilizing video + exec: "transcode -i {%input} -o {%output} -J transform -y xvid" + parse: + from: 'stderr' + regex: '/encoding frames \[0-([0-9]+?)\],\s+([0-9\.]+?) fps/' + frame:1 + eta:2 + cleanup: + exec: "rm {%inputtrf}" diff --git a/README.md b/README.md index 7a380f3..ac063c6 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,12 @@ Natron pipeline. ## Usage - vfxapply [-i ] [-o |-O] [-p ] [-c =] + vfxapply [-i ] [-o |-O] [-l] [-p ] [-c =] -i Set the input file (if omitted, a file picker will be displayed) -o Set the output file (if omitted, a file picker will be displayed) - -O Automatically assign output filename based on input filename + -O Automatically assign output filename + -l List presets -p Select the preset to apply -c Specify parameters for the preset -b Batch mode, input parameter is a list of files @@ -33,4 +34,25 @@ Examples: ### ffmpeg +This plugin allows for both video filters, audio filters and complex filters +to f.ex. normalize audio or generate spectrograms of the audio channes + ### natron + +*This plugin is currently broken* + +### executor + +The executor plugin does as the name suggests; it executes one or more custom +commands on the input file. The command output can be parsed using regular +expressions to present the progress. + +### melt + +The melt plugin uses the MLT library, the same as is used by f.ex. Kdenlive +to process the media. + +### transcode + +Transcode uses the transcode tool from transcoder.org. While powerful, it +works best with AVI files. diff --git a/check-requirements b/check-requirements new file mode 100755 index 0000000..01f0a89 --- /dev/null +++ b/check-requirements @@ -0,0 +1,31 @@ +#!/bin/bash + +function group { + echo -e "\e[1m$1\e[0m" +} +function good { + echo -e "[\e[92m ok \e[0m] \e[32m$1\e[0m" +} +function bad { + echo -e "[\e[91mfail\e[0m] \e[31m$1\e[0m" +} + +function check { + local NAME + test -z "$2" || NAME="$2" + test -z "$NAME" && NAME="$1" + if [ -z "$(which $1)" ]; then + bad "$NAME is missing" + else + good "Found $NAME at $(which $1)" + fi +} + +group "Checking for zenity" +check "zenity" +check "dialog" + +group "Checking tools" +check "ffprobe" +check "ffmpeg" +check "transcode" diff --git a/plugins/executor/README.md b/plugins/executor/README.md index 0555c11..4ac42fa 100644 --- a/plugins/executor/README.md +++ b/plugins/executor/README.md @@ -1,23 +1,5 @@ -preset: - name: Stabilize video clip - group: video - plugin: transcode - props: - set: - inputtrf: { value:"{%uinput}.trf", escape:true } - script: - analyze: - info: Getting stabilization vectors - exec: "transcode -J stabilize -i {%input}" - parse: { regex: '/^encoding frames [0-([0-9]+?)], ([0-9\.]+?) fps/', frame:1, eta:2 } - stabilize: - info Stabilizing video - exec: "transcode -J transform -i {%input} -o {%output}" - parse: { regex: '/^encoding frames [0-([0-9]+?)], ([0-9\.]+?) fps/', frame:1, eta:2 } - cleanup: - exec: "rm {%inputtrf}" - - +Executor Plugin for VfxApply +============================ ## Scripts @@ -25,6 +7,16 @@ Each script command is named, and will be executed in order of appearance. Error will be reported and execution will stop if the exit code is non-zero, using the names as logical pointers. + props: + set: + : { value:, escape: } + script: + : + info: + exec: + parse: + + ### Command lines Command line uses placeholders. @@ -40,11 +32,35 @@ Any parameters will also be available: And you can use `set` to assign stuff to variables. + +### Setting variables + +Setting variables is done using the `set` prop. The core syntax is: + + set: + myvarname: { value:"MyValue" } + +Additionally, `escape` can be used to flag that the value need to be +escaped for the command line. + + myvarname: { value:"SomeValue", escape:true } + +Variables set this way can be used for the command line, and the same +expressions can be used to define vars: + + set: + tempfile: { value:"{%uinput}.tmp", escape:true } + script: + first: + exec "first --temp-file {%tempfile}" + + ### Parsing output To parse the output, add a `parse` key to the command block. parse: regex: <- expression + from: <- stream for comparison (stdout or stderr) frame: <- index of frame number, or leave out fps: <- index of frames per second, or leave out diff --git a/plugins/melt/plugin.php b/plugins/melt/plugin.php new file mode 100644 index 0000000..5e3acf4 --- /dev/null +++ b/plugins/melt/plugin.php @@ -0,0 +1,24 @@ + + This preset uses 'transcode' to stabilize the video clip. As transcode has limited + support for other output containers than AVI, the resulting file will be an .AVI + file. props: set: inputtrf: { value:"{%uinput}.trf", escape:true } @@ -17,18 +21,3 @@ preset: parse: { from: 'stderr', regex: '/encoding frames \[0-([0-9]+?)\],\s+([0-9\.]+?) fps/', frame:1, eta:2 } cleanup: exec: "ls {%inputtrf}" -#!/bin/bash - -#INPUT="$1" -#OUTPUT="$1.stab.avi" -#CODEC="-y xvid" - -#if [ -e "$INPUT.trf" ]; then -# echo "Cache found, not re-calling stabilize..." -#else - # Start the deshake process -# transcode -J stabilize -i $INPUT || transcode -J stabilize --mplayer_probe -i $INPUT -#fi - -# Now, stabilize the video -#transcode -J transform -i $INPUT $CODEC -o $OUTPUT diff --git a/presets/melt/melt-grayscale.yml b/presets/melt/melt-grayscale.yml new file mode 100644 index 0000000..25fd42a --- /dev/null +++ b/presets/melt/melt-grayscale.yml @@ -0,0 +1,9 @@ +preset: + name: Turn the video into grayscale + group: melt + plugin: melt + props: + filters: + - { type: greyscale, in: ~, out: ~ } + + # melt test.mp4 -filter greyscale -consumer avformat:output.avi acodec=libmp3lame vcodec=libx264 \ No newline at end of file diff --git a/presets/transcode/transcode-deshake.yml b/presets/transcode/transcode-deshake.yml new file mode 100644 index 0000000..165af6c --- /dev/null +++ b/presets/transcode/transcode-deshake.yml @@ -0,0 +1,6 @@ +preset: + name: Deshake video clip (single-pass) + group: video + plugin: transcode + props: + filter: deshake diff --git a/src/Application.php b/src/Application.php index 6428b88..a090f52 100644 --- a/src/Application.php +++ b/src/Application.php @@ -12,7 +12,7 @@ class Application public function readPlugins() { $iter = new \DirectoryIterator( - __DIR__."/../plugins" + APP_ROOT."/plugins" ); foreach ($iter as $dir) { if (!$dir->isDir()) @@ -27,7 +27,7 @@ class Application { $iter = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator( - __DIR__."/../presets" + APP_ROOT."//presets" )); foreach ($iter as $file) { if ($file->isDir() || ($file->getExtension()!='yml')) @@ -83,8 +83,30 @@ class Application $this->readPlugins(); $this->readPresets(); - $opts = getopt("i:o:p:l"); + $opts = getopt("hi:o:p:l"); + if (array_key_exists('h',$opts)) { + printf("Usage:\n"); + foreach([ + "%s -h", + "%s -l", + "%s [-i FILE] [-o FILE] [-p PRESET]" + ] as $example) + printf(" ".$example."\n", "vfxapply"); + printf("Options:\n"); + foreach ([ + "-h" => "Show this help", + "-i FILE" => "Input filename", + "-o FILE" => "Output filename", + "-p PRESET" => "Select preset", + "-l" => "List presets", + ] as $opt=>$info) + printf(" %-10s %s\n", $opt, $info); + + printf("\n"); + + return 0; + } if (array_key_exists('l',$opts)) { foreach ($this->presets as $name=>$preset) { printf("%s: %s\n", $name, $preset->getName()); @@ -110,8 +132,8 @@ class Application } $dur = $input->getVideoDuration(); - printf("Input: %s %.2fs (%d frames)\n", $input->getFilename(), $dur->seconds, $dur->frames); - printf("Output: %s\n", $output->getFilename()); + printf("Input: \e[1m%s\e[0m %.2fs (%d frames)\n", $input->getFilename(), $dur->seconds, $dur->frames); + printf("Output: \e[1m%s\e[0m\n", $output->getFilename()); if (empty($opts['p'])) { if (!($preset = $this->selectPreset())) { diff --git a/src/app.php b/src/app.php index 860e8e3..f40aa72 100644 --- a/src/app.php +++ b/src/app.php @@ -11,5 +11,14 @@ define("DIALOG_SAVEFILE", "savefile"); require_once __DIR__."/../vendor/autoload.php"; use VfxApply\Application; + +// Find the actual root, follow links, and strip the bin part if local +if (!($script = dirname(@readlink($_SERVER['SCRIPT_NAME'])))) + $script = dirname($_SERVER['SCRIPT_NAME']); +if (basename($script)=='bin') + $script = dirname($script); + +define("APP_ROOT", $script); + $app = new Application(); -$app->run(); \ No newline at end of file +$app->run();