Skip to content

Instantly share code, notes, and snippets.

@hereisderek
Last active April 14, 2023 23:42
Show Gist options
  • Save hereisderek/6a2ca0afcc957858d8a465392212edb8 to your computer and use it in GitHub Desktop.
Save hereisderek/6a2ca0afcc957858d8a465392212edb8 to your computer and use it in GitHub Desktop.
script to combine xiaomi camera recordings
#!/bin/bash
## to get the latest version of this script, see: https://gist.githubusercontent.com/hereisderek/6a2ca0afcc957858d8a465392212edb8/raw/combine.sh
## to run this script as a cron task:
## * * * * * /usr/bin/wget -qO- https://gist.githubusercontent.com/hereisderek/6a2ca0afcc957858d8a465392212edb8/raw/combine.sh | /bin/bash -s /mnt/xiaomi/xiaomi_camera_videos /mnt/xiaomi/xiaomi_camera_videos/output >/dev/null 2>&1
function example_usage {
export your_healthcheck_url="https://hc-ping.com/37174a73-****-****" && wget "${your_healthcheck_url}/start" -T 10 -t 5 -O /dev/null && wget --no-cache 'https://gist.githubusercontent.com/hereisderek/6a2ca0afcc957858d8a465392212edb8/raw/combine.sh' -qO /tmp/combine.sh && chmod a+x /tmp/combine.sh && /tmp/combine.sh && wget "${your_healthcheck_url}" -T 10 -t 5 -O /dev/null
}
# Get the name of the script without the path
SCRIPT_NAME=$(basename "$0")
LOCK_FILE="/tmp/combine.sh.lock"
PROCESSING_EXTENSION=".processing"
PROCESSED_EXTENSION=".processed"
FFMPEG_PARAMS="-hide_banner"
### hardware acceleration, see https://trac.ffmpeg.org/wiki/HWAccelIntro
## intel quick sync
# FFMPEG_PARAMS="${FFMPEG_PARAMS} -hwaccel qsv -qsv_device /dev/dri/renderD128" # linux
# FFMPEG_PARAMS="${FFMPEG_PARAMS} -hwaccel dxva2" # windows
# FFMPEG_PARAMS+=" -crf 28 -preset fast -map 0:v:0 -map 0:a:0 -map 0:s? -map_metadata -1 -movflags +faststart -shortest"
### video
## for direct video
VIDEO_CODEC="copy"
# VIDEO_CODEC="hevc_videotoolbox" # m1 mac
### audio
## direct copy audio
# AUDIO_CODEC="copy"
## convert audio to aac
AUDIO_CODEC="aac"
trap "rm -rf ${LOCK_FILE}" EXIT # remove the lockdir on exit
if [ -e "${LOCK_FILE}" ]; then
echo "Script is already running, exiting."
exit 1
fi
touch "${LOCK_FILE}"
# Set the path to the directory containing the camera footage folders
INPUT_DIR="/Volumes/Xiaomi/xiaomi_camera_videos"
# Set the output directory for the processed footage and thumbnail image
OUTPUT_DIR="/Volumes/Xiaomi/xiaomi_camera_videos/output"
if [ "$#" -eq 2 ]; then
INPUT_DIR="$1"
OUTPUT_DIR="$2"
fi
echo "INPUT_DIR: $INPUT_DIR, OUTPUT_DIR: $OUTPUT_DIR"
# make sure output folder exists
mkdir -p ${OUTPUT_DIR}
# footage_folder, output_folder
function process_footage_folder {
footage_folder=$1
output_folder=$2
footage_name=$(basename "$footage_folder")
# rename footage folder to add processing extension
footage_folder_processing="${footage_folder}${PROCESSING_EXTENSION}"
footage_folder_proceed="${footage_folder}${PROCESSED_EXTENSION}"
footage_output_file_name="${output_folder}/${footage_name:0:8}_${footage_name:8:2}.mkv"
# rm -rf "$footage_folder_processing" "$footage_folder_proceed"
# check if footage folder is already processed
if [ -d "$footage_folder_proceed" ]; then
echo -e "footage folder already processed: $footage_folder_proceed"
exit 1
fi
echo -e "process_footage_folder footage_folder:$footage_folder output_folder:$output_folder footage_name:$footage_name"
mv "$footage_folder" "${footage_folder_processing}"
# make sure output folder exists
mkdir -p ${output_folder}
# get all mp4 files under footage folder
mp4_files=($(find "$footage_folder_processing" -type f -name "*.mp4" | sort))
echo -e "\n"
for i in "${!mp4_files[@]}"; do
mp4_file=${mp4_files[$i]}
mp4_file_name=$(basename "$mp4_file" | cut -f 1 -d '.')
thumbnail_output_file_name="${footage_name:0:8}_${footage_name:8:2}${mp4_file_name:0:2}.jpg"
thumbnail_output_file="${output_folder}/${thumbnail_output_file_name}"
# skip if thumbnail already exists
if [ -f "$thumbnail_output_file" ]; then
echo -ne "Progress:$i/${#mp4_files[@]} skipping:${mp4_file} \r"
continue
fi
# generate thumbnail for mp4 file and save it to output folder with the same name as the mp4 file
ffmpeg -y -i "${mp4_file}" -ss 00:00:01.000 -vframes 1 "${thumbnail_output_file}" >/dev/null 2>&1
echo -ne "Progress:$i/${#mp4_files[@]} processing:${mp4_file} \r"
done
echo -e "\nThumbnails generated, conbining footage..\n"
# rm "${footage_output_file_name}" > /dev/null 2>&1
ffmpeg ${FFMPEG_PARAMS} -f concat -safe 0 -i <(for f in "${mp4_files[@]}"; do echo "file '$f'"; done) -c:v ${VIDEO_CODEC} -c:a ${AUDIO_CODEC} "${footage_output_file_name}" | grep "frame=" || true
## chose one of the following three:
# mv "${footage_folder_processing}" "${footage_folder_proceed}"
# mv "${footage_folder_processing}" "${footage_folder}"
rm -rf "${footage_folder_processing}"
}
# Get a list of all camera folders, sorted by name in ascending order, and skip the last one, also skip the output folder
# Get a list of all camera folders, sorted by name in ascending order
CAMERA_FOLDERS=($(find "$INPUT_DIR" -mindepth 1 -maxdepth 1 -type d | sort))
# Loop through the camera folders
for CAMERA_FOLDER in "${CAMERA_FOLDERS[@]}"; do
# Skip the output directory
if [[ "$CAMERA_FOLDER" =~ ^"$OUTPUT_DIR" ]]; then
echo "skip CAMERA_FOLDER folder: $CAMERA_FOLDER"
continue
fi
CAMERA_ID=$(basename "$CAMERA_FOLDER")
FOOTAGE_FOLDERS=()
FOOTAGE_FOLDERS_TEMP=($(find "$CAMERA_FOLDER" -mindepth 1 -maxdepth 1 -type d | sort))
for folder in "${FOOTAGE_FOLDERS_TEMP[@]}"; do
if [[ "$folder" = "$OUTPUT_DIR"/* ]]; then
echo "skip FOOTAGE_FOLDER folder: $folder, because it is in the output folder"
continue
fi
if [[ "$folder" = *"$PROCESSING_EXTENSION" ]]; then
echo "skip FOOTAGE_FOLDER folder: $folder, because it is processing"
continue
fi
if [[ "$folder" = *"$PROCESSED_EXTENSION" ]]; then
echo "skip FOOTAGE_FOLDER folder: $folder, because it is proceed"
continue
fi
FOOTAGE_FOLDERS+=("$folder")
done
# loop through the footage folders except for the last one
for FOOTAGE_FOLDER in "${FOOTAGE_FOLDERS[@]:0:${#FOOTAGE_FOLDERS[@]}-1}"; do
FOOTAGE_NAME=$(basename "$FOOTAGE_FOLDER")
FOOTAGE_DATE="${FOOTAGE_NAME:0:8}"
output_folder="${OUTPUT_DIR}/${CAMERA_ID}/${FOOTAGE_DATE}"
echo -e "FOOTAGE_FOLDER:$FOOTAGE_FOLDER output_folder:$output_folder"
process_footage_folder "$FOOTAGE_FOLDER" "$output_folder"
done
done
rm -rf "${LOCK_FILE}"
@hereisderek
Copy link
Author

image

works pretty well especially if you combine with healcheck.io like shown in the example

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment