Make camera footage usable in normal players like VLC 1,MPlayer et al.


(Please note that this tool just 'extracts' audio/video from the camfile, therefore whatever is produced corresponds to camera settings)


This Page is mostly obsolete an deprecated for two reasons:
a)my camera is broken :-) and
b)there are better tools now.




if you just want to get an avi, use the console php version dot264toavi.php.gz gzipped for obvious resons :-)
or, if you want something moref fancy head over to github and use this version (which uses ffmpeg libraries)

I also came across a new 'weird' format from some of those cams, they use VFR video and code their audio into 3byte NAL's, Players (VLC et al) disregard this as an 'error.
I dissected some of these files, but was unable to sync audio and video.
I simply don't buy this stuff anymore :-)
Old page follows below.



(verbal illustrations for entertainmet purposes only)
I recently bought one of those cheap Chinese surveillance cameras w/ pan and tilt, IR cut an motion detection.



This one's called "KKMoon Model: 810", it works as expected, configuration is quiete easy you just get the app enter your WiFi password and your phone chirps that to the microphone of the camera. After that the app will find the camera on the lan identified by some obscure CloudSee Id.
You can configure the rest of the settings via the app or the web interface (like changing the login from admin/admin to something more reasonable).
Motion detection works surprisingly well ( once you've fitted a SD-Card it'll even record :-) ).
There's one menu entry in the settings which is a bit confusing as you can choose "Save to ftp-server". This should really read "Copy to ftp-server", since without a SD-Card nothings is recorded in the first place.
So I stuck in a Memory card and configured for ftp ( no sense in it just having the footage onthe card if the whole camera is gone ).
After some "motion" I had a bunch of *.264 files on my server. I tried to play them with everything I had at hand, even transferred a few files to my Windows machine, but to no avail.
Not one of those players could handle these files :-(

Mediainfo just prints the filesize and MP4Box says it can't find a MP4 header.

This really sucks so it's time to have a look at what's happening here :-).
(Let alone the case where you'd need to hand over the footage to someone i.e. as evidence)
Looking at the files with a hex editor puts some 'magic' in sight:

00000000 48 58 56 53 | 00 05 00 00 | D0 02 00 00 | 00 00 0000  HXVS....Ð.......
00000010 48 58 56 46 | 17 00 00 00 | FC BD 59 0A | 01 00 00 00 HXVF....ü½Y.....
00000020 00 00 00 01 | 47 4D 00 1F | 99 A0 14 01 | 6E 84 00 00 ....GM... ..n...
00000030 0F A0 00 02 | 71 02 10 48 | 58 56 46 08 | 00 00 00 FC . ..q..HXVF....ü
00000040 BD 59 0A 01 | 00 00 00 00 | 00 00 01 48 | EE 3C 80 48 ½Y.........Hî<.H
00000050 58 56 46 2C | 48 00 00 FC | BD 59 0A 01 | 00 00 00 00 XVF,H..ü½Y......

They all start with a bunch of 'garbage' like 'HXVS' and 'HXVF', the third line is part of the mpdeprecatedeg-header.
Digging further there a two more 'magic' strings to be found: HXAF and HXFI.

I don't know what HXVS is for but it adds to the assumption that each of these 'magic' strings are composed o 16 (0x10) bytes.
(I just throw away the HXVS, it appears only once at the start of that file)
After HXVF though there's data we want, these are exactly 0x17 bytes until the next HXVF, hey, what does it say in the long
following the first HXVF ?
Yes, 0x0017 ,  Second ? ->  0x008, Third ? -> 0x482c, heureka :-)

To make a long story short HXVF and HXAF are followed by a value indicating the size of the data following.
Where HXVF is the data we want HXAF is followed by four bytes we can throw away {0x00001,0x5000} and audio data.
HXFI seems to be some sort of file index we can safely ignore and end copying.

Hammering the old brain-case a bit produced the following C code:

(If you have other garbled .264 files, feel free to contact me @ rasp AT spitzner.org)
(Thanks to Alp Günes for the tip that HXAF could be the audio :-) )

At least two people pointed out to me that the resulting mp4 is not rendering correctly, this is due to some cameras not having any idea about the frame rate they're recording at, or the lack of being able to write a proper file-header (SPS info),

[<rant>this is getting political: all of these SOC's I've come across are perfectly able to produce mp4Video with mp3sound if only the licenses were paid for; instead, they all cook up their own bowl of rice... </rant>]

