oggz_read.c 19.1 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"

johnf's avatar
johnf committed
63 64
#include <oggz/oggz_stream.h>

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 89 90 91 92 93 94 95 96
  reader->current_unit = 0;

  return oggz;
}

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

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

  return oggz;
}

int
oggz_set_read_callback (OGGZ * oggz, long serialno,
conrad's avatar
conrad committed
105
                        OggzReadPacket read_packet, void * user_data)
andre's avatar
andre committed
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
{
  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
123
#if 0
andre's avatar
andre committed
124
    if (stream == NULL) return OGGZ_ERR_BAD_SERIALNO;
conrad's avatar
conrad committed
125 126 127 128
#else
    if (stream == NULL)
      stream = oggz_add_stream (oggz, serialno);
#endif
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 159 160 161 162 163 164 165 166
  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;
  }
167 168 169 170

  return 0;
}

andre's avatar
andre committed
171 172 173 174 175 176 177 178 179 180
/*
 * 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
181
static oggz_off_t
andre's avatar
andre committed
182 183 184 185 186 187 188
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
189
  oggz_off_t page_offset = 0, ret;
andre's avatar
andre committed
190 191 192 193 194 195 196 197 198
  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);
199
      if ((bytes = oggz_io_read (oggz, buffer, CHUNKSIZE)) == 0) {
200
#if 0
conrad's avatar
conrad committed
201 202 203 204
  if (ferror (oggz->file)) {
    oggz_set_error (oggz, OGGZ_ERR_SYSTEM);
    return -1;
  }
205
#endif
andre's avatar
andre committed
206 207 208
      }

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

      ogg_sync_wrote(&reader->ogg_sync, bytes);
#else
      return -2;
#endif
    } else if (more < 0) {
217
#ifdef DEBUG_VERBOSE
andre's avatar
andre committed
218 219 220 221
      printf ("get_next_page: skipped %ld bytes\n", -more);
#endif
      page_offset -= more;
    } else {
222
#ifdef DEBUG_VERBOSE
andre's avatar
andre committed
223 224 225 226 227 228 229 230 231
      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) {
232
    oggz->offset = oggz_io_tell (oggz) - bytes + page_offset;
andre's avatar
andre committed
233 234 235 236 237 238 239 240 241 242
    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;
}

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 302
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;
303
  ogg_int64_t unit_stored;
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 332

  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;
}

333
static int
andre's avatar
andre committed
334 335 336 337 338 339 340 341 342 343 344 345
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;

346 347
  int cb_ret = 0;

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

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

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

362
        serialno = reader->current_serialno;
andre's avatar
andre committed
363

364
        stream = oggz_get_stream (oggz, serialno);
365

366
        if (stream == NULL) {
conrad's avatar
conrad committed
367 368 369 370 371
          /* new stream ... check bos etc. */
          if ((stream = oggz_add_stream (oggz, serialno)) == NULL) {
            /* error -- could not add stream */
            return -7;
          }
372 373
        }
        os = &stream->ogg_stream;
374

375
        result = ogg_stream_packetout(os, op);
andre's avatar
andre committed
376

377
        if(result == -1) {
378
#ifdef DEBUG
conrad's avatar
conrad committed
379
          printf ("oggz_read_sync: hole in the data\n");
380
#endif
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
          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
396

397 398
        if(result > 0){
          int content;
399 400

          stream->packetno++;
401
          
conrad's avatar
conrad committed
402 403
          /* got a packet.  process it */
          granulepos = op->granulepos;
andre's avatar
andre committed
404

405
          content = oggz_stream_get_content(oggz, serialno);
406
  
407 408 409
          /*
           * if we have no metrics for this stream yet, then generate them
           */      
conrad's avatar
conrad committed
410
          if 
411 412 413 414 415 416
          (
            (!stream->metric || (content == OGGZ_CONTENT_SKELETON)) 
            && 
            (oggz->flags & OGGZ_AUTO)
          ) 
          {
417
            oggz_auto_read_bos_packet (oggz, op, serialno, NULL);
conrad's avatar
conrad committed
418
          }
419 420 421 422 423

          /* attempt to determine granulepos for this packet */
          if (oggz->flags & OGGZ_AUTO) {
            reader->current_granulepos = 
              oggz_auto_calculate_granulepos (content, granulepos, stream, op); 
424 425 426 427 428
            /* make sure that we accept any "real" gaps in the granulepos
             */
            if (granulepos != -1 && reader->current_granulepos < granulepos) {
              reader->current_granulepos = granulepos;
            }
429 430 431 432
          } else {
            reader->current_granulepos = granulepos;
          }
          stream->last_granulepos = reader->current_granulepos;
433
        
conrad's avatar
conrad committed
434 435
          /* set unit on last packet of page */
          if 
436 437 438 439
          (
            (oggz->metric || stream->metric) && reader->current_granulepos != -1
          ) 
          {
conrad's avatar
conrad committed
440 441 442 443
            reader->current_unit =
              oggz_get_unit (oggz, serialno, reader->current_granulepos);
          }

444 445 446
          if (stream->packetno == 1) {
            oggz_auto_read_comments (oggz, stream, serialno, op);
          }
447
          
448 449
          if (oggz->flags & OGGZ_AUTO) {
          
450
            /*
451 452 453
             * while we are getting invalid granulepos values, store the 
             * incoming packets in a dlist */
            if (reader->current_granulepos == -1) {
454
              OggzBufferedPacket *p = oggz_read_new_pbuffer_entry(
455 456
                                oggz, &packet, reader->current_granulepos, 
                                serialno, stream, reader);
457 458 459

              oggz_dlist_append(oggz->packet_buffer, p);
              continue;
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
            } 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;
              }
485 486 487
            }
          }

