Installation¶
Veddy is platform independent as it relies on a few open-source packages, which are all in principle installable via major package managers (e.g., apt-get or brew). Nevertheless, we strongly recommend that you use our Dockerfile since a set of tested FFmpeg/Python environments can be surely installed this way1. Don’t be intimidated, it won’t compile anything; it will just fetch a statically compiled binary from FFmpeg Static Builds and do the rest of settings for you. The whole installation procedure only takes a minutes, and your PC stays cool.
If this sounds good to you, first make sure that you have a latest copy of docker running via the official site. Next, start with cloning the Veddy repository and move into it
git clone https://github.com/ryichando/veddy.git
cd veddy
and build the docker image
docker build -t veddy .
and you are all set. You can now compile a set of examples by
docker run -v ${PWD}:/veddy --rm veddy examples/*.xml
Wait for a moment while seeing the progress of video compilation.
Output videos will be generated in the ./examples directory as
things get done.
See demos and learn what Veddy can do before you proceed.
Note
On Windows, be sure to use
PowerShell because
unfortunately ${PWD}
is not recognizable on Windows Command Line (cmd). If you persist
to stay with it, use %cd% instead.
Usage¶
As indicated above, Veddy takes XML file input(s)
docker run -v ${PWD}:/veddy --rm veddy <input_xml_file>
Be assured that this command comes with --rm flag, which means that
after the run, no suspended container is left; so things stay clean.
Keep in mind that you always execute Veddy in this handy manner.
Note that output path is not given via command line interface. They are deduced by Veddy, or manually specified in the scene file by export tag.
Note
When only one unnamed stream is defined and no <export> tags
are found, Veddy automatically picks the stream and
export it to <filename>.mp4.
For convenience, forget everything about <export> at
the moment.
Installing Launcher Script¶
For Linux and macOS users: if you find that the above command is lengthy, or wish to conveniently run Veddy anywhere outside Veddy directory, you may create/install a launcher script
# For Linux/macOS: here we create a launcher script
# Make sure that you run this script in Veddy directory
# Reachable port for remote preview; remember this
PORT=8080
# Install directory
# Change to somewhere else (e.g., ${HOME}/bin ) if you wish
INSTALL_DIR="/usr/local/bin"
# Create script
echo '#!/bin/bash
docker run -v '${PWD}':/veddy -v ${PWD}:/mount \
-p '${PORT}':8020 --rm veddy "$@"' > veddy
chmod +x veddy
# Install it to the destination path
cp veddy ${INSTALL_DIR}; rm veddy
# ---------------------------------------------------
# Do not move Veddy directory hereafter.
# If you do, just re-run this script in the new path.
# ---------------------------------------------------
and instead run
veddy <input_xml_file>
Note
${PORT} is used for remote preview. See Remote Preview
for detail.
For Windows users, you should be able to do the same by
creating .bat file and do something
(I rather leave to public web for how).
Note
When specifying XML file(s) using the above command(s),
check that all the associated files are located
in the working directory (e.g.,${PWD})
or its sub-directories.
If some of them are outside ${PWD}, Veddy can’t
access them and eventually fails.
Keep Things Up-To-Date¶
If you find that your copy of Veddy is out-of-date; just run two lines
git pull
docker build -t veddy .
and your Veddy becomes fresh again.
Uninstalling Veddy¶
If you exactly followed the above steps for running Veddy every time, no containers should be left in your system. If this is the case, simply run
docker rmi veddy
If you have installed a launcher script as above, also run
rm $(which veddy)
and you should have completely wiped Veddy out of your computer
(of course directory veddy still remains; be careful not to accidentally
lose what you have produced in it when deleting it).
If you believe that you have a suspended container, remove it first.
Minimal Example¶
Working with Veddy is all about writing XML file(s). To get a glimpse of what it looks like, let’s see a minimal example that works
<root version="0.0.1">
<material name="cat" path="videos/cat.mp4"/>
<material name="dog" path="videos/dog.mp4"/>
<stream>
<reference name="cat"/>
<reference name="dog"/>
</stream>
</root>
This scene file loads two video footages (cat.mp4,dog.mp4)
and concatenates (towards time axis) them.
First, the scene file must begin
with <root version="0.0.1>" to declare the start of the scene.
Versioning number must be specified to avoid possible incompatibility
issue arising from future format change.
Warning
When more than two videos are in a stream, they must have the same dimensions (width and height) and the FPS; otherwise compilation fails.
Transitions¶
You can easily apply transitions between video clips by adding a
transition attribute to <stream> tag by
<stream transition="fade">
<reference name="cat"/>
<reference name="dog"/>
</stream>
where fade is a transition name. The list of available
transitions can be seen from https://trac.ffmpeg.org/wiki/Xfade
Material Import¶
Typically materials are imported first through material tag. Several types of materials can be imported, such as regular videos, image sequences and even URL pointed ones (with basic password authentication). Jump to material to learn how.
Note
It is not mandatory to import materials; we can improvise using nullsrc or color compositions, which is lightly covered in Creating Solid Color Background.
The path to the materials must be a relative path from the XML file; not from the working directory.
Declaring Movie Track¶
At least one stream tag must be included to dictate a video stream to export. Multiple streams associated with a name attribute may be declared and included (re-used) in other streams. Please see Re-using Streams to know more.
Using Compositions¶
Most of the functionality of Veddy is provided through compositions. An example of usage is as follows
<stream>
<composite name="negate">
<reference name="cat"/>
</composite>
</stream>
Here, the negate composition inverts the nested video source. In general, nested inputs are passed to the parental composition and processed. The processed outputs are then further passed to its parent and processed likewise. Generally, compositions take the form
<composite name="(name)" param1="(value1)" param2="(value2)">
<input1/>
<input2/>
</composite>
where the list of parameters and number of inputs are subject to the type
of compositions. The built-in compositions are programmed in an external
file functions.xml, and their documentations are accessible
from the side menu.
Note
Some compositions come with coordinate and duration parameters. The coordinate is in accordance with FFmpeg, which sets the origin at the top-left corner. Unit is pixels. Duration is given in seconds.
Fluent Interface¶
When multiple compositions are applied in a nested fashion, the above format compromises readability. For example,
<stream>
<composite name="comp_3rd">
<composite name="comp_2nd">
<composite name="comp_1st">
<reference name="cat"/>
</composite>
</composite>
</composite>
</stream>
This is a valid syntax, but is also hard to follow at a glance.
To cope with the issue, Veddy provides a special tag pipe .
Using <pipe> , the above code can be simplified to
<stream>
<pipe>
<reference name="cat"/>
<composite name="comp_1st"/>
<composite name="comp_2nd"/>
<composite name="comp_3rd"/>
</pipe>
</stream>
Yes, this reads better. Using <pipe> , compositions are applied
from top to bottom in a sequential order, and subsequently passed to the parent
out of the scope when reaching </pipe>. Hence, you may apply another
composition to the output such as
<stream>
<composite name="negate">
<pipe>
<reference name="cat"/>
<composite name="comp_1st"/>
<composite name="comp_2nd"/>
<composite name="comp_3rd"/>
</pipe>
</composite>
</stream>
which increases the flexibility of coding style.
Video Dimensions and FPS¶
Video dimensions and FPS are inferred from the output unless explicitly specified via config tags. For example,
<material name="cat" path="videos/cat.mp4"/>
<stream>
<reference name="cat"/>
</stream>
will compile a video with both dimensions and FPS inherited from the referenced material. On the other hand,
<material name="cat" path="videos/cat.mp4"/>
<stream>
<!-- stitch two videos horizontally -->
<composite name="hstack">
<reference name="cat"/>
<reference name="cat"/>
</composite>
</stream>
will double the width of the video, while retaining other dimensions and the original FPS. In the next section we show how to explicitly specify dimensions and FPS.
Note
When deducible, they are set as global parameters
$_width_, $_height_ and $_fps_.
Creating Solid Color Background¶
You can create a solid color canvas and play with it. To do so, use color composition
<config name="shape" value="900x540"/>
<config name="fps" value="20"/>
<stream>
<pipe>
<composite name="color" duration="3"/>
<composite name="drawtext" text="Hello world!"/>
</pipe>
</stream>
This scene lays a solid black background of 3 seconds and draw a text at its center.
In this example, we specified the dimensions and FPS through config
tags. This is required because without it Veddy has no clue what
dimensions and FPS should be used to begin video compilation.
See config for detail.
For the list of configs, see Specifying Video Settings.
<config> tags should be declared directly
under the <root> tag.
Note
When video size and FPS are set through <config>,
output video must conform them.
This is automatically done if you initialize a scene starting with
color composition, like this example.
Default background color and the foreground color are set black and white, respectively. You can change them by
<root version="0.0.1">
<global name="_bgcolor_" value="your_color"/> <!-- background -->
<global name="_fgcolor_" value="your_color"/> <!-- foreground -->
...
</root>
where your_color should be the
name of a color (e.g., red or blue)
or a hex-encoded text (e.g., 0xACF9B3 ) according to
https://ffmpeg.org/ffmpeg-utils.html#Color.
Note
<global> tags, which set global variables,
should be placed directly under the <root> tag.
They will be ignored if defined elsewhere.
The same rule also applies to <config> tags.
Re-using Streams¶
It is often a good idea to define another stream when you plan to repeat (with some modification) the same video track. For example,
<material name="cat" path="videos/cat.mp4"/>
<stream name="title">
<composite name="title-slide" text="My Video Title"/>
</stream>
<stream>
<reference name="title"/>
<reference name="cat"/>
<reference name="title"/> <!-- re-using the stream -->
</stream>
will compile a video that starts with the title slide,
the cat video, and then the title slide again.
Notice that the stream named title was used twice
in the main stream. You may apply additional edits e.g.,
<stream>
<reference name="title"/>
<reference name="cat"/>
<composite name="negate">
<reference name="title"/> <!-- negate the stream -->
</composition>
</stream>
Warning
Be mindful not to make any recursion or loop when including other streams. Veddy will fail in such circumstances.
Exporting Multiple Streams¶
In some cases, you may wish to export multiple videos combining same video tracks. One example would be to export both long and short versions of a video. Veddy facilitates this task by the use of export tags. Here’s how
<stream name="long">
...
</stream>
<stream name="short">
<composite name="trim" start="0" end="3">
<reference name="long"/>
</composite>
</stream>
<export stream="short" path="short.mp4"/>
<export stream="long" path="long.mp4"/>
In this example, a short version of video is made by trimming
the long version by 3 seconds. When run,
two videos short.mp4 and long.mp4 will be compiled.
Note
Likewise <global> and <config> tags,
<export> tags are also
only allowed to be defined directly in the <root> tag.
Conventionally, <export> tags are often defined
toward the end of <root> (that is, near </root>).
Evaluating Expressions¶
One of the strength of Veddy is on-the-fly evaluation of expressions. For example,
<material name="cat" path="videos/cat.mp4"/>
<stream>
<pipe>
<reference name="cat"/>
<composite name="drawtext" text="eval(duration()) seconds"/>
</pipe>
</stream>
will draw a text telling the duration of cat.mp4.
As you may deduce, eval() is a special reserved syntax that
evaluates symbolic expressions in it at runtime.
duration() is a built-in function to get the duration of an
incoming video (that is, the duration of cat.mp4 in this case).
The list of available built-in functions can be seen from
the side menu.
Expression Examples¶
Some examples are illustrated below. See Debugging for
the use of <print> and <exit/> tags.
<stream>
<!-- print the maximal duration from the nested two videos -->
<print name="max duration" value="eval(max(durations()))">
<reference name="cat"/>
<reference name="dog"/>
</print>
<!-- print the duration sum from the nested two videos -->
<print name="max duration" value="eval(sum(durations()))">
<reference name="cat"/>
<reference name="dog"/>
</print>
<!-- print the name and the duration of the 2nd material imported -->
<print name="duration" value="eval(material_name(1)) = eval(material_duration(1))"/>
<!-- calculate a mathematical expression -->
<print name="duration" value="eval(4*3+duration())"/>
<!-- terminate parse -->
<exit/>
</stream>
Note
eval() is evaluated using Python3; hence, any pythonic expressions
are valid.
Variables¶
set and global tags allow us to define both global and local variables. For example,
<global name="hoge" value="4"/>
<stream>
<set name="foo" value="3"/>
<print name="hoge" value="$hoge"/>
<print name="foo" value="$foo"/>
<print name="output" value="eval($hoge * $foo)"/>
</stream>
the above scene will print
hoge: 4
foo: 3
output: 12
To walk through the code, the stream first assigns
a global variable hoge as 4, local
variable foo as 3. print tag is
then used to print the value of its multiplication.
As suggested, to access variables, a prefix $
shall be added to the beginning of the name.
Note
Veddy uses FFmpeg as backend. For this matter, some reserved variable provided by FFmpeg can be also used in conjunction with local/global variables. These FFmpeg variables are available depending on which FFmpeg filter is internally used. To see which are available, find corresponding compositions from the side menu, or look up definitions from
functions.xmlNote that FFmpeg variables are accessible without
$prefix, and symbolic equations are evaluated withouteval()syntax.Global variables are accessible from anywhere, while local variables are visible only from its scope and sub-scopes. Local variables can be cleared by
<clear/>tag.Some variables are reserved by Veddy (e.g., _width_, _height_, _fps_). They are all set by Veddy, and read-only.
For example, here shows an example that uses FFmpeg variables and expressions to draw texts
<config name="shape" value="600x600"/>
<config name="fps" value="20"/>
<stream>
<pipe>
<composite name="color" duration="3"/>
<composite name="drawtext" text="Top" shift_y="-h/4"/>
<composite name="drawtext" text="Bottom" shift_y="h/4"/>
</pipe>
</stream>
Here, h/4 includes FFmpeg’s variable h
and the expression shift_y="h/4" is handled by FFmpeg’s filter.
See drawtext for the list of available FFmpeg’s built-in variables.
Inserting Audios¶
Veddy provides a simplified interface for inserting audio materials. Here is an example.
<audiotrack>
<insert at="0">
<audio path="introduction.mp3"/>
</insert>
<insert at="3.5">
<audio path="method.mp3"/>
</insert>
<insert at="7">
<audio path="conlusion.mp3"/>
<audio path="acknowledgements.mp3"/>
</insert>
</audiotrack>
In this example, introduction.mp3 is inserted at the beginning of the video.
Next, method.mp3 is inserted at 3 seconds after the video starts.
Finally, conclusion.mp3 is inserted at 7 seconds after the start, followed by
acknowledgements.mp3.
Note
At the moment, audio is lightly supported, and is not able to apply complex filters.
Do not import audio materials with
<material>tags; they are reserved only for videos. Directly specify the same audio file if you plan to repeat it.All the audio-related tags must be written within
<audiotrack>tags.
If you notice that the audio volume is slightly quiet or loud, you may
change the volume by volume attribute, e.g.,
<audiotrack volume="1.5">
...
</audiotrack>
In this example, the volume is increased by 50%.
Audio track is automatically selected if you only have one stream and
one audio track; however, if you have multiple streams that you plan to
export, you should also associate corresponding audio tracks for each stream.
This can be done by providing a name attribute and linking them via audiotrack
attribute in <export> tags by
<audiotrack name="narration">
...
</audiotrack>
<audiotrack name="testaudio">
...
</audiotrack>
<export stream="main" audiotrack="narration"/>
<export stream="test" audiotrack="testaudio"/>
Remote Preview¶
Previewing video is possible through HTTP streaming. Assuming that you run Veddy on your local computer, you can start previewing a stream by
# If you plan to run on a remote server,
# login to the server via SSH first
# Choose a port that is reachable
PORT=8080
# Start video compilation on preview mode
docker run -p ${PORT}:8020 -v ${PWD}:/veddy \
--rm veddy scene.xml --preview
If you have installed a launcher script, you may alternatively run
# If you plan to run on a remote server,
# login to the server via SSH first
# Port number is embedded in the launcher script
# Change the number in it and re-install script
# if you need to
# Start video compilation on preview mode
veddy scene.xml --preview
Open a new terminal and run
# This command should be run on your local machine
# Make sure that you can reach ${SERVER_HOST}
# and the port ${PORT}
# Install ffplay if not available
# Set the same port
PORT=8080
# Set server host
SERVER_HOST=localhost
# Run client and watch the preview
ffplay http://${SERVER_HOST}:${PORT}
In this example we used ffplay as a streaming client.
If you don’t have it, you may
install it via a system-provided package manager or
choose other rich clients such as VLC.
If you plan to run Veddy on a remote server, change the server address and
set a reachable port.
In practice, you probably need to login to the server
via SSH first.
If you have multiple streams to export, or simply want to select
one specific stream to preview, add --stream option as
docker run -p ${PORT}:8020 -v ${PWD}:/veddy \
--rm veddy scene.xml \
--preview \
--stream <stream_name_to_preview>
Previewing Specific Time Range¶
You can specify the time range of preview. This is done by
docker run -p ${PORT}:8020 -v ${PWD}:/veddy \
--rm veddy scene.xml \
--preview \
--starts_from <start_time_in_seconds> \
--duration <duration_in_seconds>
Scaling Preview Size¶
If you are working on a large (in terms of dimensions) video, you may scale the dimensions for preview, e.g.,
docker run -p ${PORT}:8020 -v ${PWD}:/veddy \
--rm veddy scene.xml \
--preview \
--scale 0.75
This will scale the window size 25% smaller than the actual size (the preview is shown in 75% of the original size).
Note
When previewing, time stamp is shown at the left-top corner.
Specifying Video Settings¶
Global video settings are set through config tags. Currently, three entries are given
shape: the video size (width and height)
fps: the video FPS
bitrate: the video bitrate
pixel_format: the video pixel format
If not given, pixel format is set yuv420p and
bitrate 12M.
See config for detail.
Shape and FPS are undefined unless inferred by Veddy.
If you would like to change some of these, specify like
<root version="0.0.1">
<config name="shape" value="600x400"/>
<config name="fps" value="60"/>
<config name="bitrate" value="3M"/>
<config name="pixel_format" value="rgb24"/>
...
</root>
Note
ffmpeg_config may be also used to pass specific FFmpeg parameters.
Debugging¶
Working with a complicated video stream is easier if you can track the details of incoming information (e.g., video size and duration) in the stream. This is possible by introducing print and exit tags. Here’s how
<global name="hoge" value="2"/> <!-- dummy global variable -->
<material name="cloud" path="videos/cloud.mp4"/>
<stream>
<set name="foo" value="2"/> <!-- dummy local variable -->
<pipe>
<reference name="cloud"/>
<print name="check point 1"/>
<composite name="trim" start="5" end="8"/>
<set name="bar" value="my_text"/> <!-- dummy local variable -->
<print name="check point 2"/>
<composite name="crop" x="0" end="0" width="80" height="50"/>
<print name="check point 3"/>
</pipe>
<exit/>
</stream>
this will print (using videos/cloud.mp4 contained in the repository)
>>> check point 1
arguments (global) = {'hoge': 2.0}
arguments (local) = {'foo': 2.0}
infos = [{'duration': 10.0, 'shape': (480, 270), 'fps': 25.0}]
<<<
>>> check point 2
arguments (global) = {'hoge': 2.0}
arguments (local) = {'foo': 2.0, 'bar': 'my_text'}
infos = [{'duration': 3.0, 'shape': (480, 270), 'fps': 25.0}]
<<<
>>> check point 3
arguments (global) = {'hoge': 2.0}
arguments (local) = {'foo': 2.0, 'bar': 'my_text'}
infos = [{'duration': 3.0, 'shape': (80, 50), 'fps': 25.0}]
<<<
Notice that at the check point 1, video duration and the dimensions are
infos = [{'duration': 10.0}, 'shape': (480, 270), ... ]
After trimming the video (check point 2) the info changes to
infos = [{'duration': 3.0}, 'shape': (480, 270), ... ]
Now we can confirm that the video duration is changed to 3 seconds. Next, let’s see check point 3; just after the video is further cropped. It is
infos = [{'duration': 3.0}, 'shape': (80, 50), ... ]
Now we can see that video dimensions are altered to 80x50.
When we focus on debugging, we won’t be compiling this video.
Hence, we set <exit/> before the stream ends.
This ensures that the XML parsing terminates before starting
compiling the video.