diff --git a/README.md b/README.md index d2c7dbe..8df5558 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,24 @@ # Javicle - a JSON Video Composition Language - JVCL (pronounced "Javicle") is a JSON DSL for audio/video transformations. Under the hood, it's all shell commands: `ffmpeg`, `mediainfo` and so on. JVCL provides higher-level semantics for working with these lower level tools. +# Motivation +I don't do much video editing, so I've never bothered to learn iMovie or any +graphical video editor. My editing needs are usually pretty simple, so I bust +out ffmpeg and get it done. + +But it seems like every time, there is at least one wrinkle in my requirements +that requires some deep research into +[ffmpeg filter arcana](https://ffmpeg.org/ffmpeg-filters.html) +and before I know it, the day is done. + +I created JVCL to capture the most common things people usually do to videos: +splitting, concatenating, letterboxing, overlaying one video onto another, +and so on. + # A Quick Example Say you want to split a portion of a video into ten-second chunks. With ffmpeg and bash, you might do something like this: @@ -15,7 +28,8 @@ and bash, you might do something like this: ffmpeg -i /tmp/my/source.mp4 -ss ${i} -t $((i+INCR)) /tmp/my/slice_${i}_$((i+INCR)).mp4 done ``` -With JVCL, you'd write this spec file and save it to a file (for example `my-spec.jvcl`): +With JVCL, you'd write this spec file and save it to a file (for +example `my-spec.jvcl`): ```json { "assets": [ {"name": "src", "path": "/tmp/my/source.mp4"} ], @@ -33,13 +47,14 @@ and then run it like this: ```shell script jvcl my-spec.jvcl ``` -Yes, the JVCL is longer, but I think many would agree it is easier to read and maintain. +Yes, the JVCL is longer, but I think many would agree it is easier to read +and maintain. -**As the number of media assets and operations grows, hand-crafted shell scripts with magical -ffmpeg incantations become ever more inscrutable.** +**As the number of media assets and operations grows, hand-crafted shell +scripts with magical ffmpeg incantations become ever more inscrutable.** -JVCL is designed for readability and maintainability. JVCL will continue to evolve towards greater -coverage of the full capabilities of ffmpeg. +JVCL is designed for readability and maintainability. JVCL will continue to +evolve towards greater coverage of the full capabilities of ffmpeg. # Who is JVCL not for? If you like GUIs, JVCL is probably not for you. @@ -52,293 +67,51 @@ JVCL is for people who like CLIs and automation. JVCL is for people with relatively simple video composition needs (for now), since the range of operations supported is limited. -# Concepts -In JVCL there are a few main concepts: spec files, assets and operations. - -## JVCL Spec Files -A JVCL spec file is just a regular JSON file that happens to contain a single JSON object, -whose properties are `assets` and `operations`. - -When you run `jvcl` on a spec file, it will load the `assets`, then perform the `operations` in order. - -Unlike most JSON, comments *are* allowed in JVCL spec files: -* A line comment starts with `//` and continue to the end of the line -* A multi-line block syntax starts with `/*` and ends with `*/` - -### Executing a JVCL Spec -To execute a spec stored in the file `my-spec.json`, you would run: -```shell script -jvcl my-spec.jvcl -``` -or use stdin: -```shell script -cat my-spec.jvcl | jvcl -``` - -#### Scratch Directory -Output assets will be placed in the scratch directory, unless otherwise specified -in the spec file. By default, JVCL will create a new temporary directory to use as the scratch -directory. You can set the scratch directory explicitly using the `-t` or `--temp-dir` option: -```shell script -jvcl -t /some/tempdir my-spec.json -``` - -#### Dry Running -Use the `-n` or `--no-exec` option to print out the commands that would have been run, -but do not actually run anything. -```shell script -jvcl -n my-spec.json # will not run any ffmpeg commands -``` -Note that this breaks JVCL operations that require information from any assets created by -previous operations: since the command did not actually run, the intermediate asset was -never created. - -#### Command Help -To view a list of all `jvcl` command-line options, run `jvcl -h` or `jvcl --help` - -## Assets -Assets are your media files: generally image, audio and video files. - -All assets have a name and a path. - -Input assets are defined using the `assets` array of a JVCL spec. - -For input assets, the path can be a file or a URL. URL-based assets will be downloaded -to the scratch directory. This can be overridden using the `dest` property on the asset. - -Operations produce one or more output assets, as specified in the `creates` property of -an operation JSON object. - -For output assets, the path will be within the scratch directory. -You can override this using the `dest` property on the `creates` object associated with the operation. - -### Asset Properties -Assets expose properties that can be referenced in operations. The properties currently exposed are: - - * `duration`: duration of the audio/video in seconds (audio and video assets only) - * `width`: width of the video in pixels (video and image assets only) - * `height`: width of the video in pixels (video and image assets only) - -## Operations -Operations are transformations to perform on the inputs. - -An operation can produce one or more new assets, which can then be referenced in -later operations. - -Most of the operation settings can be JavaScript expressions, for example: - - "start": "someAsset.duration - 10" +# Running JVCL +Learn more about [running `jvcl`](docs/running.md) and other useful tools. -The above would set the `start` value to ten seconds before the end of `someAsset`. +# JVCL Concepts +Learn about [Assets and Operations](docs/concepts.md), the core concepts +of JVCL. ### Supported Operations Today, JVCL supports several basic operations. -For each operation listed below, the header links to an example from the JVCL test suite. - -### [scale](src/test/resources/tests/test_scale.jvcl) -Scale a video asset from one size to another. Scaling can be proportional or anamorphic. - -### [split](src/test/resources/tests/test_split.jvcl) -Split an audio/video asset into multiple assets of equal time lengths. +For each operation listed below, the header links to an example from the JVCL +test suite. ### [concat](src/test/resources/tests/test_concat.jvcl) Concatenate audio/video assets together into one asset. -### [trim](src/test/resources/tests/test_trim.jvcl) -Trim audio/video; crop a section of an asset, becomes a new asset. - -### [overlay](src/test/resources/tests/test_overlay.jvcl) -Overlay one asset onto another. - ### [ken-burns](src/test/resources/tests/test_ken_burns.jvcl) For transforming still images into video via a fade-pan (aka Ken Burns) effect. ### [letterbox](src/test/resources/tests/test_letterbox.jvcl) -Transform a video in one size to another size using black letterboxes on the sides or top/bottom. +Transform a video from one size to another size, maintaining the aspect ratio +of the video and adding letterboxes on the sides or top/bottom. Handy for embedding mobile videos into other screen formats. +### [overlay](src/test/resources/tests/test_overlay.jvcl) +Overlay one asset onto another. + ### [remove-track](src/test/resources/tests/test_remove_track.jvcl) Remove a track from a video asset. -# Complex Example -Here is a complex example using multiple assets and operations. - -Note that comments, which are not usually legal in JSON, are allowed in JVCL files. - -If you have other JSON-aware tools that need to read JVLC files, you may not want to -use this comment syntax. The `asset` and `operation` JSON objects also support a `comment` -field, which can be used as well. - -```js -{ - "assets": [ - // file -- will be referenced directory - { - "comment": "first video, already local", - "name": "vid1", - "path": "/tmp/path/to/video1.mp4" - }, - - // URL -- will be downloaded to scratch directory and referenced from there - { - "comment": "second video, will be downloaded", - "name": "vid2", - "path": "https://archive.org/download/gov.archives.arc.1257628/gov.archives.arc.1257628_512kb.mp4" - }, - - // URL -- will be downloaded to `dest` directory and referenced from there - { - "comment": "third video, will be downloaded", - "name": "vid3", - "path": "https://archive.org/download/gov.archives.arc.49442/gov.archives.arc.49442_512kb.mp4", - "dest": "src/test/resources/sources/" - }, - - // Image URL - { - "comment": "JPEG image, will be downloaded", - "name": "img1", - "path": "https://live.staticflickr.com/65535/48159911972_01efa0e5ea_b.jpg", - "dest": "src/test/resources/sources/" - } - ], - "operations": [ - // scale examples - { - "comment": "scale using explicity height x width", - "operation": "scale", // name of the operation - "creates": "vid2_scaled", // asset it creates - "source": "vid2", // source asset - "width": "1024", // width of scaled asset. if omitted and height is present, width will be proportional - "height": "768" // height of scaled asset. if omitted and width is present, height will be proportional - }, - { - "comment": "scale proportionally by a scale factor", - "operation": "scale", // name of the operation - "creates": "vid2_big", // asset it creates - "source": "vid2", // source asset - "factor": "2.2" // scale factor. if factor is set, width and height are ignored. - }, - - // split example - { - "comment": "split one asset into many", - "operation": "split", // name of the operation - "creates": "vid1_split_%", // assets it creates, the '%' will be replaced with a counter - "source": "vid1", // split this source asset - "interval": "10" // split every ten seconds - }, - - // concat examples - { - "comment": "re-combine previously split assets back together", - "operation": "concat", // name of the operation - "creates": "recombined_vid1", // assets it creates, the '%' will be replaced with a counter - "source": ["vid1_split"] // recombine all split assets - }, - { - "comment": "append vid2 to the end of vid1 and create a new asset", - "operation": "concat", // name of the operation - "creates": "combined_vid2", // asset it creates, can be referenced later - "source": ["vid1", "vid2"] // operation-specific: this says, concatenate these named assets - }, - { - "comment": "re-combine only some of the previously split assets", - "operation": "concat", // name of the operation - "sources": ["vid1_splits[1..2]"],// concatentate these sources -- the 2nd and 3rd files only - "creates": "combined_vid3" // name of the output asset, will be written to scratch directory - }, - - // trim example - { - "comment": "trim all of the assets that were split above", - "operation": "trim", // name of the operation - "creates": { // create multiple files, will be prefixed with `name`, store them in `dest` - "name": "vid1_trims", - "dest": "src/test/resources/outputs/trims/" - }, - "source": "vid1_split", // trim these source assets - "start": "1", // cropped region starts here, default is zero - "end": "6" // cropped region ends here, default is end of video - }, - - // overlay example - { - "comment": "overlay one video onto another", - "operation": "overlay", // name of the operation - "creates": "overlay1", // asset it creates - "source": "combined_vid1", // main video asset - "start": "30", // when (on the main video timeline) to begin showing the overlay. default is 0 (beginning) - "end": "60", // when (on the main video timeline) to stop showing the overlay. default is to play the entire overlay - "overlay": { - "source": "vid2", // overlay this video on the main video - "start": "0", // when (on the overlay video timeline) to begin playback on the overlay. default is 0 (beginning) - "end": "overlay.duration", // when (on the overlay video timeline) to end playback on the overlay. default is to play the entire overlay - "width": "overlay.width / 2", // how wide the overlay will be, in pixels. default is the full overlay width, or maintain aspect ratio if height was set - "height": "source.height", // how tall the overlay will be, in pixels. default is the full overlay height, or maintain aspect ratio if width was set - "x": "source.width / 2", // horizontal overlay position on main video. default is 0 - "y": "source.height / 2" // vertical overlay position on main video. default is 0 - } - }, +### [scale](src/test/resources/tests/test_scale.jvcl) +Scale a video asset from one size to another. Scaling can be proportional +or anamorphic. - // ken-burns example - { - "comment": "apply zoom-pan effect to image, creates video", - "operation": "ken-burns", // name of the operation - "creates": "ken1", // asset it creates - "source": "img1", // source image - "zoom": "1.3", // zoom level, from 1 to 10 - "duration": "5", // how long the resulting video will be - "start": "0", // when to start zooming, default is 0 - "end": "duration", // when to end zooming, default is duration - "x": "source.width * 0.6", // pan to this x-position - "y": "source.height * 0.4", // pan to this y-position - "upscale": "8", // upscale factor. upscaling the image results in a smoother pan, but a longer encode, default is 8 - "width": "1024", // width of output video - "height": "768" // height of output video - }, +### [split](src/test/resources/tests/test_split.jvcl) +Split an audio/video asset into multiple assets of equal time lengths. - // letterbox example - { - "comment": "increase video size without scaling, add letterboxes as needed", - "operation": "letterbox", // name of the operation - "creates": "boxed1", // asset it creates - "source": "ken1", // source asset - "width": "source.width * 1.5", // make it wider - "height": "source.height * 0.9", // and shorter - "color": "AliceBlue" // default is black. can be a hex value (0xff0000 for red) or a color name from here: https://ffmpeg.org/ffmpeg-utils.html#color-syntax - }, +### [trim](src/test/resources/tests/test_trim.jvcl) +Trim audio/video; crop a section of an asset, becomes a new asset. - // remove-track examples - { - "comment": "remove all audio tracks", - "operation": "remove-track", // name of the operation - "creates": "vid2_video_only", // name of the output asset - "source": "vid2", // main video asset - "track": "audio" // remove all audio tracks - }, - { - "comment": "remove all video tracks", - "operation": "remove-track", // name of the operation - "creates": "vid2_audio_only", // name of the output asset - "source": "vid2", // main video asset - "track": "video" // remove all video tracks - }, - { - "comment": "remove a specific audio track", - "operation": "remove-track", // name of the operation - "creates": "vid2_video_only2", // name of the output asset - "source": "vid2", // main video asset - "track": { - // only remove the first audio track - "type": "audio", // track type to remove - "number": "0" // track number to remove - } - } - ] -} -``` +## Complex Example +Here is a [long, complex example](docs/complex_example.md) that uses +every operation. -## What's up with the name? -I dunno, a cross between a javelin and an icicle? does that have any positive connotations? ok then... +## What's with the name? +A cross between a javelin and an icicle? +Does that have any positive connotations? +I really don't like naming things. diff --git a/bin/jvcl b/bin/jvcl index a239acb..bf962a2 100755 --- a/bin/jvcl +++ b/bin/jvcl @@ -4,18 +4,22 @@ # # Usage: # -# jvcl [-t temp-dir] spec-file +# jvcl [-t temp-dir] [-n] spec-file # -# If `spec-file` is omitted, then jvcl will try to read a spec from stdin. +# spec-file : the JVCL to run. If omitted, read a spec from stdin # -# Every run of JVCL will create a temp directory. You can either specify the name of the directory -# with `-t` (it will be created if it does not exist), or let jvcl create a temp directory for you. +# -t temp-dir : where to write generated assets. If omitted, jvcl will +# create a new temporary directory # -# Note: this command will build the jvcl jar file if needed. The first time you run it, it might -# take a long time to start up. +# -n : print commands that would have been run, but don't +# actually run anything +# +# Note: If the JVCL jar does not exist, it will be built from source. +# The first time you run it, it might take a long time to start up. # SCRIPT="${0}" SCRIPT_DIR="$(cd "$(dirname "${SCRIPT}")" && pwd)" +JVCL_SKIP_ENV_VAR_HELP=1 . "${SCRIPT_DIR}"/jvcl_common DEBUG="" diff --git a/bin/jvcl_common b/bin/jvcl_common index 4b4f27f..4f881b0 100755 --- a/bin/jvcl_common +++ b/bin/jvcl_common @@ -19,7 +19,8 @@ function handle_help_request() { break fi done <"${1}" - echo "# Environment Variables + if [[ -z "${JVCL_SKIP_ENV_VAR_HELP}" ]] ; then + echo "# Environment Variables # # JVCL_SCRATCH_DIR : Use this as the scratch directory # Default is to create a new temp directory @@ -28,13 +29,32 @@ function handle_help_request() { # have run but do not execute anything # " + fi exit 1 fi } +# Ensure Java is installed and that it is Java 11 +if [[ -z "$(which java)" ]] ; then + die "Java 11 not installed (java command not found on PATH)" +fi +JAVA_OK=0 +for token in $(java -version 2>&1 | grep -i version) ; do + case "${token}" in '"11.'*) + JAVA_OK=1 + esac +done +if [[ ${JAVA_OK} -eq 0 ]] ; then + die "Java 11 not installed (java -version check failed)" +fi + JVCL_DIR="$(cd "$(dirname "${0}")"/.. && pwd)" JVCL_JAR="$(find "${JVCL_DIR}"/target -type f -name "jvcl-*-prod.jar" | head -1)" if [[ -z "${JVCL_JAR}" ]] ; then + # Ensure maven is installed + if [[ -z "$(which mvn)" ]] ; then + die "Maven not installed (mvn command not found on PATH), cannot build JVCL jar" + fi mvn -DskipTests=true -Puberjar clean package || die "Error building JVCL jar" JVCL_JAR="$(find "${JVCL_DIR}"/target -type f -name "jvcl-*-prod.jar" | head -1)" if [[ -z "${JVCL_JAR}" ]] ; then @@ -43,13 +63,15 @@ if [[ -z "${JVCL_JAR}" ]] ; then fi SCRATCH_DIR="" -if [[ -n "${JVCL_SCRATCH_DIR}" ]] ; then - SCRATCH_DIR="--temp-dir ${JVCL_SCRATCH_DIR}" -fi - NO_EXEC="" -if [[ -n "${JVCL_NO_EXEC}" ]] ; then - NO_EXEC="--no-exec" +if [[ -z "${JVCL_SKIP_ENV_VAR_HELP}" ]] ; then + if [[ -n "${JVCL_SCRATCH_DIR}" ]] ; then + SCRATCH_DIR="--temp-dir ${JVCL_SCRATCH_DIR}" + fi + + if [[ -n "${JVCL_NO_EXEC}" ]] ; then + NO_EXEC="--no-exec" + fi fi JVCL_OPTIONS="${SCRATCH_DIR} ${NO_EXEC}" diff --git a/docs/complex_example.md b/docs/complex_example.md new file mode 100644 index 0000000..a9223f7 --- /dev/null +++ b/docs/complex_example.md @@ -0,0 +1,178 @@ +# Complex Example +Here is a complex example using multiple assets and operations. + +Note that comments, which are not usually legal in JSON, are allowed in JVCL files. + +If you have other JSON-aware tools that need to read JVLC files, you may not want to +use this comment syntax. The `asset` and `operation` JSON objects also support a `comment` +field, which can be used as well. + +```js +{ + "assets": [ + // file -- will be referenced directory + { + "comment": "first video, already local", + "name": "vid1", + "path": "/tmp/path/to/video1.mp4" + }, + + // URL -- will be downloaded to scratch directory and referenced from there + { + "comment": "second video, will be downloaded", + "name": "vid2", + "path": "https://archive.org/download/gov.archives.arc.1257628/gov.archives.arc.1257628_512kb.mp4" + }, + + // URL -- will be downloaded to `dest` directory and referenced from there + { + "comment": "third video, will be downloaded", + "name": "vid3", + "path": "https://archive.org/download/gov.archives.arc.49442/gov.archives.arc.49442_512kb.mp4", + "dest": "src/test/resources/sources/" + }, + + // Image URL + { + "comment": "JPEG image, will be downloaded", + "name": "img1", + "path": "https://live.staticflickr.com/65535/48159911972_01efa0e5ea_b.jpg", + "dest": "src/test/resources/sources/" + } + ], + "operations": [ + // scale examples + { + "comment": "scale using explicity height x width", + "operation": "scale", // name of the operation + "creates": "vid2_scaled", // asset it creates + "source": "vid2", // source asset + "width": "1024", // width of scaled asset. if omitted and height is present, width will be proportional + "height": "768" // height of scaled asset. if omitted and width is present, height will be proportional + }, + { + "comment": "scale proportionally by a scale factor", + "operation": "scale", // name of the operation + "creates": "vid2_big", // asset it creates + "source": "vid2", // source asset + "factor": "2.2" // scale factor. if factor is set, width and height are ignored. + }, + + // split example + { + "comment": "split one asset into many", + "operation": "split", // name of the operation + "creates": "vid1_split_%", // assets it creates, the '%' will be replaced with a counter + "source": "vid1", // split this source asset + "interval": "10" // split every ten seconds + }, + + // concat examples + { + "comment": "re-combine previously split assets back together", + "operation": "concat", // name of the operation + "creates": "recombined_vid1", // assets it creates, the '%' will be replaced with a counter + "source": ["vid1_split"] // recombine all split assets + }, + { + "comment": "append vid2 to the end of vid1 and create a new asset", + "operation": "concat", // name of the operation + "creates": "combined_vid2", // asset it creates, can be referenced later + "source": ["vid1", "vid2"] // operation-specific: this says, concatenate these named assets + }, + { + "comment": "re-combine only some of the previously split assets", + "operation": "concat", // name of the operation + "sources": ["vid1_splits[1..2]"],// concatentate these sources -- the 2nd and 3rd files only + "creates": "combined_vid3" // name of the output asset, will be written to scratch directory + }, + + // trim example + { + "comment": "trim all of the assets that were split above", + "operation": "trim", // name of the operation + "creates": { // create multiple files, will be prefixed with `name`, store them in `dest` + "name": "vid1_trims", + "dest": "src/test/resources/outputs/trims/" + }, + "source": "vid1_split", // trim these source assets + "start": "1", // cropped region starts here, default is zero + "end": "6" // cropped region ends here, default is end of video + }, + + // overlay example + { + "comment": "overlay one video onto another", + "operation": "overlay", // name of the operation + "creates": "overlay1", // asset it creates + "source": "combined_vid1", // main video asset + "start": "30", // when (on the main video timeline) to begin showing the overlay. default is 0 (beginning) + "end": "60", // when (on the main video timeline) to stop showing the overlay. default is to play the entire overlay + "overlay": { + "source": "vid2", // overlay this video on the main video + "start": "0", // when (on the overlay video timeline) to begin playback on the overlay. default is 0 (beginning) + "end": "overlay.duration", // when (on the overlay video timeline) to end playback on the overlay. default is to play the entire overlay + "width": "overlay.width / 2", // how wide the overlay will be, in pixels. default is the full overlay width, or maintain aspect ratio if height was set + "height": "source.height", // how tall the overlay will be, in pixels. default is the full overlay height, or maintain aspect ratio if width was set + "x": "source.width / 2", // horizontal overlay position on main video. default is 0 + "y": "source.height / 2" // vertical overlay position on main video. default is 0 + } + }, + + // ken-burns example + { + "comment": "apply zoom-pan effect to image, creates video", + "operation": "ken-burns", // name of the operation + "creates": "ken1", // asset it creates + "source": "img1", // source image + "zoom": "1.3", // zoom level, from 1 to 10 + "duration": "5", // how long the resulting video will be + "start": "0", // when to start zooming, default is 0 + "end": "duration", // when to end zooming, default is duration + "x": "source.width * 0.6", // pan to this x-position + "y": "source.height * 0.4", // pan to this y-position + "upscale": "8", // upscale factor. upscaling the image results in a smoother pan, but a longer encode, default is 8 + "width": "1024", // width of output video + "height": "768" // height of output video + }, + + // letterbox example + { + "comment": "increase video size without scaling, add letterboxes as needed", + "operation": "letterbox", // name of the operation + "creates": "boxed1", // asset it creates + "source": "ken1", // source asset + "width": "source.width * 1.5", // make it wider + "height": "source.height * 0.9", // and shorter + "color": "AliceBlue" // default is black. can be a hex value (0xff0000 for red) or a color name from here: https://ffmpeg.org/ffmpeg-utils.html#color-syntax + }, + + // remove-track examples + { + "comment": "remove all audio tracks", + "operation": "remove-track", // name of the operation + "creates": "vid2_video_only", // name of the output asset + "source": "vid2", // main video asset + "track": "audio" // remove all audio tracks + }, + { + "comment": "remove all video tracks", + "operation": "remove-track", // name of the operation + "creates": "vid2_audio_only", // name of the output asset + "source": "vid2", // main video asset + "track": "video" // remove all video tracks + }, + { + "comment": "remove a specific audio track", + "operation": "remove-track", // name of the operation + "creates": "vid2_video_only2", // name of the output asset + "source": "vid2", // main video asset + "track": { + // only remove the first audio track + "type": "audio", // track type to remove + "number": "0" // track number to remove + } + } + ] +} +``` diff --git a/docs/concepts.md b/docs/concepts.md new file mode 100644 index 0000000..85ecb71 --- /dev/null +++ b/docs/concepts.md @@ -0,0 +1,38 @@ +# Concepts +In JVCL the main concepts are assets and operations. + +## Assets +Assets are your media files: generally image, audio and video files. + +All assets have a name and a path. + +Input assets are defined using the `assets` array of a JVCL spec. + +For input assets, the path can be a file or a URL. URL-based assets will be downloaded +to the scratch directory. This can be overridden using the `dest` property on the asset. + +Operations produce one or more output assets, as specified in the `creates` property of +an operation JSON object. + +For output assets, the path will be within the scratch directory. +You can override this using the `dest` property on the `creates` object associated with the operation. + +### Asset Properties +Assets expose properties that can be referenced in operations. The properties currently exposed are: + +* `duration`: duration of the audio/video in seconds (audio and video assets only) +* `width`: width of the video in pixels (video and image assets only) +* `height`: width of the video in pixels (video and image assets only) + +## Operations +Operations are transformations to perform on the inputs. + +An operation can produce one or more new assets, which can then be referenced in +later operations. + +Most of the operation settings can be JavaScript expressions, for example: + + "start": "someAsset.duration - 10" + +The above would set the `start` value to ten seconds before the end of `someAsset`. + diff --git a/docs/running.md b/docs/running.md new file mode 100644 index 0000000..5017681 --- /dev/null +++ b/docs/running.md @@ -0,0 +1,71 @@ +# Running JVCL + +## The `jvcl` Script +The `bin/jvcl` script is the primary way to transform video assets with JVCL. + +## Operation-specific scripts +There are other scripts in `bin` that perform a single operation using +command-line arguments. If you need to do a quick operation and don't want to write +a JVCL spec, use one of these tools. + +For example, to extract the first 5 seconds of a video: +```shell script +jtrim /tmp/input-file.mp4 /tmp/output-5s.mp4 0 5 +``` + +All commands accept a `-h` / `--help` option, use this to print usage information: +```shell script +jscale -h +``` + +## JVCL Spec Files +A JVCL spec is just a regular JSON file that happens to contain a single JSON object, +whose properties are `assets` and `operations`. + +When you run `jvcl` on a spec file, it will load the `assets`, then perform the `operations` in order. + +Learn more about [Assets and Operations](concepts.md) + +Unlike most JSON, comments *are* allowed in JVCL spec files: +* A line comment starts with `//` and continue to the end of the line +* A multi-line block syntax starts with `/*` and ends with `*/` + +## Writing a JVCL Spec +The easiest way to write a spec is to copy one of the +[test specs](../src/test/resources/tests) and edit it. + +There examples for every JVCL operation. + +## Executing a JVCL Spec +To execute a spec stored in the file `my-spec.json`, you would run: +```shell script +jvcl my-spec.jvcl +``` +or use stdin: +```shell script +cat my-spec.jvcl | jvcl +``` + +#### Scratch Directory +Output assets will be placed in the scratch directory, unless otherwise specified +in the spec file. By default, JVCL will create a new temporary directory to use as the scratch +directory. You can set the scratch directory explicitly using the `-t` or `--temp-dir` option: +```shell script +jvcl -t /some/tempdir my-spec.json +``` + +#### Dry Run +Use the `-n` or `--no-exec` option to print out the commands that would have been run, +but do not actually run anything. +```shell script +jvcl -n my-spec.json # will not run any ffmpeg commands +``` +Note that this breaks JVCL operations that require information from any assets created by +previous operations: since the command did not actually run, the intermediate asset was +never created. + +#### Help +To view a list of all `jvcl` command-line options, run `jvcl -h` or `jvcl --help` + +# What's Next? +Learn about [Assets and Operations](concepts.md), the core concepts of JVCL.