Raspberry Pi Camera Module: More on video capture

Happy #MagPiMonday folks. Our engineer David Plowman is back in the latest issue of The MagPi with another tutorial. This time, we discover in-depth settings for capturing video with Raspberry Pi Camera Module.

We met libcamera-vid at the end of last month’s Camera Module tutorial, and found out how to record a short video clip. Like libcamera-still, libcamera-vid has many more options for controlling the resolution, frame rate, and other aspects of the video that we capture, and we’ll discover some of those in this tutorial.

Raspberry Pi OS now supports libcamera, which captures video using the H.264 video encoder by default

Changing the video resolution and frame rate

By default, libcamera-vid will capture videos at a resolution of 640×480 pixels. But it’s easy to change this with the --width and --height parameters (just like libcamera-still). For example, if you want to capture a video that is 1920×1080 pixels (known as ‘full HD’) instead, please use:

libcamera-vid --width 1920 --height 1080 -o
full_hd.h264

This particular resolution of 1920×1080 pixels should be regarded as the largest resolution that can be captured in video, unless noted otherwise in the text below.

Because we’re talking about videos now, we can also ask for different framerates. By default, libcamera-vid will deliver 30 frames per second (fps). We can ask for a different frame rate using the --framerate parameter:

libcamera-vid --width 1280 --height 720
--framerate 50 -o 50fps.h264

This will record a 720p video at 50 fps, so long as the camera can deliver the frames that quickly (otherwise you will get the maximum frame rate that the camera can supply).

Video encoders and file formats

Videos, just like still images, are normally compressed before saving them to disk. In fact, it’s even more important to compress video frames because, with hundreds or thousands of frames, the output file will explode in size.

Video files are compressed to save vital storage space
Video files are compressed to save vital storage space

After compression, the video frames are written to a file with a specific ‘container format’. There’s a choice as to how to organise video frames in a file, not least because there can be other data – such as audio – in the file too, and the container format defines exactly how playback software can access and decode the information.

H.264 Files

The default encoder used by libcamera-vid is the H.264 video encoder. There is special hardware to support this so it combines both good performance and good compression. Once compressed, the frames are written back to back directly to a file which we normally describe as an ‘H.264’ file, and by convention often denote it with the file extension ‘.h264’.

It’s worth noting that these H.264 files are very simple; they’re not really true container formats. That is, you couldn’t mix audio data in with the video, and the video frames do not even have timing information associated with them. Support for playing back such files can be limited, and even where it is supported, the software will have to guess a framerate for the video.

Popular media player software VLC used to support the H.264 file format, but more recently no longer plays them correctly. For this reason, we recommend other programs, such as FFplay.

We’ve already seen how to record a video with a different resolution; another useful option is the bitrate parameter (--bitrate or just -b). This controls the size of the video file created, and therefore the amount of compression and the perceived quality. Compare the following:

libcamera-vid -b 1000000 --width 1920
--height 1080 -o bad.h264

…versus:

libcamera-vid -b 9000000 --width 1920
--height 1080 -o good.h264

The 1,000,000 bits per second (1 megabit per second, or 1­Mb/s) video (‘bad.h264’ option) shows far more objectionable compression artefacts than the 9Mb/s ‘good’ video.

Camera Module 3
Raspberry Pi Camera Module 3 features a 12MP sensor, autofocus, and HDR support

MJPEG files

We’ve previously encountered the JPEG image encoder for compressing still images. There’s nothing to stop you from compressing one still image after another (very quickly) and saving them back to back to create a video file. That’s exactly what an MJPEG file is.

There are some drawbacks, however. The MJPEG file format grew up organically and so lacks some clear decisions on exactly how to format the JPEGs within them. Often they work well, but support can be a bit patchy.

To create and save a 20-second MJPEG file, use this:

libcamera-vid -t 20000 --codec mjpeg -o
test.mjpeg

It is necessary to specify the --codec option to get a proper MJPEG file – changing the file name on its own is not enough. Using the same JPEG encoder as libcamera-still, it supports the same JPEG quality parameter (--quality or -q), though in video files we can normally get by with lower values – indeed the default is only 50. An MJPEG file will ignore any setting made by --bitrate (or -b).

