[FFmpeg-user] Reordering frames in corrupt GoPro videos

Vincent Deconinck vdeconinck at gmail.com
Fri Jul 12 01:05:59 EEST 2024


Hi,

A few months ago, my GoPro randomly started creating some corrupt (or
partly-corrupt) video files, mixing up frames and causing jaggy playback.To
illustrate the case, here is an excerpt I generated with "ffmpeg -c copy"
from a corrupt source file :
https://drive.google.com/file/d/1GD7YiVS2z8h0r6UdsPPIXFgnh9RukoeR/view?usp=sharing

As you can see, frames are mixed up, but with a repeating pattern. By
playing the file frame by frame, I observed that the mangling pattern
repeats every 5 frames and that frames should be played in order 1-0-3-4-2
- 6-5-8-9-7 - 11-10-13-14-12 - 16-15-18-19-17 - etc.
At first I only had two 5-minute corrupted files, so I resorted to :
1) extract each frame to PNG (with "ffmpeg -i in_bad_frame_order.MP4
frame%06d.png")
2) create a text file with the frames in the right order (Excel fiddling)
3) recreate a MP4 file from the PNG files (with "ffmpeg -r 30 -f concat -i
full_list.txt -c:v libx264 -pix_fmt yuv420p out_no_audio.mp4")
4) extract audio from original (with "ffmpeg -i in_bad_frame_order.MP4 -vn
-acodec copy audio.aac"
5) reinject audio into the rebuilt file (with "ffmpeg -i out_no_audio.mp4
-i audio.aac -c:v copy -c:a copy out_fixed.mp4")

That worked pretty well and I was able to use the rebuilt video no prob,
but it took around 24 hours to process 10 minutes of video (it is 4K30) and
PNGs are huge.

Now I got back from a trip and discovered that I have around 10-15 hours of
video that are corrupt :-(.
I threw away the GoPro, but I would like to recover the footage without
spending 2-3 months with my PC burning CPU 24/7 just to rebuild the files
the same way, so I wondered if I could fiddle with the PTS of the frames to
make them play in the right order.

I thus came up with a formula and tried to apply it using ffmpeg, with the
following command (and output) :

ffmpeg -i in_bad_frame_order.MP4 -bsf:v
setts=pts='if(eq(mod(PTS,5),0),PTS+6,if(eq(mod(PTS,5),1),PTS+4,if(eq(mod(PTS,5),2),PTS+6,if(eq(mod(PTS,5),3),PTS+6,PTS+3))))'
-c copy out.mp4
ffmpeg version 7.0.1-full_build-www.gyan.dev Copyright (c) 2000-2024 the
FFmpeg developers
  built with gcc 13.2.0 (Rev5, Built by MSYS2 project)
  configuration: --enable-gpl --enable-version3 --enable-static
--disable-w32threads --disable-autodetect --enable-fontconfig
--enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-bzlib
--enable-lzma --enable-libsnappy --enable-zlib --enable-librist
--enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth
--enable-libbluray --enable-libcaca --enable-sdl2 --enable-libaribb24
--enable-libaribcaption --enable-libdav1d --enable-libdavs2
--enable-libuavs3d --enable-libxevd --enable-libzvbi --enable-librav1e
--enable-libsvtav1 --enable-libwebp --enable-libx264 --enable-libx265
--enable-libxavs2 --enable-libxeve --enable-libxvid --enable-libaom
--enable-libjxl --enable-libopenjpeg --enable-libvpx
--enable-mediafoundation --enable-libass --enable-frei0r
--enable-libfreetype --enable-libfribidi --enable-libharfbuzz
--enable-liblensfun --enable-libvidstab --enable-libvmaf --enable-libzimg
--enable-amf --enable-cuda-llvm --enable-cuvid --enable-dxva2
--enable-d3d11va --enable-d3d12va --enable-ffnvcodec --enable-libvpl
--enable-nvdec --enable-nvenc --enable-vaapi --enable-libshaderc
--enable-vulkan --enable-libplacebo --enable-opencl --enable-libcdio
--enable-libgme --enable-libmodplug --enable-libopenmpt
--enable-libopencore-amrwb --enable-libmp3lame --enable-libshine
--enable-libtheora --enable-libtwolame --enable-libvo-amrwbenc
--enable-libcodec2 --enable-libilbc --enable-libgsm
--enable-libopencore-amrnb --enable-libopus --enable-libspeex
--enable-libvorbis --enable-ladspa --enable-libbs2b --enable-libflite
--enable-libmysofa --enable-librubberband --enable-libsoxr
--enable-chromaprint
  libavutil      59.  8.100 / 59.  8.100
  libavcodec     61.  3.100 / 61.  3.100
  libavformat    61.  1.100 / 61.  1.100
  libavdevice    61.  1.100 / 61.  1.100
  libavfilter    10.  1.100 / 10.  1.100
  libswscale      8.  1.100 /  8.  1.100
  libswresample   5.  1.100 /  5.  1.100
  libpostproc    58.  1.100 / 58.  1.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'in_bad_frame_order.MP4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2mp41
    encoder         : Lavf60.16.100
  Duration: 00:00:07.03, start: 0.000000, bitrate: 43392 kb/s
  Stream #0:0[0x1](eng): Video: hevc (Main) (hvc1 / 0x31637668),
yuvj420p(pc, bt709), 3840x2160 [SAR 1:1 DAR 16:9], 46444 kb/s, 29.97 fps,
29.97 tbr, 90k tbn (default)
      Metadata:
        handler_name    : GoPro H.265
        vendor_id       : [0][0][0][0]
        encoder         : GoPro H.265 encoder
        timecode        : 16:17:19:08
      Side data:
        displaymatrix: rotation of -180.00 degrees
  Stream #0:1[0x2](eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz,
stereo, fltp, 189 kb/s (default)
      Metadata:
        handler_name    : GoPro AAC
        vendor_id       : [0][0][0][0]
  Stream #0:2[0x3](eng): Data: none (tmcd / 0x64636D74), 0 kb/s
      Metadata:
        handler_name    : GoPro H.265
        timecode        : 16:17:19:08
Stream mapping:
  Stream #0:0 -> #0:0 (copy)
  Stream #0:1 -> #0:1 (copy)
Output #0, mp4, to 'out.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2mp41
    encoder         : Lavf61.1.100
  Stream #0:0(eng): Video: hevc (Main) (hvc1 / 0x31637668), yuvj420p(pc,
bt709), 3840x2160 [SAR 1:1 DAR 16:9], q=2-31, 46444 kb/s, 29.97 fps, 29.97
tbr, 90k tbn (default)
      Metadata:
        handler_name    : GoPro H.265
        vendor_id       : [0][0][0][0]
        encoder         : GoPro H.265 encoder
        timecode        : 16:17:19:08
      Side data:
        displaymatrix: rotation of -180.00 degrees
  Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo,
fltp, 189 kb/s (default)
      Metadata:
        handler_name    : GoPro AAC
        vendor_id       : [0][0][0][0]
Press [q] to stop, [?] for help
[out#0/mp4 @ 0000020e07576a80] video:37078KiB audio:162KiB subtitle:0KiB
other streams:0KiB global headers:0KiB muxing overhead: 0.022741%
size=   37248KiB time=00:00:06.99 bitrate=43607.6kbits/s speed=8.12x

No error is thrown, but when playing back, the file looks exactly the same,
as if the PTS were left unchanged.
However, with a simpler formula, such as doubling the PTS (ffmpeg -i
in_bad_frame_order.MP4 -bsf:v setts=pts='PTS*2' -c copy out.mp4), I can see
that the video indeed plays twice as slow, so it seems the ffmpeg syntax is
correct but the formula somehow gets ignored.

Does anybody have an idea what is wrong ?
Or can you advise another way to fix the files ?

Thanks for your help,

Vincent


More information about the ffmpeg-user mailing list