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