M: In ffmpeg, is it possible to partially reencode? For example, if you want to cut between two key frames.
G: Yes, it's possible to partially
reencode with FFmpeg by using smart rendering or
stream copying where possible. If you're cutting
between two keyframes, you can use -c copy to avoid
reencoding entirely, but if you need to cut at a non-keyframe, only the
affected GOP (Group of Pictures) will need reencoding.
Cutting Without Reencoding
(-c copy)
If both cut points are keyframes, use:
ffmpeg -i input.mp4 -ss start_time -to end_time -c copy output.mp4This method is fast and lossless but only works if your cut points are at keyframes.
Partial Reencoding (Smart Reencoding with
-ss and -to)
If the cut includes non-keyframes, FFmpeg will reencode only the
affected sections:
ffmpeg -i input.mp4 -ss start_time -to end_time -c:v libx264 -preset fast -crf 18 -c:a copy output.mp4Here, video is reencoded while audio is copied to avoid unnecessary processing.
For finer control, you could use FFmpeg's segmenting and concat features to reencode only necessary portions and stitch them back together.
M: How can you tell using ffmpeg if a particular frame is a key frame, for the purpose of logic tests
G: You can determine if a frame is
a keyframe in FFmpeg using the
-select_streams v -show_frames option with
ffprobe. Here’s how:
ffprobe -i input.mp4 -select_streams v -show_frames -print_format json | jq '.frames[] | select(.key_frame == 1)'-select_streams v → Only process video frames.-show_frames → Show information about every frame.-print_format json → Output in JSON format (for easier
parsing).jq '.frames[] | select(.key_frame == 1)' → Filters only
keyframes (if jq is installed).jq:If you don’t have jq, you can use:
ffprobe -i input.mp4 -select_streams v -show_frames | grep 'key_frame=1'To get a list of keyframe timestamps:
ffprobe -i input.mp4 -select_streams v -show_frames -print_format csv | grep ",1," | cut -d',' -f6This extracts the pkt_pts_time (timestamp) of each
keyframe.
You can use this information in a script to decide if a given timestamp is a keyframe before making cuts.
M: Provide a basic bash script. Given a video file X.mp4 and a timestamp T: if T is a key frame in X, print T. Otherwise, identify and print the timestamp of the next key frame after T.
G: Here’s a basic Bash script to
check if a given timestamp T is a keyframe in
X.mp4 and, if not, find the next keyframe after
T.
find_next_keyframe.sh)#!/bin/bash
# Check arguments
if [[ $# -ne 2 ]]; then
echo "Usage: $0 <video_file> <timestamp>"
exit 1
fi
VIDEO_FILE="$1"
TIMESTAMP="$2"
# Extract keyframe timestamps
KEYFRAMES=$(ffprobe -i "$VIDEO_FILE" -select_streams v -show_frames -print_format csv 2>/dev/null | awk -F',' '$5 == 1 {print $6}')
NEXT_KEYFRAME=""
# Find the next keyframe after the given timestamp
for KF in $KEYFRAMES; do
if (( $(echo "$KF >= $TIMESTAMP" | bc -l) )); then
NEXT_KEYFRAME="$KF"
break
fi
done
# Output result
if [[ "$NEXT_KEYFRAME" == "$TIMESTAMP" ]]; then
echo "$TIMESTAMP"
else
echo "$NEXT_KEYFRAME"
fichmod +x find_next_keyframe.sh
./find_next_keyframe.sh X.mp4 12.34ffprobe.T is a keyframe.T is a keyframe, it prints T.This should work efficiently for most videos.
M: Is that script compatible with the 00:00:00.000 time format
G: No, the script as written
expects timestamps in decimal seconds (e.g., 12.34). If you
want to support HH:MM:SS.sss format, you need to convert it
to decimal seconds before comparison. Here's a revised version:
find_next_keyframe.sh)#!/bin/bash
# Check arguments
if [[ $# -ne 2 ]]; then
echo "Usage: $0 <video_file> <timestamp (HH:MM:SS.sss or seconds)>"
exit 1
fi
VIDEO_FILE="$1"
TIMESTAMP_RAW="$2"
# Function to convert HH:MM:SS.sss to seconds
convert_to_seconds() {
local time="$1"
if [[ "$time" =~ ^([0-9]+):([0-9]+):([0-9]+(\.[0-9]+)?)$ ]]; then
local h="${BASH_REMATCH[1]}"
local m="${BASH_REMATCH[2]}"
local s="${BASH_REMATCH[3]}"
echo "$(bc -l <<< "$h * 3600 + $m * 60 + $s")"
else
echo "$time" # Assume it's already in seconds
fi
}
# Convert input timestamp to seconds
TIMESTAMP=$(convert_to_seconds "$TIMESTAMP_RAW")
# Extract keyframe timestamps in decimal seconds
KEYFRAMES=$(ffprobe -i "$VIDEO_FILE" -select_streams v -show_frames -print_format csv 2>/dev/null | awk -F',' '$5 == 1 {print $6}')
NEXT_KEYFRAME=""
# Find the next keyframe after the given timestamp
for KF in $KEYFRAMES; do
if (( $(echo "$KF >= $TIMESTAMP" | bc -l) )); then
NEXT_KEYFRAME="$KF"
break
fi
done
# Output result
if [[ -z "$NEXT_KEYFRAME" ]]; then
echo "No keyframe found after $TIMESTAMP_RAW"
else
echo "$NEXT_KEYFRAME"
fiHH:MM:SS.sss format by
converting it to seconds../find_next_keyframe.sh X.mp4 00:01:12.500 # HH:MM:SS.sss format
./find_next_keyframe.sh X.mp4 72.5 # Decimal seconds formatLet me know if you need further refinements.