oggz_read.c 19.6 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"
johnf's avatar
johnf committed
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;

conrad's avatar
conrad committed
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,
conrad's avatar
conrad committed
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,
conrad's avatar
conrad committed
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
/*
conrad's avatar
conrad committed
174
 * oggz_read_get_next_page (oggz, og, do_read)
andre's avatar
andre committed
175
 *
conrad's avatar
conrad committed
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
conrad's avatar
conrad committed
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 192 193
  /* Increment oggz->offset by length of the last page processed */
  oggz->offset += reader->current_page_bytes;

andre's avatar
andre committed
194 195 196 197
  do {
    more = ogg_sync_pageseek (&reader->ogg_sync, og);

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

  } while (!found);

215
  return oggz->offset;
andre's avatar
andre committed
216 217
}

218 219 220 221 222 223 224 225 226 227
typedef struct {
  ogg_packet      packet;
  ogg_int64_t     calced_granulepos;
  oggz_stream_t * stream;
  OggzReader    * reader;
  OGGZ          * oggz;
  long            serialno;
} OggzBufferedPacket;

OggzBufferedPacket *
conrad's avatar
conrad committed
228 229
oggz_read_new_pbuffer_entry(OGGZ *oggz, ogg_packet *packet, 
            ogg_int64_t granulepos, long serialno, oggz_stream_t * stream, 
230 231
            OggzReader *reader) {

232
  OggzBufferedPacket *p = oggz_malloc(sizeof(OggzBufferedPacket));
233 234
  if (p == NULL) return NULL;

235
  memcpy(&(p->packet), packet, sizeof(ogg_packet));
236
  p->packet.packet = oggz_malloc(packet->bytes);
237 238 239 240 241 242 243 244 245 246 247 248 249
  memcpy(p->packet.packet, packet->packet, packet->bytes);

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

  return p;
}

void
oggz_read_free_pbuffer_entry(OggzBufferedPacket *p) {
conrad's avatar
conrad committed
250
  
251 252
  oggz_free(p->packet.packet);
  oggz_free(p);
253 254 255 256 257 258 259 260 261 262

}

