#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vidfile.h" #include "display-X.h" #include "ycbcr-rgb-cvt.h" extern const char *__progname; #define FRAME_X 640 #define FRAME_Y 480 #define FRAME_SIZE (FRAME_X * FRAME_Y * 2) static char *recordto = 0; static char *arg1 = 0; static char *arg2 = 0; static unsigned long long int recordstart; static int recordfd; static VFW_HANDLE *recordh; static unsigned char *framebuf; static int dpipe; static int net; static FILE *netf; static int npackets; static int totalbytes; static int sockbufsize; static unsigned int pktalloc; static unsigned char *pktbuf; #define wrt(fd,buf,len) wrt_(#fd "," #buf "," #len,(fd),(buf),len) static void wrt_(const char *s, int fd, const void *buf, int len) { int n; n = write(fd,buf,len); if (n < 0) { fprintf(stderr,"write(%s): %s\n",s,strerror(errno)); exit(1); } if (n != len) { fprintf(stderr,"write(%s): wrote %d not %d\n",s,n,len); exit(1); } } static void rcvn(int fd, void *buf, int l) { int r; r = recv(fd,buf,l,MSG_WAITALL); if (r < 0) { fprintf(stderr,"%s: recv: %s\n",__progname,strerror(errno)); exit(1); } if (r < l) { fprintf(stderr,"%s: recv wanted %d, got %d\n",__progname,l,r); exit(1); } if (r > l) { fprintf(stderr,"%s: impossible recv wanted %d, got %d\n",__progname,l,r); exit(1); } } static void net_accept(const char *portstr) { struct addrinfo *ai0; struct addrinfo *ai; struct addrinfo hints; int e; int n; int i; int nfds; hints.ai_flags = AI_PASSIVE; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = 0; hints.ai_addr = 0; hints.ai_next = 0; e = getaddrinfo(0,portstr,&hints,&ai0); if (e) { fprintf(stderr,"%s: %s: %s\n",__progname,portstr,gai_strerror(e)); exit(1); } if (! ai0) { fprintf(stderr,"%s: %s: success but no addresses\n",__progname,portstr); exit(1); } n = 0; for (ai=ai0;ai;ai=ai->ai_next) n ++; { int fds[n]; struct pollfd pfds[n]; int fd; nfds = 0; for (ai=ai0;ai;ai=ai->ai_next) { fd = socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol); if (fd < 0) { fprintf(stderr,"%s: socket (af %d): %s\n",__progname,ai->ai_family,strerror(errno)); continue; } if (bind(fd,ai->ai_addr,ai->ai_addrlen) < 0) { fprintf(stderr,"%s: bind (af %d): %s\n",__progname,ai->ai_family,strerror(errno)); close(fd); continue; } fds[nfds++] = fd; } if (nfds < 1) { fprintf(stderr,"%s: can't establish any listening sockets\n",__progname); exit(1); } for (i=nfds-1;i>=0;i--) { if (listen(fds[i],1) < 0) { fprintf(stderr,"%s: listen: %s\n",__progname,strerror(errno)); exit(1); } } while (1) { for (i=nfds-1;i>=0;i--) { pfds[i].fd = fds[i]; pfds[i].events = POLLIN | POLLRDNORM; } e = poll(&pfds[0],nfds,INFTIM); if (e < 0) { if (errno == EINTR) continue; { fprintf(stderr,"%s: poll: %s\n",__progname,strerror(errno)); exit(1); } } for (i=nfds-1;i>=0;i--) { if (pfds[i].revents & (POLLIN|POLLRDNORM|POLLERR|POLLHUP|POLLNVAL)) { struct sockaddr_storage ss; socklen_t sslen; sslen = sizeof(ss); fd = accept(fds[i],(struct sockaddr *)&ss,&sslen); if (fd >= 0) { for (i=nfds-1;i>=0;i--) close(fds[i]); freeaddrinfo(ai0); net = fd; return; } fprintf(stderr,"%s: accept: %s\n",__progname,strerror(errno)); } } } } } static void net_connect(const char *hoststr, const char *portstr) { struct addrinfo *ai0; struct addrinfo *ai; struct addrinfo hints; int e; hints.ai_flags = 0; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = 0; hints.ai_addr = 0; hints.ai_next = 0; e = getaddrinfo(hoststr,portstr,&hints,&ai0); if (e) { fprintf(stderr,"%s: %s: %s\n",__progname,portstr,gai_strerror(e)); exit(1); } if (! ai0) { fprintf(stderr,"%s: %s: success but no addresses\n",__progname,portstr); exit(1); } for (ai=ai0;ai;ai=ai->ai_next) { int fd; fd = socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol); if (fd < 0) { fprintf(stderr,"%s: socket (af %d): %s\n",__progname,ai->ai_family,strerror(errno)); continue; } if (connect(fd,ai->ai_addr,ai->ai_addrlen) < 0) { fprintf(stderr,"%s: connect (af %d): %s\n",__progname,ai->ai_family,strerror(errno)); close(fd); continue; } freeaddrinfo(ai0); net = fd; return; } fprintf(stderr,"%s: can't establish network connection\n",__progname); exit(1); } static void setup_net(void) { int v; v = 1; if (setsockopt(net,IPPROTO_TCP,TCP_NODELAY,&v,sizeof(v)) < 0) { fprintf(stderr,"%s: warning: setsockopt TCP_NODELAY: %s\n",__progname,strerror(errno)); exit(1); } sockbufsize = 0; netf = fdopen(net,"r"); } static void displayer_main(void) { unsigned char c; CVTPARAMS p; setup_X(FRAME_X,FRAME_Y,DX_FMT_YCbCr422,0); p.Kr = .299; p.Kb = .114; p.minY = 0; p.maxY = 255; p.maxCC = 127; reset_cvt(&p); while (1) { rcvn(dpipe,&c,1); display_frame(framebuf+(c*FRAME_SIZE)); wrt(dpipe,&c,1); } } static void fork_displayer(void) { int p[2]; int pid; if (socketpair(AF_LOCAL,SOCK_STREAM,0,&p[0]) < 0) { fprintf(stderr,"%s: socketpair: %s\n",__progname,strerror(errno)); exit(1); } wrt(p[1],"\0\1",2); fflush(0); pid = fork(); if (pid == 0) { close(p[0]); dpipe = p[1]; displayer_main(); _exit(0); } close(p[1]); dpipe = p[0]; } static void setup_frame(void) { void *mmrv; mmrv = mmap(0,FRAME_SIZE*2,PROT_READ|PROT_WRITE,MAP_ANON|MAP_SHARED,-1,0); if (mmrv == MAP_FAILED) { fprintf(stderr,"%s: buffer mmap (%u): %s\n",__progname,(unsigned int)(FRAME_SIZE*2),strerror(errno)); exit(1); } framebuf = mmrv; bzero(framebuf,FRAME_SIZE*2); } static void getpkt(ogg_packet *p) { char c; long int len; long int bos; long int eos; long long int gpos; long long int pno; char *s; if (fscanf(netf,"%ld%ld%ld%lld%lld",&len,&bos,&eos,&gpos,&pno) != 5) { fprintf(stderr,"input header scan failed\n"); exit(1); } c = getc(netf); if (c != '\n') { fprintf(stderr,"input header format error\n"); exit(1); } p->bytes = len; p->b_o_s = bos; p->e_o_s = eos; p->granulepos = gpos; p->packetno = pno; if (len > pktalloc) { free(pktbuf); pktalloc = len; pktbuf = malloc(len); } p->packet = pktbuf; if (fread(pktbuf,1,len,netf) != len) { fprintf(stderr,"input data read failed\n"); exit(1); } npackets ++; totalbytes += asprintf(&s,"%ld %ld %ld %lld %lld\n",len,bos,eos,gpos,pno) + len; free(s); #if 0 { int i; fprintf(stderr,"packet: len %ld bos %ld eos %ld gpos %lld pno %lld\n", (long int)p->bytes, (long int)p->b_o_s, (long int)p->e_o_s, (long long int)p->granulepos, (long long int)p->packetno ); fprintf(stderr,"packet data:"); for (i=0;ibytes;i++) fprintf(stderr," %02x",p->packet[i]); fprintf(stderr,"\n"); } #endif } static void deplanarize(th_img_plane *ycbcr, unsigned char *to) { unsigned char *yp; unsigned char *cbp; unsigned char *crp; int y; int x; if ( (ycbcr[0].width != FRAME_X) || (ycbcr[0].height != FRAME_Y) || (ycbcr[1].width != FRAME_X/2) || (ycbcr[1].height != FRAME_Y) || (ycbcr[2].width != FRAME_X/2) || (ycbcr[2].height != FRAME_Y) ) { 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; for (y=FRAME_Y;y>0;y--) { for (x=FRAME_X;x>0;x-=2) { *to++ = *yp++; *to++ = *cbp++; *to++ = *yp++; *to++ = *crp++; } yp += ycbcr[0].stride - FRAME_X; cbp += ycbcr[1].stride - (FRAME_X/2); crp += ycbcr[2].stride - (FRAME_X/2); } } 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 != '-') { if (! arg1) { arg1 = *av; } else if (! arg2) { arg2 = *av; } else { 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,"-record")) { WANTARG(); recordto = av[skip]; continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs = 1; } if (errs) exit(1); } static void record_writefn(void *cookie __attribute__((__unused__)), const void *buf, int len) { if (len < 0) abort(); write(recordfd,buf,len); } static void setup_recording(void) { recordfd = open(recordto,O_WRONLY|O_CREAT|O_TRUNC,0666); if (recordfd < 0) { fprintf(stderr,"%s: %s: %s\n",__progname,recordto,strerror(errno)); exit(1); } recordstart = 0; recordh = open_vidfile_write(&record_writefn,0,FRAME_X,FRAME_Y,VF_FMT_YCbCr422_YCbYCr_0_255,VFF_TIMESTAMPS); } static void record_frame(unsigned char *fdata) { struct timeval now; unsigned long long int nowus; gettimeofday(&now,0); nowus = now.tv_usec + (now.tv_sec * 1000000ULL); if (recordstart == 0) recordstart = nowus; nowus -= recordstart; write_vidfile_frame(recordh,fdata,nowus); } int main(int, char **); int main(int ac, char **av) { th_info thi; th_comment comm; th_setup_info *thsi; ogg_packet pkt; int e; th_dec_ctx *thdc; th_ycbcr_buffer obuf; unsigned char df; handleargs(ac,av); if (! arg1) { fprintf(stderr,"Usage: %s [options] [port | host port]\n",__progname); exit(1); } if (! arg2) { net_accept(arg1); } else { net_connect(arg1,arg2); } setup_net(); if (recordto) setup_recording(); setup_frame(); fork_displayer(); npackets = 0; totalbytes = 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 != FRAME_X) || (thi.frame_height != FRAME_Y) || (thi.pic_width != FRAME_X) || (thi.pic_height != FRAME_Y) || (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,obuf); if (e != 0) { fprintf(stderr,"th_decode_ycbcr_out error %d\n",e); exit(1); } rcvn(dpipe,&df,1); deplanarize(&obuf[0],framebuf+(df*FRAME_SIZE)); if (recordto) record_frame(framebuf+(df*FRAME_SIZE)); wrt(dpipe,&df,1); getpkt(&pkt); #if 0 if (npackets > 23) { double desbuf; int v; desbuf = totalbytes / npackets; if (desbuf < 4096) desbuf = 4096; if ((sockbufsize < desbuf) || (sockbufsize > desbuf*3)) { v = desbuf; printf("buf %d\n",v); setsockopt(net,SOL_SOCKET,SO_RCVBUF,&v,sizeof(v)); sockbufsize = v; } npackets = 0; totalbytes = 0; } #endif } }