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