oggz.c 15.1 KB
Newer Older
andre's avatar
andre committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
/*
   Copyright (C) 2003 Commonwealth Scientific and Industrial Research
   Organisation (CSIRO) Australia

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions
   are met:

   - Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

   - Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

   - Neither the name of CSIRO Australia nor the names of its
   contributors may be used to endorse or promote products derived from
   this software without specific prior written permission.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
   PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE ORGANISATION OR
   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "config.h"

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
40

41
#ifdef HAVE_UNISTD_H
andre's avatar
andre committed
42
#include <unistd.h>
43 44
#endif

andre's avatar
andre committed
45 46 47 48 49 50 51
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <time.h>

#include <ogg/ogg.h>

52
#include "oggz_compat.h"
andre's avatar
andre committed
53
#include "oggz_private.h"
conrad's avatar
conrad committed
54
#include "oggz_vector.h"
andre's avatar
andre committed
55

56 57 58 59
/* Definitions for oggz_run() */
long oggz_read (OGGZ * oggz, long n);
long oggz_write (OGGZ * oggz, long n);

andre's avatar
andre committed
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
/*#define DEBUG*/

static int
oggz_flags_disabled (int flags)
{
 if (flags & OGGZ_WRITE) {
   if (!OGGZ_CONFIG_WRITE) return OGGZ_ERR_DISABLED;
 } else {
   if (!OGGZ_CONFIG_READ) return OGGZ_ERR_DISABLED;
 }

 return 0;
}

OGGZ *
oggz_new (int flags)
{
  OGGZ * oggz;

  if (oggz_flags_disabled (flags)) return NULL;

  oggz = (OGGZ *) oggz_malloc (sizeof (OGGZ));
  if (oggz == NULL) return NULL;

  oggz->flags = flags;
conrad's avatar
conrad committed
85
  oggz->file = NULL;
86
  oggz->io = NULL;
andre's avatar
andre committed
87 88 89 90

  oggz->offset = 0;
  oggz->offset_data_begin = 0;

91
  oggz->run_blocksize = 1024;
92 93
  oggz->cb_next = 0;

conrad's avatar
conrad committed
94
  oggz->streams = oggz_vector_new ();
95 96 97 98
  if (oggz->streams == NULL) {
    goto err_oggz_new;
  }
  
andre's avatar
andre committed
99 100 101 102 103 104 105 106 107
  oggz->all_at_eos = 0;

  oggz->metric = NULL;
  oggz->metric_user_data = NULL;
  oggz->metric_internal = 0;

  oggz->order = NULL;
  oggz->order_user_data = NULL;

108
  oggz->packet_buffer = oggz_dlist_new ();
109 110 111
  if (oggz->packet_buffer == NULL) {
    goto err_streams_new;
  }
112

andre's avatar
andre committed
113
  if (OGGZ_CONFIG_WRITE && (oggz->flags & OGGZ_WRITE)) {
114 115
    if (oggz_write_init (oggz) == NULL)
      goto err_packet_buffer_new;
andre's avatar
andre committed
116 117 118 119 120
  } else if (OGGZ_CONFIG_READ) {
    oggz_read_init (oggz);
  }

  return oggz;
121 122 123 124 125 126 127 128

err_packet_buffer_new:
  oggz_free (oggz->packet_buffer);
err_streams_new:
  oggz_free (oggz->streams);
err_oggz_new:
  oggz_free (oggz);
  return NULL;
andre's avatar
andre committed
129 130 131 132 133 134
}

OGGZ *
oggz_open (char * filename, int flags)
{
  OGGZ * oggz = NULL;
conrad's avatar
conrad committed
135
  FILE * file = NULL;
andre's avatar
andre committed
136 137 138 139

  if (oggz_flags_disabled (flags)) return NULL;

  if (flags & OGGZ_WRITE) {
140
    file = fopen (filename, "wb");
andre's avatar
andre committed
141
  } else {
142
    file = fopen (filename, "rb");
andre's avatar
andre committed
143
  }
conrad's avatar
conrad committed
144
  if (file == NULL) return NULL;
andre's avatar
andre committed
145

146 147
  if ((oggz = oggz_new (flags)) == NULL) {
    fclose (file);
148
    return NULL;
149
  }
150

conrad's avatar
conrad committed
151
  oggz->file = file;
andre's avatar
andre committed
152 153 154 155 156

  return oggz;
}

