androidvideovideo-streaming3gpandroid-mediarecorder

Fix 3GP file after streaming from Android Media Recorder


I am trying to stream video from android camera through local unix socket and write file from stream to sdcard. Everything works fine, except file is not playable with any player. It's because Android not filling some gaps in the file because socket is not seekable. As I understand I need to make some modifications after video stream is over. I read several articles here, here and here, but none of them helped me. I am playing with hex editor to learn how to do it manually, so afterwards it will be trivial to do the same in the Android code.

Here is sample file that saved from stream: https://dl.dropbox.com/u/17510473/sample_not_playable.3gp

Can anyone fix to make it playable and tell how he done it?

EDIT: I erase header of the 3gp file and write new one as follows:

00 00 00 18 66 74 79 70 33 67 70 34 00 00 03 00 33 67 70 34 33 67 70 36 00 00 00 00

Then I find starting location of mdat and moov atoms with following command:

grep -aobE "ftyp|mdat|moov" sample_not_playable.3gp

And it gives me following output:

4:ftyp
28:mdat
1414676:moov

Then make 1414676 - 28 = 1,414,648 = 0x1595F8

Then I write 0x1595F8 as 25-28 bytes, just prior mdat atom. So my header now looks like this:

00 00 00 18 66 74 79 70 33 67 70 34 00 00 03 00 33 67 70 34 33 67 70 36 00 15 95 F8

And when I try to play it with mplayer I get some damaged video and audio output. Here's some part from mplayer output:

[amrwb @ 0x7f72ad652380]Frame too small (33 bytes). Truncated file?
[amrwb @ 0x7f72ad652380]Encountered a bad or corrupted frame
[amrwb @ 0x7f72ad652380]Encountered a bad or corrupted frame
[amrwb @ 0x7f72ad652380]Frame too small (33 bytes). Truncated file?
[amrwb @ 0x7f72ad652380]Encountered a bad or corrupted frame
[amrwb @ 0x7f72ad652380]Encountered a bad or corrupted frame
[amrwb @ 0x7f72ad652380]Encountered a bad or corrupted frame
A:  11.0 V:   1.4 A-V:  9.650 ct:  0.023   0/  0 10%  1%  1.6% 0 0                                                        
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x7f72adeafc40]stream 1, offset 0x15e62b: partial file
[h263 @ 0x7f72ad652380]Bad picture start code
[h263 @ 0x7f72ad652380]header damaged
Error while decoding frame!
[h263 @ 0x7f72ad652380]Bad picture start code
[h263 @ 0x7f72ad652380]header damaged
Error while decoding frame!
[h263 @ 0x7f72ad652380]Bad picture start code
[h263 @ 0x7f72ad652380]header damaged
Error while decoding frame!
A:  11.1 V:   1.5 A-V:  9.558 ct:  0.027   0/  0  9%  1%  1.4% 0 0                                                        
[h263 @ 0x7f72ad652380]Bad picture start code
[h263 @ 0x7f72ad652380]header damaged
Error while decoding frame!

What I am doing wrong?


Solution

  • What you need to understand is that mp4 is not a live streamable format. hence there is no way you will be able to hack anyway around to make it live streamable. The header [moov atom] is written at the end. Android creates a in memory table of frame sizes and other parameters which it then writes at the begining of the file at the end of recording because of which it needs the seekability of the file handle. [which a socket is not]

    If you are writing encrypted content to the disk and you want to do it so that nobody can play the file you don't have to encrypt the whole file. You just have to encrypt the header and the entire file is unplayable.

    If you desperately need full file encoding because you are not convinced of my previous para then use something like ffmpeg to do the encoding. Modify it give you encrypted output itself and save it to hard disk again - removes the socket part again.

    There is no way you can do live streaming of mp4 files in android. I have seen tons of people trying it in vain and if you understand video/formats there is not way you can do it. mp4 is not designed for live streaming. Unless you know your frame sizes in advance [which you don't] and the exact length of encoding [which you most probably don;t] you cannot pre-create the header.

    ps. mp4 and 3gp are cousins so the same thing applies.

    Many people confuse live streaming with http pd and pseudo streaming. Live streaming means I do not have the whole file, it is being created and also being streamed on the fly. http pd and pseudo streaming take place with files that are fully available.

    EDIT:

    If your aim is to encrypt the recorded file before storing in sdcard you need encoder support. This implies getting your own encoder in there. Take ffmpeg, cross compile it to android. Write a small JNI interface for your application. Get this up first without encryption. Once done, where ffmpeg fwrites the stream, add your encryption module. Same thing while decoding. Y3ng creation. This is the cleanest way to do it.

    EDIT 2: See spydroid to get some of the features for you. There are similar ones out there.

    EDIT 3: To improve the quality of the answer I am explaining an imperfect workaround also given by other answers:

    One can still stream the AV by parsing the mp4 as it gets generated and sending the elementary streams seperately over a socket. The only issue you will face is that you will not get perfect AV sync as you do not know the exact AV sample timestamps. Only android knows that and it writes that in the mp4 header at the end. So no good for you. You have to be making an assumption on perfect sampling rate of video frames and your audio will have to be amr to assume 20ms packets. In other audio cases you will start seeing drift in long runs [especially where you start having scenes of high motion]. This is because each audio packet generated does not correspond to a fixed time duration [except for amr and other speech codecs]