oggz_read.c 18.9 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_stream.h"
johnf's avatar
johnf committed
64

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

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

70
#define OGGZ_READ_EMPTY (-404)
71

andre's avatar
andre committed
72 73 74 75 76 77 78 79 80 81 82 83
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
84 85 86
  reader->read_page = NULL;
  reader->read_page_user_data = NULL;

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

89 90
  reader->current_page_bytes = 0;

andre's avatar
andre committed
91 92 93 94 95 96 97 98
  return oggz;
}

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

99
  ogg_stream_clear (&reader->ogg_stream);
andre's avatar
andre committed
100 101 102 103 104 105 106
  ogg_sync_clear (&reader->ogg_sync);

  return oggz;
}

int
oggz_set_read_callback (OGGZ * oggz, long serialno,
conrad's avatar
conrad committed
107
                        OggzReadPacket read_packet, void * user_data)
andre's avatar
andre committed
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
{
  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
125 126
    if (stream == NULL)
      stream = oggz_add_stream (oggz, serialno);
127 128
    if (stream == NULL)
      return OGGZ_ERR_OUT_OF_MEMORY;
andre's avatar
andre committed
129 130 131 132 133 134 135 136

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

  return 0;
}

137
int
138
oggz_set_read_page (OGGZ * oggz, long serialno, OggzReadPage read_page,
conrad's avatar
conrad committed
139
                    void * user_data)
140 141
{
  OggzReader * reader;
142
  oggz_stream_t * stream;
143 144 145 146 147 148 149 150 151

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

  reader =  &oggz->x.reader;

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

152 153 154 155 156 157 158
  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);
159 160
    if (stream == NULL)
      return OGGZ_ERR_OUT_OF_MEMORY;
161 162 163 164

    stream->read_page = read_page;
    stream->read_page_user_data = user_data;
  }
165 166 167 168

  return 0;
}

andre's avatar
andre committed
169
/*
conrad's avatar
conrad committed
170
 * oggz_read_get_next_page (oggz, og, do_read)
andre's avatar
andre committed
171
 *
conrad's avatar
conrad committed
172 173
 * 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
174 175 176 177 178 179
 *
 * 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
180
static oggz_off_t
conrad's avatar
conrad committed
181
oggz_read_get_next_page (OGGZ * oggz, ogg_page * og)
andre's avatar
andre committed
182 183
{
  OggzReader * reader = &oggz->x.reader;
184
  long more;
andre's avatar
andre committed
185 186
  int found = 0;

187 188 189
  /* Increment oggz->offset by length of the last page processed */
  oggz->offset += reader->current_page_bytes;

andre's avatar
andre committed
190 191 192 193
  do {
    more = ogg_sync_pageseek (&reader->ogg_sync, og);

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

  } while (!found);

211
  return oggz->offset;
andre's avatar
andre committed
212 213
}

