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