Thank you for posting this question, it helped me understand the nature of the problem I was having too. I have a solution which seems to work.
In my case I am using ffmpeg from the Ubuntu repositories. The last version that was able to decode my libx264 files was 2.8.6. After upgrading to 2.8.14 or 2.8.15, I had the decoding issues you describe. I don't want to re-encode my old videos, I just want to fix the header so that ffmpeg can properly identify the error that was introduced during the original encoding, and play them properly.
So first off I downloaded the static binary that includes the most recent version of ffmpeg, v4. I linked this binary to ffmpeg4
on my system so that I can control which version I'm using. We need some of the new features that were introduced after 2.8 (not exactly sure when). If you already have a newer version of ffmpeg installed, just use that and replace ffmpeg4
with ffmpeg
in the commands below.
Now, extract the raw bitstream from your broken video (call it BROKEN.mkv).
ffmpeg4 -i BROKEN.mkv -vcodec copy -an -bsf:v h264_mp4toannexb raw.h264
I'm not certain the h264_mp4toannexb flag is necessary, it may be auto-inserted for this format.
Now, put the bitstream into a new mp4 container, and fix the information about the old buggy x264 build in the SEI header.
ffmpeg4 -r 30 -i raw.h264 -avoid_negative_ts 1 -bsf:v h264_metadata='sei_user_data=dc45e9bde6d948b7962cd820d923eeef+x264 - core 150' -c copy FIXED.mp4
The bitstream contains no timestamp information, so you're going to get a ton of warnings here. I also found I had to manually set the frame rate to 30fps (-r 30
) because otherwise it guessed some variable frame rate between 25 and 30fps. I don't know how to properly extract the timestamps or to correctly mux them into the new container. Please let me know if you have a fix! A lot of people recommend -fflags +genpts
but this does not seem to do anything for me. Finally, I added -avoid_negative_ts 1
in order to get the timestamp of the first frame to be non-negative.
Finally, and this is optional, if you want to put the results into an MKV container you can do this
ffmpeg4 -i FIXED.mp4 -c copy FIXED.mkv
Why convert first to MP4 and then to MKV? It seems that the MKV container will simply refuse to proceed without timestamps, but MP4 will do so and just issue warnings. Then you can convert to MKV without warnings.
So after all of this I have working MP4 and MKV files. However, I inspected some frames and there are minor changes (luminance changes on the order of ~2 levels). I don't understand why this happened because this should have been lossless. Please let me know if you have suggestions about how this can be done better.
Edit: I noticed that some of my timestamps were negative in the MP4 container, starting at -0.066667s. After moving to an MKV container, all of the negative timestamps became zeros. Adding -output_ts_offset 0.066667
to the command fixed this and has them start at zero. I don't understand why it started at -0.066667 though.
Edit 2: A better way to remove negative time stamps is to use "-avoid_negative_ts 1" when encoding the mp4.
No. Of ffmpeg can't decode such streams correctly then result of re-encoding will have errors. What's wrong with SEI insertion? – Gyan – 2018-03-10T20:48:40.877
@Gyan is it possible you could please provide the commands needed to insert the correct SEI? – cxrodgers – 2018-10-29T19:15:44.773