1 /*
2  *  lavpipe - combines several input streams and pipes them trough
3  *            arbitrary filters in order to finally output a resulting
4  *            video stream based on a given "recipe" (pipe list)
5  *
6  *  Copyright (C) 2001, pHilipp Zabel <pzabel@gmx.de>
7  *  Copyright (C) 2001, Matthew Marjanovic <maddog@mir.com>
8  *
9  *  This program is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU General Public License
11  *  as published by the Free Software Foundation; either version 2
12  *  of the License, or (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <ctype.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <signal.h>
37 
38 #include "mjpeg_logging.h"
39 #include "pipelist.h"
40 #include "yuv4mpeg.h"
41 
usage(void)42 static void usage(void)
43 {
44   fprintf(
45   stderr,
46   "Usage: lavpipe [options] <pipe list>\n"
47   "Options: -o num   Frame offset - skip num frames in the beginning\n"
48   "                  if num is negative, all but the last num frames are skipped\n"
49   "         -n num   Only num frames are processed (0 means all frames)\n"
50   "         -v num  Verbosity of output [0..2]\n"
51   );
52 }
53 
54 static
parse_spawn_command(char * cmdline)55 char **parse_spawn_command(char *cmdline)
56 {
57   char **argv;
58   char *p = cmdline;
59   int i, argc = 0;
60 
61   if (p == NULL) return NULL;
62   if (*p == '\0') return NULL;
63 
64   argc = 0;
65   while (*p != '\0') {
66     while (!isspace(*p)) {
67       p++;
68       if (*p == '\0') {
69 	argc++;
70 	goto END_OF_LINE;
71       }
72     }
73     argc++;
74     while (isspace(*p)) {
75       p++;
76       if (*p == '\0') goto END_OF_LINE;
77     }
78   }
79 
80  END_OF_LINE:
81   argv = malloc(argc+1 * sizeof(argv[0]));
82 
83   for (p = cmdline, i=0; i < argc; i++) {
84     argv[i] = p;
85     while (!isspace(*(++p)));
86     p[0] = '\0';
87     while (isspace(*(++p)))
88       if (p[0] == '\0') break;
89   }
90 
91   argv[argc] = NULL;
92   return argv;
93 }
94 
95 static
fork_child_sub(char * command,int * fd_in,int * fd_out)96 pid_t fork_child_sub(char *command, int *fd_in, int *fd_out)
97 {
98   int n;
99   int pipe_in[2];
100   int pipe_out[2];
101   char **myargv;
102   pid_t pid;
103 
104   if (fd_in) {
105     if (pipe(pipe_in)) {
106       mjpeg_error_exit1( "Couldn't create input pipe from %s", command);
107     }
108   }
109   if (fd_out) {
110     if (pipe(pipe_out)) {
111       mjpeg_error_exit1( "Couldn't create output pipe to %s", command);
112     }
113   }
114   if ((pid = fork ()) < 0) {
115     mjpeg_error_exit1("Couldn't fork %s", command);
116   }
117 
118   if (pid == 0) {
119     /* child redirects stdout to pipe_in */
120     if (fd_in) {
121       close(pipe_in[0]);
122       close(1);
123       n = dup(pipe_in[1]);
124       if (n != 1) exit (1);
125     }
126     /* child redirects stdin to pipe_out */
127     if (fd_out) {
128       close(pipe_out[1]);
129       close(0);
130       n = dup(pipe_out[0]);
131       if (n != 0) exit(1);
132     }
133     myargv = parse_spawn_command(command);
134     execvp(myargv[0], myargv);
135     return -1;
136   } else {
137     /* parent */
138     if (fd_in != NULL) {
139       close(pipe_in[1]);
140       *fd_in = pipe_in[0];
141     }
142     if (fd_out != NULL) {
143       close(pipe_out[0]);
144       *fd_out = pipe_out[1];
145     }
146     return pid;
147   }
148 }
149 
150 
fork_child(const char * command,int offset,int num,int * fd_in,int * fd_out)151 static pid_t fork_child(const char *command,
152 			int offset, int num,
153 			int *fd_in, int *fd_out)
154 {
155   char tmp1[1024], tmp2[1024];
156   char *current = tmp1;
157   char *next = tmp2;
158   char *p;
159 
160   strncpy(current, command, 1024);
161 
162   /* replace $o by offset */
163   p = strstr(current, "$o");
164   if (p) {
165     p[0] = '\0';
166     p += 2;
167     snprintf(next, 1024, "%s%d%s", current, offset, p);
168     p = current;
169     current = next;
170     next = p;
171   }
172 
173   /* replace $n by number of frames */
174   p = strstr(current, "$n");
175   if (p) {
176     p[0] = '\0';
177     p += 2;
178     snprintf(next, 1024, "%s%d%s", current, num, p);
179     p = current;
180     current = next;
181     next = p;
182   }
183 
184   mjpeg_debug( "Executing: '%s'", current);
185   return fork_child_sub(current, fd_in, fd_out);
186 }
187 
alloc_yuv_buffers(unsigned char * yuv[3],y4m_stream_info_t * si)188 static void alloc_yuv_buffers(unsigned char *yuv[3], y4m_stream_info_t *si)
189 {
190 int chroma_ss, ss_v, ss_h, w, h;
191 
192   w = y4m_si_get_width(si);
193   h = y4m_si_get_height(si);
194 
195   chroma_ss = y4m_si_get_chroma(si);
196   ss_h = y4m_chroma_ss_x_ratio(chroma_ss).d;
197   ss_v = y4m_chroma_ss_y_ratio(chroma_ss).d;
198 
199   yuv[0] = malloc (w * h * sizeof(yuv[0][0]));
200   yuv[1] = malloc ((w / ss_h) * (h / ss_v) * sizeof(yuv[1][0]));
201   yuv[2] = malloc ((w / ss_h) * (h / ss_v) * sizeof(yuv[2][0]));
202 }
203 
free_yuv_buffers(unsigned char * yuv[3])204 static void free_yuv_buffers(unsigned char *yuv[3])
205 {
206   free(yuv[0]);
207   free(yuv[1]);
208   free(yuv[2]);
209   yuv[0] = yuv[1] = yuv[2] = NULL;
210 }
211 
init_pipe_source(pipe_source_t * ps,char * command)212 static void init_pipe_source(pipe_source_t *ps, char *command)
213 {
214   ps->command = strdup(command);
215   y4m_init_stream_info(&(ps->streaminfo));
216   y4m_init_frame_info(&(ps->frameinfo));
217   ps->pid = -1;
218   ps->fd = -1;
219   ps->frame_num = 0;
220 }
221 
fini_pipe_source(pipe_source_t * ps)222 static void fini_pipe_source(pipe_source_t *ps)
223 {
224   free(ps->command);
225   y4m_fini_stream_info(&(ps->streaminfo));
226   y4m_fini_frame_info(&(ps->frameinfo));
227 }
228 
spawn_pipe_source(pipe_source_t * ps,int offset,int count)229 static void spawn_pipe_source(pipe_source_t *ps, int offset, int count)
230 {
231   ps->pid = fork_child(ps->command,
232 		       offset, count,
233 		       &(ps->fd), NULL);
234   ps->frame_num = offset;
235   mjpeg_debug("spawned source '%s'", ps->command);
236 }
237 
decommission_pipe_source(pipe_source_t * source)238 static void decommission_pipe_source(pipe_source_t *source)
239 {
240   if (source->fd >= 0) {
241     close(source->fd);
242     source->fd = -1;
243   }
244   if (source->pid > 0) {
245     mjpeg_debug("DIE DIE DIE pid %d", source->pid);
246     kill(source->pid, SIGINT);
247     source->pid = -1;
248   }
249 }
250 
init_pipe_filter(pipe_filter_t * pf,const char * command)251 static void init_pipe_filter(pipe_filter_t *pf, const char *command)
252 {
253   pf->command = strdup(command);
254   y4m_init_stream_info(&(pf->out_streaminfo));
255   y4m_init_stream_info(&(pf->in_streaminfo));
256   y4m_init_frame_info(&(pf->frameinfo));
257   pf->yuv[0] = pf->yuv[1] = pf->yuv[2] = NULL;
258   pf->pid = -1;
259   pf->out_fd = -1;
260   pf->in_fd = -1;
261 }
262 
fini_pipe_filter(pipe_filter_t * pf)263 static void fini_pipe_filter(pipe_filter_t *pf)
264 {
265   free(pf->command);
266   free_yuv_buffers(pf->yuv);
267   y4m_fini_stream_info(&(pf->out_streaminfo));
268   y4m_fini_stream_info(&(pf->in_streaminfo));
269   y4m_fini_frame_info(&(pf->frameinfo));
270 }
271 
spawn_pipe_filter(pipe_filter_t * pf,int offset,int count)272 static void spawn_pipe_filter(pipe_filter_t *pf, int offset, int count)
273 {
274   pf->pid = fork_child(pf->command,
275 		       offset, count,
276 		       &(pf->in_fd), &(pf->out_fd));
277   mjpeg_debug("spawned filter '%s'", pf->command);
278 }
279 
decommission_pipe_filter(pipe_filter_t * filt)280 static void decommission_pipe_filter(pipe_filter_t *filt)
281 {
282   if (filt->in_fd >= 0) {
283     close(filt->in_fd);
284     filt->in_fd = -1;
285   }
286   if (filt->out_fd >= 0) {
287     close(filt->out_fd);
288     filt->out_fd = -1;
289   }
290   if (filt->pid > 0) {
291     mjpeg_debug("DIE DIE DIE pid %d", filt->pid);
292     kill(filt->pid, SIGINT);
293     filt->pid = -1;
294   }
295   free_yuv_buffers(filt->yuv);
296 }
297 
298 /*
299  * make sure all the sources needed for this segment are cued up
300  *  and ready to produce frames
301  *
302  */
303 
304 static
open_segment_inputs(PipeSegment * seg,pipe_filter_t * filt,int frame,int segnum,int total_frames,PipeList * pl,commandline_params_t * cl,pipe_source_t * sources)305 void open_segment_inputs(PipeSegment *seg, pipe_filter_t *filt,
306 			 int frame, int segnum, int total_frames,
307 			 PipeList *pl, commandline_params_t *cl,
308 			 pipe_source_t *sources)
309 {
310   int i, j, k;
311 
312   for (i = 0; i < seg->input_count; i++) {
313     int in_index = seg->input_index[i];
314     int in_offset = seg->input_offset[i];
315     pipe_source_t *source = &(sources[in_index]);
316 
317     mjpeg_debug("OSI:  input %d == source %d: '%s'",
318 		i, in_index, source->command);
319 
320     /* spawn the source if not already running */
321     if (source->fd < 0) {
322 
323       /* calculate # of frames we want to get from this stream */
324       /* need to look if we can use this in successive sequences and
325 	 what param_frames is */
326 
327       int offset = in_offset + frame;
328       int count = seg->frame_count - frame; /* until end of sequence */
329 
330       for (j = segnum + 1; j < pl->segment_count; j++) {
331 	PipeSegment *other = pl->segments[j];
332 	/*	mjpeg_debug("checking  i %d   j %d", i, j); */
333 	for (k = 0; k < other->input_count; k++) {
334 	  /*	  mjpeg_debug("checking  i %d   j %d  k %d", i, j, k); */
335 	  if (in_index == other->input_index[k]) {
336 	    if ((offset + count) == other->input_offset[k]) {
337 	      count += other->frame_count; /* add another sequence */
338 	    } else
339 	      goto FINISH_CHECK; /* need to reopen with other offset */
340 	  } else
341 	    goto FINISH_CHECK; /* stream will not be used in
342 				  segment j anymore */
343 	}
344       }
345     FINISH_CHECK:
346       /*      mjpeg_debug("finish-check  i %d   j %d  k %d", i, j, k); */
347       if (count > cl->frames - total_frames) {
348 	count = cl->frames - total_frames;
349       }
350 
351       /******** why have 'count'?  let the source keep making frames...
352 	 ...we'll just kill it when we are done anyway! *********/
353       /*      spawn_pipe_source(source, offset, count);*/
354       spawn_pipe_source(source, offset, 0);
355 
356       if (y4m_read_stream_header(source->fd, &(source->streaminfo)) != Y4M_OK)
357 	mjpeg_error_exit1("Bad source header!");
358 
359       mjpeg_debug("read header");
360       y4m_log_stream_info(mjpeg_loglev_t("debug"), "src: ", &(source->streaminfo));
361     } else {
362       mjpeg_debug("...source %d is still alive.", in_index);
363     }
364 
365     if (i == 0) {
366       /* first time:  copy stream info to filter */
367       y4m_copy_stream_info(&(filt->out_streaminfo), &(source->streaminfo));
368       mjpeg_debug("copied info");
369     } else {
370       /* n-th time:  make sure source streams match */
371       if (y4m_si_get_width(&(filt->out_streaminfo)) !=
372 	  y4m_si_get_width(&(source->streaminfo)))
373 	mjpeg_error_exit1("Stream mismatch:  frame width");
374       if (y4m_si_get_height(&(filt->out_streaminfo)) !=
375 	  y4m_si_get_height(&(source->streaminfo)))
376 	mjpeg_error_exit1("Stream mismatch:  frame height");
377       if (y4m_si_get_interlace(&(filt->out_streaminfo)) !=
378 	  y4m_si_get_interlace(&(source->streaminfo)))
379 	mjpeg_error_exit1("Stream mismatch:  interlace");
380       mjpeg_debug("verified info");
381     }
382   }
383 
384 }
385 
386 static
setup_segment_filter(PipeSegment * seg,pipe_filter_t * filt,int frame)387 void setup_segment_filter(PipeSegment *seg, pipe_filter_t *filt, int frame)
388 {
389   mjpeg_debug("OSO:  '%s'", filt->command);
390   if (strcmp(filt->command, "-")) {
391     /* ...via a filter command:
392      *     o spawn filter process
393      *     o write source stream info to filter
394      *     o read filter's result stream info
395      *     o alloc yuv buffers for source->filter transfers
396      */
397     /* ... why does the 'count' matter, if lavpipe controls the frame
398      *      flow anyway???????? */
399     spawn_pipe_filter(filt, frame, (seg->frame_count - frame));
400     y4m_write_stream_header(filt->out_fd, &(filt->out_streaminfo));
401     y4m_read_stream_header(filt->in_fd, &(filt->in_streaminfo));
402     mjpeg_debug("SSF:  read filter result stream header");
403     y4m_log_stream_info(mjpeg_loglev_t("debug"), "result: ", &(filt->in_streaminfo));
404     alloc_yuv_buffers(filt->yuv, &(filt->out_streaminfo));
405   } else {
406     /* ...no filter; direct output:
407      *     o result stream info is just a copy of the source stream info
408      */
409     filt->out_fd = filt->in_fd = -1;
410     y4m_copy_stream_info(&(filt->in_streaminfo), &(filt->out_streaminfo));
411     mjpeg_debug("SSF:  copied source header");
412   }
413 }
414 
415 
416 static
process_segment_frames(pipe_sequence_t * ps,int segnum,int * frame,int * total_frames)417 void process_segment_frames(pipe_sequence_t *ps, int segnum,
418 			    int *frame, int *total_frames)
419 {
420   pipe_source_t *sources = ps->sources;
421   pipe_filter_t *the_output = &(ps->output);
422   PipeList *pl = &(ps->pl);
423   commandline_params_t *cl = &(ps->cl);
424 
425   PipeSegment *seg = ps->pl.segments[segnum];
426   pipe_filter_t *filt = &(ps->filters[segnum]);
427 
428 
429   mjpeg_debug("PSF:  segment %d,  initial frame = %d", segnum, *frame);
430   while (*frame < seg->frame_count) {
431     int i;
432 
433     for (i = 0; i < seg->input_count; i++) {
434       int in_index = seg->input_index[i];
435       pipe_source_t *source = &(sources[in_index]);
436       unsigned char **yuv;
437 
438       if (filt->out_fd >= 0)
439         /* filter present; write/read through filter's buffer first */
440 	yuv = filt->yuv;
441       else
442 	/* no filter present; source -> direct to output buffer */
443 	yuv = the_output->yuv;
444 
445       mjpeg_debug("read frame %03d > input %d, src %d  fd = %d",
446 		  *frame, i, in_index, source->fd);
447       if (y4m_read_frame(source->fd,
448 			 &(source->streaminfo), &(source->frameinfo),
449 			 yuv) != Y4M_OK) {
450 	int err = errno;
451 	mjpeg_error("ERRNO says:  %s", strerror(err));
452 	mjpeg_error_exit1("lavpipe: input stream error in stream %d,"
453 			  "sequence %d, frame %d",
454 			  i, segnum, *frame);
455       }
456       source->frame_num += 1;
457 
458       if (filt->out_fd >= 0)
459 	y4m_write_frame(filt->out_fd,
460 			&(filt->out_streaminfo), &(filt->frameinfo),
461 			yuv);
462     }
463 
464     if (filt->in_fd >= 0) {
465       if (y4m_read_frame(filt->in_fd,
466 			 &(filt->in_streaminfo), &(filt->frameinfo),
467 			 the_output->yuv) != Y4M_OK) {
468 	mjpeg_error_exit1( "lavpipe: filter stream error in sequence %d,"
469 			   "frame %d",
470 			   segnum, *frame);
471       }
472     }
473 
474     /* output result */
475     y4m_write_frame(the_output->out_fd,
476 		    &(the_output->out_streaminfo),
477 		    &(the_output->frameinfo),
478 		    the_output->yuv);
479 
480     (*frame)++;
481     if (++(*total_frames) == cl->frames) {
482       segnum = pl->segment_count - 1; /* = close all input files below */
483       break;
484     }
485   }
486 }
487 
488 /*
489  * this is just being picky, but...
490  *
491  * Close all sources used by current segment, but only if they will
492  *  cannot be used in current state by upcoming segments.
493  *
494  */
495 
496 static
close_segment_inputs(pipe_sequence_t * ps,int segnum,int frame)497 void close_segment_inputs(pipe_sequence_t *ps, int segnum, int frame)
498 {
499   PipeList *pl = &(ps->pl);
500   pipe_source_t *sources = ps->sources;
501   PipeSegment *seg = pl->segments[segnum];
502   int i;
503 
504   for (i = 0; i < seg->input_count; i++) {
505     int current_index = seg->input_index[i];
506     pipe_source_t *source = &(sources[current_index]);
507 
508     if (source->fd >= 0) {
509       /* if it's still alive...
510        * ...iterate over remaining segments, and see if they can
511        *    use this source.
512        */
513       int s;
514       for (s = segnum + 1; s < pl->segment_count; s++) {
515 	int j;
516 	PipeSegment *next_seg = pl->segments[s];
517 
518 	for (j = 0; j < next_seg->input_count; j++) {
519 	  int index = next_seg->input_index[j];
520 	  int offset = next_seg->input_offset[j];
521 
522 	  if ( (index == current_index) &&
523 	       (offset == source->frame_num) ) {
524 	    /* A segment input offset matches the current frame...
525 	     * ...this source can still be used.
526 	     */
527 	    mjpeg_info("allowing input %d (source %d) to live",
528 		       i, current_index);
529 	    goto KEEP_SOURCE;
530 	  }
531 	}
532       }
533       mjpeg_info( "closing input %d (source %d)", i, current_index);
534       decommission_pipe_source(source);
535 KEEP_SOURCE: ;
536     }
537   }
538 }
539 
540 static
parse_command_line(int argc,char * argv[],commandline_params_t * cl)541 void parse_command_line(int argc, char *argv[], commandline_params_t *cl)
542 {
543   char c;
544   int err;
545 
546   cl->verbose = 1;
547   cl->offset = 0;
548   cl->frames = 0;
549   cl->listfile = NULL;
550 
551   err = 0;
552   while ((c = getopt(argc, argv, "o:n:v:")) != EOF) {
553     switch (c) {
554     case 'o':
555       cl->offset = atoi(optarg);
556       break;
557     case 'n':
558       cl->frames = atoi(optarg);
559       break;
560     case 'v':
561       cl->verbose = atoi(optarg);
562       if ( (cl->verbose < 0) || (cl->verbose > 2) ) {
563 	usage();
564 	exit(1);
565       }
566       break;
567     default:
568       err++;
569     }
570   }
571   if ((optind >= argc) || (err)) {
572     usage();
573     exit(1);
574   }
575   cl->listfile = strdup(argv[optind]);
576 }
577 
578 static
initialize_pipe_sequence(pipe_sequence_t * ps,int argc,char ** argv)579 void initialize_pipe_sequence(pipe_sequence_t *ps, int argc, char **argv)
580 {
581   int i;
582   commandline_params_t *cl = &(ps->cl);
583   PipeList *pl = &(ps->pl);
584 
585   init_pipe_filter(&(ps->output), "");
586   ps->output.out_fd = 1;
587 
588   /* parse command-line arguments */
589   parse_command_line(argc, argv, cl);
590 
591   /* set-up logging */
592   (void)mjpeg_default_handler_verbosity(cl->verbose);
593 
594   /* read pipe 'recipe' */
595   if (read_pipe_list(cl->listfile, pl) < 0) {
596     mjpeg_error_exit1( "lavpipe: couldn't open \"%s\"", cl->listfile);
597   }
598 
599   /* a negative offset means "from the end" */
600   if (cl->offset < 0) {
601     cl->offset = pl->frame_count + cl->offset;
602   }
603   if ((cl->offset >= pl->frame_count) ||
604       (cl->offset < 0)) {
605     mjpeg_error_exit1( "error: offset greater than # of frames in input");
606   }
607 
608   /* zero frame count means "all frames" */
609   if (cl->frames == 0) {
610     cl->frames = pl->frame_count - cl->offset;
611   }
612   if ((cl->offset + cl->frames) > pl->frame_count) {
613     mjpeg_warn( "input too short for -n %d", cl->frames);
614     cl->frames = pl->frame_count - cl->offset;
615   }
616 
617   /* initialize pipe sources */
618   ps->sources = malloc(pl->source_count * sizeof(ps->sources[0]));
619   for (i = 0; i < pl->source_count; i++)
620     init_pipe_source(&(ps->sources[i]), pl->source_cmd[i]);
621 
622   /* initialize pipe filters */
623   ps->filters = malloc(pl->segment_count * sizeof(ps->filters[0]));
624   for (i = 0; i < pl->segment_count; i++)
625     init_pipe_filter(&(ps->filters[i]), pl->segments[i]->output_cmd);
626 
627 }
628 
629 static
process_pipe_sequence(pipe_sequence_t * ps)630 void process_pipe_sequence(pipe_sequence_t *ps)
631 {
632   int segm_num;       /* current segment number */
633   int segm_frame;     /* frame number, within a segment */
634   int sequ_frame;     /* cumulative/total frame number  */
635   int first_iteration;
636 
637   /* find start segment/frame, given overall lavpipe offset ("-o") */
638   segm_frame = ps->cl.offset;
639   for (segm_num = 0;
640        segm_frame >= ps->pl.segments[segm_num]->frame_count;
641        segm_num++) {
642     segm_frame -= ps->pl.segments[segm_num]->frame_count;
643   }
644 
645   /* process the segments */
646   first_iteration = 1;
647   sequ_frame = 0;
648   while ( (segm_num < ps->pl.segment_count) &&
649 	  (sequ_frame < ps->cl.frames) ) {
650     PipeSegment *seg = ps->pl.segments[segm_num];
651     pipe_filter_t *filt = &(ps->filters[segm_num]);
652 
653     mjpeg_debug("starting segment %d, frame %d", segm_num, segm_frame);
654 
655     open_segment_inputs(seg, filt, segm_frame, segm_num, sequ_frame,
656 			&ps->pl, &ps->cl, ps->sources);
657     setup_segment_filter(seg, filt, segm_frame);
658 
659     if (first_iteration) {
660       /* Initialize the final output stream, just once */
661 
662       /* The final output stream parameters are taken from the output
663        *  parameters of the first segment's output filter.
664        * (If there is no filter (i.e. "-", direct output), then the
665        *  parameters will end up coming from the first segment's source
666        *  stream.)
667        */
668       y4m_copy_stream_info(&(ps->output.out_streaminfo),
669 			   &(filt->in_streaminfo));
670       y4m_write_stream_header(ps->output.out_fd, &(ps->output.out_streaminfo));
671       alloc_yuv_buffers(ps->output.yuv,  &(ps->output.out_streaminfo));
672       mjpeg_debug("output stream initialized");
673       first_iteration = 0;
674     } else {
675       /* For succeeding segments, make sure that the new filter's stream is
676        *  consistent with the final output stream.
677        */
678       if (y4m_si_get_width(&(filt->in_streaminfo)) !=
679 	  y4m_si_get_width(&(ps->output.out_streaminfo)))
680 	mjpeg_error_exit1("Stream mismatch:  frame width");
681       if (y4m_si_get_height(&(filt->in_streaminfo)) !=
682 	  y4m_si_get_height(&(ps->output.out_streaminfo)))
683 	mjpeg_error_exit1("Stream mismatch:  frame height");
684       if (y4m_si_get_interlace(&(filt->in_streaminfo)) !=
685 	  y4m_si_get_interlace(&(ps->output.out_streaminfo)))
686 	mjpeg_error_exit1("Stream mismatch:  interlace");
687       mjpeg_debug("filter stream verified");
688     }
689 
690     process_segment_frames(ps, segm_num, &segm_frame, &sequ_frame);
691     decommission_pipe_filter(filt);
692     close_segment_inputs(ps, segm_num, segm_frame);
693 
694     /* prepare for next sequence */
695     segm_num++;
696     segm_frame = 0;
697   }
698 }
699 
700 static
cleanup_pipe_sequence(pipe_sequence_t * ps)701 void cleanup_pipe_sequence(pipe_sequence_t *ps)
702 {
703   int i;
704   PipeList *pl = &(ps->pl);
705 
706   /* free/fini everything */
707   fini_pipe_filter(&ps->output);
708   for (i = 0; i < pl->source_count; i++)
709     fini_pipe_source(&(ps->sources[i]));
710   free(ps->sources);
711   for (i = 0; i < pl->segment_count; i++)
712     fini_pipe_filter(&(ps->filters[i]));
713   free(ps->filters);
714 }
715 
716 
main(int argc,char * argv[])717 int main (int argc, char *argv[])
718 {
719   pipe_sequence_t ps;
720 
721   initialize_pipe_sequence(&ps, argc, argv);
722   process_pipe_sequence(&ps);
723   cleanup_pipe_sequence(&ps);
724   return 0;
725 }
726