oggz_read.c 21.5 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
#include "oggz_private.h"
62
#include "oggz_skeleton.h"
andre's avatar
andre committed
63

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

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

69
#define OGGZ_READ_EMPTY (-404)
70

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

andre's avatar
andre committed
86 87 88 89 90 91 92 93 94 95
  reader->current_unit = 0;

  return oggz;
}

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

96
  ogg_stream_clear (&reader->ogg_stream);
andre's avatar
andre committed
97 98 99 100 101 102 103
  ogg_sync_clear (&reader->ogg_sync);

  return oggz;
}

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

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

  return 0;
}

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

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

  reader =  &oggz->x.reader;

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

151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
  if (serialno == -1) {
    reader->read_page = read_page;
    reader->read_page_user_data = user_data;
  } else {
    stream = oggz_get_stream (oggz, serialno);
#if 0
    if (stream == NULL) return OGGZ_ERR_BAD_SERIALNO;
#else
    if (stream == NULL)
      stream = oggz_add_stream (oggz, serialno);
#endif

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

  return 0;
}

andre's avatar
andre committed
170 171 172 173 174 175 176 177 178 179
/*
 * oggz_get_next_page_7 (oggz, og, do_read)
 *
 * MODIFIED COPY OF CODE FROM BELOW SEEKING STUFF
 *
 * 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
andre's avatar
andre committed
181 182 183 184 185 186 187
oggz_get_next_page_7 (OGGZ * oggz, ogg_page * og)
{
  OggzReader * reader = &oggz->x.reader;
#if _UNMODIFIED
  char * buffer;
#endif
  long bytes = 0, more;
conrad's avatar
conrad committed
188
  oggz_off_t page_offset = 0, ret;
andre's avatar
andre committed
189 190 191 192 193 194 195 196 197
  int found = 0;

  do {
    more = ogg_sync_pageseek (&reader->ogg_sync, og);

    if (more == 0) {
      page_offset = 0;
#if _UMMODIFIED_
      buffer = ogg_sync_buffer (&reader->ogg_sync, CHUNKSIZE);
198
      if ((bytes = oggz_io_read (oggz, buffer, CHUNKSIZE)) == 0) {
199
#if 0
conrad's avatar
conrad committed
200 201 202 203
  if (ferror (oggz->file)) {
    oggz_set_error (oggz, OGGZ_ERR_SYSTEM);
    return -1;
  }
204
#endif
andre's avatar
andre committed
205 206 207
      }

      if (bytes == 0) {
conrad's avatar
conrad committed
208
        return -2;
andre's avatar
andre committed
209 210 211 212 213 214 215
      }

      ogg_sync_wrote(&reader->ogg_sync, bytes);
#else
      return -2;
#endif
    } else if (more < 0) {
216
#ifdef DEBUG_VERBOSE
andre's avatar
andre committed
217 218 219 220
      printf ("get_next_page: skipped %ld bytes\n", -more);
#endif
      page_offset -= more;
    } else {
221
#ifdef DEBUG_VERBOSE
andre's avatar
andre committed
222 223 224 225 226 227 228 229 230
      printf ("get_next_page: page has %ld bytes\n", more);
#endif
      found = 1;
    }

  } while (!found);

  /* Calculate the byte offset of the page which was found */
  if (bytes > 0) {
231
    oggz->offset = oggz_io_tell (oggz) - bytes + page_offset;
andre's avatar
andre committed
232 233 234 235 236 237 238 239 240 241
    ret = oggz->offset;
  } else {
    /* didn't need to do any reading -- accumulate the page_offset */
    ret = oggz->offset + page_offset;
    oggz->offset += page_offset + more;
  }

  return ret;
}

242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
typedef struct {
  ogg_packet      packet;
  ogg_int64_t     calced_granulepos;
  oggz_stream_t * stream;
  OggzReader    * reader;
  OGGZ          * oggz;
  long            serialno;
} OggzBufferedPacket;

