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

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

390 391
        if(result > 0){
          int content;
392 393

          stream->packetno++;
conrad's avatar
conrad committed
394
          
conrad's avatar
conrad committed
395 396
          /* got a packet.  process it */
          granulepos = op->granulepos;
andre's avatar
andre committed
397

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

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

421
          stream->last_granulepos = reader->current_granulepos;
conrad's avatar
conrad committed
422
        
conrad's avatar
conrad committed
423
          /* set unit on last packet of page */
conrad's avatar
conrad committed
424
          if 
425 426
          (
            (oggz->metric || stream->metric) && reader->current_granulepos != -1
conrad's avatar
conrad committed
427
          ) 
428
          {
conrad's avatar
conrad committed
429 430 431 432
            reader->current_unit =
              oggz_get_unit (oggz, serialno, reader->current_granulepos);
          }

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

              oggz_dlist_append(oggz->packet_buffer, p);
              continue;
449 450 451 452 453 454 455 456 457 458 459 460 461
            } 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
462
               * fix up the stream granulepos 
463 464 465 466 467
               */
              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
468
                                oggz, &packet, reader->current_granulepos, 
469 470 471 472 473
                                serialno, stream, reader);

                oggz_dlist_append(oggz->packet_buffer, p);
                continue;
              }
474 475 476
            }
          }

477 478 479 480 481 482
          /* 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;

conrad's avatar
conrad committed
483 484
          if (stream->read_packet) {
            cb_ret =
485
              stream->read_packet (oggz, &packet, serialno, stream->read_user_data);
conrad's avatar
conrad committed
486 487
          } else if (reader->read_packet) {
            cb_ret =
488
              reader->read_packet (oggz, &packet, serialno, reader->read_user_data);
conrad's avatar
conrad committed
489
          }
490

491 492 493 494
          /* Prepare the position of the next page */
          reader->current_packet_pages = 1;
          reader->current_packet_begin_page_offset = oggz->offset;

495 496 497 498 499
          /* 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;
500 501
        }
        else
conrad's avatar
conrad committed
502
          break;
andre's avatar
andre committed
503 504
      }
    }
505

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

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

512
    serialno = ogg_page_serialno (&og);
513
    reader->current_serialno = serialno;
514 515

    stream = oggz_get_stream (oggz, serialno);
516

517 518 519
    if (stream == NULL) {
      /* new stream ... check bos etc. */
      if ((stream = oggz_add_stream (oggz, serialno)) == NULL) {
conrad's avatar
conrad committed
520
        /* error -- could not add stream */
521
        return OGGZ_ERR_OUT_OF_MEMORY;
andre's avatar
andre committed
522
      }
523

524
      /* identify stream type */
525
      oggz_auto_identify_page (oggz, &og, serialno);
526 527 528 529

      /* read bos data */
      if (oggz->flags & OGGZ_AUTO)
        oggz_auto_read_bos_page (oggz, &og, serialno, NULL);
530 531 532 533 534 535
    }
    else if (oggz_stream_get_content(oggz, serialno) == OGGZ_CONTENT_ANXDATA)
    {
      /*
       * re-identify ANXDATA streams as these are now content streams
       */
536
      oggz_auto_identify_page (oggz, &og, serialno);
537
    }
conrad's avatar
conrad committed
538
    
539
    os = &stream->ogg_stream;
andre's avatar
andre committed
540

541
    {
542 543 544
      ogg_int64_t granulepos;

      granulepos = ogg_page_granulepos (&og);
545
      stream->page_granulepos = granulepos;
546

547
      if ((oggz->metric || stream->metric) && granulepos != -1) {
548
       reader->current_unit = oggz_get_unit (oggz, serialno, granulepos);
549
      } else if (granulepos == 0) {
550
       reader->current_unit = 0;
andre's avatar
andre committed
551
      }
552
    }
553

554 555
    if (stream->read_page) {
      cb_ret =
conrad's avatar
conrad committed
556
        stream->read_page (oggz, &og, serialno, stream->read_page_user_data);
557
    } else if (reader->read_page) {
conrad's avatar
conrad committed
558 559
      cb_ret =
        reader->read_page (oggz, &og, serialno, reader->read_page_user_data);
560
    }
andre's avatar
andre committed
561

562
    ogg_stream_pagein(os, &og);
563 564 565 566 567 568 569 570
    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
571 572
  }

573
  return cb_ret;
andre's avatar
andre committed
574 575 576 577 578 579 580 581
}

