vid_preview.sh 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. #!/bin/sh
  2. # Bash script that generates film strip video preview using ffmpeg
  3. # You can see live demo: http://jsfiddle.net/r6wz0nz6/2/
  4. # Tutorial on Binpress.com: http://www.binpress.com/tutorial/generating-nice-video-previews-with-ffmpeg/138
  5. get_date (){
  6. echo "$(date '+%Y%m%d-%H%M')"
  7. }
  8. show_usage (){
  9. printf "Usage: $0 [options [parameters]]\n"
  10. printf " For all files in directory use like this:\n"
  11. printf " $ for file in *{.mp4,h264,mkv,avi,rm,ts}; do $0 -f \"\$file\"; done\n"
  12. printf " $ for file in *{.mp4,h264,mkv,avi,rm,ts}; do $0 -f \"\$file\" -h 120 -c 2 -r 4; done\n"
  13. printf "\n"
  14. printf "Mandatory options:\n"
  15. printf " -f|--file [FILENAME.EXT]\n"
  16. printf "\n"
  17. printf "Options:\n"
  18. printf " -h|--height [HEIGHT] (Optional, default: '120')\n"
  19. printf " -c|--cols [COLS] (Optional, default: '2')\n"
  20. printf " -r|--rows [ROWS] (Optional, default: '4')\n"
  21. printf " -h|--help, Print help\n"
  22. exit
  23. }
  24. if [ "$1" = "--help" ] || [ "$1" = "-h" ];then
  25. show_usage
  26. fi
  27. if [ -z "$1" ]; then
  28. show_usage
  29. fi
  30. while [ -n "$1" ]; do
  31. case "$1" in
  32. --file|-f)
  33. shift
  34. echo "INFO: $(get_date) - $0 - Parameter file: $1"
  35. FILENAME=$1
  36. ;;
  37. --height|-h)
  38. shift
  39. echo "INFO: $(get_date) - $0 - Parameter height: $1"
  40. HEIGHT=$1
  41. ;;
  42. --cols|-c)
  43. shift
  44. echo "INFO: $(get_date) - $0 - Parameter cols: $1"
  45. COLS=$1
  46. ;;
  47. --rows|-r)
  48. shift
  49. echo "INFO: $(get_date) - $0 - Parameter rows: $1"
  50. ROWS=$1
  51. ;;
  52. *)
  53. show_usage
  54. ;;
  55. esac
  56. shift
  57. done
  58. ### Configuration
  59. if [ -z "$HEIGHT" ]; then
  60. HEIGHT=120
  61. fi
  62. if [ -z "$COLS" ]; then
  63. COLS=2
  64. fi
  65. if [ -z "$ROWS" ]; then
  66. ROWS=4
  67. fi
  68. if [ -z "$FILENAME" ]; then
  69. show_usage
  70. fi
  71. # get video name without the path and extension
  72. VIDEO_NAME=$(basename $FILENAME)
  73. OUT_FILENAME=$(echo ${VIDEO_NAME%.*}_preview.jpg)
  74. TOTAL_IMAGES=$(echo "$COLS*$ROWS" | bc)
  75. if [ -e "$OUT_FILENAME" ]; then
  76. echo "INFO: $(get_date) - $0 - Preview found for $VIDEO_NAME. Skipping $OUT_FILENAME preview generation."
  77. exit 1
  78. else
  79. echo "INFO: $(get_date) - $0 - Preview not found for $VIDEO_NAME. Generating $OUT_FILENAME preview..."
  80. fi
  81. # get total number of frames in the video
  82. # ffprobe is fast but not 100% reliable. It might not detect number of frames correctly!
  83. #NB_FRAMES=`ffprobe -show_streams "$VIDEO" 2> /dev/null | grep nb_frames | head -n1 | sed 's/.*=//'`
  84. # `-show-streams` Show all streams found in the video. Each video has usualy two streams (video and audio).
  85. # `head -n1` We care only about the video stream which comes first.
  86. # `sed 's/.*=//'` Grab everything after `=`.
  87. #if [ "$NB_FRAMES" = "N/A" ]; then
  88. # as a fallback we'll use ffmpeg. This command basically copies this video to /dev/null and it counts
  89. # frames in the process. It's slower (few seconds usually) than ffprobe but works everytime.
  90. NB_FRAMES=$(ffmpeg -nostats -i "$FILENAME" -vcodec copy -f rawvideo -y /dev/null 2>&1 | grep frame | awk '{split($0,a,"fps")}END{print a[1]}' | sed 's/.*= *//')
  91. # I know, that `awk` and `sed` parts look crazy but it has to be like this because ffmpeg can
  92. # `-nostats` By default, `ffmpeg` prints progress information but that would be immediately caught by `grep`
  93. # because it would contain word `frame` and therefore output of this entire command would be totally
  94. # random. `-nostats` forces `ffmpeg` to print just the final result.
  95. # `-i "$VIDEO"` Input file
  96. # `-vcodec copy -f rawvideo` We don't want to do any reformating. Force `ffmpeg` to read and write the video as is.
  97. # `-y /dev/null` Dump read video data. We just want it to count frames we don't care about the data.
  98. # `awk ...` The line we're interested in has format might look like `frame= 42` or `frame=325`. Because of that
  99. # extra space we can't just use `awk` to print the first column and we have to cut everything from the
  100. # beggining of the line to the term `fps` (eg. `frame= 152`).
  101. # `sed ...` Grab everything after `=` and ignore any spaces
  102. #fi
  103. # calculate offset between two screenshots, drop the floating point part
  104. NTH_FRAME=$(echo "$NB_FRAMES/$TOTAL_IMAGES" | bc)
  105. echo "INFO: $(get_date) - $0 - Capture every ${NTH_FRAME}th frame out of $NB_FRAMES frames"
  106. # make sure output dir exists
  107. #mkdir -p $OUT_DIR
  108. FFMPEG_CMD="ffmpeg -loglevel panic -i \"$FILENAME\" -y -frames 1 -q:v 1 -vf \"select=not(mod(n\,$NTH_FRAME)),scale=-1:${HEIGHT},tile=${COLS}x${ROWS}\" \"$OUT_FILENAME\""
  109. # `-loglevel panic` We don’t want to see any output. You can remove this option if you’re having any problem to see what went wrong
  110. # `-i "$VIDEO"` Input file
  111. # `-y` Override any existing output file
  112. # `-frames 1` Tell `ffmpeg` that output from this command is just a single image (one frame).
  113. # `-q:v 3` Output quality where `0` is the best.
  114. # `-vf \"select=` That's where all the magic happens. Selector function for [video filter](https://trac.ffmpeg.org/wiki/FilteringGuide).
  115. # # `not(mod(n\,58))` Select one frame every `58` frames [see the documentation](https://www.ffmpeg.org/ffmpeg-filters.html#Examples-34).
  116. # # `scale=-1:120` Resize to fit `120px` height, width is adjusted automatically to keep correct aspect ration.
  117. # # `tile=${COLS}x${ROWS}` Layout captured frames into this grid
  118. # print enire command for debugging purposes
  119. #echo $FFMPEG_CMD
  120. eval "$FFMPEG_CMD"
  121. echo "INFO: $(get_date) - $0 - Generated $OUT_FILENAME preview successfully."