Extract media file tags with ffprobe

The ffprobe tool, which is part of ffmpeg, can be used to extract all sorts of technical information from media streams. It is very capable, but is perhaps not the most intuitive tool with regards to its options. Therefore, examples are handy to get started. This post will collect examples focusing on metadata tags and the JSON output format.

Extract format section to JSON

The format section is a global part of the media container and provides generic information about the container streams. It also contains the metadata tags, like title, artist, year, etc. In this example, the file is an MP4 audio file.

$ ffprobe -v quiet -of json -show_entries format audio.m4a
    "format": {
        "filename": "audio.m4a",
        "nb_streams": 1,
        "nb_programs": 0,
        "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
        "format_long_name": "QuickTime / MOV",
        "start_time": "0.000000",
        "duration": "3.024000",
        "size": "27730",
        "bit_rate": "73359",
        "probe_score": 100,
        "tags": {
            "major_brand": "M4A ",
            "minor_version": "512",
            "compatible_brands": "isomiso2",
            "genre": "Tone",
            "artist": "A",
            "title": "A title",
            "album": "X",
            "date": "2018",
            "encoder": "Lavf58.13.100"
-v quietBe quiet with diagnostics to stderr.
-of jsonSelect JSON output format.
-show_entries formatSelect the format section.
Options breakdown

Extract only the metadata tags

$ ffprobe -v quiet -of json -show_entries format_tags Movie.mkv 
    "format": {
        "tags": {
            "title": "The Movie",
            "DATE_RELEASED": "2008",
            "creation_time": "2022-02-11T18:26:58.000000Z",
            "ENCODER": "Lavf58.29.100"

Here we select the format_tags section from a video file in Matroska format, omitting the other bits of information under format. Finally, we can use jq to select parts of the JSON data:

$ ffprobe -v quiet -of json -show_entries format_tags Movie.mkv|\
          jq -r .format.tags.title
The Movie

Other output formats

ffprobe supports several output formats. Personally I like JSON, since it is very well defined with regard to syntax and escaping of special characters, etc. However, depending on use case, other forms may be more suitable for extraction of data in contexts like shell scripts.


Enable HTTP/2 with Apache+PHP on Ubuntu

I recently made the necessary adjustments to my Apache+PHP server to support the HTTP/2 protocol. This newer protocol comes with performance advantages and support is widespread by now. Depending on existing Apache server configuration, it is not always trivial to enable, due to specific requirements for HTTP/2 with Apache. This especially applies if you’re using PHP with the classic mod_php Apache module.

Here is a summary of the setup I used before, when my web server only supported HTTP/1.X:

  1. Ubuntu 20.04 LTS
  2. Apache 2.4 web server with the classic prefork multi processing module.
  3. mod_php for server side PHP script execution.
  4. Fully https-enabled site with certificate from Let’s Encrypt.
  5. WordPress for publishing.

Points 2 and 3 will require changes.

The Apache multi processing module (often referred to simply as an «MPM») must be switched to the generally recommended mpm_event. Since mod_php is not compatible with the multi threaded mpm_event, an alternative must be used for PHP script execution. The recommended approach is to setup PHP as a FastCGI service and have Apache proxy requests to this service for actual script execution. So the big difference here is that PHP code will execute outside of the Apache server processes, thereby decoupling Apache’s execution environment and request handling from PHP.

The steps in the following sections all apply to Ubuntu 20.04 and Debian in general (package versions may vary). There are some handy command line tools to configure the modular Apache server, which we will be using. Assuming nothing goes wrong, no significant downtime will occur when following these steps.

1. Setting up the PHP FastCGI service

Disable and remove the classic mod_php package, since it is no longer needed:

# Disable Apache mod_php:
a2dismod php7.4

# Optionally remove packages, no longer be needed:
apt autoremove libapache2-mod-php\*

Install the PHP FastCGI process manager package:

apt install php-fpm

The service should be automatically started. The package provides the following information after a successful installation:

NOTICE: Not enabling PHP 7.4 FPM by default.
NOTICE: To enable PHP 7.4 FPM in Apache2 do:
NOTICE: a2enmod proxy_fcgi setenvif
NOTICE: a2enconf php7.4-fpm
NOTICE: You are seeing this message because you have apache2 package installed.

So let’s just do what is recommended:

a2enmod proxy_fcgi setenvif
a2enconf php7.4-fpm

The proxy_fcgi module is required so that Apache can proxy requests to FastCGI services. The second command enables proxy configuration specific to the service setup by the php7.4-fpm package.

If you have customizations to php.ini, you will have to copy those from the old mod_php configuration file to the separate php-fpm configuration. These are the relevant files:

# old mod_php config file no longer in use:

# new php-fpm config file:

Finally, you can test that PHP still works on your web site after the switch to php-fpm, before moving on:

apache2ctl configtest
systemctl restart apache2

2. Switch to event multi processing module in Apache

Disable mpm_prefork, then enable mpm_event:

a2dismod mpm_prefork
a2enmod mpm_event

If those commands succeed, then restart Apache and test that your site still works:

apache2ctl configtest
systemctl restart apache2

3. Enable the HTTP/2 protocol

a2enmod http2
systemctl restart apache2

Now you can open your browser developer tools and do a page load of your site. If using https, then your browser should negotiate HTTP/2 with the server, and you will see that logged as the protocol. All done.

If not using https however, then you cannot really take advantage of HTTP/2. In theory it is supported, but most browsers will only use it over TLS connections. The protocol is then referred to as “h2”, meaning HTTP/2 over secure transport.

Tuning mpm_event and php-fpm

You can tune the Apache event MPM by editing the following configuration file:


The PHP FastCGI process manager service can be tuned by editing:


Summary of setup after enabling HTTP/2

  1. Ubuntu 20.04 LTS
  2. Apache 2.4 web server with the event multi processing, http2 and proxy_fcgi modules enabled.
  3. PHP-fpm for server side PHP script execution.
  4. Fully https-enabled site with certificate from Let’s Encrypt.
  5. WordPress for publishing.


Hardware Linux

Capture images from a webcam using ffmpeg

The examples are for Linux and access the web camera through the Video4Linux2 interface. To control web camera settings, use the tool v4l2-ctl. To list connected camera devices, you can use the command: v4l2-ctl --list-devices. On a typical Debian-ish Linux distro, you will also want to add your user to the video and audio groups, so that you can easily access the webcam from a non-desktop session.

Capture to an image file, continually overwriting it with new contents

ffmpeg -y -f v4l2 -video_size 1280x720 -i /dev/video0 \
       -r 0.2 -qscale:v 2 -update 1 /tmp/webcam.jpg
-f v4l2specify input format explicitly as capture from a Video4Linux2 device
-video_size 1280x720specify video frame size from webcam
-i /dev/video0select input device (a UVC-compatible webcam in my case)
-r 0.2set output frame rate to one per 5 seconds
-qscale:v 2set video quality [JPEG quality in this case], 2 is highest quality.
-update 1Image2 muxer option, enable in place update of image file for each video output frame
Options breakdown

Point the output file to a place served by your web server to make your camera image available on the web. The ffmpeg command will run until interrupted or killed.

Add a timestamp to captured images

ffmpeg -y -f v4l2 -video_size 1280x720 -i /dev/video0 \
       -r 0.2 \
       -vf "drawtext=text=%{localtime}:fontcolor=white@1.0:fontsize=26:borderw=1:x=980:y=25" \
       -qscale:v 2 -update 1 /tmp/webcam.jpg

Here we have inserted the drawtext video filter into the processing pipeline. We use its text expansion facilities to simply render the local time onto each video frame with filter-argument text=%{localtime}. It is placed in the top right corner of the image using the x and y arguments.

Running as background job

You can ssh to the host which has the web camera connected, and start the ffmpeg capture process as a background job:

ffmpeg -y -loglevel fatal \
       -f v4l2 -video_size 1280x720 -i /dev/video0 \
       -r 0.2 \
       -vf "drawtext=text=%{localtime}:fontcolor=white@1.0:fontsize=26:borderw=1:x=980:y=25" \
       -qscale:v 2 -update 1 /tmp/webcam.jpg \
       </dev/null &>/tmp/webcam-ffmpeg.log & disown $!

This silences ffmpeg to log only fatal errors, runs it in the background and finally detaches the process from your [bash] shell’s job control, to avoid it being killed if you log out. A more polished solution would be to create a systemd service which controls the ffmpeg webcam capture process, running as a dedicated low privilege system user.

Creating a time lapse video from a bunch of image files

As a sort of bonus chapter on this post, here is how to create a time lapse video from a bunch of captured image files. Assuming you have a directory with JPEG images named in such a way that they sort chronologically by their filenames (padded sequence numbers or timestamps), here’s how you can transform them into a video.

VP9 video in WebM container:

ffmpeg -y -f image2 -pattern_type glob -framerate 30 \
       -i webcam-images/\*.jpg \
       -pix_fmt yuv420p -b 1500k timelapsevid.webm

H264 video in MP4 container:

ffmpeg -y -f image2 -pattern_type glob -framerate 30 \
       -i webcam-images/\*.jpg \
       -pix_fmt yuv420p -b 1500k timelapsevid.mp4
-f image2Input demuxer is Image2, which can read image files.
-pattern_type globInstructs Image2 demuxer to treat input pattern as file name glob.
-framerate 30Set desired framerate; how many images to display per second in the resulting video.
-i webcam-images/\*.jpgSet input to a glob pattern matching the images files you would like to include in the video. Note that we do not want the shell to expand the glob, but rather pass the asterisk verbatim to ffmpeg.
-pix_fmt yuv420pSet video codec pixel format. YUV420p is selected to ensure compatibility with a broad range of decoders/players.
-b 1500kSet desired bitrate of video file.
Options breakdown

Note that all input images should have the same dimensions. Otherwise, you will likely have to add more options to ffmpeg to transform everything to a single suitable video size.

The resulting video files will be suitable for publishing on the web using the <video> tag.