How to simplify a Docker run command
Tags: docker, opensource, beginners, productivity
Published: 2022-04-12 13:52:54

I recently wanted to create animated gifs from videos. The idea was to get video previews, in a very lightweight file. After a quick search online, I found [FFMPEG](https://ffmpeg.org/), a fantastic multimedia framework to manipulate media. There is also a few wrappers that exists in different languages (ex: C#, JavaScript) but you still need to install FFMPEG locally, and I didn't want that. In fact, I wanted a simple solution that doesn't require any installation locally and something in the cloud. In this post, I want to share how I achieved the first one. 

> The code and container image are available on [GitHub](https://github.com/FBoucher/aciffmpeg) and [Docker Hub](https://hub.docker.com/r/fboucher/aciffmpeg).

First Contact
-------------

The ffmpeg framework is very powerful and can do so many things; therefore it's normal that it has a ton of possible parameters and extensions. After time spent on the documentation and a few trials and errors, I found how to do exactly what I needed calling it this way:

``` bash
ffmpeg -r 60 -i $INPUTFILE -loop 0 -vf scale=320:-1 -c:v gif -f gif -ss 00:00:00.500 -r 10 -t 5 - > $OUTPUTFILE
```

This will create a five second animated gif from a video. It speeds up the video and lowers the framerate of the gif to keep the output lightweight. Here is an example.

![Hello World episode 5 seconds preview](https://frankysnotes.blob.core.windows.net/blogimages/myPreview.gif)

This is great, but this is not very friendly. How can someone who only creates a video once in a while be expected to remember all those parameters?! And even harder, when the video is vertical some parameters have different values. It was time to simplify, and here is how I did it. Note that I'm a Docker beginner and if you think there is a simpler or better way to do some steps, let me know, and let's learn together.

The Plan
--------

The plan is simple: execute a simple Docker command like `docker run fboucher/aciffmpeg -i NotInTheSky.mp4` and generate a video preview. To build our ephemeral container we will start with something lightweight like `alpine`, install ffmpeg and add a script that would be executed as the container runs. That sounds like an excellent plan, let's do it!

Writing the Script
------------------

The script is simple, but I learned a few things writing it. This is why it's included in this post. The goal was simple: execute the ffmpeg command using some values from the parameters: file path, and if the video is vertical. Here is the script:

``` bash
#!/bin/sh

while getopts ":i:v" opt; do
  case $opt in
    i) inputFile="$OPTARG"
    ;;
    v) isVertical=true
    ;;
    \?) echo "Invalid option -$OPTARG" >&2
    exit 1
    ;;
  esac

  case $OPTARG in
    -*) echo "Option $opt needs a valid argument"
    exit 1
    ;;
  esac
done

if [ -z "$isVertical" ]; then isVertical=false; fi

# used for bash 
#IFS='.'
#read -a filePart <<< "$inputFile"
#outputFile="${filePart[0]}.gif"

# used for dash 
filename=$(echo "$inputFile" | cut -d "." -f 1)
outputFile="$filename.gif"

if $isVertical
then
  ffmpeg -r 60 -i $inputFile -loop 0 -vf scale=-1:320 -c:v gif -f gif -ss 00:00:00.500 -r 10 -t 5 - > $outputFile
else
  ffmpeg -r 60 -i $inputFile -loop 0 -vf scale=320:-1 -c:v gif -f gif -ss 00:00:00.500 -r 10 -t 5 - > $outputFile
fi
```

### Things I learned: Parameter without values

The script needs to be as friendly as possible, therefore any unnecessary information should be removed. Most videos will be horizontal, so let's make the parameter optional. However, I don't want users to have to specify the value `script.sh -i myvideo.mp4 -v true` but instead `script.sh -i myvideo.mp4 -v`. This is very simple to do, once you know it. On the first line of code when I get the parameters: `getopts ":i:v"` notes that there is no ":" after the "v". This is to specify that we are not expecting any values.

### Things I Learned: Bash and Dash

As mentioned earlier the container will be built from Alpine. And Alpine doesn't have **bash** but instead uses **dash** as a shell. It's mostly the same, but there are some differences. The first one will be the shebang (aka "#!/bin/sh" on the first line). And the second was the string manipulation. To generate a new file with the same name but a different extension of the script, split the file name at the ".". This can be done `IFS ... read... <<<` command (commented in the script) on bash but this will give `syntax error: unexpected redirection` and this is because there is no `<<<` in bash. Instead, you need to use the command `cut -d "." -f 1` (where -d specifies the CHAR to use as the delimiter, and -f return only this field).


Building the image
------------------

It's now time to connect all the dots in the dockerfile.


``` dockerfile
FROM alpine:3.13
LABEL Name=aciffmpeg Version=0.0.2
RUN apk add ffmpeg
COPY ./src/myscript.sh /
RUN chmod +x /myscript.sh
ENTRYPOINT ["/myscript.sh"]
```

The file is not extremely complex but let’s pass through it line by line.
- We start `FROM` Alpine version 3.13  and apply a `LABEL`. 
- `RUN` Will execute the command to install ffmpeg. The `apk` is the default utility on Alpine to install apps just like `apt` on Ubuntu.
- `COPY` Is copying the script from our local machine into the container at the root. 
- The second `RUN` command is to make sure the script is executable.
- Finally, `ENTRYPOINT` will allow us to configure the container to run as an executable in this case as the script. All parameters passed to Docker will be passed to the script.

The only things left now are to build, tag, and push it on Docker Hub.

``` bash
docker build -t fboucher/aciffmpeg .

docker tag  0f42a672d000 fboucher/aciffmpeg:2.0

docker push fboucher/aciffmpeg:2.0
```

## The Simplified version

And now to create a preview of any video you just need to map a volume and specify the file path and optionally mention if the video is vertical.

On Linux/ WSL the command would look like this: 

``` bash
docker run -v /mnt/c/dev/test:/video fboucher/aciffmpeg -i /video/sample.mp4 -v
```
And on PowerShell like that:
``` powershell
docker run -v c/dev/test:/video fboucher/aciffmpeg -i /video/sample.mp4 -v
```

I learned a lot about Docker doing that project and now I have a tool. What are the tools you built using containers that simplify your life or work?


## Video version

I recorded a video version if you are interested.

{% youtube ieqeHF0NsDk %}

~Frank