oggz_read.c 21.2 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 40 41 42 43 44 45 46 47
/*
   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.
*/

/*
 * oggz_read.c
 *
 * Conrad Parker <conrad@annodex.net>
 */

#include "config.h"

#if OGGZ_CONFIG_READ

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

49
#ifdef HAVE_UNISTD_H
andre's avatar
andre committed
50
#include <unistd.h>
51 52
#endif

andre's avatar
andre committed
53 54 55 56 57 58 59
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <time.h>

#include <ogg/ogg.h>

60
#include "oggz_compat.h"
andre's avatar
andre committed
61 62
#include "oggz_private.h"

63
#include "oggz/oggz_packet.h"
64
#include "oggz/oggz_stream.h"
65

66 67
/* #define DEBUG */
/* #define DEBUG_VERBOSE */
andre's avatar
andre committed
68

69
#define CHUNKSIZE 65536
andre's avatar
andre committed
70

71
#define OGGZ_READ_EMPTY (-404)
72

andre's avatar
andre committed
73 74 75 76 77 78 79 80 81 82 83 84
OGGZ *
oggz_read_init (OGGZ * oggz)
{
  OggzReader * reader = &oggz->x.reader;

  ogg_sync_init (&reader->ogg_sync);
  ogg_stream_init (&reader->ogg_stream, (int)-1);
  reader->current_serialno = -1;

  reader->read_packet = NULL;
  reader->read_user_data = NULL;

85 86 87
  reader->read_page = NULL;
  reader->read_page_user_data = NULL;

andre's avatar
andre committed
88 89
  reader->current_unit = 0;

90 91
  reader->current_page_bytes = 0;

92 93 94
  reader->current_packet_begin_page_offset = 0;
  reader->current_packet_pages = 0;

andre's avatar
andre committed
95 96 97 98 99 100 101 102
  return oggz;
}

OGGZ *
oggz_read_close (OGGZ * oggz)
{
  OggzReader * reader = &oggz->x.reader;

103
  ogg_stream_clear (&reader->ogg_stream);
andre's avatar
andre committed
104 105 106 107 108 109 110
  ogg_sync_clear (&reader->ogg_sync);

  return oggz;
}

int
oggz_set_read_callback (OGGZ * oggz, long serialno,
111
                        OggzReadPacket read_packet, void * user_data)
andre's avatar
andre committed
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
{
  OggzReader * reader;
  oggz_stream_t * stream;

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

  reader =  &oggz->x.reader;

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

  if (serialno == -1) {
    reader->read_packet = read_packet;
    reader->read_user_data = user_data;
  } else {
    stream = oggz_get_stream (oggz, serialno);
conrad's avatar
conrad committed
129 130
    if (stream == NULL)
      stream = oggz_add_stream (oggz, serialno);
131 132
    if (stream == NULL)
      return OGGZ_ERR_OUT_OF_MEMORY;
andre's avatar
andre committed
133 134 135 136 137 138 139 140

    stream->read_packet = read_packet;
    stream->read_user_data = user_data;
  }

  return 0;
}

141
int
142
oggz_set_read_page (OGGZ * oggz, long serialno, OggzReadPage read_page,
143
                    void * user_data)
144 145
{
  OggzReader * reader;
146
  oggz_stream_t * stream;
147 148 149 150 151 152 153 154 155

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

  reader =  &oggz->x.reader;

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

156 157 158 159 160 161 162
  if (serialno == -1) {
    reader->read_page = read_page;
    reader->read_page_user_data = user_data;
  } else {
    stream = oggz_get_stream (oggz, serialno);
    if (stream == NULL)
      stream = oggz_add_stream (oggz, serialno);
163 164
    if (stream == NULL)
      return OGGZ_ERR_OUT_OF_MEMORY;
165 166 167 168

    stream->read_page = read_page;
    stream->read_page_user_data = user_data;
  }
169 170 171 172

  return 0;
}

