#!/bin/sh #=============================================================================== # extract-frame # extract a frame from a video as a png or jpg #=============================================================================== # dependencies: # ffmpeg #=============================================================================== # script usage #=============================================================================== usage() { # if argument passed to function echo it [ -z "${1}" ] || echo "! ${1}" # display help echo "\ # extract a frame from a video as a png or jpg https://trac.ffmpeg.org/wiki/Seeking $(basename "$0") -i input.(mp4|mov|mkv|m4v|webm) -s 00:00:00.000 -t (png|jpg) -x width -y height -o output.(png|jpg) -i input.(mp4|mov|mkv|m4v) -s 00:00:00.000 : optional argument # if option not provided defaults to 00:00:00 -t (png|jpg) : optional argument # if option not provided defaults to png -x width : optional argument # -y height : optional argument # -o output.(png|jpg) : optional argument # if option not provided defaults to input-name-timecode" exit 2 } #=============================================================================== # error messages #=============================================================================== INVALID_OPT_ERR='Invalid option:' REQ_ARG_ERR='requires an argument' WRONG_ARGS_ERR='wrong number of arguments passed to script' #=============================================================================== # check the number of arguments passed to the script #=============================================================================== [ $# -gt 0 ] || usage "${WRONG_ARGS_ERR}" #=============================================================================== # getopts check the options passed to the script #=============================================================================== while getopts ':i:s:t:x:y:o:h' opt do case ${opt} in i) infile="${OPTARG}";; s) seconds="${OPTARG}";; t) image="${OPTARG}";; x) width="${OPTARG}";; y) height="${OPTARG}";; o) outfile="${OPTARG}";; h) usage;; \?) usage "${INVALID_OPT_ERR} ${OPTARG}" 1>&2;; :) usage "${INVALID_OPT_ERR} ${OPTARG} ${REQ_ARG_ERR}" 1>&2;; esac done shift $((OPTIND-1)) #=============================================================================== # variables #=============================================================================== # input, input name infile_nopath="${infile##*/}" infile_name="${infile_nopath%.*}" # seconds default seconds_default='00:00:00' # image default image_default="png" # output file recording destination outfile_default="${infile_name}-[${seconds:=${seconds_default}}].${image:=${image_default}}" #=============================================================================== # functions #=============================================================================== # image to video function extract () { ffmpeg \ -hide_banner \ -stats -v panic \ -ss "${seconds:=${seconds_default}}" \ -i "${infile}" \ -q:v 2 -f image2 \ -vframes 1 \ "${outfile:=${outfile_default}}" } # image to video with scale function extract_scale () { ffmpeg \ -hide_banner \ -stats -v panic \ -ss "${seconds:=${seconds_default}}" \ -i "${infile}" \ -q:v 2 -f image2 \ -vframes 1 \ -vf scale="${width:=-1}:${height:=-1}" \ "${outfile:=${outfile_default}}" } #=============================================================================== # run function #=============================================================================== if [ -n "${width}" ] || [ -n "${height}" ]; then extract_scale "${infile}" else extract "${infile}" fi