You can play an MJPEG file with ffplay:

ffplay test.mjpeg

MJPEG files, in contrast to H.264 files, can be recorded at resolutions greater than 1920×1080 pixels, though increasing the resolution will decrease the framerate at which libcamera-vid can keep up, resulting in dropped frames and possibly choppy videos.

Uncompressed video files

Most video compression formats are ‘lossy’, meaning that the video you save will have some degree of quality loss compared to the original camera images. Therefore, it’s also possible to save completely uncompressed video, with the caveat that these files can become extremely large, and you would need to know exactly how you intend to use them.

To capture an uncompressed video use the --codec yuv420 option as follows:

libcamera-vid --width 320 --height 240
--codec yuv420 -o test.yuv420

The relatively small size here (320×240 pixels) means the file size won’t explode too rapidly, and also that the system won’t get bogged down trying to write vast amounts of data to the disk (which can easily become quite slow).

Remember that, as usual, the --codec option is necessary and that changing the file name on its own is insufficient.

Finally, the format of the output file is a simple dump of each of the uncompressed image frames, one after the other. These are in ‘YUV420 planar’ format, which you will need to understand in order to make use of it. The use of these files lies beyond the scope of this guide, and is not recommended for beginners.

Uncompressed video files also have no resolution limit (as H.264 files do), though large resolutions will very quickly become a bottleneck due to the volume of data being written out.

When using Camera Module 3, autofocus is enabled automatically in continuous mode
When using Camera Module 3, autofocus is enabled automatically in continuous mode

MP4 files, audio and other container formats

We’ve seen that H.264 and MJPEG video files are not without certain limitations. The video file format with probably the best support is the MP4 file, and libcamera-vid can create these directly, even mixing in an audio stream if you have a microphone.

To create an MP4 file, we have to choose a different codec. It will still use Raspberry Pi’s hardware H.264 encoder, but will access it via a third-party library that will take care of the MP4 container.

To record an MP4 file without audio, use:

libcamera-vid --codec libav -o test.mp4

The usual options that we’ve seen so far (--timeout, --width, --height, --bitrate) will all work as before, though the other options discussed later in this tutorial won’t (unless stated otherwise).

To create an MP4 file with an audio stream, use:

libcamera-vid --codec libav --libav-audio -o
test.mp4

This will make certain reasonable default choices about the audio and how to encode it; please refer to our online documentation for further details.

The ‘libav’ encoder can handle other container formats such as MKV files or MPEG2 Transport Streams. It is possible to stream the latter directly over a network. Please refer to our online documentation for more information.

Pausing and resuming recordings

You can, of course, pause and resume a video recording by stopping libcamera-vid (for example with CTRL+C) and restarting it again when you want the recording to resume. But you may recall how we could get libcamera-still to capture images when we press a key, and we can get libcamera-vid to pause or resume a recording in just the same way.

There are a few more options that we need to cover:

-t or --timeout The length of time, in milliseconds, for which libcamera-vid will run. This may often be zero if you’re going to press a key to terminate the application.

-k or --keypress Pressing the ENTER key toggles between recording and not recording. Type ‘x’ and press ENTER to quit.

--initial Followed by pause or record. This starts libcamera-vid in the paused (i.e. not recording) or recording state. Pressing the ENTER key will then start or pause the recording, respectively.

--split Every time a recording is resumed, the recording starts writing a new output file.

--inline Tells libcamera-vid to insert certain extra header information at the start of every file that it writes. You should normally include this when using the --split option.

-o or --output The name of the output file or files. When we’re using the --split option, we’ll often want each output file to have a different name, so we can use the same ‘counter syntax’ that we saw with libcamera-still’s timelapse option. For example, -o test_%03d.h264 will write the output files test_000.h264,
test_001.h264, test_002.h264 and so on.

Let’s try an example:

libcamera-vid -t 0 --inline --split
--initial pause -k --width 1280 --height 720
-o video_%03d.h264

