[FFmpeg-user] exact start/end extraction of an .mp4
tracey at archive.org
Thu Sep 26 05:20:40 CEST 2013
This may be a wider question than ffmpeg per se, but I'm wondering if there's a way to use ffmpeg to instruct a "short seek" at the start of an mp4, inside the header somewhere.
So start with a keyframe and GOP, but some metadata, let's say, way to instruct to a client/player "yes, but seek 2.7 seconds in to start playback", etc.
background / reason for request…
We have 1.5M videos, growing every day, 500K+ are quite long, 30 minutes+.
Like I believe quite a few places, we make a single continuous .mp4 (h.264+aac, simple profile/no b-frames, etc.) for our videos.
We use mod_h264_streaming to server-side cut smaller pieces out of it (to say, show a 60-second clip of a longer 1 hour video) for web browsers.
mod_h264_streaming is great, but it finds the closest keyframe to request and outputs there as the start (make sense why, but...).
Since we use the default GOP size for ffmpeg w/ x264, we can have up to 10 seconds between keyframes, so the cut can be quite imprecise.
A few ideas come to mind:
use ffmpeg ideally somehow in a single invocation to do the seeking. I can do a few invocations to cause this (see below)
hack mod_h264_streaming to start at the keyframe of the GOP you'd be logically seeking in to, with some kind of seek (stco atom tweaks?) or offset to mdat? or other atoms, etc.
appeal to the smartest people I know (ie: you all) to see if there's a better / alternate way to do this (I suspect there is, as I'm no mp4 / h.264 expert)
would love any suggestions, guidance, tomatoes, etc.!
PS: carl, I didn't include any full uncut console runs since this isn't a bug/issue with ffmpeg
Current 4 invocation ffmpeg way I can make things output the way I'm interested in -- but it's you know, 4 invocations, not sure (suspect not) it'd be fast enough as a replacement for mod_h264_streaming, etc…) Pretty non ideal, but figured worth posting to illustrate at least one way to produce the desired results. In testing, was taking ~2.5 seconds to extract a 15 second clip this way.
Rewrite first wanted GOP slightly by forcing a key frame at intended start, in middle of GOP
seek rewritten GOP mp4 to desired start, otherwise copying streams and rewriting to concat-able mpegts container
seek full mp4 to start of 2nd GOP until desired end, otherwise copying streams and rewriting to concat-able mpegts container
concat mpegts pieces to output mp4
# next four times are seconds from start of a 30-minute input file *20*.mp4
# the desired start is midway through an 8-second GOP
GOP1_LEN=$(printf "%0.2f" $(echo $KEYFRAME2-$KEYFRAME1));
REST_LEN=$(printf "%0.2f" $(echo $END-$KEYFRAME2));
START_IN=$(printf "%0.2f" $(echo $START-$KEYFRAME1));
rm -fv _file*.mp4 mytmp?.ts;
ffmpeg -y -ss $KEYFRAME1 -i *20*.mp4 -t $GOP1_LEN -c:a copy -force_key_frames $START_IN -pix_fmt yuv420p lossy-gop1.mp4;
ffmpeg -y -ss $START_IN -i lossy-gop1.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts mytmp1.ts;
ffmpeg -y -ss $KEYFRAME2 -i *20*.mp4 -t $REST_LEN -c copy -bsf:v h264_mp4toannexb -f mpegts mytmp2.ts;
ffmpeg -y -i "concat:mytmp1.ts|mytmp2.ts" -c copy -bsf:a aac_adtstoasc -movflags faststart _$(basename $(tempfile)).mp4;
rm -fv mytmp?.ts lossy-gop1.mp4;
More information about the ffmpeg-user