root/liboggz/trunk/src/tools/oggz-merge.c

Revision 3811, 11.0 kB (checked in by conrad, 1 month ago)

print serialno as unsigned throughout

Line 
1 /*
2    Copyright (C) 2003 Commonwealth Scientific and Industrial Research
3    Organisation (CSIRO) Australia
4
5    Redistribution and use in source and binary forms, with or without
6    modification, are permitted provided that the following conditions
7    are met:
8
9    - Redistributions of source code must retain the above copyright
10    notice, this list of conditions and the following disclaimer.
11
12    - Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in the
14    documentation and/or other materials provided with the distribution.
15
16    - Neither the name of CSIRO Australia nor the names of its
17    contributors may be used to endorse or promote products derived from
18    this software without specific prior written permission.
19
20    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23    PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE ORGANISATION OR
24    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "config.h"
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <fcntl.h>
39
40 #include <getopt.h>
41 #include <errno.h>
42
43 #ifdef HAVE_INTTYPES_H
44 #  include <inttypes.h>
45 #else
46 #  define PRId64 "I64d"
47 #endif
48
49 #include <oggz/oggz.h>
50 #include "oggz_tools.h"
51
52 #define READ_SIZE 4096
53
54 #define ALL_VORBIS_WARNING \
55   "oggz-merge: WARNING: Merging Ogg Vorbis I files. The resulting file will\n" \
56   "  contain %d tracks in parallel, interleaved for simultaneous playback.\n"\
57   "  If you want to sequence these files one after another, use cat instead.\n"
58
59 static void
60 usage (char * progname)
61 {
62   printf ("Usage: %s [options] filename ...\n", progname);
63   printf ("Merge Ogg files together, interleaving pages in order of presentation time.\n");
64   printf ("\nMiscellaneous options\n");
65   printf ("  -o filename, --output filename\n");
66   printf ("                         Specify output filename\n");
67   printf ("  -h, --help             Display this help and exit\n");
68   printf ("  -v, --version          Output version information and exit\n");
69   printf ("  -V, --verbose          Verbose operation\n");
70   printf ("\n");
71   printf ("Please report bugs to <ogg-dev@xiph.org>\n");
72 }
73
74 typedef struct _OMData OMData;
75 typedef struct _OMInput OMInput;
76 typedef struct _OMITrack OMITrack;
77
78 struct _OMData {
79   OggzTable * inputs;
80   int verbose;
81 };
82
83 struct _OMInput {
84   OMData * omdata;
85   OGGZ * reader;
86   const ogg_page * og;
87 };
88
89 struct _OMITrack {
90   long output_serialno;
91 };
92
93 static ogg_page *
94 _ogg_page_copy (const ogg_page * og)
95 {
96   ogg_page * new_og;
97
98   new_og = malloc (sizeof (*og));
99   new_og->header = malloc (og->header_len);
100   new_og->header_len = og->header_len;
101   memcpy (new_og->header, og->header, og->header_len);
102   new_og->body = malloc (og->body_len);
103   new_og->body_len = og->body_len;
104   memcpy (new_og->body, og->body, og->body_len);
105
106   return new_og;
107 }
108
109 static int
110 _ogg_page_free (const ogg_page * og)
111 {
112   free (og->header);
113   free (og->body);
114   free ((ogg_page *)og);
115   return 0;
116 }
117
118 static void
119 ominput_delete (OMInput * input)
120 {
121   oggz_close (input->reader);
122
123   free (input);
124 }
125
126 static OMData *
127 omdata_new (void)
128 {
129   OMData * omdata;
130
131   omdata = (OMData *) malloc (sizeof (OMData));
132
133   omdata->inputs = oggz_table_new ();
134   omdata->verbose = 0;
135
136   return omdata;
137 }
138
139 static void
140 omdata_delete (OMData * omdata)
141 {
142   OMInput * input;
143   int i, ninputs;
144
145   ninputs = oggz_table_size (omdata->inputs);
146   for (i = 0; i < ninputs; i++) {
147     input = (OMInput *) oggz_table_nth (omdata->inputs, i, NULL);
148     ominput_delete (input);
149   }
150   oggz_table_delete (omdata->inputs);
151
152   free (omdata);
153 }
154
155 static int
156 read_page (OGGZ * oggz, const ogg_page * og, long serialno, void * user_data)
157 {
158   OMInput * input = (OMInput *) user_data;
159
160   input->og = _ogg_page_copy (og);
161
162   return OGGZ_STOP_OK;
163 }
164
165 static int
166 omdata_add_input (OMData * omdata, FILE * infile)
167 {
168   OMInput * input;
169   int nfiles;
170
171   input = (OMInput *) malloc (sizeof (OMInput));
172   if (input == NULL) return -1;
173
174   input->omdata = omdata;
175   input->reader = oggz_open_stdio (infile, OGGZ_READ|OGGZ_AUTO);
176   input->og = NULL;
177
178   oggz_set_read_page (input->reader, -1, read_page, input);
179
180   nfiles = oggz_table_size (omdata->inputs);
181   if (!oggz_table_insert (omdata->inputs, nfiles++, input)) {
182     ominput_delete (input);
183     return -1;
184   }
185
186   return 0;
187 }
188
189 static int
190 oggz_merge (OMData * omdata, FILE * outfile)
191 {
192   OMInput * input;
193   int ninputs, i, min_i;
194   long key, n;
195   ogg_int64_t units, min_units;
196   const ogg_page * og;
197   int active;
198
199   /* For theora+vorbis, or dirac+vorbis, ensure video bos is first */
200   int careful_for_video = 0;
201
202   /* If all input files are Ogg Vorbis I, warn that the output will not be
203    * a valid Ogg Vorbis I file as it will be multitrack. This is in response
204    * to Debian bug 280550: http://bugs.debian.org/280550
205    */
206   int v, warn_all_vorbis = 1;
207
208   if (oggz_table_size (omdata->inputs) == 2)
209     careful_for_video = 1;
210
211   while ((ninputs = oggz_table_size (omdata->inputs)) > 0) {
212     min_units = -1;
213     min_i = -1;
214     active = 1;
215
216     if (omdata->verbose)
217       printf ("------------------------------------------------------------\n");
218
219     /* Reload all pages, and find the min (earliest) */
220     for (i = 0; active && i < oggz_table_size (omdata->inputs); i++) {
221       input = (OMInput *) oggz_table_nth (omdata->inputs, i, &key);
222       if (input != NULL) {
223         while (input && input->og == NULL) {
224           n = oggz_read (input->reader, READ_SIZE);
225           if (n == 0) {
226             oggz_table_remove (omdata->inputs, key);
227             ominput_delete (input);
228             input = NULL;
229           }
230         }
231         if (input && input->og) {
232           if (ogg_page_bos ((ogg_page *)input->og)) {
233             min_i = i;
234
235             if (careful_for_video || warn_all_vorbis) {
236               int is_vorbis;
237               long serialno = ogg_page_serialno ((ogg_page *)input->og);
238
239               is_vorbis = (oggz_stream_get_content (input->reader, serialno) == OGGZ_CONTENT_VORBIS);
240
241               if (i == 0 && is_vorbis)
242                 careful_for_video = 0;
243               else
244                 active = 0;
245
246               if (!is_vorbis) warn_all_vorbis = 0;
247
248             } else {
249               active = 0;
250             }
251           } else if (warn_all_vorbis) {
252             int all_inputs_are_beyond_bos = 1;
253
254             /* All BOS pages seen so far are Ogg Vorbis. The following loop
255              * checks if all input files are single-track, ie. Ogg Vorbis I.
256              * We can only rely on this information if all inputs are beyond
257              * bos, ie. all BOS pages have been seen. */
258             for (v = 0; v < oggz_table_size (omdata->inputs); v++) {
259               OMInput * input_v;
260               OGGZ * oggz;
261
262               input_v = (OMInput *) oggz_table_nth (omdata->inputs, i, &key);
263               oggz = input_v->reader;
264
265               if (oggz_get_bos(oggz, -1)) all_inputs_are_beyond_bos = 0;
266               else if (oggz_get_numtracks(oggz) > 1) warn_all_vorbis = 0;
267             }
268
269             if (all_inputs_are_beyond_bos && warn_all_vorbis) {
270               fprintf (stderr, ALL_VORBIS_WARNING, v);
271               warn_all_vorbis = 0;
272             }
273           }
274           units = oggz_tell_units (input->reader);
275
276           if (omdata->verbose) {
277             ot_fprint_time (stdout, (double)units/1000);
278             printf (": Got index %d serialno %010u %" PRId64 " units: ",
279                     i, ogg_page_serialno ((ogg_page *)input->og), units);
280           }
281
282           if (min_units == -1 || units == 0 ||
283               (units > -1 && units < min_units)) {
284             min_units = units;
285             min_i = i;
286             if (omdata->verbose)
287               printf ("Min\n");
288           } else {
289             if (omdata->verbose)
290               printf ("Moo\n");
291           }
292         } else if (omdata->verbose) {
293           if (input == NULL) {
294             printf ("*** index %d NULL\n", i);
295           } else {
296             printf ("*** No page from index %d\n", i);
297           }
298         }
299       }
300     }
301
302     if (omdata->verbose)
303       printf ("Min index %d\n", min_i);
304
305     /* Write the earliest page */
306     if (min_i != -1) {
307       input = (OMInput *) oggz_table_nth (omdata->inputs, min_i, &key);
308       og = input->og;
309       fwrite (og->header, 1, og->header_len, outfile);
310       fwrite (og->body, 1, og->body_len, outfile);
311
312       _ogg_page_free (og);
313       input->og = NULL;
314     }
315   }
316
317   return 0;
318 }
319
320 int
321 main (int argc, char * argv[])
322 {
323   int show_version = 0;
324   int show_help = 0;
325
326   char * progname;
327   char * infilename = NULL, * outfilename = NULL;
328   FILE * infile = NULL, * outfile = NULL;
329   int used_stdin = 0; /* Flag usage of stdin, only use it once */
330   OMData * omdata;
331   int i;
332
333   char * optstring = "hvVo:";
334
335 #ifdef HAVE_GETOPT_LONG
336   static struct option long_options[] = {
337     {"help", no_argument, 0, 'h'},
338     {"version", no_argument, 0, 'v'},
339     {"verbose", no_argument, 0, 'V'},
340     {"output", required_argument, 0, 'o'},
341     {0,0,0,0}
342   };
343 #endif
344
345   ot_init ();
346
347   progname = argv[0];
348
349   if (argc < 2) {
350     usage (progname);
351     return (1);
352   }
353
354   if (!strncmp (argv[1], "-?", 2)) {
355 #ifdef HAVE_GETOPT_LONG
356     ot_print_options (long_options, optstring);
357 #else
358     ot_print_short_options (optstring);
359 #endif
360     exit (0);
361   }
362
363   omdata = omdata_new();
364
365   while (1) {
366 #ifdef HAVE_GETOPT_LONG
367     i = getopt_long (argc, argv, optstring, long_options, NULL);
368 #else
369     i = getopt (argc, argv, optstring);
370 #endif
371     if (i == -1) break;
372     if (i == ':') {
373       usage (progname);
374       goto exit_err;
375     }
376
377     switch (i) {
378     case 'h': /* help */
379       show_help = 1;
380       break;
381     case 'v': /* version */
382       show_version = 1;
383       break;
384     case 'o': /* output */
385       outfilename = optarg;
386       break;
387     case 'V': /* verbose */
388       omdata->verbose = 1;
389     default:
390       break;
391     }
392   }
393
394   if (show_version) {
395     printf ("%s version " VERSION "\n", progname);
396   }
397
398   if (show_help) {
399     usage (progname);
400   }
401
402   if (show_version || show_help) {
403     goto exit_ok;
404   }
405
406   if (optind >= argc) {
407     usage (progname);
408     goto exit_err;
409   }
410
411   if (optind >= argc) {
412     usage (progname);
413     goto exit_err;
414   }
415
416   while (optind < argc) {
417     infilename = argv[optind++];
418     if (strcmp (infilename, "-") == 0) {
419       if (used_stdin) continue;
420
421       infile = stdin;
422       used_stdin = 1;
423     } else {
424       infile = fopen (infilename, "rb");
425     }
426
427     if (infile == NULL) {
428       fprintf (stderr, "%s: unable to open input file %s\n", progname,
429                infilename);
430     } else {
431       omdata_add_input (omdata, infile);
432     }
433   }
434
435   if (outfilename == NULL) {
436     outfile = stdout;
437   } else {
438     outfile = fopen (outfilename, "wb");
439     if (outfile == NULL) {
440       fprintf (stderr, "%s: unable to open output file %s\n",
441                progname, outfilename);
442       goto exit_err;
443     }
444   }
445
446   oggz_merge (omdata, outfile);
447
448  exit_ok:
449   omdata_delete (omdata);
450   exit (0);
451
452  exit_err:
453   omdata_delete (omdata);
454   exit (1);
455 }
Note: See TracBrowser for help on using the browser.