OggzBufferedPacket *
oggz_read_new_pbuffer_entry(OGGZ *oggz, ogg_packet *packet, 
            ogg_int64_t granulepos, long serialno, oggz_stream_t * stream, 
            OggzReader *reader) {

  OggzBufferedPacket *p = malloc(sizeof(OggzBufferedPacket));
  memcpy(&(p->packet), packet, sizeof(ogg_packet));
  p->packet.packet = malloc(packet->bytes);
  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) {
  
  free(p->packet.packet);
  free(p);

}

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);
    p->calced_granulepos = 
      oggz_auto_calculate_gp_backwards(content, p->stream->last_granulepos,
      p->stream, &(p->packet), p->stream->last_packet);
      
    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;
302
  ogg_int64_t unit_stored;
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331

  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) {
    p->stream->read_packet(p->oggz, &(p->packet), p->serialno, 
            p->stream->read_user_data);
  } else if (p->reader->read_packet) {
    p->reader->read_packet(p->oggz, &(p->packet), p->serialno, 
            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;
}

332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
OggzDListIterResponse
oggz_read_create_fisbone(void *elem) {

  OggzBufferedPacket *p = (OggzBufferedPacket *)elem;

  int packets;

  ogg_packet *op = oggz_skeleton_create_fisbone(p->serialno, p->stream->content,
                  p->stream->granulerate_n, p->stream->granulerate_d,
                  p->stream->basegranule, p->stream->granuleshift, 
                  &packets, (p->oggz->skeleton_packetno)++);

  if (p->stream->read_packet) {
    p->stream->read_packet(p->oggz, op, p->oggz->skeleton_serialno, 
            p->stream->read_user_data);
  } else if (p->reader->read_packet) {
    p->reader->read_packet(p->oggz, op, p->oggz->skeleton_serialno, 
            p->reader->read_user_data);
  }

  oggz_skeleton_destroy_packet(op);

  return DLIST_ITER_CONTINUE;
}

357
static int
andre's avatar
andre committed
358 359 360 361 362 363 364 365 366 367 368 369
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;

370 371
  int cb_ret = 0;

andre's avatar
andre committed
372 373 374 375 376
  /*os = &reader->ogg_stream;*/
  op = &packet;

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

    if (reader->current_serialno != -1) {
380 381
    /* process a packet if we can.  If the machine isn't loaded,
       neither is a page */
382 383 384
      while(cb_ret == 0) {
        ogg_int64_t granulepos;
        int result;
andre's avatar
andre committed
385

386
        serialno = reader->current_serialno;
andre's avatar
andre committed
387

388
        stream = oggz_get_stream (oggz, serialno);
389

390
        if (stream == NULL) {
conrad's avatar
conrad committed
391
          /* new stream ... check bos etc. */
392
          assert(!"there should be no new streams here!\n");
conrad's avatar
conrad committed
393 394 395 396
          if ((stream = oggz_add_stream (oggz, serialno)) == NULL) {
            /* error -- could not add stream */
            return -7;
          }
397
        }
398

399
        os = &stream->ogg_stream;
400

401
        result = ogg_stream_packetout(os, op);
andre's avatar
andre committed
402

403
        if(result == -1) {
404
#ifdef DEBUG
conrad's avatar
conrad committed
405
          printf ("oggz_read_sync: hole in the data\n");
406
#endif
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
          result = ogg_stream_packetout(os, op);
          if (result == -1) {
#ifdef DEBUG
            /*
             * libogg flags "holes in the data" (which are really 
             * inconsistencies in the page sequence number) by returning
             * -1.  This occurs in some files and pretty much doesn't matter,
             *  so we silently swallow the notification and reget the packet.
             *  If the result is *still* -1 then something strange is happening.
             */
            printf ("shouldn't get here");
#endif
            return -7;
          }
        }
andre's avatar
andre committed
422

423 424
        if(result > 0){
          int content;
425 426

          stream->packetno++;
427
          
conrad's avatar
conrad committed
428 429
          /* got a packet.  process it */
          granulepos = op->granulepos;
andre's avatar
andre committed
430

431
          content = oggz_stream_get_content(oggz, serialno);
432

433 434 435
          /*
           * if we have no metrics for this stream yet, then generate them
           */      
conrad's avatar
conrad committed
436
          if 
437 438 439 440 441 442
          (
            (!stream->metric || (content == OGGZ_CONTENT_SKELETON)) 
            && 
            (oggz->flags & OGGZ_AUTO)
          ) 
          {
conrad's avatar
conrad committed
443 444
            oggz_auto_get_granulerate (oggz, op, serialno, NULL);
          }
445 446 447 448 449

          /* attempt to determine granulepos for this packet */
          if (oggz->flags & OGGZ_AUTO) {
            reader->current_granulepos = 
              oggz_auto_calculate_granulepos (content, granulepos, stream, op); 
450 451 452 453 454
            /* make sure that we accept any "real" gaps in the granulepos
             */
            if (granulepos != -1 && reader->current_granulepos < granulepos) {
              reader->current_granulepos = granulepos;
            }
455 456 457 458
          } else {
            reader->current_granulepos = granulepos;
          }
          stream->last_granulepos = reader->current_granulepos;
459
        
conrad's avatar
conrad committed
460 461
          /* set unit on last packet of page */
          if 
462 463 464 465
          (
            (oggz->metric || stream->metric) && reader->current_granulepos != -1
          ) 
          {
conrad's avatar
conrad committed
466 467 468 469
            reader->current_unit =
              oggz_get_unit (oggz, serialno, reader->current_granulepos);
          }

470 471 472
          if (stream->packetno == 1) {
            oggz_auto_read_comments (oggz, stream, serialno, op);
          }
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
        
          /*
           * don't emit BOS packets until there's a non-BOS packet or
           * until a Skeleton BOS has been seen
           */
          if (oggz->flags & OGGZ_CONSTRUCT_SKELETON) {

            if (content == OGGZ_CONTENT_SKELETON) {
              oggz->skeleton_seen = 1;
              oggz->skeleton_serialno = serialno;
            }

            if (!(oggz->non_bos_encountered || oggz->skeleton_seen)) {
              OggzBufferedPacket *p = oggz_read_new_pbuffer_entry(
                      oggz, &packet, reader->current_granulepos,
                      serialno, stream, reader);
              oggz_dlist_append(oggz->bos_buffer, p);
              continue;
            }

            if (!oggz_dlist_is_empty(oggz->bos_buffer)) {
              ogg_packet *op = oggz_skeleton_create_bos(0, 1000, 0, 1000);
              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);
              }
              oggz_skeleton_destroy_packet(op);
              oggz_dlist_iter(oggz->bos_buffer, oggz_read_deliver_packet);
              oggz->skeleton_packetno = 1;
              oggz_dlist_iter(oggz->bos_buffer, oggz_read_create_fisbone);
              oggz_dlist_delete(oggz->bos_buffer);
              oggz->bos_buffer = oggz_dlist_new();

            }

          }

513 514
          if (oggz->flags & OGGZ_AUTO) {
          
515
            /*
516 517 518
             * while we are getting invalid granulepos values, store the 
             * incoming packets in a dlist */
            if (reader->current_granulepos == -1) {
519
              OggzBufferedPacket *p = oggz_read_new_pbuffer_entry(
520 521
                                oggz, &packet, reader->current_granulepos, 
                                serialno, stream, reader);
522 523 524

              oggz_dlist_append(oggz->packet_buffer, p);
              continue;
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
            } 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);

              /*
               * fix up the stream granulepos 
               */
              stream->last_granulepos = gp_stored;

              if (!oggz_dlist_is_empty(oggz->packet_buffer)) {
                OggzBufferedPacket *p = oggz_read_new_pbuffer_entry(
                                oggz, &packet, reader->current_granulepos, 
                                serialno, stream, reader);

                oggz_dlist_append(oggz->packet_buffer, p);
                continue;
              }
550 551 552
            }
          }