OggzDListIterResponse
oggz_read_update_gp(void *elem) {

  OggzBufferedPacket *p = (OggzBufferedPacket *)elem;

  if (p->calced_granulepos == -1 && p->stream->last_granulepos != -1) {
    int content = oggz_stream_get_content(p->oggz, p->serialno);
263 264 265 266 267 268

    /* 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;

conrad's avatar
conrad committed
269
    p->calced_granulepos = 
270 271
      oggz_auto_calculate_gp_backwards(content, p->stream->last_granulepos,
      p->stream, &(p->packet), p->stream->last_packet);
conrad's avatar
conrad committed
272
      
273 274 275 276 277 278 279 280 281 282 283 284 285
    p->stream->last_granulepos = p->calced_granulepos;
    p->stream->last_packet = &(p->packet);
  }

  return DLIST_ITER_CONTINUE;

}

OggzDListIterResponse
oggz_read_deliver_packet(void *elem) {

  OggzBufferedPacket *p = (OggzBufferedPacket *)elem;
  ogg_int64_t gp_stored;
286
  ogg_int64_t unit_stored;
287 288 289 290 291 292 293 294 295 296 297 298 299 300

  if (p->calced_granulepos == -1) {
    return DLIST_ITER_CANCEL;
  }

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

  p->reader->current_granulepos = p->calced_granulepos;

  p->reader->current_unit =
    oggz_get_unit (p->oggz, p->serialno, p->calced_granulepos);

  if (p->stream->read_packet) {
conrad's avatar
conrad committed
301
    p->stream->read_packet(p->oggz, &(p->packet), p->serialno, 
302 303
            p->stream->read_user_data);
  } else if (p->reader->read_packet) {
conrad's avatar
conrad committed
304
    p->reader->read_packet(p->oggz, &(p->packet), p->serialno, 
305 306 307 308 309 310 311 312 313 314 315
            p->reader->read_user_data);
  }

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

  oggz_read_free_pbuffer_entry(p);

  return DLIST_ITER_CONTINUE;
}

316
static int
andre's avatar
andre committed
317 318 319 320 321 322 323
oggz_read_sync (OGGZ * oggz)
{
  OggzReader * reader = &oggz->x.reader;

  oggz_stream_t * stream;
  ogg_stream_state * os;
  ogg_packet * op;
324
  oggz_position * pos;
andre's avatar
andre committed
325 326
  long serialno;

327
  oggz_packet packet;
andre's avatar
andre committed
328 329
  ogg_page og;

330 331
  int cb_ret = 0;

andre's avatar
andre committed
332
  /*os = &reader->ogg_stream;*/
333 334
  op = &packet.op;
  pos = &packet.pos;
andre's avatar
andre committed
335 336 337

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

    if (reader->current_serialno != -1) {
341 342
    /* process a packet if we can.  If the machine isn't loaded,
       neither is a page */
343 344 345
      while(cb_ret == 0) {
        ogg_int64_t granulepos;
        int result;
andre's avatar
andre committed
346

347
        serialno = reader->current_serialno;
andre's avatar
andre committed
348

349
        stream = oggz_get_stream (oggz, serialno);
350

351
        if (stream == NULL) {
conrad's avatar
conrad committed
352 353 354
          /* new stream ... check bos etc. */
          if ((stream = oggz_add_stream (oggz, serialno)) == NULL) {
            /* error -- could not add stream */
355
            return OGGZ_ERR_OUT_OF_MEMORY;
conrad's avatar
conrad committed
356
          }
357 358
        }
        os = &stream->ogg_stream;
359

360
        result = ogg_stream_packetout(os, op);
andre's avatar
andre committed
361

362 363
        /* libogg flags "holes in the data" (which are really inconsistencies
         * in the page sequence number) by returning -1. */
364
        if(result == -1) {
365
#ifdef DEBUG
conrad's avatar
conrad committed
366
          printf ("oggz_read_sync: hole in the data\n");
367
#endif
368 369 370 371
          /* 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;
372 373

          /* Holes in content occur in some files and pretty much don't matter,
374
           * so we silently swallow the notification and reget the packet. */
375 376
          result = ogg_stream_packetout(os, op);
          if (result == -1) {
377 378
            /* If the result is *still* -1 then something strange is
             * happening.
379
             */
380 381
#ifdef DEBUG
            printf ("Multiple holes in data!");
382
#endif
383
            return OGGZ_ERR_HOLE_IN_DATA;
384 385
          }
        }
andre's avatar
andre committed
386

387 388
        if(result > 0){
          int content;
389 390

          stream->packetno++;
conrad's avatar
conrad committed
391
          
392
          /* Got a packet.  process it ... */
conrad's avatar
conrad committed
393
          granulepos = op->granulepos;
andre's avatar
andre committed
394

395
          content = oggz_stream_get_content(oggz, serialno);
396 397 398 399 400 401 402 403
          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);
            }
404

405 406 407 408 409 410 411 412 413
            /* 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 {
414 415
              reader->current_granulepos = granulepos;
            }
416 417
	  }

418
          stream->last_granulepos = reader->current_granulepos;
conrad's avatar
conrad committed
419
        
420 421
          /* Set unit on last packet of page */
          if ((oggz->metric || stream->metric) && reader->current_granulepos != -1) {
conrad's avatar
conrad committed
422 423 424 425
            reader->current_unit =
              oggz_get_unit (oggz, serialno, reader->current_granulepos);
          }

426 427 428
          if (stream->packetno == 1) {
            oggz_auto_read_comments (oggz, stream, serialno, op);
          }
conrad's avatar
conrad committed
429
          
430
          if (oggz->flags & OGGZ_AUTO) {
conrad's avatar
conrad committed
431
          
432
            /* While we are getting invalid granulepos values, store the 
433 434
             * incoming packets in a dlist */
            if (reader->current_granulepos == -1) {
435
              OggzBufferedPacket *p = oggz_read_new_pbuffer_entry(
conrad's avatar
conrad committed
436
                                oggz, &packet, reader->current_granulepos, 
437
                                serialno, stream, reader);
438 439 440

              oggz_dlist_append(oggz->packet_buffer, p);
              continue;
441
            } else if (!oggz_dlist_is_empty(oggz->packet_buffer)) {
442
              /* Move backward through the list assigning gp values based upon
443 444
               * the granulepos we just recieved.  Then move forward through
               * the list delivering any packets at the beginning with valid
445
               * gp values.
446 447 448 449 450 451
               */
              ogg_int64_t gp_stored = stream->last_granulepos;
              stream->last_packet = &packet;
              oggz_dlist_reverse_iter(oggz->packet_buffer, oggz_read_update_gp);
              oggz_dlist_deliter(oggz->packet_buffer, oggz_read_deliver_packet);

452
              /* Fix up the stream granulepos. */
453 454 455 456
              stream->last_granulepos = gp_stored;

              if (!oggz_dlist_is_empty(oggz->packet_buffer)) {
                OggzBufferedPacket *p = oggz_read_new_pbuffer_entry(
conrad's avatar
conrad committed
457
                                oggz, &packet, reader->current_granulepos, 
458 459 460 461 462
                                serialno, stream, reader);

                oggz_dlist_append(oggz->packet_buffer, p);
                continue;
              }
463 464 465
            }
          }

466
          /* Fill in position information. */
467 468 469 470 471
          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;

conrad's avatar
conrad committed
472 473
          if (stream->read_packet) {
            cb_ret =
474
              stream->read_packet (oggz, &packet, serialno, stream->read_user_data);
conrad's avatar
conrad committed
475 476
          } else if (reader->read_packet) {
            cb_ret =
477
              reader->read_packet (oggz, &packet, serialno, reader->read_user_data);
conrad's avatar
conrad committed
478
          }
479

480
          /* Prepare the position of the next page. */
481 482 483
          reader->current_packet_pages = 1;
          reader->current_packet_begin_page_offset = oggz->offset;

484 485 486 487 488
          /* 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;
489 490
        }
        else
conrad's avatar
conrad committed
491
          break;
andre's avatar
andre committed
492 493
      }
    }
494

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

conrad's avatar
conrad committed
498
    if(oggz_read_get_next_page (oggz, &og) < 0)
499
      return OGGZ_READ_EMPTY; /* eof. leave uninitialized */
andre's avatar
andre committed
500

501
    serialno = ogg_page_serialno (&og);
502
    reader->current_serialno = serialno;
503 504

    stream = oggz_get_stream (oggz, serialno);
505

506 507 508
    if (stream == NULL) {
      /* new stream ... check bos etc. */
      if ((stream = oggz_add_stream (oggz, serialno)) == NULL) {
conrad's avatar
conrad committed
509
        /* error -- could not add stream */
510
        return OGGZ_ERR_OUT_OF_MEMORY;
andre's avatar
andre committed
511
      }
512

513
      /* identify stream type */
514
      oggz_auto_identify_page (oggz, &og, serialno);
515 516

      /* read bos data */
517
      if (oggz->flags & OGGZ_AUTO) {
518
        oggz_auto_read_bos_page (oggz, &og, serialno, NULL);
519 520 521
      }
    } else if (oggz_stream_get_content(oggz, serialno) == OGGZ_CONTENT_ANXDATA) {
      /* re-identify ANXDATA streams as these are now content streams */
522
      oggz_auto_identify_page (oggz, &og, serialno);
523
    }
524

525
    os = &stream->ogg_stream;
andre's avatar
andre committed
526

527
    {
528 529 530
      ogg_int64_t granulepos;

      granulepos = ogg_page_granulepos (&og);
531
      stream->page_granulepos = granulepos;
532

533
      if ((oggz->metric || stream->metric) && granulepos != -1) {
534
       reader->current_unit = oggz_get_unit (oggz, serialno, granulepos);
535
      } else if (granulepos == 0) {
536
       reader->current_unit = 0;
andre's avatar
andre committed
537
      }
538
    }
539

540 541
    if (stream->read_page) {
      cb_ret =
conrad's avatar
conrad committed
542
        stream->read_page (oggz, &og, serialno, stream->read_page_user_data);
543
    } else if (reader->read_page) {
conrad's avatar
conrad committed
544 545
      cb_ret =
        reader->read_page (oggz, &og, serialno, reader->read_page_user_data);
546
    }
andre's avatar
andre committed
547

548
    ogg_stream_pagein(os, &og);
549 550 551 552 553 554 555 556
    if (ogg_page_continued(&og)) {
      if (reader->current_packet_pages != -1)
        reader->current_packet_pages++;
    } else {
      /* Prepare the position of the next page */
      reader->current_packet_pages = 1;
      reader->current_packet_begin_page_offset = oggz->offset;
    }
andre's avatar
andre committed
557 558
  }

559
  return cb_ret;
andre's avatar
andre committed
560 561 562 563 564 565 566 567
}

long
oggz_read (OGGZ * oggz, long n)
{
  OggzReader * reader;
  char * buffer;
  long bytes, bytes_read = 1, remaining = n, nread = 0;
568
  int cb_ret = 0;
andre's avatar
andre committed
569 570 571 572 573 574 575

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

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

576 577
  if ((cb_ret = oggz->cb_next) != OGGZ_CONTINUE) {
    oggz->cb_next = 0;
578
    return oggz_map_return_value_to_error (cb_ret);
579 580
  }

andre's avatar
andre committed
581 582
  reader = &oggz->x.reader;

583
  cb_ret = oggz_read_sync (oggz);
584 585
  if (cb_ret == OGGZ_ERR_OUT_OF_MEMORY)
    return cb_ret;
andre's avatar
andre committed
586

587
  while (cb_ret != OGGZ_STOP_ERR && cb_ret != OGGZ_STOP_OK &&
conrad's avatar
conrad committed
588
         bytes_read > 0 && remaining > 0) {
589
    bytes = MIN (remaining, CHUNKSIZE);
andre's avatar
andre committed
590
    buffer = ogg_sync_buffer (&reader->ogg_sync, bytes);
conrad's avatar
conrad committed
591
    bytes_read = (long) oggz_io_read (oggz, buffer, bytes);
592 593
    if (bytes_read == OGGZ_ERR_SYSTEM) {
      return OGGZ_ERR_SYSTEM;
andre's avatar
andre committed
594 595
    }

conrad's avatar
 
conrad committed
596 597
    if (bytes_read > 0) {
      ogg_sync_wrote (&reader->ogg_sync, bytes_read);
conrad's avatar
conrad committed
598
      
conrad's avatar
 
conrad committed
599 600
      remaining -= bytes_read;
      nread += bytes_read;
conrad's avatar
conrad committed
601
      
conrad's avatar
 
conrad committed
602
      cb_ret = oggz_read_sync (oggz);
603 604
      if (cb_ret == OGGZ_ERR_OUT_OF_MEMORY)
        return cb_ret;
conrad's avatar
 
conrad committed
605
    }
andre's avatar
andre committed
606 607
  }

608
  if (cb_ret == OGGZ_STOP_ERR) oggz_purge (oggz);
609

610
  /* Don't return 0 unless it's actually an EOF condition */
611
  if (nread == 0) {
conrad's avatar
conrad committed
612 613 614 615 616 617 618
    switch (bytes_read) {
    case OGGZ_ERR_IO_AGAIN:
    case OGGZ_ERR_SYSTEM:
      return bytes_read; break;
    default: break;
    }

619 620 621 622 623
    if (cb_ret == OGGZ_READ_EMPTY) {
      return 0;
    } else {
      return oggz_map_return_value_to_error (cb_ret);
    }
624 625 626 627

  } else {
    if (cb_ret == OGGZ_READ_EMPTY) cb_ret = OGGZ_CONTINUE;
    oggz->cb_next = cb_ret;
628
  }
629

andre's avatar
andre committed
630 631 632 633 634 635 636 637 638 639
  return nread;
}

/* generic */
long
oggz_read_input (OGGZ * oggz, unsigned char * buf, long n)
{
  OggzReader * reader;
  char * buffer;
  long bytes, remaining = n, nread = 0;
640
  int cb_ret = 0;
andre's avatar
andre committed
641 642 643 644 645 646 647

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

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

648 649
  if ((cb_ret = oggz->cb_next) != OGGZ_CONTINUE) {
    oggz->cb_next = 0;
650
    return oggz_map_return_value_to_error (cb_ret);
651 652
  }

andre's avatar
andre committed
653 654
  reader = &oggz->x.reader;

655
  cb_ret = oggz_read_sync (oggz);
656 657
  if (cb_ret == OGGZ_ERR_OUT_OF_MEMORY)
    return cb_ret;
658

659
  while (cb_ret != OGGZ_STOP_ERR && cb_ret != OGGZ_STOP_OK  &&
conrad's avatar
conrad committed
660
         /* !oggz->eos && */ remaining > 0) {
andre's avatar
andre committed
661 662 663 664 665 666 667 668 669
    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;

670
    cb_ret = oggz_read_sync (oggz);
671 672
    if (cb_ret == OGGZ_ERR_OUT_OF_MEMORY)
      return cb_ret;
andre's avatar
andre committed
673 674
  }

675
  if (cb_ret == OGGZ_STOP_ERR) oggz_purge (oggz);
676

677
  if (nread == 0) {
678 679
    /* Don't return 0 unless it's actually an EOF condition */
    if (cb_ret == OGGZ_READ_EMPTY) {
680
      return OGGZ_ERR_STOP_OK;
681
    } else {
682
      return oggz_map_return_value_to_error (cb_ret);
683
    }
684 685 686
  } else {
    if (cb_ret == OGGZ_READ_EMPTY) cb_ret = OGGZ_CONTINUE;
    oggz->cb_next = cb_ret;
687
  }
688

andre's avatar
andre committed
689 690 691 692 693 694 695 696 697
  return nread;
}


#else /* OGGZ_CONFIG_READ */

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

698 699 700 701 702 703 704 705 706 707 708 709
OGGZ *
oggz_read_init (OGGZ * oggz)
{
  return NULL;
}

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

andre's avatar
andre committed
710 711
int
oggz_set_read_callback (OGGZ * oggz, long serialno,
conrad's avatar
conrad committed
712
                        OggzReadPacket read_packet, void * user_data)
andre's avatar
andre committed
713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
{
  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