#include #include #include #include "vidfile.h" #define MAGICLEN 16 static const unsigned char magic[MAGICLEN] = { 0x56, 0x39, 0x02, 0x59, 0x7d, 0x7d, 0x00, 0x98, 0x88, 0x16, 0xa2, 0xbb, 0x3c, 0x74, 0x5a, 0x6e }; static const unsigned char fmtstr_RGB888_RGB[] = { 0x52, 0x47, 0x42, 0x38, 0x38, 0x38, 0x20, 0x52, 0x47, 0x42 }; static const unsigned char fmtstr_YCbCr422_YCbYCr_0_255[] = { 0x59, 0x27, 0x43, 0x62, 0x43, 0x72, 0x34, 0x32, 0x32, 0x20, 0x59, 0x27, 0x43, 0x62, 0x59, 0x27, 0x43, 0x72, 0x20, 0x30, 0x2d, 0x32, 0x35, 0x35 }; static const unsigned char fmtstr_Theora[] = { 0x54, 0x68, 0x65, 0x6f, 0x72, 0x61 }; static const unsigned char timestamp_hdr[] = { 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x02, 0x03, 0x00, 0x00, 0x00, 0x04, 0x75, 0x73, 0x65, 0x63 }; static const unsigned char timestamp_name[] = { 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70 }; static const unsigned char timestamp_kind[] = { 0x75, 0x73, 0x65, 0x63 }; #define HINTFLAG_ESSENTIAL 0x01 #define HINTFLAG_SKIP 0x02 struct vfw_handle { void (*writefn)(void *, const void *, int); void *cookie; VF_VIDFMT fmt; unsigned int framebytes; unsigned int flags; } ; struct vfr_handle { void (*err)(void *, const char *); void *ecookie; int (*readfn)(void *, void *, int); void *rcookie; unsigned int framebytes; unsigned int flags; unsigned int skip1; unsigned int skip2; VF_VIDFMT fmt; } ; #define ARRAYSIZE(a) (sizeof((a))/sizeof((a)[0])) static void u64_to_8(unsigned long long int v, unsigned char *b) { b[0] = (v >> 56) & 0xff; b[1] = (v >> 48) & 0xff; b[2] = (v >> 40) & 0xff; b[3] = (v >> 32) & 0xff; b[4] = (v >> 24) & 0xff; b[5] = (v >> 16) & 0xff; b[6] = (v >> 8) & 0xff; b[7] = v & 0xff; } static void u32_to_4(unsigned int v, unsigned char *b) { b[0] = (v >> 24) & 0xff; b[1] = (v >> 16) & 0xff; b[2] = (v >> 8) & 0xff; b[3] = v & 0xff; } static unsigned long long int u8_to_64(const unsigned char *b) { return( (b[0] * 0x0100000000000000ULL) | (b[1] * 0x0001000000000000ULL) | (b[2] * 0x0000010000000000ULL) | (b[3] * 0x0000000100000000ULL) | (b[4] * 0x0000000001000000ULL) | (b[5] * 0x0000000000010000ULL) | (b[6] * 0x0000000000000100ULL) | (b[7] * 0x0000000000000001ULL) ); } static unsigned int u4_to_32(const unsigned char *b) { return( (b[0] * 0x01000000U) | (b[1] * 0x00010000U) | (b[2] * 0x00000100U) | (b[3] * 0x00000001U) ); } VFW_HANDLE *open_vidfile_write( void (*wrt)(void *, const void *, int), void *cookie, int w, int h, VF_VIDFMT fmt, unsigned int flags, ... ) { VFW_HANDLE *vh; unsigned char szbuf[4]; const char *fmtstr; unsigned char l; unsigned int framebytes; if (w < 1) { (*wrt)(cookie,"width too small",-1); return(0); } if (w > 65536) { (*wrt)(cookie,"width too large",-1); return(0); } if (h < 1) { (*wrt)(cookie,"height too small",-1); return(0); } if (h > 65536) { (*wrt)(cookie,"height too large",-1); return(0); } if (flags & ~(VFF_TIMESTAMPS)) { (*wrt)(cookie,"unrecognized flag bits set",-1); return(0); } switch (fmt) { case VF_FMT_RGB888_RGB: fmtstr = &fmtstr_RGB888_RGB[0]; l = ARRAYSIZE(fmtstr_RGB888_RGB); framebytes = w * h * 3; break; case VF_FMT_YCbCr422_YCbYCr_0_255: if (w & 1) { (*wrt)(cookie,"YCbYCr format with odd width",-1); return(0); } fmtstr = &fmtstr_YCbCr422_YCbYCr_0_255[0]; l = ARRAYSIZE(fmtstr_YCbCr422_YCbYCr_0_255); framebytes = w * h * 2; break; case VF_FMT_THEORA: if ((w & 15) || (h & 15)) { (*wrt)(cookie,"Theora format with width/height not multiples of 16",-1); return(0); } fmtstr = &fmtstr_Theora[0]; l = ARRAYSIZE(fmtstr_Theora); framebytes = 0; break; default: (*wrt)(cookie,"unknown format",-1); return(0); break; } (*wrt)(cookie,&magic[0],MAGICLEN); w --; h --; szbuf[0] = w >> 8; szbuf[1] = w & 0xff; szbuf[2] = h >> 8; szbuf[3] = h & 0xff; (*wrt)(cookie,&szbuf[0],4); (*wrt)(cookie,&l,1); (*wrt)(cookie,fmtstr,l); if (flags & VFF_TIMESTAMPS) (*wrt)(cookie,×tamp_hdr[0],ARRAYSIZE(timestamp_hdr)); (*wrt)(cookie,"",1); vh = malloc(sizeof(VFW_HANDLE)); if (! vh) { (*wrt)(cookie,"malloc() failed",-1); return(0); } vh->writefn = wrt; vh->cookie = cookie; vh->fmt = fmt; vh->framebytes = framebytes; vh->flags = flags; return(vh); } void write_vidfile_frame(VFW_HANDLE *h, const void *data, ...) { va_list ap; unsigned int len; unsigned int flags; unsigned long long int granulepos; unsigned long long int packetno; va_start(ap,data); switch (h->fmt) { case VF_FMT_THEORA: len = va_arg(ap,unsigned int); flags = va_arg(ap,unsigned int); granulepos = va_arg(ap,unsigned long long int); packetno = va_arg(ap,unsigned long long int); break; default: break; } if (h->flags & VFF_TIMESTAMPS) { unsigned long long int stamp; unsigned char tbuf[8]; stamp = va_arg(ap,unsigned long long int); u64_to_8(stamp,&tbuf[0]); (*h->writefn)(h->cookie,&tbuf[0],8); } switch (h->fmt) { case VF_FMT_THEORA: { unsigned char wbuf[21]; /* 21 = 4+1+8+8 */ u32_to_4(len,&wbuf[0]); wbuf[4] = flags & 3; u64_to_8(granulepos,&wbuf[5]); u64_to_8(packetno,&wbuf[13]); (*h->writefn)(h->cookie,&wbuf[0],21); (*h->writefn)(h->cookie,data,len); } break; default: (*h->writefn)(h->cookie,data,h->framebytes); break; } va_end(ap); } void close_vidfile_write(VFW_HANDLE *h, ...) { free(h); } VFR_HANDLE *open_vidfile_read( void (*err)(void *, const char *), void *ecookie, int (*readfn)(void *, void *, int), void *rcookie, int *widthp, int *heightp, VF_VIDFMT *formatp, void (*feature)(void *, unsigned int, ...), void *fcookie ) { __label__ fail; unsigned char mbuf[MAGICLEN]; unsigned char sizebuf[4]; unsigned int w; unsigned int h; VF_VIDFMT fmt; unsigned char l; unsigned char sbuf[256]; unsigned char hint[2]; unsigned int framebytes; unsigned int flags; unsigned int skip1; unsigned int skip2; VFR_HANDLE *vh; void rd(void *buf, int len) { unsigned char *bp; int l; int r; bp = buf; l = len; while (l > 0) { r = (*readfn)(rcookie,bp,l); if (r < 1) { (*err)(ecookie,"read error"); goto fail; } bp += r; l -= r; } } void skip_n(int n) { while (n > 0) { int w; int r; w = sizeof(sbuf); if (w > n) w = n; r = (*readfn)(rcookie,&sbuf,w); if (r < 1) { (*err)(ecookie,"read error"); goto fail; } n -= r; } } rd(&mbuf[0],MAGICLEN); if (bcmp(&mbuf[0],&magic[0],MAGICLEN)) { (*err)(ecookie,"magic number wrong"); return(0); } rd(&sizebuf[0],4); w = (sizebuf[0] * 256) + sizebuf[1] + 1; h = (sizebuf[2] * 256) + sizebuf[3] + 1; rd(&l,1); rd(&sbuf[0],l); flags = 0; if ((l == ARRAYSIZE(fmtstr_RGB888_RGB)) && !bcmp(&sbuf[0],&fmtstr_RGB888_RGB[0],ARRAYSIZE(fmtstr_RGB888_RGB))) { fmt = VF_FMT_RGB888_RGB; framebytes = w * h * 3; } else if ((l == ARRAYSIZE(fmtstr_YCbCr422_YCbYCr_0_255)) && !bcmp(&sbuf[0],&fmtstr_YCbCr422_YCbYCr_0_255[0],ARRAYSIZE(fmtstr_YCbCr422_YCbYCr_0_255))) { if (w & 1) (*err)(ecookie,"4:2:2 chroma with odd width"); fmt = VF_FMT_YCbCr422_YCbYCr_0_255; framebytes = w * h * 2; } else if ((l == ARRAYSIZE(fmtstr_Theora)) && !bcmp(&sbuf[0],&fmtstr_Theora[0],ARRAYSIZE(fmtstr_Theora))) { fmt = VF_FMT_THEORA; flags |= VFF_CHUNKED; (*feature)(fcookie,VFF_CHUNKED); framebytes = 0; } else { (*err)(ecookie,"unrecognized format"); return(0); } skip1 = 0; skip2 = 0; while (1) { unsigned int fdlen; rd(&l,1); if (l == 0) break; rd(&sbuf[0],l); rd(&hint[0],2); rd(&sizebuf[0],4); fdlen = u4_to_32(&sizebuf[0]); if ((l == ARRAYSIZE(timestamp_name)) && !bcmp(&sbuf[0],×tamp_name[0],ARRAYSIZE(timestamp_name))) { if (fdlen == ARRAYSIZE(timestamp_kind)) { unsigned char tskind[ARRAYSIZE(timestamp_kind)]; rd(&tskind[0],ARRAYSIZE(timestamp_kind)); if (bcmp(&tskind[0],×tamp_kind[0],ARRAYSIZE(timestamp_kind))) { (*err)(ecookie,"unrecognized timestamp kind"); return(0); } } else { (*err)(ecookie,"unrecognized timestamp kind"); return(0); } if (flags & VFF_TIMESTAMPS) { (*err)(ecookie,"multiple timestamp features"); return(0); } flags |= VFF_TIMESTAMPS; (*feature)(fcookie,VFF_TIMESTAMPS); } else { if (hint[0] & HINTFLAG_ESSENTIAL) { (*err)(ecookie,"non-understood essential feature"); return(0); } skip_n(fdlen); if (hint[0] & HINTFLAG_SKIP) { if (flags & VFF_TIMESTAMPS) { skip2 += 1 << hint[1]; } else { skip1 += 1 << hint[1]; } } } } *widthp = w; *heightp = h; *formatp = fmt; vh = malloc(sizeof(VFR_HANDLE)); if (! vh) { (*err)(ecookie,"malloc() failed"); return(0); } vh->err = err; vh->ecookie = ecookie; vh->readfn = readfn; vh->rcookie = rcookie; vh->framebytes = framebytes; vh->flags = flags; vh->skip1 = skip1; vh->skip2 = skip2; vh->fmt = fmt; return(vh); fail:; return(0); } void read_vidfile_frame(VFR_HANDLE *h, void *framebuf, ...) { __label__ fail; va_list ap; unsigned long long int *tsp; unsigned char junk[256]; void rd(void *buf, int len) { unsigned char *bp; int l; int r; bp = buf; l = len; while (l > 0) { r = (*h->readfn)(h->rcookie,bp,l); if (r < 1) { (*h->err)(h->ecookie,"read error"); goto fail; } bp += r; l -= r; } } void skipn(int n) { int r; int w; while (n > 0) { w = sizeof(junk); if (n < w) w = n; r = (*h->readfn)(h->rcookie,&junk,w); if (r < 1) { (*h->err)(h->ecookie,"read error"); goto fail; } n -= r; } } va_start(ap,framebuf); tsp = (h->flags & VFF_TIMESTAMPS) ? va_arg(ap,unsigned long long int *) : 0; va_end(ap); skipn(h->skip1); if (tsp) { unsigned char tstamp[8]; rd(&tstamp[0],8); *tsp = u8_to_64(&tstamp[0]); } skipn(h->skip2); if (h->flags & VFF_CHUNKED) { const VF_CHUNKER *c; c = framebuf; switch (h->fmt) { default: abort(); break; case VF_FMT_THEORA: { unsigned char hdr[21]; /* 21 = 4+1+8+8 */ unsigned int len; unsigned int off; rd(&hdr[0],21); if (hdr[4] & ~3U) { (*h->err)(h->ecookie,"unrecognized flag bits in Theora header"); goto fail; } len = u4_to_32(&hdr[0]); (*c->header)(c->cookie,len,hdr[4]&3U,u8_to_64(&hdr[5]),u8_to_64(&hdr[13])); off = 0; while (len > 0) { unsigned char chunk[1024]; int n; n = sizeof(chunk); if (n > len) n = len; rd(&chunk[0],n); (*c->chunk)(c->cookie,off,n,&chunk[0]); len -= n; off += n; } } break; } } else { rd(framebuf,h->framebytes); } fail:; } void close_vidfile_read(VFR_HANDLE *h, ...) { free(h); }