Sunday, August 14, 2016

Mac video edit - What options are available to _losslessly_ trim mp4/m4v video on 10.8 or above?

http://apple.stackexchange.com/questions/117306/what-options-are-available-to-losslessly-trim-mp4-m4v-video-on-10-8-or-above

iMovie (not lossless, but better than before)

iMovie v10 (released some time after the question was originally asked) now better handles more media types, so avoids the import re-encode for most H.264 (mp4, m4v, mov, AVHDC, mts, mt2s) content.
It will still, however, re-encode on export so cannot be technically lossless—another new feature, however, is the ability to customise the export quality, which can be closer-to-lossless. I suspect this part of the answer can apply equally to other 'project driven' video editing software (e.g. Premiere or Final Cut) because I think they all generally re-encode on output, though are usually very easy to use for precise trimming.
Personally, I generally think a single re-encode isn't a huge problem, but understand you did specifically ask for a lossless solution, so...

Lossless (but way more complicated!)

There are technical limitations as to how precisely you can cut a video without having to re-encode at least some part of it, and it basically depends on the i-frame frequency. If every frame is an i-frame, you can cut anywhere, but if they're only every few seconds, then you can only cut losslessly at those i-frames without losing content or having to re-encode (at least part of the stream) so it can start with an i-frame.

ffmpeg

This SO Q&A specifically raises the question of how to cut between i-frames using ffmpeg. I don't know of any GUI apps to do this, but basically you run a command something like the following:
ffmpeg -i input.m4v -vcodec copy -acodec copy -ss 00:01:20.000 -t 00:37:50.000 output.m4v
The two times specified are start and duration, and can be specified either as seconds or hh:mm:ss.ss, and the -acodec copy and -vcodec copy tell ffmpeg not to re-encode.
I'm not exactly sure what happens if you cut too early, but I think the video is essentially blank (or maybe corrupt, depending on player) until it encounters an i-frame. So you'll probably want to find the nearest i-frame before your cut. This answer solves that problem using ffprobe and awk, albeit a little awkwardly. Essentially you use ffprobe to scan the frames and find the nearest keyframe (flags=K) before your ideal cut-point. Full output for each frame of the video can be seen like this:
ffprobe -select_streams v -show_frames <INPUT>
The linked answer supplies this command to find a keyframe before a specific time:
ffprobe -select_streams v -show_frames -v quiet INPUT.mp4 | 
awk -F= ' 
  /pict_type=/ { if (index($2, "I")) { i=1; } else { i=0; } } 
  /pkt_pts_time/ { if (i && ($2 >= 150)) print $2; }  
' | head -n 1
And finally, if you really need to cut somewhere between two i-frames, you can split the video and re-join. Based on the info from this answer, it should be something like:
ffmpeg -f concat -i list_of_videos.txt -c copy OUTPUT.mp4
Where list_of_videos.txt is a simple text file listing the files you want to concatenate.

Summary

iMovie is probably good enough for most cases (since v10), and very easy.
ffmpeg can do it losslessly (or very close to losslessly), with a bit of fiddling; level of difficulty depends on how picky you are about the precise starting point, and frequency of i-frames.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.