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