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

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 quiet 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 for some hardball 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 mpeg-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 :-) )
Here's a convert.exe


/* * 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); }






Chnagelog:
02/14/18 - fixed typo in convert.c /*comment*/.