#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; static const char *videodev = "/dev/video0"; static const char *audiodev = "/dev/sound0"; #define AUDIO_NBUFS 64 #define AUDIO_BUFSIZE 65536 #define VIDEO_NBUFS 16 typedef struct kid KID; struct kid { KID *link; pid_t pid; const char *what; } ; static int intfd_p; static int intfd_c; static int vfd; static int afd; static int audio_enc; static int anbufs; static int abufsize; static unsigned char **abufs; static int vnbufs; static struct v4l2_buffer *vbufs; static void **vbufmem; static int go_p; static int go_c; static int apipefd[2]; static int vpipefd[2]; static int apipe; static int vpipe; static int cpipe; static KID *kids = 0; static const char *mytag; static AIO_OQ poq; static AIO_OQ aoq; static AIO_OQ voq; #define AUDIO_CAN_PARTIAL #if AUDIO_NBUFS <= 256 typedef unsigned char ABUFNO; #undef AUDIO_CAN_PARTIAL #elif AUDIO_NBUFS < 65536 typedef unsignes short int ABUFNO; #else typedef int ABUFNO; #endif #ifdef AUDIO_CAN_PARTIAL static ABUFNO apartial; static int apartlen; #endif static ABUFNO *abufown; static int nabufown; #define VIDEO_CAN_PARTIAL #if VIDEO_NBUFS <= 256 typedef unsigned char VBUFNO; #undef VIDEO_CAN_PARTIAL #elif VIDEO_NBUFS < 65536 typedef unsignes short int VBUFNO; #else typedef int VBUFNO; #endif #ifdef VIDEO_CAN_PARTIAL static VBUFNO vpartial; static int vpartlen; #endif static VBUFNO *vbufown; static int nvbufown; static ogg_stream_state oggss; static ogg_page oggpg; static vorbis_info aenci; static vorbis_comment aencc; static vorbis_dsp_state aencd; static vorbis_block aencb; static volatile sig_atomic_t interrupted; static FILE *of_ra; static FILE *of_rv; static FILE *of_vorbis; static FILE *of_ogg; static const char *vorb_strerror(int ec) { switch (ec) { case OV_FALSE: return("FALSE"); break; case OV_EOF: return("EOF"); break; case OV_HOLE: return("HOLE"); break; case OV_EREAD: return("EREAD"); break; case OV_EFAULT: return("EFAULT"); break; case OV_EIMPL: return("EIMPL"); break; case OV_EINVAL: return("EINVAL"); break; case OV_ENOTVORBIS: return("ENOTVORBIS"); break; case OV_EBADHEADER: return("EBADHEADER"); break; case OV_EVERSION: return("EVERSION"); break; case OV_ENOTAUDIO: return("ENOTAUDIO"); break; case OV_EBADPACKET: return("EBADPACKET"); break; case OV_EBADLINK: return("EBADLINK"); break; case OV_ENOSEEK: return("ENOSEEK"); break; } return("unknown libvorbis code"); } 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: stray 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,"-video")) { WANTARG(); videodev = av[skip]; continue; } if (!strcmp(*av,"-audio")) { WANTARG(); audiodev = av[skip]; continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs = 1; } if (errs) exit(1); } static void set_nbio(int fd) { fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0)|O_NONBLOCK); } static void open_video(void) { vfd = open(videodev,O_RDWR,0); if (vfd < 0) { fprintf(stderr,"%s: %s: %s\n",__progname,videodev,strerror(errno)); exit(1); } set_nbio(vfd); } static void open_audio(void) { afd = open(audiodev,O_RDWR,0); if (afd < 0) { fprintf(stderr,"%s: %s: %s\n",__progname,audiodev,strerror(errno)); exit(1); } set_nbio(afd); } static void setup_video(void) { struct v4l2_requestbuffers rb; int i; void *mmrv; rb.count = VIDEO_NBUFS; rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; rb.memory = V4L2_MEMORY_MMAP; if (ioctl(vfd,VIDIOC_REQBUFS,&rb) < 0) { fprintf(stderr,"%s: VIDIOC_REQBUFS: %s\n",__progname,strerror(errno)); exit(1); } vnbufs = rb.count; fprintf(stderr,"video buffer count %d\n",vnbufs); vbufs = malloc(vnbufs*sizeof(*vbufs)); vbufmem = malloc(vnbufs*sizeof(*vbufmem)); for (i=0;iflags ^ over->flags) & AUDIO_ENCODINGFLAG_EMULATED) return((enc->flags&AUDIO_ENCODINGFLAG_EMULATED)?0:1); return(0); } static void dump_vorbis_packet(ogg_packet *p) { if (p->b_o_s) putc('B',of_vorbis); if (p->e_o_s) putc('E',of_vorbis); fprintf(of_vorbis,"%lld %lld %lld\n",(long long int)p->granulepos,(long long int)p->packetno,(long long int)p->bytes); fwrite(p->packet,1,p->bytes,of_vorbis); } static void dump_ogg_block(ogg_page *pg) { fwrite(pg->header,1,pg->header_len,of_ogg); fwrite(pg->body,1,pg->body_len,of_ogg); } /* * There is a bug of some sort here. The kernel reports, via * AUDIO_GETENC, that (at least on my test machine) it supports 32-bit * SLINEAR_LE, but when I try to set that it gets rejected. So I've * made this insist on 16-bit. I don't know whether the bug is that * it's incorrectly advertising 32-bit or that it's incorrectly * rejecting it, but, except when it comes to actually fixing it, * there's not much difference. */ static void setup_audio(void) { int i; audio_encoding_t enc; audio_encoding_t pref; int any; audio_info_t ai; void *mmrv; if (ioctl(afd,AUDIO_GETPROPS,&i) < 0) { fprintf(stderr,"%s: can't get audio properties: %s\n",__progname,strerror(errno)); exit(1); } if (! (i & AUDIO_PROP_CAPTURE)) { fprintf(stderr,"%s: audio device not capable of recording\n",__progname); exit(1); } i = 0; any = 0; while (1) { enc.index = i; if (ioctl(afd,AUDIO_GETENC,&enc) < 0) break; fprintf(stderr,"encoding [%d]: %.*s enc=%d prec=%d %s\n",i,(int)sizeof(enc.name),&enc.name[0],enc.encoding,enc.precision,(enc.flags&AUDIO_ENCODINGFLAG_EMULATED)?"emulated":"native"); switch (enc.encoding) { case AUDIO_ENCODING_SLINEAR_LE: case AUDIO_ENCODING_SLINEAR_BE: case AUDIO_ENCODING_ULINEAR_LE: case AUDIO_ENCODING_ULINEAR_BE: if (enc.precision == 16) { if (!any || audio_prefer(&enc,&pref)) { pref = enc; any = 1; } } break; } i ++; } if (i == 0) { fprintf(stderr,"%s: no audio encodings at all found (?""?)\n",__progname); exit(1); } if (! any) { fprintf(stderr,"%s: no suitable audio encoding found\n",__progname); exit(1); } fprintf(stderr,"using encoding [%d]\n",pref.index); audio_enc = pref.encoding; // Would save pref.precision too, except see function header comment. i = 0; if (ioctl(afd,AUDIO_SETFD,&i) < 0) { fprintf(stderr,"%s: can't set half-duplex mode: %s\n",__progname,strerror(errno)); exit(1); } AUDIO_INITINFO(&ai); ai.record.sample_rate = 44100; ai.record.channels = 1; ai.record.precision = 16; ai.record.encoding = pref.encoding; ai.record.gain = AUDIO_MAX_GAIN; // XXX maybe set ai.record.port? ai.record.pause = 0; ai.monitor_gain = 0; ai.blocksize = 0; ai.mode = AUMODE_RECORD; if (ioctl(afd,AUDIO_SETINFO,&ai) < 0) { fprintf(stderr,"%s: can't set up audio device: %s\n",__progname,strerror(errno)); exit(1); } if (ioctl(afd,AUDIO_FLUSH,0) < 0) { fprintf(stderr,"%s: can't reset audio device: %s\n",__progname,strerror(errno)); exit(1); } anbufs = AUDIO_NBUFS; abufs = malloc(anbufs*sizeof(*abufs)); abufsize = 65536 - sizeof(int); for (i=anbufs-1;i>=0;i--) { mmrv = mmap(0,abufsize+sizeof(int),PROT_READ|PROT_WRITE,MAP_ANON|MAP_SHARED,-1,0); if (mmrv == MAP_FAILED) { fprintf(stderr,"%s: mmap audio buffer [%d]: %s\n",__progname,i,strerror(errno)); exit(1); } abufs[i] = mmrv; } abufown = malloc(anbufs*sizeof(*abufown)); nabufown = 0; #ifdef AUDIO_CAN_PARTIAL apartlen = 0; #endif vorbis_info_init(&aenci); i = vorbis_encode_init_vbr(&aenci,1,44100,1); if (i) { fprintf(stderr,"%s: vorbis_encode_init_vbr failed: %s\n",__progname,vorb_strerror(i)); exit(1); } vorbis_comment_init(&aencc); vorbis_comment_add_tag(&aencc,"ENCODER","avrec"); vorbis_analysis_init(&aencd,&aenci); vorbis_block_init(&aencd,&aencb); ogg_stream_init(&oggss,time(0)); { ogg_packet h1; ogg_packet h2; ogg_packet h3; vorbis_analysis_headerout(&aencd,&aencc,&h1,&h2,&h3); dump_vorbis_packet(&h1); dump_vorbis_packet(&h2); dump_vorbis_packet(&h3); ogg_stream_packetin(&oggss,&h1); ogg_stream_packetin(&oggss,&h2); ogg_stream_packetin(&oggss,&h3); while (ogg_stream_flush(&oggss,&oggpg)) dump_ogg_block(&oggpg); } } static void setup_pipes(void) { int p[2]; if (pipe(&p[0]) < 0) { fprintf(stderr,"%s: pipe: %s\n",__progname,strerror(errno)); exit(1); } go_p = p[1]; go_c = p[0]; if ( (socketpair(AF_LOCAL,SOCK_STREAM,0,&apipefd[0]) < 0) || (socketpair(AF_LOCAL,SOCK_STREAM,0,&vpipefd[0]) < 0) ) { fprintf(stderr,"%s: AF_LOCAL SOCK_STREAM socketpair: %s\n",__progname,strerror(errno)); exit(1); } set_nbio(apipefd[0]); set_nbio(apipefd[1]); set_nbio(vpipefd[0]); set_nbio(vpipefd[1]); } static void fork_process(void (*fn)(void), const char *what) { pid_t kid; KID *k; fflush(0); kid = fork(); if (kid < 0) { fprintf(stderr,"%s: fork (for %s): %s\n",__progname,what,strerror(errno)); exit(1); } if (kid == 0) { close(intfd_p); signal(SIGINT,SIG_IGN); mytag = what; (*fn)(); _exit(1); } k = malloc(sizeof(KID)); k->pid = kid; k->what = what; k->link = kids; kids = k; } static int wtest_poq(void *arg __attribute__((__unused__))) { return(aio_oq_nonempty(&poq)); } static void collector_rd_a(void *arg __attribute__((__unused__))) { ABUFNO abns[64]; int nr; #ifdef AUDIO_CAN_PARTIAL if (apartlen) bcopy(&apartial,&abns[0],apartlen); nr = read(cpipe,apartlen+(char *)&abns[0],sizeof(abns)-apartlen); #else nr = read(cpipe,&abns[0],sizeof(abns)); #endif if (nr < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: %s read from collector: %s\n",__progname,mytag,strerror(errno)); exit(1); } #ifdef AUDIO_CAN_PARTIAL nr += apartlen; apartlen = nr % sizeof(ABUFNO); nr /= sizeof(ABUFNO); if (apartlen) bcopy(&abns[nr],&apartial,apartlen); #else nr /= sizeof(ABUFNO); #endif for (nr--;nr>=0;nr--) { if (nabufown >= anbufs) abort(); abufown[nabufown++] = abns[nr]; // printf("reader got audio buffer %d\n",(int)abns[nr]); } } static void collector_rd_v(void *arg __attribute__((__unused__))) { VBUFNO vbns[64]; int nr; #ifdef VIDEO_CAN_PARTIAL if (vpartlen) bcopy(&vpartial,&vbns[0],vpartlen); nr = read(cpipe,vpartlen+(char *)&vbns[0],sizeof(vbns)-vpartlen); #else nr = read(cpipe,&vbns[0],sizeof(vbns)); #endif if (nr < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: %s read from collector: %s\n",__progname,mytag,strerror(errno)); exit(1); } #ifdef VIDEO_CAN_PARTIAL nr += vpartlen; vpartlen = nr % sizeof(VBUFNO); nr /= sizeof(VBUFNO); if (vpartlen) bcopy(&vbns[nr],&vpartial,vpartlen); #else nr /= sizeof(VBUFNO); #endif for (nr--;nr>=0;nr--) { struct v4l2_buffer b; if (nvbufown >= vnbufs) abort(); vbufown[nvbufown++] = vbns[nr]; b.index = vbns[nr]; b.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; b.memory = V4L2_MEMORY_MMAP; if (ioctl(vfd,VIDIOC_QBUF,&b) < 0) { fprintf(stderr,"%s: VIDIOC_QBUF (%d): %s\n",__progname,(int)b.index,strerror(errno)); exit(1); } } } static void do_writev(AIO_OQ *q, int fd, const char *tag) { int w; w = aio_oq_writev(q,fd,-1); switch (w) { case AIO_WRITEV_SPECIAL: abort(); break; case AIO_WRITEV_ERROR: switch (errno) { case EINTR: case EWOULDBLOCK: return; } fprintf(stderr,"%s: %s %s: %s\n",__progname,mytag,tag,strerror(errno)); exit(1); break; } if (w < 0) { fprintf(stderr,"%s: %s %s returned weird status %d\n",__progname,mytag,tag,w); exit(1); } aio_oq_dropdata(q,w); } static void collector_wr(void *arg __attribute__((__unused__))) { do_writev(&poq,cpipe,"writev to collector"); } static int audio_rtest(void *arg __attribute__((__unused__))) { return(nabufown>0); } static void audio_rd(void *arg __attribute__((__unused__))) { ABUFNO bn; int fill; if (nabufown < 1) abort(); bn = abufown[--nabufown]; if (bn >= anbufs) abort(); // printf("audio read using audio buffer [%d] = %d\n",nabufown,(int)bn); fill = read(afd,abufs[bn]+sizeof(int),abufsize); if (fill < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: audio read: %s\n",__progname,strerror(errno)); exit(1); } if (fill == 0) { fprintf(stderr,"%s: audio read EOF\n",__progname); exit(1); } // This next test assumes record.precision is 16.... if (fill & 1) { fprintf(stderr,"%s: audio read: odd length %d\n",__progname,fill); exit(1); } bcopy(&fill,abufs[bn],sizeof(int)); aio_oq_queue_copy(&poq,&bn,sizeof(ABUFNO)); } static void reader_sigint(void *arg __attribute__((__unused__))) { fprintf(stderr,"%s dying from SIGINT\n",mytag); exit(0); } static void audio_reader(void) { close(apipefd[0]); close(vpipefd[0]); close(vpipefd[1]); close(go_p); cpipe = apipefd[1]; if (read(go_c,&go_p,1) != 1) _exit(0); close(go_c); close(vfd); aio_poll_init(); aio_add_poll(intfd_c,&aio_rwtest_always,&aio_rwtest_never,&reader_sigint,0,0); aio_add_poll(afd,&audio_rtest,&aio_rwtest_never,&audio_rd,0,0); aio_oq_init(&poq); aio_add_poll(cpipe,&aio_rwtest_always,&wtest_poq,&collector_rd_a,&collector_wr,0); aio_event_loop(); exit(0); } static void start_video(void) { int i; i = V4L2_BUF_TYPE_VIDEO_CAPTURE; // XXX undocumented! if (ioctl(vfd,VIDIOC_STREAMON,&i) < 0) { fprintf(stderr,"%s: VIDIOC_STREAMON: %s\n",__progname,strerror(errno)); exit(1); } } static int video_rtest(void *arg __attribute__((__unused__))) { return(nvbufown>0); } static void video_rd(void *arg __attribute__((__unused__))) { VBUFNO bn; struct v4l2_buffer b; if (nvbufown < 1) abort(); bn = vbufown[--nvbufown]; if (bn >= vnbufs) abort(); b.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // XXX undocumented! b.memory = V4L2_MEMORY_MMAP; // XXX undocumented! if (ioctl(vfd,VIDIOC_DQBUF,&b) < 0) { fprintf(stderr,"%s: VIDIOC_DQBUF: %s\n",__progname,strerror(errno)); exit(1); } bn = b.index; aio_oq_queue_copy(&poq,&bn,sizeof(VBUFNO)); } static void video_reader(void) { close(vpipefd[0]); close(apipefd[0]); close(apipefd[1]); close(go_p); cpipe = vpipefd[1]; if (read(go_c,&go_p,1) != 1) _exit(0); close(go_c); close(afd); aio_poll_init(); aio_oq_init(&poq); aio_add_poll(intfd_c,&aio_rwtest_always,&aio_rwtest_never,&reader_sigint,0,0); aio_add_poll(vfd,&video_rtest,&aio_rwtest_never,&video_rd,0,0); aio_add_poll(cpipe,&aio_rwtest_always,&wtest_poq,&collector_rd_v,&collector_wr,0); start_video(); aio_event_loop(); exit(0); } static int wtest_ao(void *arg __attribute__((__unused__))) { return(aio_oq_nonempty(&aoq)); } static void coll_wr_a(void *arg __attribute__((__unused__))) { do_writev(&aoq,apipe,"writev to audio reader"); } static void vorb_postprocess(void) { while (vorbis_analysis_blockout(&aencd,&aencb) == 1) { ogg_packet opkt; vorbis_analysis(&aencb,0); vorbis_bitrate_addblock(&aencb); while (vorbis_bitrate_flushpacket(&aencd,&opkt)) { dump_vorbis_packet(&opkt); ogg_stream_packetin(&oggss,&opkt); while (ogg_stream_pageout(&oggss,&oggpg)) { dump_ogg_block(&oggpg); if (ogg_page_eos(&oggpg)) { ogg_stream_clear(&oggss); vorbis_block_clear(&aencb); vorbis_dsp_clear(&aencd); vorbis_comment_clear(&aencc); vorbis_info_clear(&aenci); fflush(0); exit(0); } } } } } static void process_audio_buffers(ABUFNO *bv, int n) { int i; ABUFNO b; int l; float **aebuf; int j; unsigned char *bp; for (i=0;i= anbufs) abort(); bcopy(abufs[b],&l,sizeof(int)); // printf("audio buffer [%d] len %d\n",b,l); // This next test assumes precision is 16.... if (l & 1) { fprintf(stderr,"%s: %s: odd length %d\n",__progname,__func__,l); exit(1); } fwrite(abufs[b]+sizeof(int),1,l,of_ra); aebuf = vorbis_analysis_buffer(&aencd,l/2); bp = abufs[b] + sizeof(int); for (j=0;j>1] = (bp[0] + ((bp[1] ^ 0x80) * 256) - 32768) / 32768.0; break; case AUDIO_ENCODING_SLINEAR_BE: aebuf[0][j>>1] = (bp[1] + ((bp[0] ^ 0x80) * 256) - 32768) / 32768.0; break; case AUDIO_ENCODING_ULINEAR_LE: aebuf[0][j>>1] = (bp[0] + (bp[1] * 256) - 32768) / 32768.0; break; case AUDIO_ENCODING_ULINEAR_BE: aebuf[0][j>>1] = (bp[1] + (bp[0] * 256) - 32768) / 32768.0; break; default: abort(); break; } bp += 2; } vorbis_analysis_wrote(&aencd,l/2); vorb_postprocess(); } } static void process_video_buffers(VBUFNO *bv, int n) { int i; VBUFNO b; for (i=0;i= vnbufs) abort(); fwrite(vbufmem[b],1,vbufs[b].length,of_rv); // printf("video buffer [%d]\n",b); } } static void coll_rd_a(void *arg __attribute__((__unused__))) { ABUFNO abns[64]; int nr; #ifdef AUDIO_CAN_PARTIAL if (apartlen) bcopy(&apartial,&abns[0],apartlen); nr = read(apipe,apartlen+(char *)&abns[0],sizeof(abns)-apartlen); #else nr = read(apipe,&abns[0],sizeof(abns)); #endif if (nr < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: %s collector read from audio reader: %s\n",__progname,mytag,strerror(errno)); exit(1); } #ifdef AUDIO_CAN_PARTIAL nr += apartlen; apartlen = nr % sizeof(ABUFNO); nr /= sizeof(ABUFNO); if (apartlen) bcopy(&abns[nr],&apartial,apartlen); #else nr /= sizeof(ABUFNO); #endif process_audio_buffers(&abns[0],nr); // { int i; // printf("collector returning audio buffers:"); // for (i=0;i=0;i--) { ab = i; aio_oq_queue_copy(&aoq,&ab,sizeof(ab)); } for (i=vnbufs-1;i>=0;i--) { vb = i; aio_oq_queue_copy(&voq,&vb,sizeof(vb)); } } static void collector_sigint(void *arg __attribute__((__unused__))) { fprintf(stderr,"collector got SIGINT, flushing and exiting\n"); vorbis_analysis_wrote(&aencd,0); vorb_postprocess(); fprintf(stderr,"*** vorb_postprocess didn't exit!\n"); exit(1); } static void collector(void) { close(apipefd[1]); close(vpipefd[1]); close(go_p); apipe = apipefd[0]; vpipe = vpipefd[0]; if (read(go_c,&go_p,1) != 1) _exit(0); close(go_c); close(afd); close(vfd); aio_poll_init(); aio_oq_init(&aoq); aio_oq_init(&voq); aio_add_poll(intfd_c,&aio_rwtest_always,&aio_rwtest_never,&collector_sigint,0,0); aio_add_poll(apipe,&aio_rwtest_always,&wtest_ao,&coll_rd_a,&coll_wr_a,0); aio_add_poll(vpipe,&aio_rwtest_always,&wtest_vo,&coll_rd_v,&coll_wr_v,0); queue_initial(); aio_event_loop(); } static void fork_audio_reader(void) { fork_process(&audio_reader,"audio reader"); } static void fork_video_reader(void) { fork_process(&video_reader,"video reader"); } static void fork_collector(void) { fork_process(&collector,"collector"); } static void go(void) { write(go_p,"xxx",3); close(go_p); close(go_c); } static void await(void) { pid_t dead; int status; KID *k; int s; while <"wait"> (1) { if (interrupted) { if (intfd_p >= 0) { fprintf(stderr,"await interrupted, closing intfd\n"); close(intfd_p); intfd_p = -1; } } dead = wait(&status); if (dead < 0) { if (errno == EINTR) continue; if (interrupted && (errno == ECHILD)) exit(0); fprintf(stderr,"%s: wait: %s\n",__progname,strerror(errno)); break; } for (k=kids;k;k=k->link) { if (dead == k->pid) { fprintf(stderr,"%s: %s child died: ",__progname,k->what); if (WIFEXITED(status)) { fprintf(stderr,"exit %d\n",WEXITSTATUS(status)); k->pid = -1; } else if (WIFSIGNALED(status)) { s = WTERMSIG(status); fprintf(stderr,"signal %d",s); if ((s >= 0) || (s < NSIG)) fprintf(stderr," [%s]",sys_signame[s]); if (WCOREDUMP(status)) fprintf(stderr," (core dumped)"); fprintf(stderr,"\n"); k->pid = -1; } else if (WIFSTOPPED(status)) { s = WSTOPSIG(status); fprintf(stderr,"stopped (impossible!) on signal %d",s); if ((s >= 0) || (s < NSIG)) fprintf(stderr," [%s]",sys_signame[s]); fprintf(stderr,"\n"); kill(k->pid,SIGKILL); k->pid = -1; } if (! interrupted) { for (k=kids;k;k=k->link) { if (k->pid >= 0) { kill(k->pid,SIGKILL); k->pid = -1; } } break <"wait">; } } } } } static void handle_sigint(int sig __attribute__((__unused__))) { write(2,"SIGINT\n",7); interrupted = 1; } static void setup_sigint(void) { struct sigaction sa; int p[2]; if (pipe(&p[0]) < 0) { fprintf(stderr,"%s: SIGINT-handling pipe: %s\n",__progname,strerror(errno)); exit(1); } intfd_p = p[1]; intfd_c = p[0]; interrupted = 0; sa.sa_handler = &handle_sigint; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGINT,&sa,0); } static FILE *open_output(const char *fn) { FILE *f; f = fopen(fn,"w"); if (! f) { fprintf(stderr,"%s: can't open %s: %s\n",__progname,fn,strerror(errno)); exit(1); } return(f); } static void open_outputs(void) { of_ra = open_output("z.ra"); of_rv = open_output("z.rv"); of_vorbis = open_output("z.vorbis"); of_ogg = open_output("z.ogg"); } int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); open_outputs(); setup_sigint(); open_audio(); setup_audio(); open_video(); setup_video(); setup_pipes(); fork_audio_reader(); fork_video_reader(); fork_collector(); close(intfd_c); go(); await(); return(0); }