andre's avatar
andre committed
173
/*
174
 * oggz_read_get_next_page (oggz, og, do_read)
andre's avatar
andre committed
175
 *
176 177
 * This differs from oggz_get_next_page() in oggz_seek.c in that it
 * does not attempt to call oggz_io_read() if the sync buffer is empty.
andre's avatar
andre committed
178 179 180 181 182 183
 *
 * retrieves the next page.
 * returns >= 0 if found; return value is offset of page start
 * returns -1 on error
 * returns -2 if EOF was encountered
 */
conrad's avatar
conrad committed
184
static oggz_off_t
185
oggz_read_get_next_page (OGGZ * oggz, ogg_page * og)
andre's avatar
andre committed
186 187
{
  OggzReader * reader = &oggz->x.reader;
188
  long more;
andre's avatar
andre committed
189 190
  int found = 0;

191
  /* Increment oggz->offset by length of the last page processed */
192 193 194 195
#ifdef DEBUG_VERBOSE
  printf ("%s: incrementing oggz->offset (0x%llx) by 0x%lx\n", __func__,
          oggz->offset, reader->current_page_bytes);
#endif
196 197
  oggz->offset += reader->current_page_bytes;

andre's avatar
andre committed
198 199 200 201
  do {
    more = ogg_sync_pageseek (&reader->ogg_sync, og);

    if (more == 0) {
202
      /* No page available */
andre's avatar
andre committed
203 204
      return -2;
    } else if (more < 0) {
205
#ifdef DEBUG_VERBOSE
206
  printf ("%s: skipping; incrementing oggz->offset by 0x%lx bytes\n", __func__, -more);
andre's avatar
andre committed
207
#endif
208
      oggz->offset += (-more);
andre's avatar
andre committed
209
    } else {
210
#ifdef DEBUG_VERBOSE
andre's avatar
andre committed
211 212
      printf ("get_next_page: page has %ld bytes\n", more);
#endif
213
      reader->current_page_bytes = more;
andre's avatar
andre committed
214 215 216 217 218
      found = 1;
    }

  } while (!found);

219
  return oggz->offset;
andre's avatar
andre committed
220 221
}

222
typedef struct {
223
  oggz_packet     zp;
224 225 226 227 228 229 230
  oggz_stream_t * stream;
  OggzReader    * reader;
  OGGZ          * oggz;
  long            serialno;
} OggzBufferedPacket;

OggzBufferedPacket *
231
oggz_read_new_pbuffer_entry(OGGZ *oggz, oggz_packet * zp, 
232 233
                            long serialno, oggz_stream_t * stream, 
                            OggzReader *reader)
234 235 236
{
  OggzBufferedPacket *p;
  ogg_packet * op = &zp->op;
237

238 239 240
  if ((p = oggz_malloc(sizeof(OggzBufferedPacket))) == NULL)
    return NULL;
  memcpy(&(p->zp), zp, sizeof(oggz_packet));
241

242 243 244 245 246
  if ((p->zp.op.packet = oggz_malloc(op->bytes)) == NULL) {
    oggz_free (p);
    return NULL;
  }
  memcpy(p->zp.op.packet, op->packet, op->bytes);
247 248 249 250 251 252 253 254 255 256

  p->stream = stream;
  p->serialno = serialno;
  p->reader = reader;
  p->oggz = oggz;

  return p;
}

void
257 258 259
oggz_read_free_pbuffer_entry(OggzBufferedPacket *p)
{
  oggz_free(p->zp.op.packet);
260
  oggz_free(p);
261 262
}

263 264 265 266 267 268 269 270 271 272
OggzDListIterResponse
oggz_read_free_pbuffers(void *elem)
{
  OggzBufferedPacket *p = (OggzBufferedPacket *)elem;

  oggz_read_free_pbuffer_entry(p);

  return DLIST_ITER_CONTINUE;
}

