1 /*===========================================================================*
2  * combine.c								     *
3  *									     *
4  *	Procedures to combine frames or GOPS into an MPEG sequence	     *
5  *									     *
6  * EXPORTED PROCEDURES:							     *
7  *	GOPStoMPEG							     *
8  *	FramesToMPEG							     *
9  *									     *
10  *===========================================================================*/
11 
12 /*
13  * Copyright (c) 1995 The Regents of the University of California.
14  * All rights reserved.
15  *
16  * Permission to use, copy, modify, and distribute this software and its
17  * documentation for any purpose, without fee, and without written agreement is
18  * hereby granted, provided that the above copyright notice and the following
19  * two paragraphs appear in all copies of this software.
20  *
21  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
22  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
23  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
24  * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
27  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
28  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
29  * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
30  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
31  */
32 
33 /*
34  *  $Header$
35  *  $Log$
36  *  Revision 1.4  2004/04/02 15:12:40  rwcox
37  *  Cput
38  *
39  *  Revision 1.3  2003/12/23 13:50:08  rwcox
40  *  Cput
41  *
42  *  Revision 1.2  2003/12/03 14:46:14  rwcox
43  *  Cput
44  *
45  *  Revision 1.1  2001/12/17 16:11:53  rwcox
46  *  Cadd
47  *
48  *  Revision 1.9  1995/08/07 21:42:38  smoot
49  *  Sleeps when files do not exist.
50  *  renamed index to idx
51  *
52  *  Revision 1.8  1995/06/21 22:20:45  smoot
53  *  added a sleep for NFS to complete file writes
54  *
55  * Revision 1.7  1995/06/08  20:23:19  smoot
56  * added "b"'s to fopen so PCs are happy
57  *
58  * Revision 1.6  1995/01/19  23:07:22  eyhung
59  * Changed copyrights
60  *
61  * Revision 1.5  1995/01/16  07:53:55  eyhung
62  * Added realQuiet
63  *
64  * Revision 1.4  1994/11/12  02:11:46  keving
65  * nothing
66  *
67  * Revision 1.3  1994/03/15  00:27:11  keving
68  * nothing
69  *
70  * Revision 1.2  1993/12/22  19:19:01  keving
71  * nothing
72  *
73  * Revision 1.1  1993/07/22  22:23:43  keving
74  * nothing
75  *
76  */
77 
78 
79 /*==============*
80  * HEADER FILES *
81  *==============*/
82 
83 #include "all.h"
84 #include <time.h>
85 #include <errno.h>
86 #include "mtypes.h"
87 #include "frames.h"
88 #include "search.h"
89 #include "mpeg.h"
90 #include "prototypes.h"
91 #include "parallel.h"
92 #include "param.h"
93 #include "readframe.h"
94 #include "mheaders.h"
95 #include "fsize.h"
96 #include "combine.h"
97 #include <unistd.h>
98 
99 /* note, remove() might not have a prototype in the standard header files,
100  * but it really should -- it's not my fault!
101  */
102 
103 
104 static int	currentGOP;
105 
106 #define READ_ATTEMPTS 5 /* number of times (seconds) to retry an input file */
107 
108 /*==================*
109  * GLOBAL VARIABLES *
110  *==================*/
111 extern int  yuvWidth, yuvHeight;
112 char	currentGOPPath[MAXPATHLEN];
113 char	currentFramePath[MAXPATHLEN];
114 
115 
116 /*===============================*
117  * INTERNAL PROCEDURE prototypes *
118  *===============================*/
119 
120 static void	AppendFile _ANSI_ARGS_((FILE *outputFile, FILE *inputFile));
121 
122 
123 /*=====================*
124  * EXPORTED PROCEDURES *
125  *=====================*/
126 
127 /*===========================================================================*
128  *
129  * GOPStoMPEG
130  *
131  *	convert some number of GOP files into a single MPEG sequence file
132  *
133  * RETURNS:	nothing
134  *
135  * SIDE EFFECTS:    none
136  *
137  *===========================================================================*/
138 void
GOPStoMPEG(numGOPS,outputFileName,outputFilePtr)139 GOPStoMPEG(numGOPS, outputFileName, outputFilePtr)
140     int numGOPS;
141     char *outputFileName;
142     FILE *outputFilePtr;
143 {
144     register int ind;
145     BitBucket *bb;
146     char    fileName[1024];
147     char    inputFileName[1024];
148     FILE *inputFile;
149     int q;
150 
151     {
152       /* Why is this reset called? */
153       int x=Fsize_x, y=Fsize_y;
154       Fsize_Reset();
155       Fsize_Note(0, yuvWidth, yuvHeight);
156       if (Fsize_x == 0 || Fsize_y == 0) {
157 	Fsize_Note(0, x, y);
158       }}
159 
160     bb = Bitio_New(outputFilePtr);
161 
162     Mhead_GenSequenceHeader(bb, Fsize_x, Fsize_y, /* pratio */ aspectRatio,
163 	       /* pict_rate */ frameRate, /* bit_rate */ -1,
164 	       /* buf_size */ -1, /*c_param_flag */ 1,
165 	       /* iq_matrix */ customQtable, /* niq_matrix */ customNIQtable,
166 	       /* ext_data */ NULL, /* ext_data_size */ 0,
167 	       /* user_data */ NULL, /* user_data_size */ 0);
168 
169     /* it's byte-padded, so we can dump it now */
170     Bitio_Flush(bb);
171 
172     if ( numGOPS > 0 ) {
173 	for ( ind = 0; ind < numGOPS; ind++ ) {
174 	    GetNthInputFileName(inputFileName, ind);
175 	    sprintf(fileName, "%s/%s", currentGOPPath, inputFileName);
176 
177 	    for (q = 0;   q < READ_ATTEMPTS;  ++q ) {
178 	      if ( (inputFile = fopen(fileName, "rb")) != NULL ) break;
179 	      fprintf(stderr, "ERROR:  Couldn't read (GOPStoMPEG):  %s retry %d\n",
180 		      fileName, q);
181 	      fflush(stderr);
182 	      sleep(1);
183 	    }
184 	    if (q == READ_ATTEMPTS) {
185 	      fprintf(stderr, "Giving up (%d attepmts).\n", READ_ATTEMPTS);
186 	      exit(1);
187 	    }
188 
189 	    if (! realQuiet) {
190 	    	fprintf(stdout, "appending file:  %s\n", fileName);
191 	    }
192 
193 	    AppendFile(outputFilePtr, inputFile);
194 	}
195     } else {
196 	ind = 0;
197 	while ( TRUE ) {
198 	    sprintf(fileName, "%s.gop.%d", outputFileName, ind);
199 
200 	    if ( (inputFile = fopen(fileName, "rb")) == NULL ) {
201 		break;
202 	    }
203 
204 	    if (! realQuiet) {
205 	    	fprintf(stdout, "appending file:  %s\n", fileName);
206 	    }
207 
208 	    AppendFile(outputFilePtr, inputFile);
209 
210 	    ind++;
211 	}
212     }
213 
214     bb = Bitio_New(outputFilePtr);
215 
216     /* SEQUENCE END CODE */
217     Mhead_GenSequenceEnder(bb);
218 
219     Bitio_Flush(bb);
220 
221     fclose(outputFilePtr);
222 }
223 
224 
225 /*===========================================================================*
226  *
227  * FramestoMPEG
228  *
229  *	convert some number of frame files into a single MPEG sequence file
230  *
231  *	if parallel == TRUE, then when appending a file, blocks until that
232  *	file is actually ready
233  *
234  * RETURNS:	nothing
235  *
236  * SIDE EFFECTS:    none
237  *
238  *===========================================================================*/
239 void
FramesToMPEG(numFrames,outputFileName,outputFile,parallel)240 FramesToMPEG(numFrames, outputFileName, outputFile, parallel)
241     int numFrames;
242     char *outputFileName;
243     FILE *outputFile;
244     boolean parallel;
245 {
246     register int ind;
247     BitBucket *bb;
248     char    fileName[1024];
249     char    inputFileName[1024];
250     FILE *inputFile;
251     int	pastRefNum = -1;
252     int	futureRefNum = -1;
253     int q;
254 
255     tc_hrs = 0;	tc_min = 0; tc_sec = 0; tc_pict = 0; tc_extra = 0;
256 
257     {
258       /* Why is this reset called? */
259       int x=Fsize_x, y=Fsize_y;
260       Fsize_Reset();
261       Fsize_Note(0, yuvWidth, yuvHeight);
262       if (Fsize_x == 0 || Fsize_y == 0) {
263 	Fsize_Note(0, x, y);
264       }}
265     SetBlocksPerSlice();
266 
267     bb = Bitio_New(outputFile);
268     Mhead_GenSequenceHeader(bb, Fsize_x, Fsize_y, /* pratio */ aspectRatio,
269 	       /* pict_rate */ frameRate, /* bit_rate */ -1,
270 	       /* buf_size */ -1, /*c_param_flag */ 1,
271 	       /* iq_matrix */ qtable, /* niq_matrix */ niqtable,
272 	       /* ext_data */ NULL, /* ext_data_size */ 0,
273 	       /* user_data */ NULL, /* user_data_size */ 0);
274     /* it's byte-padded, so we can dump it now */
275     Bitio_Flush(bb);
276 
277     /* need to do these in the right order!!! */
278     /* also need to add GOP headers */
279 
280     currentGOP = gopSize;
281     totalFramesSent = 0;
282 
283     if ( numFrames > 0 ) {
284 	for ( ind = 0; ind < numFrames; ind++ ) {
285 	    if ( FRAME_TYPE(ind) == 'b' ) {
286 		continue;
287 	    }
288 
289 	    pastRefNum = futureRefNum;
290 	    futureRefNum = ind;
291 
292 	    if ( (FRAME_TYPE(ind) == 'i') && (currentGOP >= gopSize) ) {
293 		int closed;
294 
295 		/* first, check to see if closed GOP */
296 		if ( totalFramesSent == ind ) {
297 		    closed = 1;
298 		} else {
299 		    closed = 0;
300 		}
301 
302 		if (! realQuiet) {
303 			fprintf(stdout, "Creating new GOP (closed = %d) after %d frames\n",
304 				closed, currentGOP);
305 		}
306 
307 		/* new GOP */
308 		bb = Bitio_New(outputFile);
309 		Mhead_GenGOPHeader(bb, /* drop_frame_flag */ 0,
310 			   tc_hrs, tc_min, tc_sec, tc_pict,
311 			   closed, /* broken_link */ 0,
312 			   /* ext_data */ NULL, /* ext_data_size */ 0,
313 			   /* user_data */ NULL, /* user_data_size */ 0);
314 		Bitio_Flush(bb);
315 		SetGOPStartTime(ind);
316 
317 		currentGOP -= gopSize;
318 	    }
319 
320 	    if ( parallel ) {
321 		WaitForOutputFile(ind);
322 		sprintf(fileName, "%s.frame.%d", outputFileName, ind);
323 	    } else {
324 		GetNthInputFileName(inputFileName, ind);
325 		sprintf(fileName, "%s/%s", currentFramePath, inputFileName);
326 	    }
327 
328 	    for (q = 0;   q < READ_ATTEMPTS;  ++q ) {
329 	      if ( (inputFile = fopen(fileName, "rb")) != NULL ) break;
330 	      fprintf(stderr, "ERROR:  Couldn't read 2:  %s retry %d\n", fileName, q);
331 	      fflush(stderr);
332 	      sleep(1);
333 	    }
334 	    if (q == READ_ATTEMPTS) {
335 	      fprintf(stderr, "Giving up (%d attepmts).\n", READ_ATTEMPTS);
336 	      exit(1);
337 	    }
338 
339 	    AppendFile(outputFile, inputFile);
340 	    if ( parallel ) {
341 		remove(fileName);
342 	    }
343 
344 	    currentGOP++;
345 	    IncrementTCTime();
346 
347 	    /* now, output the B-frames */
348 	    if ( pastRefNum != -1 ) {
349 		register int bNum;
350 
351 		for ( bNum = pastRefNum+1; bNum < futureRefNum; bNum++ ) {
352 		    if ( parallel ) {
353 			WaitForOutputFile(bNum);
354 			sprintf(fileName, "%s.frame.%d", outputFileName, bNum);
355 		    } else {
356 			GetNthInputFileName(inputFileName, bNum);
357 			sprintf(fileName, "%s/%s", currentFramePath, inputFileName);
358 		    }
359 
360 
361 		    for (q = 0;   q < READ_ATTEMPTS;  ++q ) {
362 		      if ( (inputFile = fopen(fileName, "rb")) != NULL ) break;
363 		      fprintf(stderr, "ERROR:  Couldn't read (bNum=%d):  %s retry %d\n",
364 			      bNum, fileName, q);
365 		      fflush(stderr);
366 		      sleep(1);
367 		    }
368 		    if (q == READ_ATTEMPTS) {
369 		      fprintf(stderr, "Giving up (%d attepmts).\n", READ_ATTEMPTS);
370 		      exit(1);
371 		    }
372 
373 		    AppendFile(outputFile, inputFile);
374 		    if ( parallel ) {
375 			remove(fileName);
376 		    }
377 
378 		    currentGOP++;
379 		    IncrementTCTime();
380 		}
381 	    }
382 	}
383     } else {
384 	if ( parallel ) {
385 	    fprintf(stderr, "ERROR:  PARALLEL COMBINE WITH 0 FRAMES\n");
386 	    fprintf(stderr, "(please send bug report!)\n");
387 	    exit(1);
388 	}
389 
390 	ind = 0;
391 	while ( TRUE ) {
392 	    if ( FRAME_TYPE(ind) == 'b' ) {
393 		ind++;
394 		continue;
395 	    }
396 
397 	    if ( (FRAME_TYPE(ind) == 'i') && (currentGOP >= gopSize) ) {
398 		int closed;
399 
400 		/* first, check to see if closed GOP */
401 		if ( totalFramesSent == ind ) {
402 		    closed = 1;
403 		} else {
404 		    closed = 0;
405 		}
406 
407 		if (! realQuiet) {
408 		fprintf(stdout, "Creating new GOP (closed = %d) before frame %d\n",
409 			closed, ind);
410 		}
411 
412 		/* new GOP */
413 		bb = Bitio_New(outputFile);
414 		Mhead_GenGOPHeader(bb, /* drop_frame_flag */ 0,
415 			   tc_hrs, tc_min, tc_sec, tc_pict,
416 			   closed, /* broken_link */ 0,
417 			   /* ext_data */ NULL, /* ext_data_size */ 0,
418 			   /* user_data */ NULL, /* user_data_size */ 0);
419 		Bitio_Flush(bb);
420 		SetGOPStartTime(ind);
421 
422 		currentGOP -= gopSize;
423 	    }
424 
425 	    sprintf(fileName, "%s.frame.%d", outputFileName, ind);
426 
427 	    if ( (inputFile = fopen(fileName, "rb")) == NULL ) {
428 		break;
429 	    }
430 
431 	    AppendFile(outputFile, inputFile);
432 	    if ( parallel ) {
433 		remove(fileName);
434 	    }
435 
436 	    currentGOP++;
437 	    IncrementTCTime();
438 
439 	    /* now, output the B-frames */
440 	    if ( pastRefNum != -1 ) {
441 		register int bNum;
442 
443 		for ( bNum = pastRefNum+1; bNum < futureRefNum; bNum++ ) {
444 		    sprintf(fileName, "%s.frame.%d", outputFileName, bNum);
445 
446 		    for (q = 0;   q < READ_ATTEMPTS;  ++q ) {
447 		      if ( (inputFile = fopen(fileName, "rb")) != NULL ) break;
448 		      fprintf(stderr, "ERROR:  Couldn't read (FramestoMPEG):  %s retry %d\n",
449 			      fileName, q);
450 		      fflush(stderr);
451 		      sleep(1);
452 		    }
453 		    if (q == READ_ATTEMPTS) {
454 		      fprintf(stderr, "Giving up (%d attepmts).\n", READ_ATTEMPTS);
455 		      exit(1);
456 		    }
457 
458 		    AppendFile(outputFile, inputFile);
459 		    if ( parallel ) {
460 			remove(fileName);
461 		    }
462 
463 		    currentGOP++;
464 		    IncrementTCTime();
465 		}
466 	    }
467 
468 	    ind++;
469 	}
470     }
471 
472     if (! realQuiet) {
473 	fprintf(stdout, "Wrote %d frames\n", totalFramesSent);
474 	fflush(stdout);
475     }
476 
477     bb = Bitio_New(outputFile);
478 
479     /* SEQUENCE END CODE */
480     Mhead_GenSequenceEnder(bb);
481 
482     Bitio_Flush(bb);
483 
484     fclose(outputFile);
485 }
486 
487 
488 /*=====================*
489  * INTERNAL PROCEDURES *
490  *=====================*/
491 
492 /*===========================================================================*
493  *
494  * AppendFile
495  *
496  *	appends the output file with the contents of the given input file
497  *
498  * RETURNS:	nothing
499  *
500  * SIDE EFFECTS:    none
501  *
502  *===========================================================================*/
503 static void
AppendFile(outputFile,inputFile)504 AppendFile(outputFile, inputFile)
505     FILE *outputFile;
506     FILE *inputFile;
507 {
508     uint8   data[9999];
509     int	    readItems;
510 
511     readItems = 9999;
512     while ( readItems == 9999 ) {
513 	readItems = fread(data, sizeof(uint8), 9999, inputFile);
514 	if ( readItems > 0 ) {
515 	    fwrite(data, sizeof(uint8), readItems, outputFile);
516 	}
517     }
518 
519     fclose(inputFile);
520 }
521 
522 
523