[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