Hacker News new | ask | show | jobs
by neRok 87 days ago
There's nothing easy about it. Here's a taste.

  # make a 6 second long video that alternates from green to red every second.
  ffmpeg -f lavfi -i "color=red[a];color=green[b];[a][b]overlay='mod(floor(t)\,2)*w'" -t 6 master.mp4; # creates 150 frames @ 25fps.

  # try make a 1 second clip starting at 0sec. it should be all green.
  ffmpeg -ss 0 -i "master.mp4" -t 1 -c copy "clip1.mp4"; # exports 27 frames. you see some red.
  ffmpeg -ss 0 -t  1 -i "master.mp4" -c copy "clip2.mp4"; # exports 27 frames. you see some red.
  ffmpeg -ss 0 -to 1 -i "master.mp4" -c copy "clip3.mp4"; # exports 27 frames. you see some red.

  # -t and -to stop after the limit, so subtract a frame. but that leaves 26...
  # so perhaps offset the start time so that frame#0 is at 0.04 (ie, list starts at 1)?
  ffmpeg -itsoffset 0.04 -ss 0 -i "master.mp4" -t 0.96 -c copy "clip4.mp4"; # exports 25 frames, all green, time = 1.00. success.

  # try make another 1 second clip starting at 2sec. it should be all green.
  ffmpeg -itsoffset 0.04 -ss 2 -i "master.mp4" -t 0.96 -c copy "clip5.mp4"; # exports 75 frames, time = 1.08, and you see red-green-red.
  # maybe don't offset the start, and drop 2 at the end?
  ffmpeg -ss 2 -i "master.mp4" -t 0.92 -c copy "clip6.mp4"; # exports 75 frames, time = 1.08, and you see green-red.
  ffmpeg -ss 2 -t 0.92 -i "master.mp4" -c copy "clip7.mp4"; # exports 75 frames, time = 0.92, and you see green-red.
  
  # try something different...
  ffmpeg -ss 2 -i "master.mp4" -c copy -frames 25 "clip8.mp4"; # video is broken.
  ffmpeg -ss 2 -i "master.mp4" -c copy -frames 25 -avoid_negative_ts make_zero "clip9.mp4"; # exports 25 frames, all green, time = 1.00. success?
  # try export a red video the same way.
  ffmpeg -ss 3 -i "master.mp4" -c copy -frames 25 -avoid_negative_ts make_zero "clip10.mp4"; # oh no, it's all green!
3 comments

I've never tried doing frame perfect clips like that, that does sound annoying. But from a cursory read of the source, I don't think this program will solve that issue either? Because the time stamps in your examples are all correct, and the TUI is using ffmpeg with -ss and -t as well.

  func BuildFFmpegCommand(opts ExportOptions) string {
   output := opts.Output
   if output == "" {
    output = generateOutputName(opts.Input)
   }
   duration := opts.OutPoint - opts.InPoint
  
   args := []string{"ffmpeg", "-y",
    "-ss", fmt.Sprintf("%.3f", opts.InPoint.Seconds()),
    "-i", filepath.Base(opts.Input),
    "-t", fmt.Sprintf("%.3f", duration.Seconds()),
   }
I think the best way of getting frame accurate clips like that is putting the starting time after the input (or rather before the output), which decodes the video up to that time, and reencode it instead of copying. Both of these commands gives the expected output:

  ffmpeg -i master.mp4 -ss 0 -t 1 -c:v libx264 green.mp4
  ffmpeg -i master.mp4 -ss 1 -t 1 -c:v libx264 red.mp4
Yer, I noticed that this tool was just doing `-ss -i -t` from its demo gif, which is what prompted me to reply. I'm sure people will discover that all sorts of problems will manifest if they don't start a lossless clip on a keyframe. One such scenario is when you make a clip that plays perfect on your PC, but then you send it someone over FB Messenger, and all of a sudden there's a few seconds of extra video at the start!
Can't make frame perfect cuts without re-encoding, unless your cut points just so happen to be keyframe aligned.

There are incantations that can dump for you metadata about the individual packets a given video stream is made up of, ordered by timecode. That way you can sanity check things.

This is terribly frustrating. The paths of least resistance either lead to improper cuts or wasteful re-encoding. Re-encoding just until the nearest keyframe I'm sure is also possible, but yeah, this does suck, and the tool above doesn't seem to make this any more accessible either according to the sibling comment.

> Re-encoding just until the nearest keyframe I'm sure is also possible Yer, I've done that, and it's a pain to do "manually" (ie, without having a script ready to do it for you). I've also manually sliced the bitstream to re-insert the keyframe, which if applied to my clip5.mp4 example, could potentially reduce the 50* negative ts frames to maybe 2 or 3. It would be easier if there were tools that could "unpack" and "repack" the frames within the bitstream, and allow you to modify "pointers"/etc in the process - but I don't know of any such thing.
For frame perfect cuts you need to re-encode. You can use lossless H264 encoding for intermediary cuts before the final one so that you don't unnecessarily degrade quality.

I wonder if there is a solution which would just copy the pieces in between the starting and ending points while only re-encoding the first and last piece as required.