Properly fix videos which were encoded with old version of x264

6

2

Due to a bug in old versions of x264, h.264 video streams with the following three properties:

  1. encoded with x264 build 150 or earlier
  2. using 4:4:4 chroma subsampling
  3. bitstream contains no x264 version info

will not be played properly by many video players. New versions of the video player mpv have a dedicated option

--vd-lavc-assume-old-x264

specifically addressing this issue (see: https://mpv.io/manual/master/).

On the FFmpeg bug tracker it is suggested to add the proper SEI.h264 to the video stream (containing the x264 version info, I guess). I prefer not to rely on such hacks, so my question is: Is there a "proper" way (ideally using ffmpeg) to repair the files as if they had been encoded with a new (fixed) version of x264 in the first place?

Obviously I'd like to remain (more or less) video quality and file size. If re-encoding is necessary then it should change nothing but fix the buggy behaviour of the old x264 implementation. (Further info: The bug report gives an example corrupted file. It is conjectured that the bug in old x264 was probably introduced here.)

videoguy

Posted 2018-03-10T19:23:50.167

Reputation: 61

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

Answers

2

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.

cxrodgers

Posted 2018-03-10T19:23:50.167

Reputation: 121

@Gyan I see you have posted a lot of great answers about timestamps, can you please suggest how to properly specify the timestamps in the container when providing a raw h264 stream? thanks for any tips!! – cxrodgers – 2018-10-30T01:07:55.310