User Tools

Site Tools


doc:appunti:linux:video:ffmpeg_stack_horizontally

Stack two videos side-by-side with ffmpeg

I created a Unix shell script to do a side-by-side comparison between videos recorded with my two action cameras: a new SJCAM 8 Pro and the old Xiaomi Yi.

Suppose that we have two videos taken simultaneously with the two cameras:

The result will be a video like the following: the central part of each video is stacked horizontally on the final frame, two labels are overlayed on the bottom corners, the audio from the first video is mixed into the left channel of the resulting video and the audio from the second video goes to the right channel.

The script uses ffmpeg to do all the incantation. It is required that both videos share the same resolution.

The script can take into account that the videos may be out of sync: it is possible to pass two timestamps referring at the same instant captured into both videos. The script will shift the videos presentation time each-other, so that both will be synced.

The script has some constraints. It requires the Bash shell, because it uses the ((expression)) to do arithmetic evaluation. The script requires also the Python interpreter, just to do a floating point difference and properly formats the result.

#!/bin/bash

# Take the central part of two videos and stack them horizontally.
# Audio from the first video is merged into the left channel of the
# resulting video, audio from the second video goes to the right.
# Two lables are overlayed on the bottom left and right corners.

# https://stackoverflow.com/questions/35349935/ffmpeg-crop-with-side-by-side-merge
# https://unix.stackexchange.com/questions/233832/merge-two-video-clips-into-one-placing-them-next-to-each-other
# https://trac.ffmpeg.org/wiki/AudioChannelManipulation

# Left and right videos.
#VIDEO_1='20211023135104_0322.MP4'
#VIDEO_2='YDXJ0851.mp4'
VIDEO_1="$1"
VIDEO_2="$2"

# Two timestamps which should be synced.
#TIME_1='15.482'
#TIME_2='16.282'
TIME_1="$3"
TIME_2="$4"

# Labels to be superimposed on the two halves.
#LABEL_1='SJ8Pro'
#LABEL_2='Xiaomi Yi'
LABEL_1="$5"
LABEL_2="$6"

#OUTPUT='output.mkv'
OUTPUT="$7"

if [ -z "$VIDEO_1" -o -z "$VIDEO_2" -o \
     -z "$TIME_1"  -o -z "$TIME_2"  -o \
     -z "$LABEL_1" -o -z "$LABEL_2" -o \
     -z "$OUTPUT" ]; then
    echo
    echo "Usage: $(basename $0) [video1] [video2] [time1] [time2] [label1] [label2] [output]"
    echo
    echo "  Example: $(basename $0) 20211023135104_0322.MP4 YDXJ0851.mp4 15.482 16.282 SJ8Pro 'Xiaomi Yi' output.mkv"
    echo
    exit 1
fi

# Do the math, etc...
if (( $(echo "$TIME_1 > $TIME_2" | bc -l) )) ; then
    TO_1="$(python -c "print('%0.3f' % ($TIME_1 - $TIME_2,))")"
    TO_2='0.000'
else
    TO_1='0.000'
    TO_2="$(python -c "print('%0.3f' % ($TIME_2 - $TIME_1,))")"
fi

# Font and style for labels.
FONT='fontfile=/usr/share/fonts/truetype/msttcorefonts/Arial_Bold.ttf:'
STYLE='fontcolor=yellow:fontsize=48:box=1:boxcolor=black@0.5:boxborderw=10'
# Labels placement.
L1X='20'
L2X='(w-text_w-20)'
L1Y='(h-text_h-20)'
L2Y='(h-text_h-20)'

ffmpeg -ss "$TO_1" -i "$VIDEO_1" -ss "$TO_2" -i "$VIDEO_2" \
    -filter_complex \
     "[0:v]crop=iw/2:ih:ow/2:0[left]; \
      [1:v]crop=iw/2:ih:ow/2:0[right]; \
      [left][right]hstack=shortest=1[vs]; \
      [0:a][1:a]amerge=inputs=2,pan=stereo|c0<c0+c1|c1<c2+c3[a]; \
      [vs]drawtext=${FONT}text='${LABEL_1}':${STYLE}:x=${L1X}:y=${L1Y},drawtext=${FONT}text='${LABEL_2}':${STYLE}:x=${L2X}:y=${L2Y} [v]" \
    -map '[v]' -map '[a]' \
    "$OUTPUT"
doc/appunti/linux/video/ffmpeg_stack_horizontally.txt · Last modified: 2021/10/28 11:43 by niccolo