214 215 216 217 218 219 220 221 222 223
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
224 225
oggz_read_new_pbuffer_entry(OGGZ *oggz, ogg_packet *packet, 
            ogg_int64_t granulepos, long serialno, oggz_stream_t * stream, 
226 227
            OggzReader *reader) {

228
  OggzBufferedPacket *p = oggz_malloc(sizeof(OggzBufferedPacket));
229 230
  if (p == NULL) return NULL;

231
  memcpy(&(p->packet), packet, sizeof(ogg_packet));
232
  p->packet.packet = oggz_malloc(packet->bytes);
233 234 235 236 237 238 239 240 241 242 243 244 245
  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
246
  
247 248
  oggz_free(p->packet.packet);
  oggz_free(p);
249 250 251 252 253 254 255 256 257 258

}

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);
259 260 261 262 263 264

    /* 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
265
    p->calced_granulepos = 
266 267
      oggz_auto_calculate_gp_backwards(content, p->stream->last_granulepos,
      p->stream, &(p->packet), p->stream->last_packet);
conrad's avatar
conrad committed
268
      
269 270 271 272 273 274 275 276 277 278 279 280 281
    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;
282
  ogg_int64_t unit_stored;
283 284 285 286 287 288 289 290 291 292 293 294 295 296

  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
297
    p->stream->read_packet(p->oggz, &(p->packet), p->serialno, 
298 299
            p->stream->read_user_data);
  } else if (p->reader->read_packet) {
conrad's avatar
conrad committed
300
    p->reader->read_packet(p->oggz, &(p->packet), p->serialno, 
301 302 303 304 305 306 307 308 309 310 311
            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;
}

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

  oggz_stream_t * stream;
  ogg_stream_state * os;
  ogg_packet * op;
  long serialno;

  ogg_packet packet;
  ogg_page og;

325 326
  int cb_ret = 0;

andre's avatar
andre committed
327 328 329 330 331
  /*os = &reader->ogg_stream;*/
  op = &packet;

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

    if (reader->current_serialno != -1) {
335 336
    /* process a packet if we can.  If the machine isn't loaded,
       neither is a page */
337 338 339
      while(cb_ret == 0) {
        ogg_int64_t granulepos;
        int result;
andre's avatar
andre committed
340

341
        serialno = reader->current_serialno;
andre's avatar
andre committed
342

343
        stream = oggz_get_stream (oggz, serialno);
344

345
        if (stream == NULL) {
conrad's avatar
conrad committed
346 347 348
          /* new stream ... check bos etc. */
          if ((stream = oggz_add_stream (oggz, serialno)) == NULL) {
            /* error -- could not add stream */
349
            return OGGZ_ERR_OUT_OF_MEMORY;
conrad's avatar
conrad committed
350
          }
351 352
        }
        os = &stream->ogg_stream;
353

354
        result = ogg_stream_packetout(os, op);
andre's avatar
andre committed
355

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

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

384 385
        if(result > 0){
          int content;
386 387

          stream->packetno++;
conrad's avatar
conrad committed
388
          
conrad's avatar
conrad committed
389 390
          /* got a packet.  process it */
          granulepos = op->granulepos;
andre's avatar
andre committed
391

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

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

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

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

              oggz_dlist_append(oggz->packet_buffer, p);
              continue;
443 444 445 446 447 448 449 450 451 452 453 454 455
            } else if (!oggz_dlist_is_empty(oggz->packet_buffer)) {
              /*
               * move backward through the list assigning gp values based upon
               * the granulepos we just recieved.  Then move forward through
               * the list delivering any packets at the beginning with valid
               * gp values
               */
              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);

              /*
conrad's avatar
conrad committed
456
               * fix up the stream granulepos 
457 458 459 460 461
               */
              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
462
                                oggz, &packet, reader->current_granulepos, 
463 464 465 466 467
                                serialno, stream, reader);

                oggz_dlist_append(oggz->packet_buffer, p);
                continue;
              }
468 469 470
            }
          }

conrad's avatar
conrad committed
471 472 473 474 475 476 477
          if (stream->read_packet) {
            cb_ret =
              stream->read_packet (oggz, op, serialno, stream->read_user_data);
          } else if (reader->read_packet) {
            cb_ret =
              reader->read_packet (oggz, op, serialno, reader->read_user_data);
          }
478 479 480 481 482 483

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

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

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

496 497 498 499
    serialno = ogg_page_serialno (&og);
    reader->current_serialno = serialno; /* XXX: maybe not necessary */

    stream = oggz_get_stream (oggz, serialno);
500

501 502 503
    if (stream == NULL) {
      /* new stream ... check bos etc. */
      if ((stream = oggz_add_stream (oggz, serialno)) == NULL) {
conrad's avatar
conrad committed
504
        /* error -- could not add stream */
505
        return OGGZ_ERR_OUT_OF_MEMORY;
andre's avatar
andre committed
506
      }
507

508
      /* identify stream type */
509
      oggz_auto_identify_page (oggz, &og, serialno);
510 511 512 513

      /* read bos data */
      if (oggz->flags & OGGZ_AUTO)
        oggz_auto_read_bos_page (oggz, &og, serialno, NULL);
514 515 516 517 518 519
    }
    else if (oggz_stream_get_content(oggz, serialno) == OGGZ_CONTENT_ANXDATA)
    {
      /*
       * re-identify ANXDATA streams as these are now content streams
       */
520
      oggz_auto_identify_page (oggz, &og, serialno);
521
    }
conrad's avatar
conrad committed
522
    
523
    os = &stream->ogg_stream;
andre's avatar
andre committed
524

525
    {
526 527 528
      ogg_int64_t granulepos;

      granulepos = ogg_page_granulepos (&og);
529
      stream->page_granulepos = granulepos;
530

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

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

546
    ogg_stream_pagein(os, &og);
andre's avatar
andre committed
547 548
  }

549
  return cb_ret;
andre's avatar
andre committed
550 551 552 553 554 555 556 557
}