OGGZ *
conrad's avatar
conrad committed
157
oggz_open_stdio (FILE * file, int flags)
andre's avatar
andre committed
158 159 160 161 162
{
  OGGZ * oggz = NULL;

  if (oggz_flags_disabled (flags)) return NULL;

163 164 165
  if ((oggz = oggz_new (flags)) == NULL)
    return NULL;

conrad's avatar
conrad committed
166
  oggz->file = file;
andre's avatar
andre committed
167 168 169 170 171 172 173 174 175

  return oggz;
}

int
oggz_flush (OGGZ * oggz)
{
  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

176 177 178 179
  if (OGGZ_CONFIG_WRITE && (oggz->flags & OGGZ_WRITE)) {
    oggz_write_flush (oggz);
  }

180
  return oggz_io_flush (oggz);
andre's avatar
andre committed
181 182 183 184 185 186 187
}

static int
oggz_stream_clear (void * data)
{
  oggz_stream_t * stream = (oggz_stream_t *) data;

188 189
  oggz_comments_free (stream);

andre's avatar
andre committed
190 191 192 193 194 195
  if (stream->ogg_stream.serialno != -1)
    ogg_stream_clear (&stream->ogg_stream);

  if (stream->metric_internal)
    oggz_free (stream->metric_user_data);

196 197 198
  if (stream->calculate_data != NULL)
    oggz_free (stream->calculate_data);
  
199 200
  oggz_free (stream);

andre's avatar
andre committed
201 202 203 204 205 206 207 208 209 210 211 212 213 214
  return 0;
}

int
oggz_close (OGGZ * oggz)
{
  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

  if (OGGZ_CONFIG_WRITE && (oggz->flags & OGGZ_WRITE)) {
    oggz_write_close (oggz);
  } else if (OGGZ_CONFIG_READ) {
    oggz_read_close (oggz);
  }

215 216 217
  oggz_vector_foreach (oggz->streams, oggz_stream_clear);
  oggz_vector_delete (oggz->streams);

218
  oggz_dlist_deliter(oggz->packet_buffer, oggz_read_free_pbuffers);
219 220
  oggz_dlist_delete(oggz->packet_buffer);
  
221 222 223
  if (oggz->metric_internal)
    oggz_free (oggz->metric_user_data);

conrad's avatar
conrad committed
224 225
  if (oggz->file != NULL) {
    if (fclose (oggz->file) == EOF) {
andre's avatar
andre committed
226 227 228 229
      return OGGZ_ERR_SYSTEM;
    }
  }

230
  if (oggz->io != NULL) {
231
    oggz_io_flush (oggz);
232 233 234
    oggz_free (oggz->io);
  }

andre's avatar
andre committed
235 236 237 238 239
  oggz_free (oggz);

  return 0;
}

240
oggz_off_t
andre's avatar
andre committed
241 242 243 244 245 246 247
oggz_tell (OGGZ * oggz)
{
  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

  return oggz->offset;
}

conrad's avatar
conrad committed
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
ogg_int64_t
oggz_tell_units (OGGZ * oggz)
{
  OggzReader * reader;

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

  if (oggz->flags & OGGZ_WRITE) {
    return OGGZ_ERR_INVALID;
  }

  reader = &oggz->x.reader;

  if (OGGZ_CONFIG_READ) {
    return reader->current_unit;
  } else {
    return OGGZ_ERR_DISABLED;
  }
}

268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
ogg_int64_t
oggz_tell_granulepos (OGGZ * oggz)
{
  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
  
  if (oggz->flags & OGGZ_WRITE) {
    return OGGZ_ERR_INVALID;
  }

  if (OGGZ_CONFIG_READ) {
    return oggz->x.reader.current_granulepos;
  } else {
    return OGGZ_ERR_DISABLED;
  }
}

284 285 286 287 288 289 290 291
long
oggz_run (OGGZ * oggz)
{
  long n = OGGZ_ERR_DISABLED;

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

  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 298 299
  }

  return n;
}

