1 /*===========================================================================*
2 * combine.c
3 *
4 * Procedures to combine frames or GOPS into an MPEG sequence
5 *
6 *===========================================================================*/
7
8 /*
9 * Copyright (c) 1995 The Regents of the University of California.
10 * All rights reserved.
11 *
12 * Permission to use, copy, modify, and distribute this software and its
13 * documentation for any purpose, without fee, and without written agreement is
14 * hereby granted, provided that the above copyright notice and the following
15 * two paragraphs appear in all copies of this software.
16 *
17 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
18 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
19 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
20 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
21 *
22 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
23 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
24 * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
25 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
26 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
27 */
28
29 /*==============*
30 * HEADER FILES *
31 *==============*/
32
33 #include "all.h"
34 #include <time.h>
35 #include <errno.h>
36 #include <unistd.h>
37
38 #include "nstring.h"
39 #include "nsleep.h"
40
41 #include "mtypes.h"
42 #include "frames.h"
43 #include "frametype.h"
44 #include "motion_search.h"
45 #include "mpeg.h"
46 #include "prototypes.h"
47 #include "parallel.h"
48 #include "param.h"
49 #include "readframe.h"
50 #include "mheaders.h"
51 #include "fsize.h"
52 #include "input.h"
53 #include "combine.h"
54
55
56 #define READ_ATTEMPTS 5 /* number of times (seconds) to retry an input file */
57
58 /*==================*
59 * GLOBAL VARIABLES *
60 *==================*/
61 extern int yuvWidth, yuvHeight;
62
63
64 /*===========================================================================*
65 *
66 * AppendFile
67 *
68 * appends the output file with the contents of the given input file
69 *
70 * RETURNS: nothing
71 *
72 * SIDE EFFECTS: none
73 *
74 *===========================================================================*/
75 static void
AppendFile(FILE * const ofP,FILE * const ifP)76 AppendFile(FILE * const ofP,
77 FILE * const ifP) {
78
79 uint8 data[9999];
80 unsigned int readItems;
81
82 readItems = 9999;
83 while (readItems == 9999) {
84 readItems = fread(data, sizeof(uint8), 9999, ifP);
85 if (readItems > 0)
86 fwrite(data, sizeof(uint8), readItems, ofP);
87 }
88 fclose(ifP);
89 }
90
91
92
93 static void
appendSpecifiedGopFiles(struct inputSource * const inputSourceP,const char * const currentGopPath,FILE * const ofP)94 appendSpecifiedGopFiles(struct inputSource * const inputSourceP,
95 const char * const currentGopPath,
96 FILE * const ofP) {
97
98 unsigned int fileSeq;
99
100 for (fileSeq = 0; fileSeq < inputSourceP->numInputFiles; ++fileSeq) {
101 const char * inputFileName;
102 const char * fileName;
103 unsigned int nAttempts;
104 FILE * ifP;
105
106 GetNthInputFileName(inputSourceP, fileSeq, &inputFileName);
107 pm_asprintf(&fileName, "%s/%s", currentGOPPath, inputFileName);
108
109 for (nAttempts = 0, ifP = NULL;
110 nAttempts < READ_ATTEMPTS && !ifP;
111 ++nAttempts) {
112
113 ifP = fopen(fileName, "rb");
114 if (ifP == NULL)
115 pm_message("ERROR: Couldn't read file '%s'. retry %u",
116 fileName, nAttempts);
117 }
118 if (ifP) {
119 if (!realQuiet)
120 pm_message("Appending file %u '%s'", fileSeq, fileName);
121 AppendFile(ofP, ifP);
122 } else
123 pm_error("Unable to read file '%s' after %u attempts.",
124 fileName, READ_ATTEMPTS);
125 pm_strfree(fileName);
126 pm_strfree(inputFileName);
127 }
128 }
129
130
131
132 static void
appendDefaultGopFiles(const char * const outputFileName,FILE * const ofP)133 appendDefaultGopFiles(const char * const outputFileName,
134 FILE * const ofP) {
135
136 unsigned int fileSeq;
137 bool endOfFiles;
138
139 for (fileSeq = 0, endOfFiles = FALSE; !endOfFiles; ++fileSeq) {
140 const char * fileName;
141 FILE * ifP;
142
143 pm_asprintf(&fileName, "%s.gop.%u", outputFileName, fileSeq);
144
145 ifP = fopen(fileName, "rb");
146 if (ifP == NULL)
147 endOfFiles = TRUE;
148 else {
149 if (!realQuiet)
150 pm_message("appending file: %s", fileName);
151
152 AppendFile(ofP, ifP);
153 }
154 pm_strfree(fileName);
155 }
156 }
157
158
159
160 void
GOPsToMPEG(struct inputSource * const inputSourceP,const char * const outputFileName,FILE * const ofP)161 GOPsToMPEG(struct inputSource * const inputSourceP,
162 const char * const outputFileName,
163 FILE * const ofP) {
164 /*----------------------------------------------------------------------------
165 Combine individual GOPs (one per file) into a single MPEG sequence file.
166 -----------------------------------------------------------------------------*/
167 BitBucket * bb;
168
169 {
170 /* Why is this reset called? */
171 int x=Fsize_x, y=Fsize_y;
172 Fsize_Reset();
173 Fsize_Note(0, yuvWidth, yuvHeight);
174 if (Fsize_x == 0 || Fsize_y == 0)
175 Fsize_Note(0, x, y);
176 }
177
178 bb = Bitio_New(ofP);
179
180 Mhead_GenSequenceHeader(bb, Fsize_x, Fsize_y, /* pratio */ aspectRatio,
181 /* pict_rate */ frameRate, /* bit_rate */ -1,
182 /* buf_size */ -1, /*c_param_flag */ 1,
183 /* iq_matrix */ customQtable,
184 /* niq_matrix */ customNIQtable,
185 /* ext_data */ NULL, /* ext_data_size */ 0,
186 /* user_data */ NULL, /* user_data_size */ 0);
187
188 /* it's byte-padded, so we can dump it now */
189 Bitio_Flush(bb);
190
191 if (inputSourceP->stdinUsed)
192 AppendFile(ofP, stdin);
193 else {
194 if (inputSourceP->numInputFiles > 0)
195 appendSpecifiedGopFiles(inputSourceP, currentGOPPath, ofP);
196 else
197 appendDefaultGopFiles(outputFileName, ofP);
198 }
199 bb = Bitio_New(ofP);
200
201 /* SEQUENCE END CODE */
202 Mhead_GenSequenceEnder(bb);
203
204 Bitio_Flush(bb);
205
206 fclose(ofP);
207 }
208
209
210
211 static void
makeGOPHeader(FILE * const outputFileP,unsigned int const totalFramesSent,unsigned int const frameNumber,unsigned int const seqWithinGop)212 makeGOPHeader(FILE * const outputFileP,
213 unsigned int const totalFramesSent,
214 unsigned int const frameNumber,
215 unsigned int const seqWithinGop) {
216
217 boolean closed = (totalFramesSent == frameNumber);
218
219 BitBucket * bbP;
220
221 if (!realQuiet)
222 fprintf(stdout, "Creating new GOP (closed = %d) after %d frames\n",
223 closed, seqWithinGop);
224
225 /* new GOP */
226 bbP = Bitio_New(outputFileP);
227 Mhead_GenGOPHeader(bbP, /* drop_frame_flag */ 0,
228 tc_hrs, tc_min, tc_sec, tc_pict,
229 closed, /* broken_link */ 0,
230 /* ext_data */ NULL, /* ext_data_size */ 0,
231 /* user_data */ NULL,
232 /* user_data_size */ 0);
233 Bitio_Flush(bbP);
234 SetGOPStartTime(frameNumber);
235 }
236
237
238
239 void
FramesToMPEG(FILE * const outputFile,void * const inputHandle,fileAcquisitionFn acquireInputFile,fileDispositionFn disposeInputFile)240 FramesToMPEG(FILE * const outputFile,
241 void * const inputHandle,
242 fileAcquisitionFn acquireInputFile,
243 fileDispositionFn disposeInputFile) {
244 /*----------------------------------------------------------------------------
245 Combine a bunch of frames, one per file, into a single MPEG
246 sequence file.
247
248 acquireInputFile() opens a file that contains an encoded frame,
249 identified by frame number. It returns NULL when the frame number
250 is beyond the frames available.
251 -----------------------------------------------------------------------------*/
252 unsigned int frameNumber;
253 BitBucket *bb;
254 FILE *inputFile;
255 int pastRefNum = -1;
256 int futureRefNum = -1;
257 boolean inputLeft;
258 unsigned int seqWithinGop;
259 /* The sequence of the current frame within its GOP. 0 is the
260 first frame of a GOP, etc.
261 */
262
263 tc_hrs = 0; tc_min = 0; tc_sec = 0; tc_pict = 0; tc_extra = 0;
264
265 {
266 /* Why is this reset called? */
267 int x=Fsize_x, y=Fsize_y;
268 Fsize_Reset();
269 Fsize_Note(0, yuvWidth, yuvHeight);
270 if (Fsize_x == 0 || Fsize_y == 0)
271 Fsize_Note(0, x, y);
272 }
273 SetBlocksPerSlice();
274
275 bb = Bitio_New(outputFile);
276 Mhead_GenSequenceHeader(bb, Fsize_x, Fsize_y, /* pratio */ aspectRatio,
277 /* pict_rate */ frameRate, /* bit_rate */ -1,
278 /* buf_size */ -1, /*c_param_flag */ 1,
279 /* iq_matrix */ qtable, /* niq_matrix */ niqtable,
280 /* ext_data */ NULL, /* ext_data_size */ 0,
281 /* user_data */ NULL, /* user_data_size */ 0);
282 /* it's byte-padded, so we can dump it now */
283 Bitio_Flush(bb);
284
285 /* need to do these in the right order!!! */
286 /* also need to add GOP headers */
287
288 seqWithinGop = 0;
289 totalFramesSent = 0;
290
291 inputLeft = TRUE;
292 frameNumber = 0;
293
294 makeGOPHeader(outputFile, totalFramesSent, frameNumber, seqWithinGop);
295
296 while (inputLeft) {
297 if (FType_Type(frameNumber) != 'b') {
298 pastRefNum = futureRefNum;
299 futureRefNum = frameNumber;
300
301 if ((FType_Type(frameNumber) == 'i') && seqWithinGop >= gopSize) {
302 makeGOPHeader(outputFile,
303 totalFramesSent, frameNumber, seqWithinGop);
304 seqWithinGop -= gopSize;
305 }
306
307 acquireInputFile(inputHandle, frameNumber, &inputFile);
308 if (inputFile == NULL)
309 inputLeft = FALSE;
310 else {
311 AppendFile(outputFile, inputFile);
312
313 disposeInputFile(inputHandle, frameNumber);
314
315 ++seqWithinGop;
316 IncrementTCTime();
317
318 /* now, output the B-frames */
319 if (pastRefNum != -1) {
320 unsigned int bNum;
321
322 for (bNum = pastRefNum+1; bNum < futureRefNum; ++bNum) {
323 acquireInputFile(inputHandle, bNum, &inputFile);
324
325 if (inputFile) {
326 AppendFile(outputFile, inputFile);
327
328 disposeInputFile(inputHandle, bNum);
329
330 ++seqWithinGop;
331 IncrementTCTime();
332 }
333 }
334 }
335 }
336 }
337 ++frameNumber;
338 }
339
340 if (!realQuiet)
341 fprintf(stdout, "Wrote %d frames\n", totalFramesSent);
342
343 bb = Bitio_New(outputFile);
344
345 /* SEQUENCE END CODE */
346 Mhead_GenSequenceEnder(bb);
347
348 Bitio_Flush(bb);
349
350 fclose(outputFile);
351 }
352
353
354
355