so if you just see lots of mplayer errors and/or vertical stripes in the display window,or it plays at wrong speed try:
$> mplayer -fps <whateverframerateyourcamuses> whaterveryourfileiscalled.mp4
or create an envelope:
mkvmerge -o bla.mkv --defaultduration0:<yourframerate>fps wrongspeedfromconvert.mp4
original writeup follows - use the second version below...

If you don't want to bother compiling the the source and/or using mkvmerge Tony Mobily has built a self contained(no deps) application using convert2.c and mkvmerge which does the conversion in one go, find his broken264fixer here.

here's another approach done by Ivan Ustûžanin which will produce an .AVI done in PHP, so only php is required:-)
dot264toavi.php.gz gzipped for obvious resons :-)



/* * convert weird *.264 file to mp4 and wav * -raspo666 2018 */ #include <stdio.h> #include <stdlib.h> #include <strings.h> #include <string.h> #ifndef bzero #define bzero(b,len) (memset((b), '\0', (len)), (void) 0) #endif // bzero #ifndef bcopy #define bcopy(b1,b2,len) (memmove((b2), (b1), (len)), (void) 0) #endif // bcopy /*WAV*/ typedef struct wav_header { // RIFF Header char riff_header[4]; // Contains "RIFF" int wav_size; // Size of the wav portion of the file, which follows the first 8 bytes. File size - 8 char wave_header[4]; // Contains "WAVE" // Format Header char fmt_header[4]; // Contains "fmt " (includes trailing space) int fmt_chunk_size; // Should be 16 for PCM short audio_format; // Should be 1 for PCM. 3 for IEEE Float short num_channels; int sample_rate; int byte_rate; // Number of bytes per second. sample_rate * num_channels * Bytes Per Sample short sample_alignment; // num_channels * Bytes Per Sample short bit_depth; // Number of bits per sample // Data char data_header[4]; // Contains "data" int data_bytes; // Number of bytes in data. Number of samples * num_channels * sample byte size // uint8_t bytes[]; // Remainder of wave file is bytes } wav_header; int main(int ac, char **av) { FILE *in,*out,*outa; wav_header ahead; char *buffer,*p,ccode[5],*strbuf,*strp; int count,bufsiz,abytes; int len = 0; if(ac != 2) exit(printf("use %s <file> \n",av[0])); if(!(in = fopen(av[1],"rb"))) exit(printf("cannot open %s for reading.\n",av[1])); /* try to allocate a buffer size of infile */ fseek(in,0,SEEK_END); bufsiz = ftell(in); rewind(in); if(!(buffer = malloc(bufsiz))) exit(printf("unable to allocate %d bytes\n",bufsiz)); p = buffer; /* open output */ if(!(strbuf = malloc(strlen(av[1])+5))) exit(printf("no mem for strings\n")); strp = strstr(av[1],".264"); if(strp != NULL) *strp = '\0'; strp = av[1]; sprintf(strbuf,"%s.mp4",strp); if(!(out = fopen(strbuf,"wb"))) exit(printf("cannot open %s for writing.\n",av[2])); sprintf(strbuf,"%s.wav",strp); if(!(outa = fopen(strbuf,"wb"))) exit(printf("cannot open %s for writing.\n",strbuf)); fwrite(&ahead,sizeof(wav_header),1,outa); /* get data */ count = fread(buffer,1,bufsiz,in); abytes = 0; if(count != bufsiz) { printf("could only read %d of %d bytes\n",count,bufsiz); exit(0); } /* Throw first 0x10 bytes of garbage/fileheader plus first videoheader */ p += 0x10; while(p-buffer < bufsiz) { bzero(ccode,5); bcopy(p,ccode,4); p += 4; bcopy(p,&len,4); if(!(strncmp((char *)ccode,"HXAF",4))) { p += 0xc; p += 4; // {0x0001, 0x5000} whatever that means, it must go, it's no audio len -= 4; printf("code: HXAF audio %d bytes\n",len); fwrite(p,1,len,outa); p += len; abytes += len; continue; } else if(!(strncmp((char *)ccode,"HXFI",4))) { printf("found code HXFI, exit\n"); break; /* some sort of table follows */ } /* this will break if there's some other ccode ! */ printf("code: %s video %d bytes\n",ccode,len); p += 0xc; fwrite(p,1,len,out); p += len; } /* wav header */ strncpy(ahead.riff_header,"RIFF",4); strncpy(ahead.wave_header,"WAVE",4); strncpy(ahead.fmt_header,"fmt ",4); strncpy(ahead.data_header,"data",4); ahead.fmt_chunk_size = 16; ahead.audio_format = 0x6; ahead.num_channels = 1; ahead.sample_rate = 8000; ahead.byte_rate = 8000; // 16 ?? ahead.sample_alignment = 2; ahead.bit_depth = 16; ahead.data_bytes = abytes; ahead.wav_size = abytes + sizeof(wav_header) - 8; fseek(outa,0,SEEK_SET); fwrite(&ahead,sizeof(wav_header),1,outa); free(buffer); fclose(in); fclose(out); fclose(outa); exit(0); }