conrad's avatar
conrad committed
553 554 555 556 557 558 559
          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);
          }
560 561 562 563 564 565

          /* 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;
566 567
        }
        else
conrad's avatar
conrad committed
568
          break;
andre's avatar
andre committed
569 570
      }
    }
571

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

575
    if(oggz_get_next_page_7 (oggz, &og) < 0)
576
      return OGGZ_READ_EMPTY; /* eof. leave uninitialized */
andre's avatar
andre committed
577

578 579 580 581
    serialno = ogg_page_serialno (&og);
    reader->current_serialno = serialno; /* XXX: maybe not necessary */

    stream = oggz_get_stream (oggz, serialno);
582

583 584 585 586
    if (stream != NULL) {
      oggz->non_bos_encountered = 1;
    }

587 588 589
    if (stream == NULL) {
      /* new stream ... check bos etc. */
      if ((stream = oggz_add_stream (oggz, serialno)) == NULL) {
conrad's avatar
conrad committed
590 591
        /* error -- could not add stream */
        return -7;
andre's avatar
andre committed
592
      }
593 594 595 596 597 598 599 600 601
      /* identify stream type */
      oggz_auto_identify(oggz, &og, serialno);
    }
    else if (oggz_stream_get_content(oggz, serialno) == OGGZ_CONTENT_ANXDATA)
    {
      /*
       * re-identify ANXDATA streams as these are now content streams
       */
      oggz_auto_identify(oggz, &og, serialno);
602
    }
