picam motion detection and overlaying timestamps
I wanted to add timestamps to the video that's captured while using my picam, opencv-based motion detection application. The easiest way would probably be to read and modify every frame in userspace on the Pi, before letting the GPU encode it to h264, however I'd like to keep frame rates as high as possible - so would rather the Pi did the minimum amount of work it needs to do. I only really need the frame number and a timestamp and some way to merge them with the video.
The MMAL API for the Pi's camera has a bunch of flags in the 'buffer header' objects that get passed around as the image data flows from component to component. By counting the instances of one of these - MMAL_BUFFER_HEADER_FLAG_FRAME_END - you can track frame numbers. I've modified the code to emit the frame number and an epoch timestamp when motion is detected. To prevent it from producing hundreds of these events, it only emits when another flag: MMAL_BUFFER_HEADER_FLAG_KEYFRAME - is set at the start of a keyframe. This pegs the rate a the keyframe rate; every two seconds. An example keyframe event looks like this in the logs:
KEYFRAME (1397291533:1)
The logs are written to stderr and the h264 video stream is written to stdout. To record them both, the app is run like this:
./mmal_opencv_modect > video.h264 2> video.log
As it turns out the h264 file it spits out is less than perfect, some things play it at a high frame rate and some things don't play it at all. To clean it up I've been pushing it through ffmpeg to create a nice avi:
./ffmpeg -r 30 -i video.h264 -q:v 1 -r 30 video.avi
This step doesn't need to happen on the pi, and can be offloaded to faster machine - and while we're post processing we may as well be adding timestamp overlays too :) Initially I tried to use ffmpeg's 'drawtext' filter and to start with it looked promising; it has expressions so that text strings can be positioned across exact frame ranges. Unfortunately each of these are specified individually on the command line and more than a thousand exceeds the maximum command line length - at least here on my machine.
Instead I've written a python script that can take the log output from mmal_opencv_modect and convert it into a MicroDVD subtitle file. The MicroDVD format uses frame numbers, so is easy to translate from the keyframe events in the logs. The epoch timestamp is also converted to something a little more human friendly. The script is run like this:
./scripts/modect2sub.py video.log > video.sub
Now we have the subtitle file it just needs to be merged with the h264 stream. Using ffmpeg, compiled with libass support:
./ffmpeg -r 30 -i video.h264 -vf "subtitles=video.sub" -q:v 1 -r 30 video.avi
Github repo can be found here.