long
oggz_read (OGGZ * oggz, long n)
{
  OggzReader * reader;
  char * buffer;
  long bytes, bytes_read = 1, remaining = n, nread = 0;
558
  int cb_ret = 0;
andre's avatar
andre committed
559 560 561 562 563 564 565

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

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

566 567
  if ((cb_ret = oggz->cb_next) != OGGZ_CONTINUE) {
    oggz->cb_next = 0;
568
    return oggz_map_return_value_to_error (cb_ret);
569 570
  }

andre's avatar
andre committed
571 572
  reader = &oggz->x.reader;

573
  cb_ret = oggz_read_sync (oggz);
574 575
  if (cb_ret == OGGZ_ERR_OUT_OF_MEMORY)
    return cb_ret;
andre's avatar
andre committed
576

577
  while (cb_ret != OGGZ_STOP_ERR && cb_ret != OGGZ_STOP_OK &&
conrad's avatar
conrad committed
578
         bytes_read > 0 && remaining > 0) {
579
    bytes = MIN (remaining, CHUNKSIZE);
andre's avatar
andre committed
580
    buffer = ogg_sync_buffer (&reader->ogg_sync, bytes);
conrad's avatar
conrad committed
581
    bytes_read = (long) oggz_io_read (oggz, buffer, bytes);
582 583
    if (bytes_read == OGGZ_ERR_SYSTEM) {
      return OGGZ_ERR_SYSTEM;
andre's avatar
andre committed
584 585
    }

conrad's avatar
 
conrad committed
586 587
    if (bytes_read > 0) {
      ogg_sync_wrote (&reader->ogg_sync, bytes_read);
conrad's avatar
conrad committed
588
      
conrad's avatar
 
conrad committed
589 590
      remaining -= bytes_read;
      nread += bytes_read;
conrad's avatar
conrad committed
591
      
conrad's avatar
 
conrad committed
592
      cb_ret = oggz_read_sync (oggz);
593 594
      if (cb_ret == OGGZ_ERR_OUT_OF_MEMORY)
        return cb_ret;
conrad's avatar
 
conrad committed
595
    }
andre's avatar
andre committed
596 597
  }

598
  if (cb_ret == OGGZ_STOP_ERR) oggz_purge (oggz);
599

600
  /* Don't return 0 unless it's actually an EOF condition */
601
  if (nread == 0) {
conrad's avatar
conrad committed
602 603 604 605 606 607 608
    switch (bytes_read) {
    case OGGZ_ERR_IO_AGAIN:
    case OGGZ_ERR_SYSTEM:
      return bytes_read; break;
    default: break;
    }

609 610 611 612 613
    if (cb_ret == OGGZ_READ_EMPTY) {
      return 0;
    } else {
      return oggz_map_return_value_to_error (cb_ret);
    }
614 615 616 617

  } else {
    if (cb_ret == OGGZ_READ_EMPTY) cb_ret = OGGZ_CONTINUE;
    oggz->cb_next = cb_ret;
618
  }
619

andre's avatar
andre committed
620 621 622 623 624 625 626 627 628 629
  return nread;
}

/* generic */
long
oggz_read_input (OGGZ * oggz, unsigned char * buf, long n)
{
  OggzReader * reader;
  char * buffer;
  long bytes, remaining = n, nread = 0;
630
  int cb_ret = 0;
andre's avatar
andre committed
631 632 633 634 635 636 637

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

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

638 639
  if ((cb_ret = oggz->cb_next) != OGGZ_CONTINUE) {
    oggz->cb_next = 0;
640
    return oggz_map_return_value_to_error (cb_ret);
641 642
  }

andre's avatar
andre committed
643 644
  reader = &oggz->x.reader;

645
  cb_ret = oggz_read_sync (oggz);
646 647
  if (cb_ret == OGGZ_ERR_OUT_OF_MEMORY)
    return cb_ret;
648

649
  while (cb_ret != OGGZ_STOP_ERR && cb_ret != OGGZ_STOP_OK  &&
conrad's avatar
conrad committed
650
         /* !oggz->eos && */ remaining > 0) {
andre's avatar
andre committed
651 652 653 654 655 656 657 658 659
    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;

660
    cb_ret = oggz_read_sync (oggz);
661 662
    if (cb_ret == OGGZ_ERR_OUT_OF_MEMORY)
      return cb_ret;
andre's avatar
andre committed
663 664
  }

665
  if (cb_ret == OGGZ_STOP_ERR) oggz_purge (oggz);
666

667
  if (nread == 0) {
668 669
    /* Don't return 0 unless it's actually an EOF condition */
    if (cb_ret == OGGZ_READ_EMPTY) {
670
      return OGGZ_ERR_STOP_OK;
671
    } else {
672
      return oggz_map_return_value_to_error (cb_ret);
673
    }
674 675 676
  } else {
    if (cb_ret == OGGZ_READ_EMPTY) cb_ret = OGGZ_CONTINUE;
    oggz->cb_next = cb_ret;
677
  }
678

andre's avatar
andre committed
679 680 681 682 683 684 685 686 687
  return nread;
}


#else /* OGGZ_CONFIG_READ */

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

688 689 690 691 692 693 694 695 696 697 698 699
OGGZ *
oggz_read_init (OGGZ * oggz)
{
  return NULL;
}

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

andre's avatar
andre committed
700 701
int
oggz_set_read_callback (OGGZ * oggz, long serialno,
conrad's avatar
conrad committed
702
                        OggzReadPacket read_packet, void * user_data)
andre's avatar
andre committed
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
{
  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