300 301 302 303 304 305 306 307 308 309 310 311
int
oggz_run_set_blocksize (OGGZ * oggz, long blocksize)
{
  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

  if (blocksize <= 0) return OGGZ_ERR_INVALID;

  oggz->run_blocksize = blocksize;

  return 0;
}

andre's avatar
andre committed
312 313 314 315 316 317 318 319 320 321 322 323 324
/******** oggz_stream management ********/

static int
oggz_find_stream (void * data, long serialno)
{
  oggz_stream_t * stream = (oggz_stream_t *) data;

  return (stream->ogg_stream.serialno == serialno);
}

oggz_stream_t *
oggz_get_stream (OGGZ * oggz, long serialno)
{
325
  if (serialno == -1) return NULL;
andre's avatar
andre committed
326

327
  return oggz_vector_find_with (oggz->streams, oggz_find_stream, serialno);
andre's avatar
andre committed
328 329 330 331 332 333 334 335 336 337 338 339
}

oggz_stream_t *
oggz_add_stream (OGGZ * oggz, long serialno)
{
  oggz_stream_t * stream;

  stream = oggz_malloc (sizeof (oggz_stream_t));
  if (stream == NULL) return NULL;

  ogg_stream_init (&stream->ogg_stream, (int)serialno);

340 341 342 343
  if (oggz_comments_init (stream) == -1) {
    oggz_free (stream);
    return NULL;
  }
344

345
  stream->content = OGGZ_CONTENT_UNKNOWN;
346
  stream->numheaders = 3; /* Default to 3 headers for Ogg logical bitstreams */
347 348 349
  stream->preroll = 0;
  stream->granulerate_n = 1;
  stream->granulerate_d = 1;
350
  stream->first_granule = 0;
351 352
  stream->basegranule = 0;
  stream->granuleshift = 0;
353

andre's avatar
andre committed
354 355 356 357
  stream->delivered_non_b_o_s = 0;
  stream->b_o_s = 1;
  stream->e_o_s = 0;
  stream->granulepos = 0;
358
  stream->packetno = -1; /* will be incremented on first read or write */
andre's avatar
andre committed
359 360 361 362 363 364 365 366

  stream->metric = NULL;
  stream->metric_user_data = NULL;
  stream->metric_internal = 0;
  stream->order = NULL;
  stream->order_user_data = NULL;
  stream->read_packet = NULL;
  stream->read_user_data = NULL;
367 368
  stream->read_page = NULL;
  stream->read_page_user_data = NULL;
andre's avatar
andre committed
369

370 371
  stream->calculate_data = NULL;
  
conrad's avatar
conrad committed
372
  oggz_vector_insert_p (oggz->streams, stream);
andre's avatar
andre committed
373 374 375 376 377 378 379 380

  return stream;
}

int
oggz_get_bos (OGGZ * oggz, long serialno)
{
  oggz_stream_t * stream;
conrad's avatar
conrad committed
381
  int i, size;
andre's avatar
andre committed
382 383 384 385

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

  if (serialno == -1) {
conrad's avatar
conrad committed
386 387 388
    size = oggz_vector_size (oggz->streams);
    for (i = 0; i < size; i++) {
      stream = (oggz_stream_t *)oggz_vector_nth_p (oggz->streams, i);
andre's avatar
andre committed
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
#if 1
      /* If this stream has delivered a non bos packet, return FALSE */
      if (stream->delivered_non_b_o_s) return 0;
#else
      /* If this stream has delivered its bos packet, return FALSE */
      if (!stream->b_o_s) return 0;
#endif
    }
    return 1;
  } else {
    stream = oggz_get_stream (oggz, serialno);
    if (stream == NULL)
      return OGGZ_ERR_BAD_SERIALNO;

    return stream->b_o_s;
  }
}