273 274 275 276 277
OggzDListIterResponse
oggz_read_update_gp(void *elem) {

  OggzBufferedPacket *p = (OggzBufferedPacket *)elem;

278
  if (p->zp.pos.calc_granulepos == -1 && p->stream->last_granulepos != -1) {
279
    int content = oggz_stream_get_content(p->oggz, p->serialno);
280 281 282 283 284 285

    /* Cancel the iteration (backwards through buffered packets)
     * if we don't know the codec */
    if (content < 0 || content >= OGGZ_CONTENT_UNKNOWN)
      return DLIST_ITER_CANCEL;

286
    p->zp.pos.calc_granulepos = 
287
      oggz_auto_calculate_gp_backwards(content, p->stream->last_granulepos,
288 289
                                       p->stream, &(p->zp.op),
                                       p->stream->last_packet);
290
      
291
    p->stream->last_granulepos = p->zp.pos.calc_granulepos;
292
    p->stream->last_packet = &(p->zp.op);
293 294 295 296 297 298 299 300 301 302
  }

  return DLIST_ITER_CONTINUE;
}

OggzDListIterResponse
oggz_read_deliver_packet(void *elem) {

  OggzBufferedPacket *p = (OggzBufferedPacket *)elem;
  ogg_int64_t gp_stored;
303
  ogg_int64_t unit_stored;
304

305
  if (p->zp.pos.calc_granulepos == -1) {
306 307 308 309 310 311
    return DLIST_ITER_CANCEL;
  }

  gp_stored = p->reader->current_granulepos;
  unit_stored = p->reader->current_unit;

312
  p->reader->current_granulepos = p->zp.pos.calc_granulepos;
313 314

  p->reader->current_unit =
315
    oggz_get_unit (p->oggz, p->serialno, p->zp.pos.calc_granulepos);
316 317

  if (p->stream->read_packet) {
318 319
    p->stream->read_packet(p->oggz, &(p->zp), p->serialno, 
                           p->stream->read_user_data);
320
  } else if (p->reader->read_packet) {
321 322
    p->reader->read_packet(p->oggz, &(p->zp), p->serialno, 
                           p->reader->read_user_data);
323 324 325 326 327 328 329 330 331 332
  }

  p->reader->current_granulepos = gp_stored;
  p->reader->current_unit = unit_stored;

  oggz_read_free_pbuffer_entry(p);

  return DLIST_ITER_CONTINUE;
}

