Last active
May 20, 2023 16:58
-
-
Save sgqy/f551e8630d6f9d55684e60706e6d41fb to your computer and use it in GitHub Desktop.
multicore minterpolate in ffmpeg
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
### multicore minterpolate in ffmpeg | |
# just slice & process & concat | |
# the concat points between sliced may be weird, but it just works | |
# default args | |
ff=/usr/bin/ffmpeg | |
fps=60000/1001 | |
nut=00:00:10 | |
enc=libx264 | |
task=$(nproc --all) | |
ff_args=( -hide_banner -loglevel level+warning -stats -y ) | |
############################# | |
err_args () { | |
printf "\nUsage: %s <-i in_file> [args...] <out_file>\n" "$(basename "$0")" | |
printf "\n" | |
printf "Arguments:\n" | |
printf "\t-r N\tTarget FPS\t\t\t[%s]\n" "${fps}" | |
printf "\t-s T\tStart point of source video\n" | |
printf "\t-t T\tEnd point of source video\n" | |
printf "\t-e str\tEncoder name\t\t\t[%s]\n" "${enc}" | |
printf "\t-P N\tParallel count. 0 is max\t[%s]\n" "${task}" | |
printf "\t-C T\tVideo duration of parallel unit\t[%s]\n" "${nut}" | |
printf "\t-F str\tAlternative ffmpeg path\t\t[%s]\n" "${ff}" | |
exit 1 | |
} | |
clean () { | |
rm -f _cut.mp4 | |
rm -f _s_?????.mp4 | |
rm -f _m__s_?????.mp4 | |
rm -f _list.txt | |
} | |
while getopts 'i:r:s:t:e:P:C:F:' OPTION; do | |
case $OPTION in | |
i) | |
in=$OPTARG # input file | |
;; | |
r) | |
fps=$OPTARG # target fps, fraction is also OK | |
;; | |
s) | |
ss=$OPTARG # begin time of input | |
;; | |
t) | |
to=$OPTARG # end time of input | |
;; | |
e) | |
enc=$OPTARG # encoder | |
;; | |
P) | |
task=$((OPTARG)) # process count, xargs use max when it is 0 | |
;; | |
C) | |
nut=$OPTARG # parallel block length | |
;; | |
F) | |
ff=$OPTARG # ffmpeg binary | |
;; | |
*) | |
err_args | |
;; | |
esac | |
done | |
shift $((OPTIND - 1)) | |
# set target file | |
out="$1" | |
# select a temp file to slice: original file or cut from original | |
temp="${in}" | |
# check i/o files | |
if [ -z "${in}" ]; then | |
echo '*** No input file' | |
err_args | |
fi | |
if [ -z "${out}" ]; then | |
echo '*** No output file' | |
err_args | |
fi | |
# set duration to convert and temp file | |
duration=() | |
if [ -n "${ss}" ]; then | |
duration+=(-ss) | |
duration+=("${ss}") | |
temp=_cut.mp4 | |
fi | |
if [ -n "${to}" ]; then | |
duration+=(-to) | |
duration+=("${to}") | |
temp=_cut.mp4 | |
fi | |
############################# | |
set -x | |
clean | |
# use origin file or cut a part from origin file | |
if [ "${temp}" == _cut.mp4 ]; then | |
"${ff}" "${ff_args[@]}" "${duration[@]}" -i "${in}" -c copy "${temp}" | |
fi | |
# make slices for parallel use | |
"${ff}" "${ff_args[@]}" -i "${temp}" -c copy -f segment -segment_time "${nut}" '_s_%05d.mp4' | |
# convert the slices ### this progress may be extremely SLOW | |
find . -name '_s_?????.mp4' | sed 's@^.*/@@g' | xargs -i -t -P ${task} "${ff}" "${ff_args[@]}" -i '{}' -vf "minterpolate=fps=${fps}" -c:a copy -c:v "${enc}" '_m_{}' | |
# make the list | |
printf "file '%s'\n" ./_m__s_?????.mp4 > _list.txt | |
# concat the result | |
"${ff}" "${ff_args[@]}" -f concat -safe 0 -i _list.txt -c copy "${out}" | |
# clean dir | |
clean |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
super nice idea !
question:
Is making slice temp file really necessary ?
Can't you get list of key_frames, pick interresting ones to cut the full dataset and then use them to process the transcoding ?
Here is a sample script I did for you to achieve that:
Sample output:
Note: I guess target frame rate must be double (or any integer multiplier) from the original to get a clear transcoding..