|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +# Export moving image frames for preview in search results and on timeline |
| 4 | +# Extract frames from mp4 file: |
| 5 | +# - 6 frames per minute |
| 6 | +# Create matrix file: |
| 7 | +# - max. 36 frames per matrix |
| 8 | +# - one matrix file corresponds to 6 minutes of film |
| 9 | + |
| 10 | +set -e |
| 11 | + |
| 12 | +dir=$(pwd) |
| 13 | +sep='---------------------------------' |
| 14 | + |
| 15 | +die() { |
| 16 | + echo >&2 "$@" |
| 17 | + exit 1 |
| 18 | +} |
| 19 | + |
| 20 | +usage() { |
| 21 | + echo "usage: <command> -i [infile]>" |
| 22 | + echo ${sep} |
| 23 | + echo "-i = Inpute file" |
| 24 | + echo ${sep} |
| 25 | +} |
| 26 | + |
| 27 | +collect() { |
| 28 | + film='' |
| 29 | + cnt=1 |
| 30 | + #for b in `ls`; do |
| 31 | + for file in *.$1; do |
| 32 | + [ -f ${file} ] |
| 33 | + name=$2 |
| 34 | + |
| 35 | + for c in ${cnt}; do |
| 36 | + if [ "$film" == '' ] || [ "$film" == 'VIDEO_TS.VOB' ]; then |
| 37 | + film=${file} |
| 38 | + else |
| 39 | + case $3 in |
| 40 | + true) film="$film|$file" ;; |
| 41 | + esac |
| 42 | + fi |
| 43 | + done |
| 44 | + cnt=$(expr ${cnt} + 1) |
| 45 | + done |
| 46 | + needle="|" |
| 47 | + num=$(grep -o "$needle" <<<"$film" | wc -l) |
| 48 | + num=$(expr ${num} + 1) |
| 49 | +} |
| 50 | + |
| 51 | +# default variables |
| 52 | +infile='' |
| 53 | + |
| 54 | +# alternative to get some information from the source file: |
| 55 | +# ffprobe -v quiet -print_format json -show_format -show_streams ~/sourceFile.mov |
| 56 | + |
| 57 | +while getopts ':i:' OPTION; do |
| 58 | + case ${OPTION} in |
| 59 | + i) infile=$OPTARG ;; |
| 60 | + *) echo 'Unknown Parameter' ;; |
| 61 | + esac |
| 62 | +done |
| 63 | + |
| 64 | +if [ $# -eq 0 ]; then |
| 65 | + usage |
| 66 | + die '' |
| 67 | +fi |
| 68 | + |
| 69 | +# check the arguments: if they exist |
| 70 | +num='^[0-9]+$' |
| 71 | + |
| 72 | +# infile must be given and has to be an mp4 |
| 73 | +if [ -z "${infile}" ]; then |
| 74 | + # must be given |
| 75 | + usage |
| 76 | + die "ERROR: The Input File (-i) is missing" |
| 77 | +fi |
| 78 | + |
| 79 | +# check if mime-type is video/mp4 |
| 80 | +if file -b --mime-type "${infile}" -ne "video/mp4"; then |
| 81 | + die "ERROR: The Input File (-i) is not mp4" |
| 82 | +fi |
| 83 | + |
| 84 | +# get the right output path, the file and the filename (without extension) |
| 85 | +dir=$(dirname "${infile}") |
| 86 | +file=$(basename "${infile}") |
| 87 | +name="${file%.*}" |
| 88 | + |
| 89 | +cd $dir |
| 90 | + |
| 91 | +# -- |
| 92 | +# store the frames and matrix files in specific folder; |
| 93 | +# folder has same unique name as the uploaded file |
| 94 | +mkdir -p $name |
| 95 | +cd $name |
| 96 | + |
| 97 | +# -- |
| 98 | +# read frame rate from input file |
| 99 | +framerate=$(ffprobe -v 0 -of csv=p=0 -select_streams v:0 -show_entries stream=r_frame_rate ../"${file}") |
| 100 | + |
| 101 | +IFS="/" read -a array <<<"$framerate" |
| 102 | +numerator="${array[0]}" |
| 103 | +denumerator="${array[1]}" |
| 104 | + |
| 105 | +fps=$(expr "scale=2;${numerator}/${denumerator}" | bc) |
| 106 | + |
| 107 | +# -- |
| 108 | +# read aspect ratio from input file |
| 109 | +aspect=$(ffprobe -v error -select_streams v:0 -show_entries stream=display_aspect_ratio -of csv=s=x:p=0 ../"${file}") |
| 110 | + |
| 111 | +echo $aspect |
| 112 | + |
| 113 | +# -- |
| 114 | +# split the aspect ratio to have two separated numbers for calculating |
| 115 | +IFS=':' read -a array <<<"$aspect" |
| 116 | +aspectW="${array[0]}" |
| 117 | +aspectH="${array[1]}" |
| 118 | + |
| 119 | +# -- |
| 120 | +# framesize |
| 121 | +framewidth='256' |
| 122 | +frameheight=$(($framewidth * $aspectH / $aspectW)) |
| 123 | +framesize=${framewidth}'x'${frameheight} |
| 124 | + |
| 125 | +echo ${sep} |
| 126 | +echo 'Start with frame export' |
| 127 | +# check the outpath for the frames; |
| 128 | +# if exists, delete it and create new |
| 129 | +if [ -d "frames" ]; then |
| 130 | + rm -rf ./frames |
| 131 | +fi |
| 132 | +mkdir -p 'frames' |
| 133 | + |
| 134 | +framestart=$(echo ${start} + 1 | bc) |
| 135 | +ffmpeg -i ../${file} -an -ss 1 -f image2 -s ${framesize} -vf framestep=${fps} frames/${name}'_f_%d.jpg' 2>&1 |
| 136 | + |
| 137 | +echo 'Done' |
| 138 | +echo ${sep} |
| 139 | + |
| 140 | +# -- |
| 141 | +# create the matrix files |
| 142 | +echo 'Start with creating matrix file' |
| 143 | + |
| 144 | +# change directory frames |
| 145 | +cd frames |
| 146 | +# Get the number of files: one image = one second of movie |
| 147 | +# numfiles is equivalent to the movie duration (in seconds) |
| 148 | +numfiles=(*) |
| 149 | +numfiles=${#numfiles[@]} |
| 150 | + |
| 151 | +image="${name}_f_1.jpg" |
| 152 | + |
| 153 | +# check if file exists |
| 154 | +if [[ ! -f "${image}" ]]; then |
| 155 | + die "File not found: ${image}" |
| 156 | +fi |
| 157 | + |
| 158 | +# grab the identify string, make sure it succeeded |
| 159 | +# IMG_CHARS=$(identify "${image}" 2> /dev/null) || die "${image} is not a proper image" |
| 160 | +# grab width and height |
| 161 | +IMG_CHARS=$(echo "${IMG_CHARS}" | sed -n 's/\(^.*\)\ \([0-9]*\)x\([0-9]*\)\ \(.*$\)/\2 \3/p') |
| 162 | + |
| 163 | +width=$(echo "${IMG_CHARS}" | awk '{print $1}') |
| 164 | +height=$(echo "${IMG_CHARS}" | awk '{print $2}') |
| 165 | + |
| 166 | +ar=$(echo "scale=6; ${width}/${height}" | bc) |
| 167 | +# width for the whole output matrix image: 960px |
| 168 | +# so one preview image has a width of 160px |
| 169 | +# calculate the height for the output matrix image form the aspect ratio |
| 170 | +matrix_width="960" |
| 171 | +matrix_height=$(echo "scale=0; ${matrix_width}/${ar}" | bc) |
| 172 | +matrix_size="${matrix_width}x${matrix_height}" |
| 173 | + |
| 174 | +frame_width="160" |
| 175 | +frame_height=$(echo "scale=0; ${matrix_height}/6" | bc) |
| 176 | +frame_size="${frame_width}x${frame_height}" |
| 177 | +# get every 10th image; start with the image number 5; |
| 178 | +# how many matrixes will it produce? |
| 179 | +calcmatrix=$(echo "scale=4; (${numfiles}/10)/36" | bc) |
| 180 | +echo '#Matrix (calculated) '${calcmatrix} |
| 181 | +nummatrix=$(echo ${calcmatrix} | awk '{printf("%.0f\n",$1 + 0.75)}') |
| 182 | +echo '#Matrix (rounded) '${nummatrix} |
| 183 | + |
| 184 | +t=0 |
| 185 | +while [ ${t} -lt ${nummatrix} ]; do |
| 186 | + echo 'Matrix nr '${t} |
| 187 | + firstframe=$(echo "scale=0; ${t}*360+5" | bc) |
| 188 | + MATRIX=$(find *_${firstframe}.jpg)' ' |
| 189 | + |
| 190 | + c=1 |
| 191 | + while [ ${c} -lt 36 ]; do |
| 192 | + sec=$(echo "scale=0; ${firstframe}+(${c}*10)" | bc) |
| 193 | + if [ $sec -lt $numfiles ]; then |
| 194 | + img="${name}_f_${sec}.jpg" |
| 195 | + if [ -f ${img} ]; then |
| 196 | + MATRIX+=${img}' ' |
| 197 | + fi |
| 198 | + fi |
| 199 | + |
| 200 | + let c=c+1 |
| 201 | + done |
| 202 | + |
| 203 | + # here we make the montage of every matrix file |
| 204 | + # montage -size 320x180 DB_4_5.jpg DB_4_15.jpg DB_4_25.jpg DB_4_35.jpg -geometry +0+0 montage1.jpg |
| 205 | + # $(echo "montage -size ${matrix_size} ${MATRIX} -tile 6x6 -geometry +0+0 -resize ${frame_size} ../matrix/${name}'_m_'${t}'.jpg'") |
| 206 | + montage -size ${matrix_size} ${MATRIX} -tile 6x6 -geometry +0+0 -resize ${frame_size} ../${name}'_m_'${t}'.jpg' 2>&1 |
| 207 | + |
| 208 | + let t=t+1 |
| 209 | + |
| 210 | +done |
| 211 | + |
| 212 | +echo 'Done' |
| 213 | +echo ${sep} |
0 commit comments