333
static int
andre's avatar
andre committed
334 335 336 337 338 339 340
oggz_read_sync (OGGZ * oggz)
{
  OggzReader * reader = &oggz->x.reader;

  oggz_stream_t * stream;
  ogg_stream_state * os;
  ogg_packet * op;
341
  oggz_position * pos;
andre's avatar
andre committed
342 343
  long serialno;

344
  oggz_packet packet;
andre's avatar
andre committed
345 346
  ogg_page og;

347 348
  int cb_ret = 0;

andre's avatar
andre committed
349
  /*os = &reader->ogg_stream;*/
350 351
  op = &packet.op;
  pos = &packet.pos;
andre's avatar
andre committed
352 353 354

  /* handle one packet.  Try to fetch it from current stream state */
  /* extract packets from page */
355
  while(cb_ret == 0) {
andre's avatar
andre committed
356 357

    if (reader->current_serialno != -1) {
358 359
    /* process a packet if we can.  If the machine isn't loaded,
       neither is a page */
360 361 362
      while(cb_ret == 0) {
        ogg_int64_t granulepos;
        int result;
andre's avatar
andre committed
363

364
        serialno = reader->current_serialno;
andre's avatar
andre committed
365

366
        stream = oggz_get_stream (oggz, serialno);
367

368
        if (stream == NULL) {
369 370 371
          /* new stream ... check bos etc. */
          if ((stream = oggz_add_stream (oggz, serialno)) == NULL) {
            /* error -- could not add stream */
372
            return OGGZ_ERR_OUT_OF_MEMORY;
373
          }
374 375
        }
        os = &stream->ogg_stream;
376

377
        result = ogg_stream_packetout(os, op);
andre's avatar
andre committed
378

379 380
        /* libogg flags "holes in the data" (which are really inconsistencies
         * in the page sequence number) by returning -1. */
381
        if(result == -1) {
382
#ifdef DEBUG
383
          printf ("oggz_read_sync: hole in the data\n");
384
#endif
385 386 387 388
          /* We can't tolerate holes in headers, so bail out. NB. as stream->packetno
           * has not yet been incremented, the current value refers to how many packets
           * have been processed prior to this one. */
          if (stream->packetno < 2) return OGGZ_ERR_HOLE_IN_DATA;
389 390

          /* Holes in content occur in some files and pretty much don't matter,
391
           * so we silently swallow the notification and reget the packet. */
392 393
          result = ogg_stream_packetout(os, op);
          if (result == -1) {
394
            /* If the result is *still* -1 then something strange is happening. */
395 396
#ifdef DEBUG
            printf ("Multiple holes in data!");
397
#endif
398
            return OGGZ_ERR_HOLE_IN_DATA;
399
          }
400 401 402 403

          /* Reset the position of the next page. */
          reader->current_packet_pages = 1;
          reader->current_packet_begin_page_offset = oggz->offset;
404
          reader->current_packet_begin_segment_index = 1;
405
        }
andre's avatar
andre committed
406

407 408
        if(result > 0){
          int content;
409 410

          stream->packetno++;
411
          
412
          /* Got a packet.  process it ... */
413
          granulepos = op->granulepos;
andre's avatar
andre committed
414

415
          content = oggz_stream_get_content(oggz, serialno);
416 417 418 419 420 421 422 423
          if (content < 0 || content >= OGGZ_CONTENT_UNKNOWN) {
            reader->current_granulepos = granulepos;
	  } else {
            /* if we have no metrics for this stream yet, then generate them */      
            if ((!stream->metric || content == OGGZ_CONTENT_SKELETON) && 
                (oggz->flags & OGGZ_AUTO)) {
              oggz_auto_read_bos_packet (oggz, op, serialno, NULL);
            }
424

425 426 427 428 429 430 431 432 433
            /* attempt to determine granulepos for this packet */
            if (oggz->flags & OGGZ_AUTO) {
              reader->current_granulepos = 
                oggz_auto_calculate_granulepos (content, granulepos, stream, op); 
              /* make sure that we accept any "real" gaps in the granulepos */
              if (granulepos != -1 && reader->current_granulepos < granulepos) {
                reader->current_granulepos = granulepos;
              }
            } else {
434 435
              reader->current_granulepos = granulepos;
            }
436 437
	  }

438
          stream->last_granulepos = reader->current_granulepos;
439
        
440 441
          /* Set unit on last packet of page */
          if ((oggz->metric || stream->metric) && reader->current_granulepos != -1) {
442 443 444 445
            reader->current_unit =
              oggz_get_unit (oggz, serialno, reader->current_granulepos);
          }

446 447 448
          if (stream->packetno == 1) {
            oggz_auto_read_comments (oggz, stream, serialno, op);
          }
449
          
450 451 452 453 454
          /* Fill in position information. */
          pos->calc_granulepos = reader->current_granulepos;
          pos->begin_page_offset = reader->current_packet_begin_page_offset;
          pos->end_page_offset = oggz->offset;
          pos->pages = reader->current_packet_pages;
455
          pos->begin_segment_index = reader->current_packet_begin_segment_index;
456 457

          /* Handle reverse buffering */
458
          if (oggz->flags & OGGZ_AUTO) {
459
            /* While we are getting invalid granulepos values, store the 
460 461
             * incoming packets in a dlist */
            if (reader->current_granulepos == -1) {
462
              OggzBufferedPacket *p;
463

464 465
              p = oggz_read_new_pbuffer_entry (oggz, &packet,
                                               serialno, stream, reader);
466
              oggz_dlist_append(oggz->packet_buffer, p);
467

468
              goto prepare_position;
469
            } else if (!oggz_dlist_is_empty(oggz->packet_buffer)) {
470
              /* Move backward through the list assigning gp values based upon
471 472
               * the granulepos we just recieved.  Then move forward through
               * the list delivering any packets at the beginning with valid
473
               * gp values.
474 475
               */
              ogg_int64_t gp_stored = stream->last_granulepos;
476
              stream->last_packet = op;
477 478 479
              oggz_dlist_reverse_iter(oggz->packet_buffer, oggz_read_update_gp);
              oggz_dlist_deliter(oggz->packet_buffer, oggz_read_deliver_packet);

480
              /* Fix up the stream granulepos. */
481 482 483
              stream->last_granulepos = gp_stored;

              if (!oggz_dlist_is_empty(oggz->packet_buffer)) {
484 485 486 487
                OggzBufferedPacket *p;

                p = oggz_read_new_pbuffer_entry(oggz, &packet,
                                                serialno, stream, reader);
488 489

                oggz_dlist_append(oggz->packet_buffer, p);
490

491
                goto prepare_position;
492
              }
493 494 495
            }
          }

496 497 498
#ifdef DEBUG_VERBOSE
          printf ("%s: set begin_page to %llx, calling read_packet\n", __func__, pos->begin_page_offset);
#endif
499

500 501
          if (stream->read_packet) {
            cb_ret =
502
              stream->read_packet (oggz, &packet, serialno, stream->read_user_data);
503 504
          } else if (reader->read_packet) {
            cb_ret =
505
              reader->read_packet (oggz, &packet, serialno, reader->read_user_data);
506
          }
507

508 509 510 511
#ifdef DEBUG_VERBOSE
          fprintf (stdout, "%s: Done packet, setting next begin_page to 0x%llx\n", __func__, oggz->offset);
#endif

512 513
prepare_position:

514
          /* Prepare the position of the next page. */
515 516 517 518 519 520 521 522 523
          if (reader->current_packet_begin_page_offset == oggz->offset) {
            /* The previous packet processed also started on this page */
            reader->current_packet_begin_segment_index++;
          } else {
            /* The previous packet started on an earlier page */
            reader->current_packet_begin_page_offset = oggz->offset;
            reader->current_packet_begin_segment_index = 1;
          }

524 525
          reader->current_packet_pages = 1;

526 527 528 529 530
          /* Mark this stream as having delivered a non b_o_s packet if so.
           * In the case where there is no packet reading callback, this is
           * also valid as the page reading callback has already been called.
           */
          if (!op->b_o_s) stream->delivered_non_b_o_s = 1;
531 532
        }
        else
533
          break;
andre's avatar
andre committed
534 535
      }
    }
536

537
    /* If we've got a stop already, don't read more data in */
538
    if (cb_ret == OGGZ_STOP_OK || cb_ret == OGGZ_STOP_ERR) return cb_ret;
andre's avatar
andre committed
539

540
    if(oggz_read_get_next_page (oggz, &og) < 0)
541
      return OGGZ_READ_EMPTY; /* eof. leave uninitialized */
andre's avatar
andre committed
542

543
    serialno = ogg_page_serialno (&og);
544
    reader->current_serialno = serialno;
545 546

    stream = oggz_get_stream (oggz, serialno);
547

548 549 550
    if (stream == NULL) {
      /* new stream ... check bos etc. */
      if ((stream = oggz_add_stream (oggz, serialno)) == NULL) {
551
        /* error -- could not add stream */
552
        return OGGZ_ERR_OUT_OF_MEMORY;
andre's avatar
andre committed
553
      }
554

555
      /* identify stream type */
556
      oggz_auto_identify_page (oggz, &og, serialno);
557 558

      /* read bos data */
559
      if (oggz->flags & OGGZ_AUTO) {
560
        oggz_auto_read_bos_page (oggz, &og, serialno, NULL);
561 562 563
      }
    } else if (oggz_stream_get_content(oggz, serialno) == OGGZ_CONTENT_ANXDATA) {
      /* re-identify ANXDATA streams as these are now content streams */
564
      oggz_auto_identify_page (oggz, &og, serialno);
565
    }
566

567
    os = &stream->ogg_stream;
andre's avatar
andre committed
568

569
    {
570 571 572
      ogg_int64_t granulepos;

      granulepos = ogg_page_granulepos (&og);
573
      stream->page_granulepos = granulepos;
574

575
      if ((oggz->metric || stream->metric) && granulepos != -1) {
576
       reader->current_unit = oggz_get_unit (oggz, serialno, granulepos);
577
      } else if (granulepos == 0) {
578
       reader->current_unit = 0;
andre's avatar
andre committed
579
      }
580
    }
581

582 583
    if (stream->read_page) {
      cb_ret =
584
        stream->read_page (oggz, &og, serialno, stream->read_page_user_data);
585
    } else if (reader->read_page) {
586 587
      cb_ret =
        reader->read_page (oggz, &og, serialno, reader->read_page_user_data);
588
    }
andre's avatar
andre committed
589

590
    ogg_stream_pagein(os, &og);
591 592 593 594
    if (ogg_page_continued(&og)) {
      if (reader->current_packet_pages != -1)
        reader->current_packet_pages++;
    } else {
595 596 597
#ifdef DEBUG_VERBOSE
      fprintf (stdout, "%s: New non-cont page, setting next begin_page to 0x%llx\n", __func__, oggz->offset);
#endif
598 599 600
      /* Prepare the position of the next page */
      reader->current_packet_pages = 1;
      reader->current_packet_begin_page_offset = oggz->offset;
601
      reader->current_packet_begin_segment_index = 0;
602
    }
andre's avatar
andre committed
603 604
  }

605
  return cb_ret;
andre's avatar
andre committed
606 607 608 609 610 611 612 613
}

