#include #include #include #include #include #include "vidfile.h" extern const char *__progname; static VFR_HANDLE *rh; static VFW_HANDLE *wh; static int have_timestamps; static int picw; static int pich; static int picsize; static unsigned long long int timestamp; static unsigned char *pktbuf; static unsigned int pktalloc; static unsigned char *framebuf; static void vidfile_err(void *cookie __attribute__((__unused__)), const char *msg) { fprintf(stderr,"%s: error in input: %s\n",__progname,msg); exit(1); } static int vidfile_read(void *cookie __attribute__((__unused__)), void *buf, int len) { return(fread(buf,1,len,stdin)); } static void read_header(void) { VF_VIDFMT fmt; int chunked; void feature(void *cookie __attribute__((__unused__)), unsigned int bit, ...) { switch (bit) { case VFF_TIMESTAMPS: have_timestamps = 1; break; case VFF_CHUNKED: chunked = 1; break; default: fprintf(stderr,"%s: don't understand feature bit %#x\n",__progname,bit); exit(1); break; } } have_timestamps = 0; chunked = 0; rh = open_vidfile_read(&vidfile_err,0,&vidfile_read,0,&picw,&pich,&fmt,&feature,0); switch (fmt) { case VF_FMT_THEORA: if (! chunked) { fprintf(stderr,"%s: non-chunked VF_FMT_THEORA?\n",__progname); exit(1); } break; default: fprintf(stderr,"%s: don't understand format %d\n",__progname,(int)fmt); exit(1); break; } picsize = picw * pich * 2; framebuf = malloc(picsize); } static void vidfile_write(void *cookie __attribute__((__unused__)), const void *data, int len) { fwrite(data,1,len,stdout); } static void write_header(void) { wh = open_vidfile_write(&vidfile_write,0,picw,pich,VF_FMT_YCbCr422_YCbYCr_0_255,have_timestamps?VFF_TIMESTAMPS:0); } static void handleargs(int ac, char **av) { int skip; int errs; skip = 0; errs = 0; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { fprintf(stderr,"%s: extra argument `%s'\n",__progname,*av); errs = 1; continue; } fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs = 1; } if (errs) exit(1); } static void pkt_hdr(void *pv, int len, ...) { va_list ap; ogg_packet *p; unsigned int ui; if (len > pktalloc) { free(pktbuf); pktalloc = len; pktbuf = malloc(len); } p = pv; va_start(ap,len); p->bytes = len; ui = va_arg(ap,unsigned int); p->b_o_s = (ui & 1) ? 1 : 0; p->e_o_s = (ui & 2) ? 1 : 0; p->granulepos = va_arg(ap,unsigned long long int); p->packetno = va_arg(ap,unsigned long long int); va_end(ap); } static void pkt_chunk(void *pv, int off, int len, const void *data, ...) { ogg_packet *p; p = pv; if ( (off < 0) || (len < 0) || (off > p->bytes) || (off+len > p->bytes) ) abort(); bcopy(data,pktbuf+off,len); } static void getpkt(ogg_packet *p) { VF_CHUNKER c; c.cookie = p; c.header = &pkt_hdr; c.chunk = &pkt_chunk; if (have_timestamps) { read_vidfile_frame(rh,&c,×tamp); } else { read_vidfile_frame(rh,&c); } p->packet = pktbuf; } static void deplanarize(th_img_plane *ycbcr) { unsigned char *pp; unsigned char *yp; unsigned char *cbp; unsigned char *crp; int y; int x; if ( (ycbcr[0].width != picw) || (ycbcr[0].height != pich) || (ycbcr[1].width != picw/2) || (ycbcr[1].height != pich) || (ycbcr[2].width != picw/2) || (ycbcr[2].height != pich) ) { fprintf(stderr,"planar sizes wrong (%dx%d %dx%d %dx%d)\n", ycbcr[0].width, ycbcr[0].height, ycbcr[1].width, ycbcr[1].height, ycbcr[2].width, ycbcr[2].height ); exit(1); } yp = ycbcr[0].data; cbp = ycbcr[1].data; crp = ycbcr[2].data; pp = framebuf; for (y=pich;y>0;y--) { for (x=picw;x>0;x-=2) { *pp++ = *yp++; *pp++ = *cbp++; *pp++ = *yp++; *pp++ = *crp++; } yp += ycbcr[0].stride - picw; cbp += ycbcr[1].stride - (picw/2); crp += ycbcr[2].stride - (picw/2); } } static void gen_frame(void) { if (have_timestamps) { write_vidfile_frame(wh,framebuf,timestamp); } else { write_vidfile_frame(wh,framebuf); } } int main(int, char **); int main(int ac, char **av) { th_info thi; th_dec_ctx *thdc; th_comment comm; ogg_packet pkt; th_ycbcr_buffer thyb; th_setup_info *thsi; int e; handleargs(ac,av); read_header(); write_header(); pktbuf = 0; pktalloc = 0; th_info_init(&thi); /* * The documentation for th_decode_headerin doesn't mention that its * th_comment argument must be initialized! Oddly, only the vendor * field actually needs it. */ comm.vendor = 0; thsi = 0; while (1) { getpkt(&pkt); e = th_decode_headerin(&thi,&comm,&thsi,&pkt); if (e > 0) continue; if (e == 0) break; fprintf(stderr,"th_decode_headerin error %d\n",e); exit(1); } fprintf(stderr,"frame %dx%d picture %dx%d+%d+%d\n", thi.frame_width, thi.frame_height, thi.pic_width, thi.pic_height, thi.pic_x, thi.pic_y); fprintf(stderr,"colorspace %d pixel_fmt %d target_bitrate %d quality %d keyframe_granule_shift %d\n", (int)thi.colorspace, (int)thi.pixel_fmt, thi.target_bitrate, thi.quality, thi.keyframe_granule_shift); if ( (thi.frame_width != picw) || (thi.frame_height != pich) || (thi.pic_width != picw) || (thi.pic_height != pich) || (thi.pic_x != 0) || (thi.pic_y != 0) ) { fprintf(stderr,"size/location wrong\n"); exit(1); } thdc = th_decode_alloc(&thi,thsi); if (! thdc) { fprintf(stderr,"th_decode_alloc failed\n"); exit(1); } th_setup_free(thsi); while (1) { e = th_decode_packetin(thdc,&pkt,0); switch (e) { case 0: case TH_DUPFRAME: break; default: fprintf(stderr,"th_decode_packetin error %d\n",e); exit(1); break; } /* XXX th_ycbcr_buffer is an array (!), hence no & */ e = th_decode_ycbcr_out(thdc,thyb); if (e != 0) { fprintf(stderr,"th_decode_ycbcr_out error %d\n",e); exit(1); } deplanarize(&thyb[0]); gen_frame(); getpkt(&pkt); } }