int
oggz_get_eos (OGGZ * oggz, long serialno)
{
  oggz_stream_t * stream;
conrad's avatar
conrad committed
411
  int i, size;
andre's avatar
andre committed
412 413 414 415

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

  if (serialno == -1) {
conrad's avatar
conrad committed
416 417 418
    size = oggz_vector_size (oggz->streams);
    for (i = 0; i < size; i++) {
      stream = (oggz_stream_t *)oggz_vector_nth_p (oggz->streams, i);
andre's avatar
andre committed
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
      if (!stream->e_o_s) return 0;
    }
    return 1;
  } else {
    stream = oggz_get_stream (oggz, serialno);
    if (stream == NULL)
      return OGGZ_ERR_BAD_SERIALNO;

    return stream->e_o_s;
  }
}

int
oggz_set_eos (OGGZ * oggz, long serialno)
{
  oggz_stream_t * stream;
conrad's avatar
conrad committed
435
  int i, size;
andre's avatar
andre committed
436 437 438 439

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

  if (serialno == -1) {
conrad's avatar
conrad committed
440 441 442
    size = oggz_vector_size (oggz->streams);
    for (i = 0; i < size; i++) {
      stream = (oggz_stream_t *) oggz_vector_nth_p (oggz->streams, i);
andre's avatar
andre committed
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
      stream->e_o_s = 1;
    }
    oggz->all_at_eos = 1;
  } else {
    stream = oggz_get_stream (oggz, serialno);
    if (stream == NULL)
      return OGGZ_ERR_BAD_SERIALNO;

    stream->e_o_s = 1;

    if (oggz_get_eos (oggz, -1))
      oggz->all_at_eos = 1;
  }

  return 0;
}

460 461 462 463 464 465 466
int
oggz_get_numtracks (OGGZ * oggz)
{
  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
  return oggz_vector_size (oggz->streams);
}

467 468 469 470 471 472 473 474
/* Generate a pseudorandom serialno on request, ensuring that the number
 * selected is not -1 or the serialno of an existing logical bitstream.
 * NB. This inlines a simple linear congruential generator to avoid problems
 * of portability of rand() vs. the POSIX random()/initstate()/getstate(), and
 * in the case of rand() in order to avoid interfering with the random number
 * sequence.
 * Adapated from a patch by Erik de Castro Lopo, July 2007.
 */
andre's avatar
andre committed
475 476 477
long
oggz_serialno_new (OGGZ * oggz)
{
478 479 480 481
  /* Ensure that the returned value is within the range of an int, so that
   * it passes cleanly through ogg_stream_init(). In any case, the size of
   * a serialno in the Ogg page header is 32 bits; it should never have been
   * declared a long in ogg.h's ogg_packet in the first place. */
482
  static ogg_int32_t serialno = 0;
483 484 485
  int k;

  if (serialno == 0) serialno = time(NULL);
andre's avatar
andre committed
486 487

  do {
488 489 490
    for (k = 0; k < 3 || serialno == 0; k++)
      serialno = 11117 * serialno + 211231;
  } while (serialno == -1 || oggz_get_stream (oggz, serialno) != NULL);
andre's avatar
andre committed
491

492 493
  /* Cast the result back to a long for API consistency */
  return (long)serialno;
andre's avatar
andre committed
494 495 496 497
}

/******** OggzMetric management ********/

conrad's avatar
conrad committed
498
int
andre's avatar
andre committed
499 500 501 502 503 504 505 506
oggz_set_metric_internal (OGGZ * oggz, long serialno,
			  OggzMetric metric, void * user_data, int internal)
{
  oggz_stream_t * stream;

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

  if (serialno == -1) {
507
    if (oggz->metric_internal && oggz->metric_user_data)
andre's avatar
andre committed
508 509 510 511 512 513 514 515
      oggz_free (oggz->metric_user_data);
    oggz->metric = metric;
    oggz->metric_user_data = user_data;
    oggz->metric_internal = internal;
  } else {
    stream = oggz_get_stream (oggz, serialno);
    if (stream == NULL) return OGGZ_ERR_BAD_SERIALNO;

516
    if (stream->metric_internal && stream->metric_user_data)
andre's avatar
andre committed
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
      oggz_free (stream->metric_user_data);
    stream->metric = metric;
    stream->metric_user_data = user_data;
    stream->metric_internal = internal;
  }

  return 0;
}

int
oggz_set_metric (OGGZ * oggz, long serialno,
		 OggzMetric metric, void * user_data)
{
  return oggz_set_metric_internal (oggz, serialno, metric, user_data, 0);
}

