2

I have tried to split an mp4 file into smaller parts of equal time length like this ffmpeg -i ../data/2024-06-02_12-34-51.mp4 -c copy -map 0 -segment_time 00:00:05 -f segment v1_%03d.mp4. However, this produced videos of highly variables size, some 25x larger than others. I assume this was due to inconsistent framerate during recording.

Next, I tried a script that would split based and limit each part to a specific size:

#!/bin/sh
# Short script to split videos by filesize using ffmpeg by LukeLR

if [ $# -ne 3 ]; then
    echo 'Illegal number of parameters. Needs 3 parameters:'
    echo 'Usage:'
    echo './split-video.sh FILE SIZELIMIT "FFMPEG_ARGS'
    echo 
    echo 'Parameters:'
    echo '    - FILE:        Name of the video file to split'
    echo '    - SIZELIMIT:   Maximum file size of each part (in bytes)'
    echo '    - FFMPEG_ARGS: Additional arguments to pass to each ffmpeg-call'
    echo '                   (video format and quality options etc.)'
    exit 1
fi

FILE="../data/$1"
SIZELIMIT="$2"
FFMPEG_ARGS="$3"

# Duration of the source video
DURATION=$(ffprobe -i "$FILE" -show_entries format=duration -v quiet -of default=noprint_wrappers=1:nokey=1|cut -d. -f -2)

# Duration that has been encoded so far
CURDURATION=0

# Filename of the source video (without extension)
BASENAME="${FILE%.*}"

# Extension for the video parts
#EXTENSION="${FILE##*.}"
EXTENSION="mp4"

# Number of the current video part
i=1

# Filename of the next video part
NEXTFILENAME="$BASENAME-$i.$EXTENSION"

echo "Duration of source video: $DURATION"

# Until the duration of all partial videos has reached the duration of the source video
#while [[ $CUR_DURATION -lt $DURATION ]]; do
while [[ $(bc <<< "$CURDURATION < $DURATION") -eq 1 ]]; do
    # Encode next part
    echo ffmpeg -i "$FILE" -ss "$CURDURATION" -fs "$SIZELIMIT" $FFMPEG_ARGS "$NEXTFILENAME"
    ffmpeg -ss "$CURDURATION" -i "$FILE" -fs "$SIZELIMIT" $FFMPEG_ARGS "$NEXTFILENAME"

    # Duration of the new part
    NEWDURATION=$(ffprobe -i "$NEXTFILENAME" -show_entries format=duration -v quiet -of default=noprint_wrappers=1:nokey=1|cut -d. -f -2)

    # Total duration encoded so far
    echo $CURDURATION
    CURDURATION=$(bc <<< "$CURDURATION + $NEWDURATION")
    echo $CURDURATION

    i=$((i + 1))

    echo "Duration of $NEXTFILENAME: $NEWDURATION"
    echo "Part No. $i starts at $CURDURATION"
    echo "Current Duration: $CURDURATION"

    NEXTFILENAME="$BASENAME-$i.$EXTENSION"
done

I call the script like this: bash split-video.sh 2024-06-02_12-34-51.mp4 10000000 "-c copy" Unfortunately, this has an issue where some of the sub videos are extremely short and have wildly inconsistent numbers of frames in them (some with nearly 400, others with 1), despite being similar sizes. I am guessing this has something to do with inconsistent framerate and keyframes or something?

I am curious what the best way to split a video into equally sized parts, and ideally with similar numbers of frames, is using ffmpeg.

7
  • 1
    You would have to re-encode the file into a CBR (constant bit rate) file rather than VBR (variable bit rate). Currently the encoding is intelligently using more data when it has to and saving data when it can, hence for the same amount of data you can end up with wildly different durations of video using the same amount of data. Using CBR will give worse quality though..
    – Mokubai
    Commented Jun 18 at 16:00
  • In general we can split a video stream only in Key Frames (in case we want the file parts to be playable). You may use FFprobe with -show_frames for getting the keyframes and packets sizes for example: ffprobe -select_streams v -of default=noprint_wrappers=1:nokey=1 -show_entries frame=key_frame,pkt_size input.mp4. Sum the sizes to get the approximated size of each GOP (packets from keyframe to next keyframe). Do the math where to split using a Python script for example. Split by frame numbers (-segment_frames) using segment demuxer. The accuracy is dependent on the "keyframes density".
    – Rotem
    Commented Jun 18 at 16:02
  • @Mokubai Thanks a lot. That gives me some stuff to look into. Roughly how much quality is lost with CBR? Are we talking "technically lossy but barely noticeable" or "JPG that's been compressed 100 times"?
    – GBPU
    Commented Jun 18 at 16:03
  • 1
    It depends entirely on what quality/bitrate settings you choose. It sounds like your file has a very large variance in bitrate which means to retain good quality in the high bitrate sections you will have to have a high constant bitrate which could mean a much larger file. Conversely if you reduce the bitrate to the same as the lower sections you could see much worse quality during the previously high bitrate sections. Going from VBR to CBR could mean both "technically lossy but barely noticeable" and "JPG that's been compressed 100 times" at the same time.
    – Mokubai
    Commented Jun 18 at 16:13
  • @Rotem So, it sounds like my best option might be to re-record the footage and try to maintain a more stable framerate, or use a faster keyframe interval to make the frame splitting more consistent?
    – GBPU
    Commented Jun 18 at 16:38

0

You must log in to answer this question.

Browse other questions tagged .