root/liboggz/trunk/src/liboggz/oggz_read.c

Revision 3790, 18.2 kB (checked in by conrad, 2 weeks ago)

remove dead code from oggz_read.c
for ticket:439, reported by Coverity

Line 
1 /*
2    Copyright (C) 2003 Commonwealth Scientific and Industrial Research
3    Organisation (CSIRO) Australia
4
5    Redistribution and use in source and binary forms, with or without
6    modification, are permitted provided that the following conditions
7    are met:
8
9    - Redistributions of source code must retain the above copyright
10    notice, this list of conditions and the following disclaimer.
11
12    - Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in the
14    documentation and/or other materials provided with the distribution.
15
16    - Neither the name of CSIRO Australia nor the names of its
17    contributors may be used to endorse or promote products derived from
18    this software without specific prior written permission.
19
20    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23    PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE ORGANISATION OR
24    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 /*
34  * oggz_read.c
35  *
36  * Conrad Parker <conrad@annodex.net>
37  */
38
39 #include "config.h"
40
41 #if OGGZ_CONFIG_READ
42
43 #include <assert.h>
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48
49 #ifdef HAVE_UNISTD_H
50 #include <unistd.h>
51 #endif
52
53 #include <fcntl.h>
54 #include <errno.h>
55 #include <string.h>
56 #include <time.h>
57
58 #include <ogg/ogg.h>
59
60 #include "oggz_compat.h"
61 #include "oggz_private.h"
62
63 #include <oggz/oggz_stream.h>
64
65 /* #define DEBUG */
66 /* #define DEBUG_VERBOSE */
67
68 #define CHUNKSIZE 65536
69
70 #define OGGZ_READ_EMPTY (-404)
71
72 OGGZ *
73 oggz_read_init (OGGZ * oggz)
74 {
75   OggzReader * reader = &oggz->x.reader;
76
77   ogg_sync_init (&reader->ogg_sync);
78   ogg_stream_init (&reader->ogg_stream, (int)-1);
79   reader->current_serialno = -1;
80
81   reader->read_packet = NULL;
82   reader->read_user_data = NULL;
83
84   reader->read_page = NULL;
85   reader->read_page_user_data = NULL;
86
87   reader->current_unit = 0;
88
89   return oggz;
90 }
91
92 OGGZ *
93 oggz_read_close (OGGZ * oggz)
94 {
95   OggzReader * reader = &oggz->x.reader;
96
97   ogg_stream_clear (&reader->ogg_stream);
98   ogg_sync_clear (&reader->ogg_sync);
99
100   return oggz;
101 }
102
103 int
104 oggz_set_read_callback (OGGZ * oggz, long serialno,
105                         OggzReadPacket read_packet, void * user_data)
106 {
107   OggzReader * reader;
108   oggz_stream_t * stream;
109
110   if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
111
112   reader =  &oggz->x.reader;
113
114   if (oggz->flags & OGGZ_WRITE) {
115     return OGGZ_ERR_INVALID;
116   }
117
118   if (serialno == -1) {
119     reader->read_packet = read_packet;
120     reader->read_user_data = user_data;
121   } else {
122     stream = oggz_get_stream (oggz, serialno);
123     if (stream == NULL)
124       stream = oggz_add_stream (oggz, serialno);
125
126     stream->read_packet = read_packet;
127     stream->read_user_data = user_data;
128   }
129
130   return 0;
131 }
132
133 int
134 oggz_set_read_page (OGGZ * oggz, long serialno, OggzReadPage read_page,
135                     void * user_data)
136 {
137   OggzReader * reader;
138   oggz_stream_t * stream;
139
140   if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
141
142   reader =  &oggz->x.reader;
143
144   if (oggz->flags & OGGZ_WRITE) {
145     return OGGZ_ERR_INVALID;
146   }
147
148   if (serialno == -1) {
149     reader->read_page = read_page;
150     reader->read_page_user_data = user_data;
151   } else {
152     stream = oggz_get_stream (oggz, serialno);
153     if (stream == NULL)
154       stream = oggz_add_stream (oggz, serialno);
155
156     stream->read_page = read_page;
157     stream->read_page_user_data = user_data;
158   }
159
160   return 0;
161 }
162
163 /*
164  * oggz_read_get_next_page (oggz, og, do_read)
165  *
166  * This differs from oggz_get_next_page() in oggz_seek.c in that it
167  * does not attempt to call oggz_io_read() if the sync buffer is empty.
168  *
169  * retrieves the next page.
170  * returns >= 0 if found; return value is offset of page start
171  * returns -1 on error
172  * returns -2 if EOF was encountered
173  */
174 static oggz_off_t
175 oggz_read_get_next_page (OGGZ * oggz, ogg_page * og)
176 {
177   OggzReader * reader = &oggz->x.reader;
178   long bytes = 0, more;
179   oggz_off_t page_offset = 0, ret;
180   int found = 0;
181
182   do {
183     more = ogg_sync_pageseek (&reader->ogg_sync, og);
184
185     if (more == 0) {
186       page_offset = 0;
187       return -2;
188     } else if (more < 0) {
189 #ifdef DEBUG_VERBOSE
190       printf ("get_next_page: skipped %ld bytes\n", -more);
191 #endif
192       page_offset -= more;
193     } else {
194 #ifdef DEBUG_VERBOSE
195       printf ("get_next_page: page has %ld bytes\n", more);
196 #endif
197       found = 1;
198     }
199
200   } while (!found);
201
202   /* Calculate the byte offset of the page which was found */
203   if (bytes > 0) {
204     oggz->offset = oggz_io_tell (oggz) - bytes + page_offset;
205     ret = oggz->offset;
206   } else {
207     /* didn't need to do any reading -- accumulate the page_offset */
208     ret = oggz->offset + page_offset;
209     oggz->offset += page_offset + more;
210   }
211
212   return ret;
213 }
214
215 typedef struct {
216   ogg_packet      packet;
217   ogg_int64_t     calced_granulepos;
218   oggz_stream_t * stream;
219   OggzReader    * reader;
220   OGGZ          * oggz;
221   long            serialno;
222 } OggzBufferedPacket;
223
224 OggzBufferedPacket *
225 oggz_read_new_pbuffer_entry(OGGZ *oggz, ogg_packet *packet,
226             ogg_int64_t granulepos, long serialno, oggz_stream_t * stream,
227             OggzReader *reader) {
228
229   OggzBufferedPacket *p = malloc(sizeof(OggzBufferedPacket));
230   memcpy(&(p->packet), packet, sizeof(ogg_packet));
231   p->packet.packet = malloc(packet->bytes);
232   memcpy(p->packet.packet, packet->packet, packet->bytes);
233
234   p->calced_granulepos = granulepos;
235   p->stream = stream;
236   p->serialno = serialno;
237   p->reader = reader;
238   p->oggz = oggz;
239
240   return p;
241 }
242
243 void
244 oggz_read_free_pbuffer_entry(OggzBufferedPacket *p) {
245  
246   free(p->packet.packet);
247   free(p);
248
249 }
250
251 OggzDListIterResponse
252 oggz_read_update_gp(void *elem) {
253
254   OggzBufferedPacket *p = (OggzBufferedPacket *)elem;
255
256   if (p->calced_granulepos == -1 && p->stream->last_granulepos != -1) {
257     int content = oggz_stream_get_content(p->oggz, p->serialno);
258     p->calced_granulepos =
259       oggz_auto_calculate_gp_backwards(content, p->stream->last_granulepos,
260       p->stream, &(p->packet), p->stream->last_packet);
261      
262     p->stream->last_granulepos = p->calced_granulepos;
263     p->stream->last_packet = &(p->packet);
264   }
265
266   return DLIST_ITER_CONTINUE;
267
268 }
269
270 OggzDListIterResponse
271 oggz_read_deliver_packet(void *elem) {
272
273   OggzBufferedPacket *p = (OggzBufferedPacket *)elem;
274   ogg_int64_t gp_stored;
275   ogg_int64_t unit_stored;
276
277   if (p->calced_granulepos == -1) {
278     return DLIST_ITER_CANCEL;
279   }
280
281   gp_stored = p->reader->current_granulepos;
282   unit_stored = p->reader->current_unit;
283
284   p->reader->current_granulepos = p->calced_granulepos;
285
286   p->reader->current_unit =
287     oggz_get_unit (p->oggz, p->serialno, p->calced_granulepos);
288
289   if (p->stream->read_packet) {
290     p->stream->read_packet(p->oggz, &(p->packet), p->serialno,
291             p->stream->read_user_data);
292   } else if (p->reader->read_packet) {
293     p->reader->read_packet(p->oggz, &(p->packet), p->serialno,
294             p->reader->read_user_data);
295   }
296
297   p->reader->current_granulepos = gp_stored;
298   p->reader->current_unit = unit_stored;
299
300   oggz_read_free_pbuffer_entry(p);
301
302   return DLIST_ITER_CONTINUE;
303 }
304
305 static int
306 oggz_read_sync (OGGZ * oggz)
307 {
308   OggzReader * reader = &oggz->x.reader;
309
310   oggz_stream_t * stream;
311   ogg_stream_state * os;
312   ogg_packet * op;
313   long serialno;
314
315   ogg_packet packet;
316   ogg_page og;
317
318   int cb_ret = 0;
319
320   /*os = &reader->ogg_stream;*/
321   op = &packet;
322
323   /* handle one packet.  Try to fetch it from current stream state */
324   /* extract packets from page */
325   while(cb_ret == 0) {
326
327     if (reader->current_serialno != -1) {
328     /* process a packet if we can.  If the machine isn't loaded,
329        neither is a page */
330       while(cb_ret == 0) {
331         ogg_int64_t granulepos;
332         int result;
333
334         serialno = reader->current_serialno;
335
336         stream = oggz_get_stream (oggz, serialno);
337
338         if (stream == NULL) {
339           /* new stream ... check bos etc. */
340           if ((stream = oggz_add_stream (oggz, serialno)) == NULL) {
341             /* error -- could not add stream */
342             return -7;
343           }
344         }
345         os = &stream->ogg_stream;
346
347         result = ogg_stream_packetout(os, op);
348
349         /*
350          * libogg flags "holes in the data" (which are really inconsistencies
351          * in the page sequence number) by returning -1.
352          */
353         if(result == -1) {
354 #ifdef DEBUG
355           printf ("oggz_read_sync: hole in the data\n");
356 #endif
357           /* We can't tolerate holes in headers, so bail out. */
358           if (stream->packetno < 3) return OGGZ_ERR_HOLE_IN_DATA;
359
360           /* Holes in content occur in some files and pretty much don't matter,
361            * so we silently swallow the notification and reget the packet.
362            */
363           result = ogg_stream_packetout(os, op);
364           if (result == -1) {
365             /* If the result is *still* -1 then something strange is
366              * happening.
367              */
368 #ifdef DEBUG
369             printf ("Multiple holes in data!");
370 #endif
371             return OGGZ_ERR_HOLE_IN_DATA;
372           }
373         }
374
375         if(result > 0){
376           int content;
377
378           stream->packetno++;
379          
380           /* got a packet.  process it */
381           granulepos = op->granulepos;
382
383           content = oggz_stream_get_content(oggz, serialno);
384  
385           /*
386            * if we have no metrics for this stream yet, then generate them
387            */     
388           if
389           (
390             (!stream->metric || (content == OGGZ_CONTENT_SKELETON))
391             &&
392             (oggz->flags & OGGZ_AUTO)
393           )
394           {
395             oggz_auto_read_bos_packet (oggz, op, serialno, NULL);
396           }
397
398           /* attempt to determine granulepos for this packet */
399           if (oggz->flags & OGGZ_AUTO) {
400             reader->current_granulepos =
401               oggz_auto_calculate_granulepos (content, granulepos, stream, op);
402             /* make sure that we accept any "real" gaps in the granulepos
403              */
404             if (granulepos != -1 && reader->current_granulepos < granulepos) {
405               reader->current_granulepos = granulepos;
406             }
407           } else {
408             reader->current_granulepos = granulepos;
409           }
410           stream->last_granulepos = reader->current_granulepos;
411        
412           /* set unit on last packet of page */
413           if
414           (
415             (oggz->metric || stream->metric) && reader->current_granulepos != -1
416           )
417           {
418             reader->current_unit =
419               oggz_get_unit (oggz, serialno, reader->current_granulepos);
420           }
421
422           if (stream->packetno == 1) {
423             oggz_auto_read_comments (oggz, stream, serialno, op);
424           }
425          
426           if (oggz->flags & OGGZ_AUTO) {
427          
428             /*
429              * while we are getting invalid granulepos values, store the
430              * incoming packets in a dlist */
431             if (reader->current_granulepos == -1) {
432               OggzBufferedPacket *p = oggz_read_new_pbuffer_entry(
433                                 oggz, &packet, reader->current_granulepos,
434                                 serialno, stream, reader);
435
436               oggz_dlist_append(oggz->packet_buffer, p);
437               continue;
438             } else if (!oggz_dlist_is_empty(oggz->packet_buffer)) {
439               /*
440                * move backward through the list assigning gp values based upon
441                * the granulepos we just recieved.  Then move forward through
442                * the list delivering any packets at the beginning with valid
443                * gp values
444                */
445               ogg_int64_t gp_stored = stream->last_granulepos;
446               stream->last_packet = &packet;
447               oggz_dlist_reverse_iter(oggz->packet_buffer, oggz_read_update_gp);
448               oggz_dlist_deliter(oggz->packet_buffer, oggz_read_deliver_packet);
449
450               /*
451                * fix up the stream granulepos
452                */
453               stream->last_granulepos = gp_stored;
454
455               if (!oggz_dlist_is_empty(oggz->packet_buffer)) {
456                 OggzBufferedPacket *p = oggz_read_new_pbuffer_entry(
457                                 oggz, &packet, reader->current_granulepos,
458                                 serialno, stream, reader);
459
460                 oggz_dlist_append(oggz->packet_buffer, p);
461                 continue;
462               }
463             }
464           }
465
466           if (stream->read_packet) {
467             cb_ret =
468               stream->read_packet (oggz, op, serialno, stream->read_user_data);
469           } else if (reader->read_packet) {
470             cb_ret =
471               reader->read_packet (oggz, op, serialno, reader->read_user_data);
472           }
473
474           /* Mark this stream as having delivered a non b_o_s packet if so.
475            * In the case where there is no packet reading callback, this is
476            * also valid as the page reading callback has already been called.
477            */
478           if (!op->b_o_s) stream->delivered_non_b_o_s = 1;
479         }
480         else
481           break;
482       }
483     }
484
485     /* If we've got a stop already, don't read more data in */
486     if (cb_ret == OGGZ_STOP_OK || cb_ret == OGGZ_STOP_ERR) return cb_ret;
487
488     if(oggz_read_get_next_page (oggz, &og) < 0)
489       return OGGZ_READ_EMPTY; /* eof. leave uninitialized */
490
491     serialno = ogg_page_serialno (&og);
492     reader->current_serialno = serialno; /* XXX: maybe not necessary */
493
494     stream = oggz_get_stream (oggz, serialno);
495
496     if (stream == NULL) {
497       /* new stream ... check bos etc. */
498       if ((stream = oggz_add_stream (oggz, serialno)) == NULL) {
499         /* error -- could not add stream */
500         return -7;
501       }
502
503       /* identify stream type */
504       oggz_auto_identify_page (oggz, &og, serialno);
505
506       /* read bos data */
507       if (oggz->flags & OGGZ_AUTO)
508         oggz_auto_read_bos_page (oggz, &og, serialno, NULL);
509     }
510     else if (oggz_stream_get_content(oggz, serialno) == OGGZ_CONTENT_ANXDATA)
511     {
512       /*
513        * re-identify ANXDATA streams as these are now content streams
514        */
515       oggz_auto_identify_page (oggz, &og, serialno);
516     }
517    
518     os = &stream->ogg_stream;
519
520     {
521       ogg_int64_t granulepos;
522
523       granulepos = ogg_page_granulepos (&og);
524       stream->page_granulepos = granulepos;
525
526       if ((oggz->metric || stream->metric) && granulepos != -1) {
527        reader->current_unit = oggz_get_unit (oggz, serialno, granulepos);
528       } else if (granulepos == 0) {
529        reader->current_unit = 0;
530       }
531     }
532
533     if (stream->read_page) {
534       cb_ret =
535         stream->read_page (oggz, &og, serialno, stream->read_page_user_data);
536     } else if (reader->read_page) {
537       cb_ret =
538         reader->read_page (oggz, &og, serialno, reader->read_page_user_data);
539     }
540
541     ogg_stream_pagein(os, &og);
542   }
543
544   return cb_ret;
545 }
546
547 long
548 oggz_read (OGGZ * oggz, long n)
549 {
550   OggzReader * reader;
551   char * buffer;
552   long bytes, bytes_read = 1, remaining = n, nread = 0;
553   int cb_ret = 0;
554
555   if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
556
557   if (oggz->flags & OGGZ_WRITE) {
558     return OGGZ_ERR_INVALID;
559   }
560
561   if ((cb_ret = oggz->cb_next) != OGGZ_CONTINUE) {
562     oggz->cb_next = 0;
563     return oggz_map_return_value_to_error (cb_ret);
564   }
565
566   reader = &oggz->x.reader;
567
568   cb_ret = oggz_read_sync (oggz);
569
570   while (cb_ret != OGGZ_STOP_ERR && cb_ret != OGGZ_STOP_OK &&
571          bytes_read > 0 && remaining > 0) {
572     bytes = MIN (remaining, CHUNKSIZE);
573     buffer = ogg_sync_buffer (&reader->ogg_sync, bytes);
574     bytes_read = (long) oggz_io_read (oggz, buffer, bytes);
575     if (bytes_read == OGGZ_ERR_SYSTEM) {
576       return OGGZ_ERR_SYSTEM;
577     }
578
579     if (bytes_read ><