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