long
oggz_read (OGGZ * oggz, long n)
{
  OggzReader * reader;
  char * buffer;
  long bytes, bytes_read = 1, remaining = n, nread = 0;
614
  int cb_ret = 0;
andre's avatar
andre committed
615 616 617 618 619 620 621

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

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

622 623
  if ((cb_ret = oggz->cb_next) != OGGZ_CONTINUE) {
    oggz->cb_next = 0;
624
    return oggz_map_return_value_to_error (cb_ret);
625 626
  }

andre's avatar
andre committed
627 628
  reader = &oggz->x.reader;

629
  cb_ret = oggz_read_sync (oggz);
630 631
  if (cb_ret == OGGZ_ERR_OUT_OF_MEMORY)
    return cb_ret;
andre's avatar
andre committed
632

633
  while (cb_ret != OGGZ_STOP_ERR && cb_ret != OGGZ_STOP_OK &&
634
         bytes_read > 0 && remaining > 0) {
635
    bytes = MIN (remaining, CHUNKSIZE);
andre's avatar
andre committed
636
    buffer = ogg_sync_buffer (&reader->ogg_sync, bytes);
637
    bytes_read = (long) oggz_io_read (oggz, buffer, bytes);
638 639
    if (bytes_read == OGGZ_ERR_SYSTEM) {
      return OGGZ_ERR_SYSTEM;
andre's avatar
andre committed
640 641
    }

642 643
    if (bytes_read > 0) {
      ogg_sync_wrote (&reader->ogg_sync, bytes_read);
644
      
645 646
      remaining -= bytes_read;
      nread += bytes_read;
647
      
648
      cb_ret = oggz_read_sync (oggz);
649 650
      if (cb_ret == OGGZ_ERR_OUT_OF_MEMORY)
        return cb_ret;
651
    }
andre's avatar
andre committed
652 653
  }

654
  if (cb_ret == OGGZ_STOP_ERR) oggz_purge (oggz);
655

656
  /* Don't return 0 unless it's actually an EOF condition */
657
  if (nread == 0) {
conrad's avatar
conrad committed
658 659 660 661 662 663 664
    switch (bytes_read) {
    case OGGZ_ERR_IO_AGAIN:
    case OGGZ_ERR_SYSTEM:
      return bytes_read; break;
    default: break;
    }

665 666 667 668 669
    if (cb_ret == OGGZ_READ_EMPTY) {
      return 0;
    } else {
      return oggz_map_return_value_to_error (cb_ret);
    }
670 671 672 673

  } else {
    if (cb_ret == OGGZ_READ_EMPTY) cb_ret = OGGZ_CONTINUE;
    oggz->cb_next = cb_ret;
674
  }
675

andre's avatar
andre committed
676 677 678 679 680 681 682 683 684 685
  return nread;
}

