#include #include #include #include #include "vidfile.h" extern const char *__progname; static int quality = 20; static VFR_HANDLE *rh; static VFW_HANDLE *wh; static int have_timestamps; static int picw; static int pich; static int picsize; static int framew; static int frameh; static int framesize; static unsigned char *ibuf; static unsigned int ibuflen; static unsigned long long int timestamp; static unsigned char *planar_y; static unsigned char *planar_cb; static unsigned char *planar_cr; static void planarize(void) { unsigned char *ip; unsigned char *op_y; unsigned char *op_cb; unsigned char *op_cr; int y; int x; ip = ibuf; op_y = planar_y; op_cb = planar_cb; op_cr = planar_cr; for (y=pich;y>0;y--) { for (x=picw;x>0;x-=2) { *op_y++ = ip[0]; *op_y++ = ip[2]; *op_cb++ = ip[1]; *op_cr++ = ip[3]; ip += 4; } if (picw != framew) { op_y += framew - picw; op_cb += (framew - picw) / 2; op_cr += (framew - picw) / 2; } } } static void gen_pkt(const ogg_packet *p) { #if 0 fprintf(stderr,"pkt: %lu %lu %lu %llu %llu\n", (unsigned long int) p->bytes, (unsigned long int) p->b_o_s, (unsigned long int) p->e_o_s, (unsigned long long int) p->granulepos, (unsigned long long int) p->packetno ); #endif if (have_timestamps) { write_vidfile_frame( wh, p->packet, (unsigned int) p->bytes, (p->b_o_s?1U:0U) | (p->e_o_s?2U:0U), (unsigned long long int) p->granulepos, (unsigned long long int) p->packetno, timestamp ); } else { write_vidfile_frame( wh, p->packet, (unsigned int) p->bytes, (p->b_o_s?1U:0U) | (p->e_o_s?2U:0U), (unsigned long long int) p->granulepos, (unsigned long long int) p->packetno ); } } 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; void feature(void *cookie __attribute__((__unused__)), unsigned int bit, ...) { switch (bit) { case VFF_TIMESTAMPS: have_timestamps = 1; break; default: fprintf(stderr,"%s: don't understand feature bit %#x\n",__progname,bit); exit(1); break; } } have_timestamps = 0; rh = open_vidfile_read(&vidfile_err,0,&vidfile_read,0,&picw,&pich,&fmt,&feature,0); switch (fmt) { case VF_FMT_YCbCr422_YCbYCr_0_255: picsize = picw * pich * 2; ibuflen = picsize; ibuf = malloc(ibuflen); break; default: fprintf(stderr,"%s: don't understand format %d\n",__progname,(int)fmt); exit(1); break; } framew = (picw + 15) & ~15; frameh = (pich + 15) & ~15; framesize = framew * frameh * 2; planar_y = malloc(framesize); planar_cb = planar_y + (framesize / 2); planar_cr = planar_cb + (framesize / 4); } 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,framew,frameh,VF_FMT_THEORA,have_timestamps?VFF_TIMESTAMPS:0); } static void read_input(void) { if (have_timestamps) { read_vidfile_frame(rh,ibuf,×tamp); } else { read_vidfile_frame(rh,ibuf); } } 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; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs = 1; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-q")) { WANTARG(); quality = atoi(av[skip]); if ((quality < 0) || (quality > 63)) { fprintf(stderr,"%s: -q value must be 0..63\n",__progname); errs = 1; } continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs = 1; } if (errs) exit(1); } int main(int, char **); int main(int ac, char **av) { th_info thi; th_enc_ctx *thec; th_comment comm; ogg_packet pkt; th_ycbcr_buffer thyb; int e; handleargs(ac,av); read_header(); read_input(); write_header(); thi.version_major = 1; // XXX undocumented thi.version_minor = 1; // XXX undocumented thi.version_subminor = 1; // XXX undocumented thi.frame_width = framew; thi.frame_height = frameh; thi.pic_width = picw; thi.pic_height = pich; thi.pic_x = 0; thi.pic_y = 0; /* * The documentation says of these "If either is 0, the frame rate is * undefined.". What it doesn't say is that the encoder will happily * accept such settings, encode them in the resulting stream, and the * decoder will then barf with TH_EBADHEADER. */ thi.fps_numerator = 10; thi.fps_denominator = 1; thi.aspect_numerator = 1; thi.aspect_denominator = 1; thi.colorspace = TH_CS_ITU_REC_470M; thi.pixel_fmt = TH_PF_422; thi.target_bitrate = 0; thi.quality = quality; thi.keyframe_granule_shift = 6; thec = th_encode_alloc(&thi); // th_encode_ctl(...) none needed comm.user_comments = 0; comm.comment_lengths = 0; comm.comments = 0; comm.vendor = 0; while (1) { e = th_encode_flushheader(thec,&comm,&pkt); if (e == 0) break; if (e > 0) { gen_pkt(&pkt); } else { fprintf(stderr,"th_encode_flushheader error %d\n",e); exit(1); } } while (1) { planarize(); thyb[0].width = framew; thyb[0].height = frameh; thyb[0].stride = framew; thyb[0].data = &planar_y[0]; thyb[1].width = framew / 2; thyb[1].height = frameh; thyb[1].stride = framew / 2; thyb[1].data = &planar_cb[0]; thyb[2].width = framew / 2; thyb[2].height = frameh; thyb[2].stride = framew / 2; thyb[2].data = &planar_cr[0]; /* XXX th_ycbcr_buffer is an array (!), hence no & */ e = th_encode_ycbcr_in(thec,thyb); if (e != 0) { fprintf(stderr,"th_encode_ycbcr_in error %d\n",e); exit(1); } while (1) { e = th_encode_packetout(thec,0,&pkt); if (e == 0) break; if (e > 0) { gen_pkt(&pkt); } else { fprintf(stderr,"th_encode_packetout error %d\n",e); exit(1); } } read_input(); } }