conrad's avatar
conrad committed
488 489 490 491 492 493 494
          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);
          }
495 496 497 498 499 500

          /* 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;
501 502
        }
        else
conrad's avatar
conrad committed
503
          break;
andre's avatar
andre committed
504 505
      }
    }
506

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

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

513 514 515 516
    serialno = ogg_page_serialno (&og);
    reader->current_serialno = serialno; /* XXX: maybe not necessary */

    stream = oggz_get_stream (oggz, serialno);
517

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

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

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

542
    {
543 544 545
      ogg_int64_t granulepos;

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

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

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

#if 0
564 565 566
    /* 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
567 568
#endif

569
    ogg_stream_pagein(os, &og);
andre's avatar
andre committed
570 571
  }

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

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

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

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

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

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

596
  cb_ret = oggz_read_sync (oggz);
andre's avatar
andre committed
597

598
#if 0
599
  if (cb_ret == OGGZ_READ_EMPTY) {
600 601
    /* If there's nothing to read yet, don't return 0 (eof) */
    if (reader->current_unit == 0) cb_ret = 0;
602 603 604
    else {
#if 0
      printf ("oggz_read: EMPTY, current_unit %ld != 0\n",
conrad's avatar
conrad committed
605
              reader->current_unit);
606 607 608
      return 0;
#endif
    }
609
  }
610
#endif
611

612
  while (cb_ret != OGGZ_STOP_ERR && cb_ret != OGGZ_STOP_OK &&
conrad's avatar
conrad committed
613
         bytes_read > 0 && remaining > 0) {
614
    bytes = MIN (remaining, CHUNKSIZE);
andre's avatar
andre committed
615
    buffer = ogg_sync_buffer (&reader->ogg_sync, bytes);
616
    if ((bytes_read = (long) oggz_io_read (oggz, buffer, bytes)) == 0) {
617 618 619 620
      /* schyeah! */
    }
    if (bytes_read == OGGZ_ERR_SYSTEM) {
      return OGGZ_ERR_SYSTEM;
andre's avatar
andre committed
621 622
    }

conrad's avatar
 
conrad committed
623 624 625 626 627 628 629 630
    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
631 632
  }

633
  if (cb_ret == OGGZ_STOP_ERR) oggz_purge (oggz);
634

635
  /* Don't return 0 unless it's actually an EOF condition */
636
  if (nread == 0) {
conrad's avatar
conrad committed
637 638 639 640 641 642 643
    switch (bytes_read) {
    case OGGZ_ERR_IO_AGAIN:
    case OGGZ_ERR_SYSTEM:
      return bytes_read; break;
    default: break;
    }

644 645 646 647 648
    if (cb_ret == OGGZ_READ_EMPTY) {
      return 0;
    } else {
      return oggz_map_return_value_to_error (cb_ret);
    }
649 650 651 652

  } else {
    if (cb_ret == OGGZ_READ_EMPTY) cb_ret = OGGZ_CONTINUE;
    oggz->cb_next = cb_ret;
653
  }
654

andre's avatar
andre committed
655 656 657 658 659 660 661 662 663 664
  return nread;
}

/* generic */
long
oggz_read_input (OGGZ * oggz, unsigned char * buf, long n)
{
  OggzReader * reader;
  char * buffer;
  long bytes, remaining = n, nread = 0;
665
  int cb_ret = 0;
andre's avatar
andre committed
666 667 668 669 670 671 672

  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;

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

673 674
  if ((cb_ret = oggz->cb_next) != OGGZ_CONTINUE) {
    oggz->cb_next = 0;
675
    return oggz_map_return_value_to_error (cb_ret);
676 677
  }

andre's avatar
andre committed
678 679
  reader = &oggz->x.reader;

680 681
  cb_ret = oggz_read_sync (oggz);

682
#if 0
683
  if (cb_ret == OGGZ_READ_EMPTY) {
684 685 686 687
    /* If there's nothing to read yet, don't return 0 (eof) */
    if (reader->current_unit == 0) cb_ret = 0;
    else return 0;
  }
688
#endif
conrad's avatar
conrad committed
689

690
  while (cb_ret != OGGZ_STOP_ERR && cb_ret != OGGZ_STOP_OK  &&
conrad's avatar
conrad committed
691
         /* !oggz->eos && */ remaining > 0) {
andre's avatar
andre committed
692 693 694 695 696 697 698 699 700
    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;

701
    cb_ret = oggz_read_sync (oggz);
andre's avatar
andre committed
702 703
  }

704
  if (cb_ret == OGGZ_STOP_ERR) oggz_purge (oggz);
705

706
  if (nread == 0) {
707 708
    /* Don't return 0 unless it's actually an EOF condition */
    if (cb_ret == OGGZ_READ_EMPTY) {
709
      return OGGZ_ERR_STOP_OK;
710
    } else {
711
      return oggz_map_return_value_to_error (cb_ret);
712
    }
713 714 715
  } else {
    if (cb_ret == OGGZ_READ_EMPTY) cb_ret = OGGZ_CONTINUE;
    oggz->cb_next = cb_ret;
716
  }
717

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


#else /* OGGZ_CONFIG_READ */

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

727 728 729 730 731 732 733 734 735 736 737 738
OGGZ *
oggz_read_init (OGGZ * oggz)
{
  return NULL;
}

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

andre's avatar
andre committed
739 740
int
oggz_set_read_callback (OGGZ * oggz, long serialno,
conrad's avatar
conrad committed
741
                        OggzReadPacket read_packet, void * user_data)
andre's avatar
andre committed
742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
{
  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