#include #include #include #include #include #include #include extern const char *__progname; typedef struct track TRACK; struct track { TRACK *link; unsigned int data; unsigned int len; } ; static int file_fd; static const char *filename; static const unsigned char *file; static unsigned int filesize; static unsigned int moov_at = 0; static unsigned int moov_len; static unsigned int mdat_at = 0; static unsigned int mdat_len; static TRACK *tracks = 0; #define FOURCHAR(a,b,c,d) ( ((a) * 0x01000000) + \ ((b) * 0x010000) + \ ((c) * 0x0100) + \ ((d) * 0x01) ) /* * Comments below that talk about "ver 1" refer here. * * There are various places where the file contains a version number * which specifies, for example, whether another value is 32 bits or * 64 bits. In at least some of these cases, my reference specifies * that one thing happens for version 1 and something else for all * other versions. I do not know whether the spec really does specify * that, or "0 means this, 1 means that, other values reserved", or * what. (The actual spec appears to be a pay-to-play * pseudo-standard; apparently they'd rather have people implementing * based on collections of miscellanous tidbits, as I'm doing, rather * than referring to the real spec.) */ static void usage(void) { fprintf(stderr,"Usage: %s filename\n",__progname); exit(1); } static void map_file(const char *name) { struct stat stb; void *mmrv; filename = name; file_fd = open(name,O_RDONLY,0); if (file_fd < 0) { fprintf(stderr,"%s: %s: %s\n",__progname,name,strerror(errno)); exit(1); } fstat(file_fd,&stb); filesize = stb.st_size; if (filesize != stb.st_size) { fprintf(stderr,"%s: %s: too large\n",__progname,name); exit(1); } mmrv = mmap(0,stb.st_size,PROT_READ,MAP_FILE|MAP_SHARED,file_fd,0); if (mmrv == MAP_FAILED) { fprintf(stderr,"%s: mmap %s: %s\n",__progname,name,strerror(errno)); exit(1); } file = mmrv; } static unsigned int get4be(unsigned int o) { return( (file[o ] * 0x01000000) + (file[o+1] * 0x010000) + (file[o+2] * 0x0100) + (file[o+3] * 0x01) ); } static unsigned long long int get8be(unsigned int o) { return( (file[o ] * 0x0100000000000000ULL) + (file[o+1] * 0x01000000000000ULL) + (file[o+2] * 0x010000000000ULL) + (file[o+3] * 0x0100000000ULL) + (file[o+4] * 0x01000000ULL) + (file[o+5] * 0x010000ULL) + (file[o+6] * 0x0100ULL) + (file[o+7] * 0x01ULL) ); } static void print_str_vis(FILE *to, const unsigned char *d, int len) { int i; for (i=len;i>0;i--) { if (*d < 32) { fprintf(to,"^%c",64+*d); } else if (*d < 127) { putc(*d,to); } else if (*d == 127) { fprintf(to,"^?"); } else if (*d < 160) { fprintf(to,"^%c",64+*d); } else { putc(*d,to); } d ++; } } static void scan_chunks(int depth, unsigned int start, unsigned int len, void (*chunk)(const unsigned char [4], unsigned int, unsigned int, void *), void *arg) { unsigned int o; unsigned int l; int i; o = 0; while (1) { if (o == len) break; if (o+8 > len) { printf("*** chunk overrun 1 (%u+8 > %u)\n",o,len); exit(1); } l = get4be(start+o); if (l == 1) { printf("*** 64-bit chunk length at %u+%u\n",start,o); exit(1); } if (l < 8) { printf("*** runt chunk (%u) at %u+%u\n",l,start,o); exit(1); } for (i=depth;i>0;i--) putchar('\t'); printf("%08x+%08x: chunk len=%x type=",start,o,l); print_str_vis(stdout,file+start+o+4,4); printf("\n"); if (o+l > len) { printf("*** chunk overrun 2 (%u+%u > %u)\n",o,l,len); exit(1); } (*chunk)(file+start+o+4,start+o+8,l-8,arg); o += l; } } static void file_chunk(const unsigned char type[4], unsigned int data, unsigned int len, void *arg __attribute__((__unused__))) { int i; switch (FOURCHAR(type[0],type[1],type[2],type[3])) { default: printf("skipping unknown chunk\n"); return; break; case FOURCHAR('f','t','y','p'): if (len < 8) { printf("*** ftyp chunk too small (%u)\n",len); exit(1); } if (len % 4) { printf("*** ftyp chunk length (%u) isn't a multiple of 4\n",len); exit(1); } printf("ftyp: major `"); print_str_vis(stdout,file+data,4); printf("' minor %08x compat:",get4be(data+4)); for (i=8;idata = data; t->len = len; t->link = tracks; tracks = t; } static void moov_chunk(const unsigned char type[4], unsigned int data, unsigned int len, void *arg __attribute__((__unused__))) { switch (FOURCHAR(type[0],type[1],type[2],type[3])) { default: printf("skipping unknown chunk\n"); return; break; case FOURCHAR('m','v','h','d'): { unsigned char ver; unsigned long long int dur; unsigned int ts; if (len < 1) { printf("*** mvhd chunk too small 1 (%u)\n",len); exit(1); } /* XXX what are the rest of the values here? */ ver = file[data]; /* XXX see "ver 1" comment at file top */ if (len < ((ver==1) ? 32 : 20)) { printf("*** mvhd chunk too small 2 (%u, ver=%d)\n",len,ver); exit(1); } if (ver == 1) { ts = get4be(data+20); dur = get8be(data+24); } else { ts = get4be(data+12); dur = get4be(data+16); } printf("movie: ts=%u dur=%llu\n",ts,dur); } break; case FOURCHAR('t','r','a','k'): printf("saving trak chunk location\n"); save_track(data,len); break; case FOURCHAR('u','d','t','a'): printf("skipping udta chunk\n"); break; } } int main(int, char **); int main(int ac, char **av) { if (ac != 2) usage(); map_file(av[1]); printf("scanning file\n"); scan_chunks(0,0,filesize,&file_chunk,0); if (moov_at == 0) printf("no `moov' chunk\n"); if (mdat_at == 0) printf("no `mdat' chunk\n"); if (moov_at && mdat_at) { printf("scanning moov chunk\n"); scan_chunks(0,moov_at,moov_len,&moov_chunk,0); } return(0); }