1

I'm trying to detect scenes and save them as individual files with FFmpeg. I found this long command somewhere, but it fails with a good detection. I change the scenecut value, but I don't see any effects.

ffmpeg -y -i test.mp4 -max_muxing_queue_size 1024 -vf yadif -preset:v fast -x264opts scenecut=20 -f segment -segment_format mp4 -segment_format_options movflags=faststart %05d.mp4

Is there a better command than this? There should be a shorter, more efficient command I guess.

2

Using the encoder's scenecut to detect scenes is not advisable, since its purpose is efficient GOP splitting taking into account other encoding parameters. It is not a "pure" scene detector. Its propensity to mark a "scene change" increases as it gets closer to the the next forced keyframe target (set by keyint).

Consider using https://github.com/Breakthrough/PySceneDetect instead.

| improve this answer | |
  • Yes I was using that library before. But much slower than ffmpeg. So was looking for a ffmpeg-based solution. – Tina J Oct 2 '19 at 15:30
0

This script makes it simple, better results than other posts around town: https://gist.github.com/achesco/4dc2ebf13378a0a61fc26c7fe01f539e

Splits file.mp4 file to scenes with change more than 0.5 and saves output parts to /tmp/parts folder

split.sh -d 0.5 -o /tmp/parts -f file.mp4

Including the script here for reference (note the version below has some differences, primarily a bug fix):

# Splits video to separate scenes files
# Inspired by https://stackoverflow.com/a/38205105

#!/bin/bash

file=""
out="./"
diff=0.4
bitrate=""
trim=0
stripaudio=""

usage () {
  echo "Usage: $(basename $0) [[[-o folder] [-d ratio]] | [-h]] -f file.mp4"
  echo
  echo "Options:"
  echo "-f, --file          Input file"
  echo "-o, --out           Outpup files folder path, default"
  echo "                    to current folder"
  echo "-d, --diff          Number from 0 to 1, default to 0.4."
  echo "                    Scene change difference factor"
  echo "-b, --bitrate       Bitrate to encode parts"
  echo "-t, --trim          Trim last given seconds number, default 0"
  echo "-sa, --strip-audio  Strip audio"
  echo "-h, --help          Display this help message"
  echo
  echo "Example: split.sh -d 0.5 -o /tmp/parts -f file.mp4"
  echo "Splits file.mp4 file to scenes with change more than 0.5"
  echo "and saves output parts to /tmp/parts folder"
}

if [ "$1" = "" ]; then
  usage
fi

while [ "$1" != "" ]; do
  case $1 in
    -f | --file )
      shift
      file=$1
      ;;
    -d | --diff )
      shift
      diff=$1
      ;;
    -o | --out )
      shift
      out=$1
      ;;
    -b | --bitrate )
      shift
      bitrate="-b $1"
      ;;
    -t | --trim )
      shift
      trim=$1
      ;;
    -sa | --strip-audio )
      stripaudio="-an"
      ;;
    -h | --help )
      usage
      exit
      ;;
    * )
      usage
      exit 1
  esac
  shift
done

cut_part () {
  duration_flag=""
  if [ "$3" != "" ]; then
    duration_flag="-t"
  fi
  ffmpeg -loglevel error -hide_banner -ss $1 -i $file $duration_flag $3 \
    -vcodec libx264 -movflags faststart $bitrate $stripaudio \
    -y $out/`printf "%04d_%s" $2 $filename` < /dev/null
}

filename=`basename $file`
mkdir -p $out
timefrom=0
i=1
while read -r timestamp; do
  duration=`bc <<< "$timestamp-$timefrom-$trim" | awk '{printf "%f", $0}'`
  cut_part $timefrom $i $duration
  timefrom=$timestamp
  i=`expr $i + 1`
done < <(
  ffmpeg -i $file -filter:v "select='gt(scene,$diff)',showinfo" -f null - 2>&1 | \
    grep Parsed_showinfo | grep pts_time:[0-9.]* -o | grep "[0-9]*\.[0-9]*" -o
)

if [ $timefrom != 0 ]; then
  cut_part $timefrom $i
fi
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.