603
    
604
    os = &stream->ogg_stream;
andre's avatar
andre committed
605

606
    {
607 608 609
      ogg_int64_t granulepos;

      granulepos = ogg_page_granulepos (&og);
610
      stream->page_granulepos = granulepos;
611

612
      if ((oggz->metric || stream->metric) && granulepos != -1) {
613
       reader->current_unit = oggz_get_unit (oggz, serialno, granulepos);
614
      } else if (granulepos == 0) {
615
       reader->current_unit = 0;
andre's avatar
andre committed
616
      }
617
    }
618

619 620
    if (stream->read_page) {
      cb_ret =
conrad's avatar
conrad committed
621
        stream->read_page (oggz, &og, serialno, stream->read_page_user_data);
622
    } else if (reader->read_page) {
conrad's avatar
conrad committed
623 624
      cb_ret =
        reader->read_page (oggz, &og, serialno, reader->read_page_user_data);
625
    }
andre's avatar
andre committed
626 627

#if 0
628 629 630
    /* bitrate tracking; add the header's bytes here, the body bytes
       are done by packet above */
    vf->bittrack+=og.header_len*8;
andre's avatar
andre committed
631 632
#endif

633
    ogg_stream_pagein(os, &og);
andre's avatar
andre committed
634 635
  }

636
  return cb_ret;
andre's avatar
andre committed
637 638 639 640 641 642 643 644
}

long
oggz_read (OGGZ * oggz, long n)
{
  OggzReader * reader;
  char * buffer;
  long bytes, bytes_read = 1, remaining = n, nread = 0;
645
  int cb_ret = 0;
andre's avatar
andre committed
646 647 648 649 650 651 652

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

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

653 654
  if ((cb_ret = oggz->cb_next) != OGGZ_CONTINUE) {
    oggz->cb_next = 0;
655
    return oggz_map_return_value_to_error (cb_ret);
656 657
  }

andre's avatar
andre committed
658 659
  reader = &oggz->x.reader;

660
  cb_ret = oggz_read_sync (oggz);
andre's avatar
andre committed
661

662
#if 0
663
  if (cb_ret == OGGZ_READ_EMPTY) {
664 665
    /* If there's nothing to read yet, don't return 0 (eof) */
    if (reader->current_unit == 0) cb_ret = 0;
666 667 668
    else {
#if 0
      printf ("oggz_read: EMPTY, current_unit %ld != 0\n",
conrad's avatar
conrad committed
669
              reader->current_unit);
670 671 672
      return 0;
#endif
    }
673
  }
674
#endif
675

676
  while (cb_ret != OGGZ_STOP_ERR && cb_ret != OGGZ_STOP_OK &&
conrad's avatar
conrad committed
677
         bytes_read > 0 && remaining > 0) {
678
    bytes = MIN (remaining, CHUNKSIZE);
andre's avatar
andre committed
679
    buffer = ogg_sync_buffer (&reader->ogg_sync, bytes);
680
    if ((bytes_read = (long) oggz_io_read (oggz, buffer, bytes)) == 0) {
681 682 683 684
      /* schyeah! */
    }
    if (bytes_read == OGGZ_ERR_SYSTEM) {
      return OGGZ_ERR_SYSTEM;
andre's avatar
andre committed
685 686
    }

conrad's avatar
 
conrad committed
687 688 689 690 691 692 693 694
    if (bytes_read > 0) {
      ogg_sync_wrote (&reader->ogg_sync, bytes_read);
      
      remaining -= bytes_read;
      nread += bytes_read;
      
      cb_ret = oggz_read_sync (oggz);
    }
andre's avatar
andre committed
695 696
  }

697
  if (cb_ret == OGGZ_STOP_ERR) oggz_purge (oggz);
698

699
  /* Don't return 0 unless it's actually an EOF condition */
700
  if (nread == 0) {
conrad's avatar
conrad committed
701 702 703 704 705 706 707
    switch (bytes_read) {
    case OGGZ_ERR_IO_AGAIN:
    case OGGZ_ERR_SYSTEM:
      return bytes_read; break;
    default: break;
    }

708 709 710 711 712
    if (cb_ret == OGGZ_READ_EMPTY) {
      return 0;
    } else {
      return oggz_map_return_value_to_error (cb_ret);
    }
713 714 715 716

  } else {
    if (cb_ret == OGGZ_READ_EMPTY) cb_ret = OGGZ_CONTINUE;
    oggz->cb_next = cb_ret;
717
  }
718

andre's avatar
andre committed
719 720 721 722 723 724 725 726 727 728
  return nread;
}

