anthwlock / untrunc

Restore a truncated mp4/mov. Improved version of ponchio/untrunc
GNU General Public License v2.0
2k stars 191 forks source link

Invalid length -1 / unable to find correct codec #4

Closed golimarrrr closed 6 years ago

golimarrrr commented 6 years ago

I have a broken .3gp audio file whose codec is (I think) amr_wb. I tried to run untrunc with the broken file against two different correct files from the same phone (one shorter and another longer) and I get a 670-byte output file only.

Info: reading /tmp/ok2.3gp
Info: parsing healthy moov atom ... 
Info: parsing mdat from truncated file ... 
Error: Invalid length. -1. Wrong match in track: 0
Error: unable to find correct codec -> premature end
Info: Found 0 packets ( sawb: 0 )
Info: Duration of sawb = 0s
Info: saving /tmp/mal.3gp_fixed.mp4

I tried with the standard libav package and also compiling the latest libav from source, selecting both the default 'amr' codec and also the 'opencore amr' codec, but the output is the same. Are audio-only files also supported?

anthwlock commented 6 years ago

They are, but your audio codec is currently not. Find a way to tell the length of a amr_wb packet and untrunc can work with your file. See Codec::getLength and Codec::getLength. Without an example I won't be able to help you.

golimarrrr commented 6 years ago

This is the output from ffprobe for the reference file:

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'ok.3gp':
  Metadata:
    major_brand     : 3gp4
    minor_version   : 0
    compatible_brands: isom3gp4
    creation_time   : 2018-02-23T19:43:39.000000Z
    com.android.version: 7.1.1
  Duration: 00:59:55.62, start: 0.000000, bitrate: 26 kb/s
    Stream #0:0(eng): Audio: amr_wb (sawb / 0x62776173), 16000 Hz, mono, flt, 24 kb/s (default)
    Metadata:
      creation_time   : 2018-02-23T19:43:39.000000Z
      handler_name    : SoundHandle

I think there is a mistake in your post, did you mean to specify two different methods?

anthwlock commented 6 years ago

Oh yes. I meant Codec::matchSample and Codec::getLength. You would have to add a else if(name_ == "sawb") branch there.

golimarrrr commented 6 years ago

I tried some packet sizes as per the table in https://hal.archives-ouvertes.fr/hal-01319434/document and now the output file is not empty but the sound is garbled (it does slightly sound like voice though!). I guess the bitrate is variable and so the packet size, but I'm not sure.

I attach a sample of this format just in case anyone can help: amrwb_sample.3gp.gz

golimarrrr commented 6 years ago

I fixed it by hand like this:

  1. record another file created with the same app on the same phone, I got a bigger one but probably any length will work
  2. open both files in an hex editor
  3. replace the mdat section in the "ok" file with the mdat section in the "broken" file, without the header (i.e. copy only the bytes after 'mdat' header)
  4. it would probably already work like that, if not, the 4 bytes before 'mdat' specify the size of the mdat section, the current value will be the old size ("ok" file mdat section size), replace it with the new size ("broken" file mdat section size)
golimarrrr commented 6 years ago

Also, this program should do the same procedure I mentioned above: https://github.com/golimarrrr/fix-3gp

anthwlock commented 6 years ago

You are lucky that your two files have matching audio frames sizes. This allows you to simply replace the header. This might be specific to the codec, or your recording device. For example in the sample you uploaded it looks like every frame is 61 bytes long. I dont think your hack works if the corrupted file is longer than the healthy one, have you tried?

anthwlock commented 6 years ago

I looked at your file and apparently sawb uses 0x44 as identifier. For determining the length avcodec does the job. It is satisfying how only one new line of code can add support for another codec.

golimarrrr commented 6 years ago

I dont think your hack works if the corrupted file is longer than the healthy one, have you tried?

It works but the playing length of the fixed file will be too short (will be the same the healthy file)