Ian Macallan took the time to think of a way to fix the fps issue and came up with the following code:


Hi I have done some modifications in your program. The reason is that when you do mkvmerge in a file you need timestamp (mainly for the video). So I create timestamp file (skipping SPS end PPS units). Then enable to have a correct .mkv file from the .264 using timestamp file. /* * convert weird *.264 file to mp4 and wav * -raspo666 2018 */ #include <stdio.h> #include <stdlib.h> #include <string.h> /* WAV */ typedef struct wav_header { // RIFF Header char riff_header[4]; // Contains "RIFF" int wav_size; // Size of the wav portion of the file, which follows the first 8 bytes. File size - 8 char wave_header[4]; // Contains "WAVE" // Format Header char fmt_header[4]; // Contains "fmt " (includes trailing space) int fmt_chunk_size; // Should be 16 for PCM short audio_format; // Should be 1 for PCM. 3 for IEEE Float short num_channels; int sample_rate; int byte_rate; // Number of bytes per second. sample_rate * num_channels * Bytes Per Sample short sample_alignment; // num_channels * Bytes Per Sample short bit_depth; // Number of bits per sample // Data char data_header[4]; // Contains "data" int data_bytes; // Number of bytes in data. Number of samples * num_channels * sample byte size // uint8_t bytes[]; // Remainder of wave file is bytes } wav_header; /** Main **/ int main(int ac, char **argValue) { FILE *rawFile,*videoFile,*audioFile; FILE *videoTSFile,*audioTSFile; int firstVideoTS = -1; int firstAudioTS = -1; wav_header ahead; char *pFullBuffer,*p, ccode[5], *pFilename, *pString; int count,bufsiz,abytes; int len = 0; if(ac != 2) { exit(printf("use %s <file> \n",argValue[0])); } if(!(rawFile = fopen(argValue[1],"rb"))) { exit(printf("cannot open %s for reading.\n",argValue[1])); } /* try to allocate a pFullBuffer size of infile */ fseek(rawFile,0,SEEK_END); bufsiz = ftell(rawFile); rewind(rawFile); if(!(pFullBuffer = ( char * ) malloc(bufsiz))) { exit(printf("unable to allocate %d bytes\n",bufsiz)); } p = pFullBuffer; /* open output */ if(!(pFilename = (char*) malloc(strlen(argValue[1])+5))) { exit(printf("no mem for strings\n")); } // Input Raw file pString = strstr(argValue[1],".264"); if(pString != NULL) { *pString = '\0'; } pString = argValue[1]; // Video File sprintf(pFilename,"%s.h264",pString); if(!(videoFile = fopen(pFilename,"wb"))) { exit(printf("cannot open %s for writing.\n",argValue[2])); } // Video Timestamp sprintf(pFilename,"%s.video.ts.txt",pString); if(!(videoTSFile = fopen(pFilename,"w"))) { exit(printf("cannot open %s for writing.\n",argValue[2])); } fprintf ( videoTSFile, "# timestamp format v2\n" ); // Wav File sprintf(pFilename,"%s.wav",pString); if(!(audioFile = fopen(pFilename,"wb"))) { exit(printf("cannot open %s for writing.\n",pFilename)); } // Wav Timestamp File sprintf(pFilename,"%s.audio.ts.txt",pString); if(!(audioTSFile = fopen(pFilename,"w"))) { exit(printf("cannot open %s for writing.\n",pFilename)); } fprintf ( audioTSFile, "# timestamp format v2\n" ); fwrite(&ahead,sizeof(wav_header),1,audioFile); /* get data */ count = fread(pFullBuffer,1,bufsiz,rawFile); abytes = 0; if(count != bufsiz) { printf("could only read %d of %d bytes\n",count,bufsiz); exit(0); } /* Throw first 0x10 bytes of garbage/fileheader plus first videoheader */ // HXVS memset(ccode,0,sizeof(ccode)); memcpy(ccode, p, 4); if( ! strncmp((char *)ccode,"HXVS",4) == 0 ) { exit(printf("No HXVS.\n")); } p += 4; // ?? p += 4; // Duration int duration; memcpy(&duration, p, sizeof(int)); printf("Duration %d\n", duration); p += 4; // ?? p += 4; while(p - pFullBuffer < bufsiz) { // Code memset(ccode,0,sizeof(ccode)); memcpy(ccode, p, 4); p += 4; // The Length memcpy(&len, p, sizeof(int)); p += 4; // if( strncmp((char *)ccode,"HXAF",4) == 0 ) { // Timestamp int ts; memcpy(&ts,p,sizeof(int)); if ( firstAudioTS == -1 ) { firstAudioTS = ts; } fprintf ( audioTSFile, "%d\n", ts - firstAudioTS ); p += 4; // ?? p += 4; // Audio p += 4; // {0x0001, 0x5000} whatever that means, it must go, it's no audio len -= 4; // printf("code: HXAF audio %d bytes\n",len); fwrite(p,1,len,audioFile); p += len; abytes += len; continue; } if( strncmp((char *)ccode,"HXVF",4) == 0 ) { // Timestamp int ts; memcpy(&ts,p,sizeof(int)); if ( firstVideoTS == -1 ) { firstVideoTS = ts; } int NalUnit = p [ 12 ] & 0x1f; // Do not set TimeStampp for SPS... if ( NalUnit != 0x6 && NalUnit != 0x7 && NalUnit != 0x8 ) { fprintf ( videoTSFile, "%d\n", ts - firstVideoTS ); } p += 4; // ?? p += 4; // fwrite(p,1,len,videoFile); p += len; continue; } else if(!(strncmp((char *)ccode,"HXFI",4))) { printf("HXFI End if File\n"); break; /* some sort of table follows */ } else { printf("Unknown Code\n"); } } /* wav header */ strncpy(ahead.riff_header,"RIFF",4); strncpy(ahead.wave_header,"WAVE",4); strncpy(ahead.fmt_header,"fmt ",4); strncpy(ahead.data_header,"data",4); ahead.fmt_chunk_size = 16; ahead.audio_format = 0x6; ahead.num_channels = 1; ahead.sample_rate = 8000; ahead.byte_rate = 8000; // 16 ?? ahead.sample_alignment = 2; ahead.bit_depth = 16; ahead.data_bytes = abytes; ahead.wav_size = abytes + sizeof(wav_header) - 8; fseek(audioFile,0,SEEK_SET); fwrite(&ahead,sizeof(wav_header),1,audioFile); free(pFullBuffer); fclose(rawFile); fclose(videoFile); fclose(audioFile); fclose(videoTSFile); fclose(audioTSFile); exit(0); }



Quote from his mail:

The .264 does not have a constant framerate but the file contains the timestamp (in milliseconds). So I extract those timestamp in timecode v2 format for audio and video (with the .wav and .h264). Then I convert the ALAW/G711/PCMU/PCMA to mp3 : ffmpeg -i "the.wav" -y "the.mp3" The timestamp for audio are not used (audio is correct because they are small and cannot miss any part). Then I merge the .h264, the .mp3 are use the video timestamp. mkvmerge.exe --ui-language fr --output "output.mkv" --timestamps "0:the.video.ts.txt" "the.h264" "the.mp3" This produces a correct mkv file that have frames correctly synchronized. In the program I produce timestamp only for video part so I omit the H264 SPS, PPS and so on because only video parts (IDR and slices) are normally with timestamp.


Feel free to use the Paypal button if you find this useful and you can afford it :-).

1. [Some Installations of VLC need tweaking h/x264 settings to make it play mp4, without a container header....]*

Changelog:
02/14/18 - fixed typo in convert.c /*comment*/.
07/30/18 - added framerate comment and rant...
01/12/20 - added Ian Macallan's version
09/13/20 - added Link to Tony's app
09/25/23 - added new tools , deprecated page