This will start libcamera-vid in the paused state, so it won’t be recording anything yet. When ENTER is pressed, it will start recording video_000.h264. It will pause when ENTER is pressed again, and then start recording video_001.h264 when ENTER is pressed for the third time. This will carry on indefinitely until ‘x’ is typed followed by ENTER.

Recording in response to a signal

For those familiar with Linux signals, an alternative to pressing a key is to send a signal instead. To do this, simply use -s or --signal instead of -k (or --keypress).

To send a record/pause signal to libcamera-vid, first start it and then type the following into another terminal window:

kill -SIGUSR1 `pidof libcamera-vid`

And you can force libcamera-vid to quit with:

kill -SIGUSR2 `pidof libcamera-vid`

This should be familiar as it matches libcamera-still’s behaviour!

Other recording options

libcamera-vid has many other parameters and we’ll look very briefly through a few of them here.

Circular buffer output

One useful feature is the ability to keep writing the recorded video stream to a buffer in Raspberry Pi’s memory. This is a limited size, and when it fills up, the earliest data in the buffer is evicted to make space for the new frames. When libcamera-vid exits, this circular memory buffer is flushed to the disk. This enables you to leave the camera running indefinitely, but to save only the last several seconds of data when some event occurs that causes the program to be stopped.

Let’s see an example:

libcamera-vid -t 0 --keypress --circular
8 --inline --width 1280 --height 720 -o
circular.h264

This will run indefinitely writing to a circular memory buffer, and when the user types ‘x’ and ENTER, the program will quit after saving the last 8MB (megabytes) of data to the file circular.h264. The size of this memory buffer is determined by the number (which is in units of megabytes) after the --circular option.

You can use --signal instead of --keypress if you prefer.

Timing Information

We remarked earlier how H.264 format files contain no timing information. libcamera-vid allows this information to be output to a separate file for later use or analysis.

Here we simply specify the --save-pts option followed by a file name where the frame times from the start of the video are stored in text form in units of milliseconds. For example:

libcamera-vid --save-pts timestamps.txt -o
circular.h264

The file timestamps.txt will (for a 30 frames per second recording) start like this:

# timecode format v2
0.000
33.332
66.664
99.996

…and so on.

Autofocus and high dynamic range

Autofocus and high dynamic range imaging are supported at the time of writing only on Raspberry Pi Camera Module 3. The support mirrors that in libcamera-still very closely.

Autofocus

When using Camera Module 3, autofocus is enabled automatically in continuous mode. This means that the camera lens will move whenever necessary to maintain optimal focus on the centre part of the image, and this is probably what most users will want most of the time.

It’s also possible to turn off autofocus and set the focus position of the lens manually. To do this, use the --lens-position parameter, and pass it a value measured in ‘dioptres’, meaning the reciprocal of the focus distance. Thus, to focus at a very close distance of about 0.1 m, pass in the value 10 (which is 1 / 0.1). To focus at infinity, pass in 0 (informally, the value of
1 / infinity). You can pass in non-integer values too. For example:

libcamera-vid --lens-position 0.5 -o 2_
metres.h264

…will set the focus position to two metres and not move the lens again.

High dynamic range imaging

The Camera Module 3 supports high dynamic range (HDR) imaging. To use it, specify the --hdr option on the command line, for example

libcamera-vid --hdr -o hdr.h264

Note that non-HDR captures can be performed at a maximum resolution of 4608×2592 pixels, but HDR captures, because of the special nature of the sensor required to support HDR, are limited to 2304×1296 pixels (exactly half the width and height of the non-HDR mode). H.264 video files are, in any case, limited to 1920×1080 pixels, so this restriction is often not relevant.

The MagPi #131 out NOW!

You can grab the brand-new issue right now from Tesco, Sainsbury’s, Asda, WHSmith, and other newsagents, including the Raspberry Pi Store in Cambridge. It’s also available at our online store which ships around the world. You can also get it via our app on Android or iOS.

The MagPi issue 131

You can also subscribe to the print version of The MagPi. Not only do we deliver it globally, but people who sign up to the six- or twelve-month print subscription get a FREE Raspberry Pi Pico W!

The free PDF will be available in three weeks time. Visit the issue page for more details.

No comments

Comments are closed