533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
/*
 * Check if a stream in an oggz has a metric
 */
int
oggz_stream_has_metric (OGGZ * oggz, long serialno)
{
  oggz_stream_t * stream;

  if (oggz->metric != NULL) return 1;

  stream = oggz_get_stream (oggz, serialno);
  if (stream == NULL) return OGGZ_ERR_BAD_SERIALNO;

  if (stream->metric != NULL) return 1;

  return 0;
}

551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
/*
 * Check if an oggz has metrics for all streams
 */
int
oggz_has_metrics (OGGZ * oggz)
{
  int i, size;
  oggz_stream_t * stream;

  if (oggz->metric != NULL) return 1;

  size = oggz_vector_size (oggz->streams);
  for (i = 0; i < size; i++) {
    stream = (oggz_stream_t *)oggz_vector_nth_p (oggz->streams, i);
    if (stream->metric == NULL) return 0;
  }

  return 1;
}

andre's avatar
andre committed
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
ogg_int64_t
oggz_get_unit (OGGZ * oggz, long serialno, ogg_int64_t granulepos)
{
  oggz_stream_t * stream;

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

  if (granulepos == -1) return -1;

  if (serialno == -1) {
    if (oggz->metric)
      return oggz->metric (oggz, serialno, granulepos,
			   oggz->metric_user_data);
  } else {
    stream = oggz_get_stream (oggz, serialno);
    if (!stream) return -1;

    if (stream->metric) {
      return stream->metric (oggz, serialno, granulepos,
			     stream->metric_user_data);
    } else if (oggz->metric) {
      return oggz->metric (oggz, serialno, granulepos,
			   oggz->metric_user_data);
    }
  }

  return -1;
}

int
oggz_set_order (OGGZ * oggz, long serialno,
		OggzOrder order, void * user_data)
{
  oggz_stream_t * stream;

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

  if (oggz->flags & OGGZ_WRITE) {
    return OGGZ_ERR_INVALID;
  }

  if (serialno == -1) {
    oggz->order = order;
    oggz->order_user_data = user_data;
  } else {
    stream = oggz_get_stream (oggz, serialno);
conrad's avatar
conrad committed
617 618
    if (stream == NULL) return OGGZ_ERR_BAD_SERIALNO;

andre's avatar
andre committed
619 620 621 622 623 624 625
    stream->order = order;
    stream->order_user_data = user_data;
  }

  return 0;
}

626 627 628 629 630 631 632 633 634 635 636 637
/* Map callback return values to error return values */
int
oggz_map_return_value_to_error (int cb_ret)
{
  switch (cb_ret) {
  case OGGZ_CONTINUE:
    return OGGZ_ERR_OK;
  case OGGZ_STOP_OK:
    return OGGZ_ERR_STOP_OK;
  case OGGZ_STOP_ERR:
    return OGGZ_ERR_STOP_ERR;
  default:
638
    return cb_ret;
639 640 641
  }
}

642 643 644
const char *
oggz_content_type (OggzStreamContent content)
{
conrad's avatar
conrad committed
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
  /* 20080805:
   * Re: http://lists.xiph.org/pipermail/ogg-dev/2008-July/001108.html
   *
   * "The ISO C standard, in section 6.7.2.2 "enumeration specifiers",
   * paragraph 4, says
   *
   *   Each enumerated type shall be compatible with *char*, a signed
   *   integer type, or an unsigned integer type.  The choice of type is
   *   implementation-defined, but shall be capable of representing the
   *   values of all the members of the declaration."
   *
   * -- http://gcc.gnu.org/ml/gcc-bugs/2000-09/msg00271.html
   *
   *  Hence, we cannot remove the (content < 0) guard, even though current
   *  GCC gives a warning for it -- other compilers (including earlier GCC
   *  versions) may use a signed type for enum OggzStreamContent.
   */
662 663 664 665 666
  if (
#ifdef ALLOW_SIGNED_ENUMS
      content < OGGZ_CONTENT_THEORA ||
#endif
      content >= OGGZ_CONTENT_UNKNOWN)
667 668 669 670
    return NULL;

  return oggz_auto_codec_ident[content].content_type;
}