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 <assert.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40
41 #ifdef HAVE_UNISTD_H
42 #include <unistd.h>
43 #endif
44
45 #include <fcntl.h>
46 #include <errno.h>
47 #include <string.h>
48 #include <time.h>
49
50 #include <ogg/ogg.h>
51
52 #include "oggz_compat.h"
53 #include "oggz_private.h"
54 #include "oggz_vector.h"
55
56 /* Definitions for oggz_run() */
57 long oggz_read (OGGZ * oggz, long n);
58 long oggz_write (OGGZ * oggz, long n);
59
60 /*#define DEBUG*/
61
62 static int
oggz_flags_disabled(int flags)63 oggz_flags_disabled (int flags)
64 {
65 if (flags & OGGZ_WRITE) {
66 if (!OGGZ_CONFIG_WRITE) return OGGZ_ERR_DISABLED;
67 } else {
68 if (!OGGZ_CONFIG_READ) return OGGZ_ERR_DISABLED;
69 }
70
71 return 0;
72 }
73
74 OGGZ *
oggz_new(int flags)75 oggz_new (int flags)
76 {
77 OGGZ * oggz;
78
79 if (oggz_flags_disabled (flags)) return NULL;
80
81 oggz = (OGGZ *) oggz_malloc (sizeof (OGGZ));
82 if (oggz == NULL) return NULL;
83
84 oggz->flags = flags;
85 oggz->file = NULL;
86 oggz->io = NULL;
87
88 oggz->offset = 0;
89 oggz->offset_data_begin = 0;
90
91 oggz->run_blocksize = 1024;
92 oggz->cb_next = 0;
93
94 oggz->streams = oggz_vector_new ();
95 if (oggz->streams == NULL) {
96 goto err_oggz_new;
97 }
98
99 oggz->all_at_eos = 0;
100
101 oggz->metric = NULL;
102 oggz->metric_user_data = NULL;
103 oggz->metric_internal = 0;
104
105 oggz->order = NULL;
106 oggz->order_user_data = NULL;
107
108 oggz->packet_buffer = oggz_dlist_new ();
109 if (oggz->packet_buffer == NULL) {
110 goto err_streams_new;
111 }
112
113 if (OGGZ_CONFIG_WRITE && (oggz->flags & OGGZ_WRITE)) {
114 if (oggz_write_init (oggz) == NULL)
115 goto err_packet_buffer_new;
116 } else if (OGGZ_CONFIG_READ) {
117 oggz_read_init (oggz);
118 }
119
120 return oggz;
121
122 err_packet_buffer_new:
123 oggz_free (oggz->packet_buffer);
124 err_streams_new:
125 oggz_free (oggz->streams);
126 err_oggz_new:
127 oggz_free (oggz);
128 return NULL;
129 }
130
131 OGGZ *
oggz_open(char * filename,int flags)132 oggz_open (char * filename, int flags)
133 {
134 OGGZ * oggz = NULL;
135 FILE * file = NULL;
136
137 if (oggz_flags_disabled (flags)) return NULL;
138
139 if (flags & OGGZ_WRITE) {
140 file = fopen (filename, "wb");
141 } else {
142 file = fopen (filename, "rb");
143 }
144 if (file == NULL) return NULL;
145
146 if ((oggz = oggz_new (flags)) == NULL) {
147 fclose (file);
148 return NULL;
149 }
150
151 oggz->file = file;
152
153 return oggz;
154 }
155
156 OGGZ *
oggz_open_stdio(FILE * file,int flags)157 oggz_open_stdio (FILE * file, int flags)
158 {
159 OGGZ * oggz = NULL;
160
161 if (oggz_flags_disabled (flags)) return NULL;
162
163 if ((oggz = oggz_new (flags)) == NULL)
164 return NULL;
165
166 oggz->file = file;
167
168 return oggz;
169 }
170
171 int
oggz_flush(OGGZ * oggz)172 oggz_flush (OGGZ * oggz)
173 {
174 if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
175
176 if (OGGZ_CONFIG_WRITE && (oggz->flags & OGGZ_WRITE)) {
177 oggz_write_flush (oggz);
178 }
179
180 return oggz_io_flush (oggz);
181 }
182
183 static int
oggz_stream_clear(void * data)184 oggz_stream_clear (void * data)
185 {
186 oggz_stream_t * stream = (oggz_stream_t *) data;
187
188 oggz_comments_free (stream);
189
190 if (stream->ogg_stream.serialno != -1)
191 ogg_stream_clear (&stream->ogg_stream);
192
193 if (stream->metric_internal)
194 oggz_free (stream->metric_user_data);
195
196 if (stream->calculate_data != NULL)
197 oggz_free (stream->calculate_data);
198
199 oggz_free (stream);
200
201 return 0;
202 }
203
204 int
oggz_close(OGGZ * oggz)205 oggz_close (OGGZ * oggz)
206 {
207 if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
208
209 if (OGGZ_CONFIG_WRITE && (oggz->flags & OGGZ_WRITE)) {
210 oggz_write_close (oggz);
211 } else if (OGGZ_CONFIG_READ) {
212 oggz_read_close (oggz);
213 }
214
215 oggz_vector_foreach (oggz->streams, oggz_stream_clear);
216 oggz_vector_delete (oggz->streams);
217
218 oggz_dlist_deliter(oggz->packet_buffer, oggz_read_free_pbuffers);
219 oggz_dlist_delete(oggz->packet_buffer);
220
221 if (oggz->metric_internal)
222 oggz_free (oggz->metric_user_data);
223
224 if (oggz->file != NULL) {
225 if (fclose (oggz->file) == EOF) {
226 return OGGZ_ERR_SYSTEM;
227 }
228 }
229
230 if (oggz->io != NULL) {
231 oggz_io_flush (oggz);
232 oggz_free (oggz->io);
233 }
234
235 oggz_free (oggz);
236
237 return 0;
238 }
239
240 oggz_off_t
oggz_tell(OGGZ * oggz)241 oggz_tell (OGGZ * oggz)
242 {
243 if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
244
245 return oggz->offset;
246 }
247
248 ogg_int64_t
oggz_tell_units(OGGZ * oggz)249 oggz_tell_units (OGGZ * oggz)
250 {
251 OggzReader * reader;
252
253 if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
254
255 if (oggz->flags & OGGZ_WRITE) {
256 return OGGZ_ERR_INVALID;
257 }
258
259 reader = &oggz->x.reader;
260
261 if (OGGZ_CONFIG_READ) {
262 return reader->current_unit;
263 } else {
264 return OGGZ_ERR_DISABLED;
265 }
266 }
267
268 ogg_int64_t
oggz_tell_granulepos(OGGZ * oggz)269 oggz_tell_granulepos (OGGZ * oggz)
270 {
271 if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
272
273 if (oggz->flags & OGGZ_WRITE) {
274 return OGGZ_ERR_INVALID;
275 }
276
277 if (OGGZ_CONFIG_READ) {
278 return oggz->x.reader.current_granulepos;
279 } else {
280 return OGGZ_ERR_DISABLED;
281 }
282 }
283
284 long
oggz_run(OGGZ * oggz)285 oggz_run (OGGZ * oggz)
286 {
287 long n = OGGZ_ERR_DISABLED;
288
289 if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
290
291 if (OGGZ_CONFIG_WRITE && (oggz->flags & OGGZ_WRITE)) {
292 while ((n = oggz_write (oggz, oggz->run_blocksize)) > 0);
293 } else if (OGGZ_CONFIG_READ) {
294 while ((n = oggz_read (oggz, oggz->run_blocksize)) > 0);
295 }
296
297 return n;
298 }
299
300 int
oggz_run_set_blocksize(OGGZ * oggz,long blocksize)301 oggz_run_set_blocksize (OGGZ * oggz, long blocksize)
302 {
303 if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
304
305 if (blocksize <= 0) return OGGZ_ERR_INVALID;
306
307 oggz->run_blocksize = blocksize;
308
309 return 0;
310 }
311
312 /******** oggz_stream management ********/
313
314 static int
oggz_find_stream(void * data,long serialno)315 oggz_find_stream (void * data, long serialno)
316 {
317 oggz_stream_t * stream = (oggz_stream_t *) data;
318
319 return (stream->ogg_stream.serialno == serialno);
320 }
321
322 oggz_stream_t *
oggz_get_stream(OGGZ * oggz,long serialno)323 oggz_get_stream (OGGZ * oggz, long serialno)
324 {
325 if (serialno == -1) return NULL;
326
327 return oggz_vector_find_with (oggz->streams, oggz_find_stream, serialno);
328 }
329
330 oggz_stream_t *
oggz_add_stream(OGGZ * oggz,long serialno)331 oggz_add_stream (OGGZ * oggz, long serialno)
332 {
333 oggz_stream_t * stream;
334
335 stream = oggz_malloc (sizeof (oggz_stream_t));
336 if (stream == NULL) return NULL;
337
338 ogg_stream_init (&stream->ogg_stream, (int)serialno);
339
340 if (oggz_comments_init (stream) == -1) {
341 oggz_free (stream);
342 return NULL;
343 }
344
345 stream->content = OGGZ_CONTENT_UNKNOWN;
346 stream->numheaders = 3; /* Default to 3 headers for Ogg logical bitstreams */
347 stream->preroll = 0;
348 stream->granulerate_n = 1;
349 stream->granulerate_d = 1;
350 stream->first_granule = 0;
351 stream->basegranule = 0;
352 stream->granuleshift = 0;
353
354 stream->delivered_non_b_o_s = 0;
355 stream->b_o_s = 1;
356 stream->e_o_s = 0;
357 stream->granulepos = 0;
358 stream->packetno = -1; /* will be incremented on first read or write */
359
360 stream->metric = NULL;
361 stream->metric_user_data = NULL;
362 stream->metric_internal = 0;
363 stream->order = NULL;
364 stream->order_user_data = NULL;
365 stream->read_packet = NULL;
366 stream->read_user_data = NULL;
367 stream->read_page = NULL;
368 stream->read_page_user_data = NULL;
369
370 stream->calculate_data = NULL;
371
372 oggz_vector_insert_p (oggz->streams, stream);
373
374 return stream;
375 }
376
377 int
oggz_get_bos(OGGZ * oggz,long serialno)378 oggz_get_bos (OGGZ * oggz, long serialno)
379 {
380 oggz_stream_t * stream;
381 int i, size;
382
383 if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
384
385 if (serialno == -1) {
386 size = oggz_vector_size (oggz->streams);
387 for (i = 0; i < size; i++) {
388 stream = (oggz_stream_t *)oggz_vector_nth_p (oggz->streams, i);
389 #if 1
390 /* If this stream has delivered a non bos packet, return FALSE */
391 if (stream->delivered_non_b_o_s) return 0;
392 #else
393 /* If this stream has delivered its bos packet, return FALSE */
394 if (!stream->b_o_s) return 0;
395 #endif
396 }
397 return 1;
398 } else {
399 stream = oggz_get_stream (oggz, serialno);
400 if (stream == NULL)
401 return OGGZ_ERR_BAD_SERIALNO;
402
403 return stream->b_o_s;
404 }
405 }
406
407 int
oggz_get_eos(OGGZ * oggz,long serialno)408 oggz_get_eos (OGGZ * oggz, long serialno)
409 {
410 oggz_stream_t * stream;
411 int i, size;
412
413 if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
414
415 if (serialno == -1) {
416 size = oggz_vector_size (oggz->streams);
417 for (i = 0; i < size; i++) {
418 stream = (oggz_stream_t *)oggz_vector_nth_p (oggz->streams, i);
419 if (!stream->e_o_s) return 0;
420 }
421 return 1;
422 } else {
423 stream = oggz_get_stream (oggz, serialno);
424 if (stream == NULL)
425 return OGGZ_ERR_BAD_SERIALNO;
426
427 return stream->e_o_s;
428 }
429 }
430
431 int
oggz_set_eos(OGGZ * oggz,long serialno)432 oggz_set_eos (OGGZ * oggz, long serialno)
433 {
434 oggz_stream_t * stream;
435 int i, size;
436
437 if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
438
439 if (serialno == -1) {
440 size = oggz_vector_size (oggz->streams);
441 for (i = 0; i < size; i++) {
442 stream = (oggz_stream_t *) oggz_vector_nth_p (oggz->streams, i);
443 stream->e_o_s = 1;
444 }
445 oggz->all_at_eos = 1;
446 } else {
447 stream = oggz_get_stream (oggz, serialno);
448 if (stream == NULL)
449 return OGGZ_ERR_BAD_SERIALNO;
450
451 stream->e_o_s = 1;
452
453 if (oggz_get_eos (oggz, -1))
454 oggz->all_at_eos = 1;
455 }
456
457 return 0;
458 }
459
460 int
oggz_get_numtracks(OGGZ * oggz)461 oggz_get_numtracks (OGGZ * oggz)
462 {
463 if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
464 return oggz_vector_size (oggz->streams);
465 }
466
467 /* Generate a pseudorandom serialno on request, ensuring that the number
468 * selected is not -1 or the serialno of an existing logical bitstream.
469 * NB. This inlines a simple linear congruential generator to avoid problems
470 * of portability of rand() vs. the POSIX random()/initstate()/getstate(), and
471 * in the case of rand() in order to avoid interfering with the random number
472 * sequence.
473 * Adapated from a patch by Erik de Castro Lopo, July 2007.
474 */
475 long
oggz_serialno_new(OGGZ * oggz)476 oggz_serialno_new (OGGZ * oggz)
477 {
478 /* Ensure that the returned value is within the range of an int, so that
479 * it passes cleanly through ogg_stream_init(). In any case, the size of
480 * a serialno in the Ogg page header is 32 bits; it should never have been
481 * declared a long in ogg.h's ogg_packet in the first place. */
482 static ogg_int32_t serialno = 0;
483 int k;
484
485 if (serialno == 0) serialno = time(NULL);
486
487 do {
488 for (k = 0; k < 3 || serialno == 0; k++)
489 serialno = 11117 * serialno + 211231;
490 } while (serialno == -1 || oggz_get_stream (oggz, serialno) != NULL);
491
492 /* Cast the result back to a long for API consistency */
493 return (long)serialno;
494 }
495
496 /******** OggzMetric management ********/
497
498 int
oggz_set_metric_internal(OGGZ * oggz,long serialno,OggzMetric metric,void * user_data,int internal)499 oggz_set_metric_internal (OGGZ * oggz, long serialno,
500 OggzMetric metric, void * user_data, int internal)
501 {
502 oggz_stream_t * stream;
503
504 if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
505
506 if (serialno == -1) {
507 if (oggz->metric_internal && oggz->metric_user_data)
508 oggz_free (oggz->metric_user_data);
509 oggz->metric = metric;
510 oggz->metric_user_data = user_data;
511 oggz->metric_internal = internal;
512 } else {
513 stream = oggz_get_stream (oggz, serialno);
514 if (stream == NULL) return OGGZ_ERR_BAD_SERIALNO;
515
516 if (stream->metric_internal && stream->metric_user_data)
517 oggz_free (stream->metric_user_data);
518 stream->metric = metric;
519 stream->metric_user_data = user_data;
520 stream->metric_internal = internal;
521 }
522
523 return 0;
524 }
525
526 int
oggz_set_metric(OGGZ * oggz,long serialno,OggzMetric metric,void * user_data)527 oggz_set_metric (OGGZ * oggz, long serialno,
528 OggzMetric metric, void * user_data)
529 {
530 return oggz_set_metric_internal (oggz, serialno, metric, user_data, 0);
531 }
532
533 /*
534 * Check if a stream in an oggz has a metric
535 */
536 int
oggz_stream_has_metric(OGGZ * oggz,long serialno)537 oggz_stream_has_metric (OGGZ * oggz, long serialno)
538 {
539 oggz_stream_t * stream;
540
541 if (oggz->metric != NULL) return 1;
542
543 stream = oggz_get_stream (oggz, serialno);
544 if (stream == NULL) return OGGZ_ERR_BAD_SERIALNO;
545
546 if (stream->metric != NULL) return 1;
547
548 return 0;
549 }
550
551 /*
552 * Check if an oggz has metrics for all streams
553 */
554 int
oggz_has_metrics(OGGZ * oggz)555 oggz_has_metrics (OGGZ * oggz)
556 {
557 int i, size;
558 oggz_stream_t * stream;
559
560 if (oggz->metric != NULL) return 1;
561
562 size = oggz_vector_size (oggz->streams);
563 for (i = 0; i < size; i++) {
564 stream = (oggz_stream_t *)oggz_vector_nth_p (oggz->streams, i);
565 if (stream->metric == NULL) return 0;
566 }
567
568 return 1;
569 }
570
571 ogg_int64_t
oggz_get_unit(OGGZ * oggz,long serialno,ogg_int64_t granulepos)572 oggz_get_unit (OGGZ * oggz, long serialno, ogg_int64_t granulepos)
573 {
574 oggz_stream_t * stream;
575
576 if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
577
578 if (granulepos == -1) return -1;
579
580 if (serialno == -1) {
581 if (oggz->metric)
582 return oggz->metric (oggz, serialno, granulepos,
583 oggz->metric_user_data);
584 } else {
585 stream = oggz_get_stream (oggz, serialno);
586 if (!stream) return -1;
587
588 if (stream->metric) {
589 return stream->metric (oggz, serialno, granulepos,
590 stream->metric_user_data);
591 } else if (oggz->metric) {
592 return oggz->metric (oggz, serialno, granulepos,
593 oggz->metric_user_data);
594 }
595 }
596
597 return -1;
598 }
599
600 int
oggz_set_order(OGGZ * oggz,long serialno,OggzOrder order,void * user_data)601 oggz_set_order (OGGZ * oggz, long serialno,
602 OggzOrder order, void * user_data)
603 {
604 oggz_stream_t * stream;
605
606 if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
607
608 if (oggz->flags & OGGZ_WRITE) {
609 return OGGZ_ERR_INVALID;
610 }
611
612 if (serialno == -1) {
613 oggz->order = order;
614 oggz->order_user_data = user_data;
615 } else {
616 stream = oggz_get_stream (oggz, serialno);
617 if (stream == NULL) return OGGZ_ERR_BAD_SERIALNO;
618
619 stream->order = order;
620 stream->order_user_data = user_data;
621 }
622
623 return 0;
624 }
625
626 /* Map callback return values to error return values */
627 int
oggz_map_return_value_to_error(int cb_ret)628 oggz_map_return_value_to_error (int cb_ret)
629 {
630 switch (cb_ret) {
631 case OGGZ_CONTINUE:
632 return OGGZ_ERR_OK;
633 case OGGZ_STOP_OK:
634 return OGGZ_ERR_STOP_OK;
635 case OGGZ_STOP_ERR:
636 return OGGZ_ERR_STOP_ERR;
637 default:
638 return cb_ret;
639 }
640 }
641
642 const char *
oggz_content_type(OggzStreamContent content)643 oggz_content_type (OggzStreamContent content)
644 {
645 /* 20080805:
646 * Re: http://lists.xiph.org/pipermail/ogg-dev/2008-July/001108.html
647 *
648 * "The ISO C standard, in section 6.7.2.2 "enumeration specifiers",
649 * paragraph 4, says
650 *
651 * Each enumerated type shall be compatible with *char*, a signed
652 * integer type, or an unsigned integer type. The choice of type is
653 * implementation-defined, but shall be capable of representing the
654 * values of all the members of the declaration."
655 *
656 * -- http://gcc.gnu.org/ml/gcc-bugs/2000-09/msg00271.html
657 *
658 * Hence, we cannot remove the (content < 0) guard, even though current
659 * GCC gives a warning for it -- other compilers (including earlier GCC
660 * versions) may use a signed type for enum OggzStreamContent.
661 */
662 if (
663 #ifdef ALLOW_SIGNED_ENUMS
664 content < OGGZ_CONTENT_THEORA ||
665 #endif
666 content >= OGGZ_CONTENT_UNKNOWN)
667 return NULL;
668
669 return oggz_auto_codec_ident[content].content_type;
670 }
671