/* generic */
long
oggz_read_input (OGGZ * oggz, unsigned char * buf, long n)
{
  OggzReader * reader;
  char * buffer;
  long bytes, remaining = n, nread = 0;
729
  int cb_ret = 0;
andre's avatar
andre committed
730 731 732 733 734 735 736

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

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

737 738
  if ((cb_ret = oggz->cb_next) != OGGZ_CONTINUE) {
    oggz->cb_next = 0;
739
    return oggz_map_return_value_to_error (cb_ret);
740 741
  }

andre's avatar
andre committed
742 743
  reader = &oggz->x.reader;

744 745
  cb_ret = oggz_read_sync (oggz);

746
#if 0
747
  if (cb_ret == OGGZ_READ_EMPTY) {
748 749 750 751
    /* If there's nothing to read yet, don't return 0 (eof) */
    if (reader->current_unit == 0) cb_ret = 0;
    else return 0;
  }
752
#endif
conrad's avatar
conrad committed
753

754
  while (cb_ret != OGGZ_STOP_ERR && cb_ret != OGGZ_STOP_OK  &&
conrad's avatar
conrad committed
755
         /* !oggz->eos && */ remaining > 0) {
andre's avatar
andre committed
756 757 758 759 760 761 762 763 764
    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;

765
    cb_ret = oggz_read_sync (oggz);
andre's avatar
andre committed
766 767
  }

768
  if (cb_ret == OGGZ_STOP_ERR) oggz_purge (oggz);
769

770
  if (nread == 0) {
771 772
    /* Don't return 0 unless it's actually an EOF condition */
    if (cb_ret == OGGZ_READ_EMPTY) {
773
      return OGGZ_ERR_STOP_OK;
774
    } else {
775
      return oggz_map_return_value_to_error (cb_ret);
776
    }
777 778 779
  } else {
    if (cb_ret == OGGZ_READ_EMPTY) cb_ret = OGGZ_CONTINUE;
    oggz->cb_next = cb_ret;
780
  }
781

andre's avatar
andre committed
782 783 784 785 786 787 788 789 790
  return nread;
}


#else /* OGGZ_CONFIG_READ */

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

791 792 793 794 795 796 797 798 799 800 801 802
OGGZ *
oggz_read_init (OGGZ * oggz)
{
  return NULL;
}

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

andre's avatar
andre committed
803 804
int
oggz_set_read_callback (OGGZ * oggz, long serialno,
conrad's avatar
conrad committed
805
                        OggzReadPacket read_packet, void * user_data)
andre's avatar
andre committed
806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822
{
  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