long
oggz_read (OGGZ * oggz, long n)
{
  OggzReader * reader;
  char * buffer;
  long bytes, bytes_read = 1, remaining = n, nread = 0;
582
  int cb_ret = 0;
andre's avatar
andre committed
583 584 585 586 587 588 589

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

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

590 591
  if ((cb_ret = oggz->cb_next) != OGGZ_CONTINUE) {
    oggz->cb_next = 0;
592
    return oggz_map_return_value_to_error (cb_ret);
593 594
  }

andre's avatar
andre committed
595 596
  reader = &oggz->x.reader;

597
  cb_ret = oggz_read_sync (oggz);
598 599
  if (cb_ret == OGGZ_ERR_OUT_OF_MEMORY)
    return cb_ret;
andre's avatar
andre committed
600

601
  while (cb_ret != OGGZ_STOP_ERR && cb_ret != OGGZ_STOP_OK &&
conrad's avatar
conrad committed
602
         bytes_read > 0 && remaining > 0) {
603
    bytes = MIN (remaining, CHUNKSIZE);
andre's avatar
andre committed
604
    buffer = ogg_sync_buffer (&reader->ogg_sync, bytes);
conrad's avatar
conrad committed
605
    bytes_read = (long) oggz_io_read (oggz, buffer, bytes);
606 607
    if (bytes_read == OGGZ_ERR_SYSTEM) {
      return OGGZ_ERR_SYSTEM;
andre's avatar
andre committed
608 609
    }

conrad's avatar
 
conrad committed
610 611
    if (bytes_read > 0) {
      ogg_sync_wrote (&reader->ogg_sync, bytes_read);
conrad's avatar
conrad committed
612
      
conrad's avatar
 
conrad committed
613 614
      remaining -= bytes_read;
      nread += bytes_read;
conrad's avatar
conrad committed
615
      
conrad's avatar
 
conrad committed
616
      cb_ret = oggz_read_sync (oggz);
617 618
      if (cb_ret == OGGZ_ERR_OUT_OF_MEMORY)
        return cb_ret;
conrad's avatar
 
conrad committed
619
    }
andre's avatar
andre committed
620 621
  }

622
  if (cb_ret == OGGZ_STOP_ERR) oggz_purge (oggz);
623

624
  /* Don't return 0 unless it's actually an EOF condition */
625
  if (nread == 0) {
conrad's avatar
conrad committed
626 627 628 629 630 631 632
    switch (bytes_read) {
    case OGGZ_ERR_IO_AGAIN:
    case OGGZ_ERR_SYSTEM:
      return bytes_read; break;
    default: break;
    }

633 634 635 636 637
    if (cb_ret == OGGZ_READ_EMPTY) {
      return 0;
    } else {
      return oggz_map_return_value_to_error (cb_ret);
    }
638 639 640 641

  } else {
    if (cb_ret == OGGZ_READ_EMPTY) cb_ret = OGGZ_CONTINUE;
    oggz->cb_next = cb_ret;
642
  }
643

andre's avatar
andre committed
644 645 646 647 648 649 650 651 652 653
  return nread;
}

/* generic */
long
oggz_read_input (OGGZ * oggz, unsigned char * buf, long n)
{
  OggzReader * reader;
  char * buffer;
  long bytes, remaining = n, nread = 0;
654
  int cb_ret = 0;
andre's avatar
andre committed
655 656 657 658 659 660 661

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

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

662 663
  if ((cb_ret = oggz->cb_next) != OGGZ_CONTINUE) {
    oggz->cb_next = 0;
664
    return oggz_map_return_value_to_error (cb_ret);
665 666
  }

andre's avatar
andre committed
667 668
  reader = &oggz->x.reader;

669
  cb_ret = oggz_read_sync (oggz);
670 671
  if (cb_ret == OGGZ_ERR_OUT_OF_MEMORY)
    return cb_ret;
672

673
  while (cb_ret != OGGZ_STOP_ERR && cb_ret != OGGZ_STOP_OK  &&
conrad's avatar
conrad committed
674
         /* !oggz->eos && */ remaining > 0) {
andre's avatar
andre committed
675 676 677 678 679 680 681 682 683
    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;

684
    cb_ret = oggz_read_sync (oggz);
685 686
    if (cb_ret == OGGZ_ERR_OUT_OF_MEMORY)
      return cb_ret;
andre's avatar
andre committed
687 688
  }

689
  if (cb_ret == OGGZ_STOP_ERR) oggz_purge (oggz);
690

691
  if (nread == 0) {
692 693
    /* Don't return 0 unless it's actually an EOF condition */
    if (cb_ret == OGGZ_READ_EMPTY) {
694
      return OGGZ_ERR_STOP_OK;
695
    } else {
696
      return oggz_map_return_value_to_error (cb_ret);
697
    }
698 699 700
  } else {
    if (cb_ret == OGGZ_READ_EMPTY) cb_ret = OGGZ_CONTINUE;
    oggz->cb_next = cb_ret;
701
  }
702

andre's avatar
andre committed
703 704 705 706 707 708 709 710 711
  return nread;
}


#else /* OGGZ_CONFIG_READ */

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

712 713 714 715 716 717 718 719 720 721 722 723
OGGZ *
oggz_read_init (OGGZ * oggz)
{
  return NULL;
}

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

andre's avatar
andre committed
724 725
int
oggz_set_read_callback (OGGZ * oggz, long serialno,
conrad's avatar
conrad committed
726
                        OggzReadPacket read_packet, void * user_data)
andre's avatar
andre committed
727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
{
  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