/* generic */
long
oggz_read_input (OGGZ * oggz, unsigned char * buf, long n)
{
  OggzReader * reader;
  char * buffer;
  long bytes, remaining = n, nread = 0;
686
  int cb_ret = 0;
andre's avatar
andre committed
687 688 689 690 691 692 693

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

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

694 695
  if ((cb_ret = oggz->cb_next) != OGGZ_CONTINUE) {
    oggz->cb_next = 0;
696
    return oggz_map_return_value_to_error (cb_ret);
697 698
  }

andre's avatar
andre committed
699 700
  reader = &oggz->x.reader;

701
  cb_ret = oggz_read_sync (oggz);
702 703
  if (cb_ret == OGGZ_ERR_OUT_OF_MEMORY)
    return cb_ret;
704

705
  while (cb_ret != OGGZ_STOP_ERR && cb_ret != OGGZ_STOP_OK  &&
706
         /* !oggz->eos && */ remaining > 0) {
andre's avatar
andre committed
707 708 709 710 711 712 713 714 715
    bytes = MIN (remaining, 4096);
    buffer = ogg_sync_buffer (&reader->ogg_sync, bytes);
    memcpy (buffer, buf, bytes);
    ogg_sync_wrote (&reader->ogg_sync, bytes);

    buf += bytes;
    remaining -= bytes;
    nread += bytes;

716
    cb_ret = oggz_read_sync (oggz);
717 718
    if (cb_ret == OGGZ_ERR_OUT_OF_MEMORY)
      return cb_ret;
andre's avatar
andre committed
719 720
  }

721
  if (cb_ret == OGGZ_STOP_ERR) oggz_purge (oggz);
722

723
  if (nread == 0) {
724 725
    /* Don't return 0 unless it's actually an EOF condition */
    if (cb_ret == OGGZ_READ_EMPTY) {
726
      return OGGZ_ERR_STOP_OK;
727
    } else {
728
      return oggz_map_return_value_to_error (cb_ret);
729
    }
730 731 732
  } else {
    if (cb_ret == OGGZ_READ_EMPTY) cb_ret = OGGZ_CONTINUE;
    oggz->cb_next = cb_ret;
733
  }
734

andre's avatar
andre committed
735 736 737 738 739 740 741 742 743
  return nread;
}


#else /* OGGZ_CONFIG_READ */

#include <ogg/ogg.h>
#include "oggz_private.h"

744 745 746 747 748 749 750 751 752 753 754 755
OGGZ *
oggz_read_init (OGGZ * oggz)
{
  return NULL;
}

OGGZ *
oggz_read_close (OGGZ * oggz)
{
  return NULL;
}

andre's avatar
andre committed
756 757
int
oggz_set_read_callback (OGGZ * oggz, long serialno,
758
                        OggzReadPacket read_packet, void * user_data)
andre's avatar
andre committed
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
{
  return OGGZ_ERR_DISABLED;
}

long
oggz_read (OGGZ * oggz, long n)
{
  return OGGZ_ERR_DISABLED;
}

long
oggz_read_input (OGGZ * oggz, unsigned char * buf, long n)
{
  return OGGZ_ERR_DISABLED;
}

#endif