xref: /aosp_15_r20/external/tremolo/Tremolo/vorbisfile.c (revision bda690e46497e1f65c5077173b9c548e6e0cd5a1)
1*bda690e4SXin Li /************************************************************************
2*bda690e4SXin Li  * Copyright (C) 2002-2009, Xiph.org Foundation
3*bda690e4SXin Li  * Copyright (C) 2010, Robin Watts for Pinknoise Productions Ltd
4*bda690e4SXin Li  * All rights reserved.
5*bda690e4SXin Li  *
6*bda690e4SXin Li  * Redistribution and use in source and binary forms, with or without
7*bda690e4SXin Li  * modification, are permitted provided that the following conditions
8*bda690e4SXin Li  * are met:
9*bda690e4SXin Li  *
10*bda690e4SXin Li  *     * Redistributions of source code must retain the above copyright
11*bda690e4SXin Li  * notice, this list of conditions and the following disclaimer.
12*bda690e4SXin Li  *     * Redistributions in binary form must reproduce the above
13*bda690e4SXin Li  * copyright notice, this list of conditions and the following disclaimer
14*bda690e4SXin Li  * in the documentation and/or other materials provided with the
15*bda690e4SXin Li  * distribution.
16*bda690e4SXin Li  *     * Neither the names of the Xiph.org Foundation nor Pinknoise
17*bda690e4SXin Li  * Productions Ltd nor the names of its contributors may be used to
18*bda690e4SXin Li  * endorse or promote products derived from this software without
19*bda690e4SXin Li  * specific prior written permission.
20*bda690e4SXin Li  *
21*bda690e4SXin Li  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22*bda690e4SXin Li  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23*bda690e4SXin Li  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24*bda690e4SXin Li  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25*bda690e4SXin Li  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26*bda690e4SXin Li  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27*bda690e4SXin Li  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28*bda690e4SXin Li  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29*bda690e4SXin Li  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30*bda690e4SXin Li  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31*bda690e4SXin Li  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32*bda690e4SXin Li  ************************************************************************
33*bda690e4SXin Li 
34*bda690e4SXin Li  function: stdio-based convenience library for opening/seeking/decoding
35*bda690e4SXin Li  last mod: $Id: vorbisfile.c,v 1.6.2.5 2003/11/20 06:16:17 xiphmont Exp $
36*bda690e4SXin Li 
37*bda690e4SXin Li  ************************************************************************/
38*bda690e4SXin Li 
39*bda690e4SXin Li #include <stdlib.h>
40*bda690e4SXin Li #include <stdio.h>
41*bda690e4SXin Li //#include <gerrno.h>
42*bda690e4SXin Li #include <string.h>
43*bda690e4SXin Li #include <math.h>
44*bda690e4SXin Li 
45*bda690e4SXin Li #include "codec_internal.h"
46*bda690e4SXin Li #include "ivorbisfile.h"
47*bda690e4SXin Li 
48*bda690e4SXin Li #include "os.h"
49*bda690e4SXin Li #include "misc.h"
50*bda690e4SXin Li 
51*bda690e4SXin Li int gerrno;
52*bda690e4SXin Li 
53*bda690e4SXin Li #define  NOTOPEN   0
54*bda690e4SXin Li #define  PARTOPEN  1
55*bda690e4SXin Li #define  OPENED    2
56*bda690e4SXin Li #define  STREAMSET 3 /* serialno and link set, but not to current link */
57*bda690e4SXin Li #define  LINKSET   4 /* serialno and link set to current link */
58*bda690e4SXin Li #define  INITSET   5
59*bda690e4SXin Li 
60*bda690e4SXin Li /* A 'chained bitstream' is a Vorbis bitstream that contains more than
61*bda690e4SXin Li    one logical bitstream arranged end to end (the only form of Ogg
62*bda690e4SXin Li    multiplexing allowed in a Vorbis bitstream; grouping [parallel
63*bda690e4SXin Li    multiplexing] is not allowed in Vorbis) */
64*bda690e4SXin Li 
65*bda690e4SXin Li /* A Vorbis file can be played beginning to end (streamed) without
66*bda690e4SXin Li    worrying ahead of time about chaining (see decoder_example.c).  If
67*bda690e4SXin Li    we have the whole file, however, and want random access
68*bda690e4SXin Li    (seeking/scrubbing) or desire to know the total length/time of a
69*bda690e4SXin Li    file, we need to account for the possibility of chaining. */
70*bda690e4SXin Li 
71*bda690e4SXin Li /* We can handle things a number of ways; we can determine the entire
72*bda690e4SXin Li    bitstream structure right off the bat, or find pieces on demand.
73*bda690e4SXin Li    This example determines and caches structure for the entire
74*bda690e4SXin Li    bitstream, but builds a virtual decoder on the fly when moving
75*bda690e4SXin Li    between links in the chain. */
76*bda690e4SXin Li 
77*bda690e4SXin Li /* There are also different ways to implement seeking.  Enough
78*bda690e4SXin Li    information exists in an Ogg bitstream to seek to
79*bda690e4SXin Li    sample-granularity positions in the output.  Or, one can seek by
80*bda690e4SXin Li    picking some portion of the stream roughly in the desired area if
81*bda690e4SXin Li    we only want coarse navigation through the stream. */
82*bda690e4SXin Li 
83*bda690e4SXin Li /*************************************************************************
84*bda690e4SXin Li  * Many, many internal helpers.  The intention is not to be confusing;
85*bda690e4SXin Li  * rampant duplication and monolithic function implementation would be
86*bda690e4SXin Li  * harder to understand anyway.  The high level functions are last.  Begin
87*bda690e4SXin Li  * grokking near the end of the file */
88*bda690e4SXin Li 
89*bda690e4SXin Li 
90*bda690e4SXin Li /* read a little more data from the file/pipe into the ogg_sync framer */
_get_data(OggVorbis_File * vf)91*bda690e4SXin Li static long _get_data(OggVorbis_File *vf){
92*bda690e4SXin Li   gerrno=0;
93*bda690e4SXin Li   if(vf->datasource){
94*bda690e4SXin Li     unsigned char *buffer=ogg_sync_bufferin(vf->oy,CHUNKSIZE);
95*bda690e4SXin Li     long bytes=(vf->callbacks.read_func)(buffer,1,CHUNKSIZE,vf->datasource);
96*bda690e4SXin Li     if(bytes>0)ogg_sync_wrote(vf->oy,bytes);
97*bda690e4SXin Li     if(bytes==0 && gerrno)return -1;
98*bda690e4SXin Li     return bytes;
99*bda690e4SXin Li   }else
100*bda690e4SXin Li     return 0;
101*bda690e4SXin Li }
102*bda690e4SXin Li 
103*bda690e4SXin Li /* save a tiny smidge of verbosity to make the code more readable */
_seek_helper(OggVorbis_File * vf,ogg_int64_t offset)104*bda690e4SXin Li static void _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){
105*bda690e4SXin Li   if(vf->datasource){
106*bda690e4SXin Li     (vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET);
107*bda690e4SXin Li     vf->offset=offset;
108*bda690e4SXin Li     ogg_sync_reset(vf->oy);
109*bda690e4SXin Li   }else{
110*bda690e4SXin Li     /* shouldn't happen unless someone writes a broken callback */
111*bda690e4SXin Li     return;
112*bda690e4SXin Li   }
113*bda690e4SXin Li }
114*bda690e4SXin Li 
115*bda690e4SXin Li /* The read/seek functions track absolute position within the stream */
116*bda690e4SXin Li 
117*bda690e4SXin Li /* from the head of the stream, get the next page.  boundary specifies
118*bda690e4SXin Li    if the function is allowed to fetch more data from the stream (and
119*bda690e4SXin Li    how much) or only use internally buffered data.
120*bda690e4SXin Li 
121*bda690e4SXin Li    boundary: -1) unbounded search
122*bda690e4SXin Li               0) read no additional data; use cached only
123*bda690e4SXin Li 	      n) search for a new page beginning for n bytes
124*bda690e4SXin Li 
125*bda690e4SXin Li    return:   <0) did not find a page (OV_FALSE, OV_EOF, OV_EREAD)
126*bda690e4SXin Li               n) found a page at absolute offset n
127*bda690e4SXin Li 
128*bda690e4SXin Li               produces a refcounted page */
129*bda690e4SXin Li 
_get_next_page(OggVorbis_File * vf,ogg_page * og,ogg_int64_t boundary)130*bda690e4SXin Li static ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og,
131*bda690e4SXin Li 				  ogg_int64_t boundary){
132*bda690e4SXin Li   if(boundary>0)boundary+=vf->offset;
133*bda690e4SXin Li   while(1){
134*bda690e4SXin Li     long more;
135*bda690e4SXin Li 
136*bda690e4SXin Li     if(boundary>0 && vf->offset>=boundary)return OV_FALSE;
137*bda690e4SXin Li     more=ogg_sync_pageseek(vf->oy,og);
138*bda690e4SXin Li 
139*bda690e4SXin Li     if(more<0){
140*bda690e4SXin Li       /* skipped n bytes */
141*bda690e4SXin Li       vf->offset-=more;
142*bda690e4SXin Li     }else{
143*bda690e4SXin Li       if(more==0){
144*bda690e4SXin Li 	/* send more paramedics */
145*bda690e4SXin Li 	if(!boundary)return OV_FALSE;
146*bda690e4SXin Li 	{
147*bda690e4SXin Li 	  long ret=_get_data(vf);
148*bda690e4SXin Li 	  if(ret==0)return OV_EOF;
149*bda690e4SXin Li 	  if(ret<0)return OV_EREAD;
150*bda690e4SXin Li 	}
151*bda690e4SXin Li       }else{
152*bda690e4SXin Li 	/* got a page.  Return the offset at the page beginning,
153*bda690e4SXin Li            advance the internal offset past the page end */
154*bda690e4SXin Li 	ogg_int64_t ret=vf->offset;
155*bda690e4SXin Li 	vf->offset+=more;
156*bda690e4SXin Li 	return ret;
157*bda690e4SXin Li 
158*bda690e4SXin Li       }
159*bda690e4SXin Li     }
160*bda690e4SXin Li   }
161*bda690e4SXin Li }
162*bda690e4SXin Li 
163*bda690e4SXin Li /* find the latest page beginning before the current stream cursor
164*bda690e4SXin Li    position. Much dirtier than the above as Ogg doesn't have any
165*bda690e4SXin Li    backward search linkage.  no 'readp' as it will certainly have to
166*bda690e4SXin Li    read. */
167*bda690e4SXin Li /* returns offset or OV_EREAD, OV_FAULT and produces a refcounted page */
168*bda690e4SXin Li 
_get_prev_page(OggVorbis_File * vf,ogg_page * og)169*bda690e4SXin Li static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_page *og){
170*bda690e4SXin Li   ogg_int64_t begin=vf->offset;
171*bda690e4SXin Li   ogg_int64_t end=begin;
172*bda690e4SXin Li   ogg_int64_t ret;
173*bda690e4SXin Li   ogg_int64_t offset=-1;
174*bda690e4SXin Li 
175*bda690e4SXin Li   while(offset==-1){
176*bda690e4SXin Li     begin-=CHUNKSIZE;
177*bda690e4SXin Li     if(begin<0)
178*bda690e4SXin Li       begin=0;
179*bda690e4SXin Li     _seek_helper(vf,begin);
180*bda690e4SXin Li     while(vf->offset<end){
181*bda690e4SXin Li       ret=_get_next_page(vf,og,end-vf->offset);
182*bda690e4SXin Li       if(ret==OV_EREAD)return OV_EREAD;
183*bda690e4SXin Li       if(ret<0){
184*bda690e4SXin Li 	break;
185*bda690e4SXin Li       }else{
186*bda690e4SXin Li 	offset=ret;
187*bda690e4SXin Li       }
188*bda690e4SXin Li     }
189*bda690e4SXin Li   }
190*bda690e4SXin Li 
191*bda690e4SXin Li   /* we have the offset.  Actually snork and hold the page now */
192*bda690e4SXin Li   _seek_helper(vf,offset);
193*bda690e4SXin Li   ret=_get_next_page(vf,og,CHUNKSIZE);
194*bda690e4SXin Li   if(ret<0)
195*bda690e4SXin Li     /* this shouldn't be possible */
196*bda690e4SXin Li     return OV_EFAULT;
197*bda690e4SXin Li 
198*bda690e4SXin Li   return offset;
199*bda690e4SXin Li }
200*bda690e4SXin Li 
201*bda690e4SXin Li /* finds each bitstream link one at a time using a bisection search
202*bda690e4SXin Li    (has to begin by knowing the offset of the lb's initial page).
203*bda690e4SXin Li    Recurses for each link so it can alloc the link storage after
204*bda690e4SXin Li    finding them all, then unroll and fill the cache at the same time */
_bisect_forward_serialno(OggVorbis_File * vf,ogg_int64_t begin,ogg_int64_t searched,ogg_int64_t end,ogg_uint32_t currentno,long m)205*bda690e4SXin Li static int _bisect_forward_serialno(OggVorbis_File *vf,
206*bda690e4SXin Li 				    ogg_int64_t begin,
207*bda690e4SXin Li 				    ogg_int64_t searched,
208*bda690e4SXin Li 				    ogg_int64_t end,
209*bda690e4SXin Li 				    ogg_uint32_t currentno,
210*bda690e4SXin Li 				    long m){
211*bda690e4SXin Li   ogg_int64_t endsearched=end;
212*bda690e4SXin Li   ogg_int64_t next=end;
213*bda690e4SXin Li   ogg_page og={0,0,0,0};
214*bda690e4SXin Li   ogg_int64_t ret;
215*bda690e4SXin Li 
216*bda690e4SXin Li   /* the below guards against garbage seperating the last and
217*bda690e4SXin Li      first pages of two links. */
218*bda690e4SXin Li   while(searched<endsearched){
219*bda690e4SXin Li     ogg_int64_t bisect;
220*bda690e4SXin Li 
221*bda690e4SXin Li     if(endsearched-searched<CHUNKSIZE){
222*bda690e4SXin Li       bisect=searched;
223*bda690e4SXin Li     }else{
224*bda690e4SXin Li       bisect=(searched+endsearched)/2;
225*bda690e4SXin Li     }
226*bda690e4SXin Li 
227*bda690e4SXin Li     _seek_helper(vf,bisect);
228*bda690e4SXin Li     ret=_get_next_page(vf,&og,-1);
229*bda690e4SXin Li     if(ret==OV_EREAD)return OV_EREAD;
230*bda690e4SXin Li     if(ret<0 || ogg_page_serialno(&og)!=currentno){
231*bda690e4SXin Li       endsearched=bisect;
232*bda690e4SXin Li       if(ret>=0)next=ret;
233*bda690e4SXin Li     }else{
234*bda690e4SXin Li       searched=ret+og.header_len+og.body_len;
235*bda690e4SXin Li     }
236*bda690e4SXin Li     ogg_page_release(&og);
237*bda690e4SXin Li   }
238*bda690e4SXin Li 
239*bda690e4SXin Li   _seek_helper(vf,next);
240*bda690e4SXin Li   ret=_get_next_page(vf,&og,-1);
241*bda690e4SXin Li   if(ret==OV_EREAD)return OV_EREAD;
242*bda690e4SXin Li 
243*bda690e4SXin Li   if(searched>=end || ret<0){
244*bda690e4SXin Li     ogg_page_release(&og);
245*bda690e4SXin Li     vf->links=m+1;
246*bda690e4SXin Li     vf->offsets=_ogg_calloc((vf->links+1), sizeof(*vf->offsets));
247*bda690e4SXin Li     vf->serialnos=_ogg_calloc(vf->links, sizeof(*vf->serialnos));
248*bda690e4SXin Li     vf->offsets[m+1]=searched;
249*bda690e4SXin Li   }else{
250*bda690e4SXin Li     ret=_bisect_forward_serialno(vf,next,vf->offset,
251*bda690e4SXin Li 				 end,ogg_page_serialno(&og),m+1);
252*bda690e4SXin Li     ogg_page_release(&og);
253*bda690e4SXin Li     if(ret==OV_EREAD)return OV_EREAD;
254*bda690e4SXin Li   }
255*bda690e4SXin Li 
256*bda690e4SXin Li   vf->offsets[m]=begin;
257*bda690e4SXin Li   vf->serialnos[m]=currentno;
258*bda690e4SXin Li   return 0;
259*bda690e4SXin Li }
260*bda690e4SXin Li 
_decode_clear(OggVorbis_File * vf)261*bda690e4SXin Li static int _decode_clear(OggVorbis_File *vf){
262*bda690e4SXin Li   if(vf->ready_state==INITSET){
263*bda690e4SXin Li     vorbis_dsp_destroy(vf->vd);
264*bda690e4SXin Li     vf->vd=0;
265*bda690e4SXin Li     vf->ready_state=STREAMSET;
266*bda690e4SXin Li   }
267*bda690e4SXin Li 
268*bda690e4SXin Li   if(vf->ready_state>=STREAMSET){
269*bda690e4SXin Li     vorbis_info_clear(&vf->vi);
270*bda690e4SXin Li     vorbis_comment_clear(&vf->vc);
271*bda690e4SXin Li     vf->ready_state=OPENED;
272*bda690e4SXin Li   }
273*bda690e4SXin Li   return 0;
274*bda690e4SXin Li }
275*bda690e4SXin Li 
276*bda690e4SXin Li /* uses the local ogg_stream storage in vf; this is important for
277*bda690e4SXin Li    non-streaming input sources */
278*bda690e4SXin Li /* consumes the page that's passed in (if any) */
279*bda690e4SXin Li /* state is LINKSET upon successful return */
280*bda690e4SXin Li 
_fetch_headers(OggVorbis_File * vf,vorbis_info * vi,vorbis_comment * vc,ogg_uint32_t * serialno,ogg_page * og_ptr)281*bda690e4SXin Li static int _fetch_headers(OggVorbis_File *vf,
282*bda690e4SXin Li 			  vorbis_info *vi,
283*bda690e4SXin Li 			  vorbis_comment *vc,
284*bda690e4SXin Li 			  ogg_uint32_t *serialno,
285*bda690e4SXin Li 			  ogg_page *og_ptr){
286*bda690e4SXin Li   ogg_page og={0,0,0,0};
287*bda690e4SXin Li   ogg_packet op={0,0,0,0,0,0};
288*bda690e4SXin Li   int i,ret;
289*bda690e4SXin Li 
290*bda690e4SXin Li   if(vf->ready_state>OPENED)_decode_clear(vf);
291*bda690e4SXin Li 
292*bda690e4SXin Li   if(!og_ptr){
293*bda690e4SXin Li     ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE);
294*bda690e4SXin Li     if(llret==OV_EREAD)return OV_EREAD;
295*bda690e4SXin Li     if(llret<0)return OV_ENOTVORBIS;
296*bda690e4SXin Li     og_ptr=&og;
297*bda690e4SXin Li   }
298*bda690e4SXin Li 
299*bda690e4SXin Li   ogg_stream_reset_serialno(vf->os,ogg_page_serialno(og_ptr));
300*bda690e4SXin Li   if(serialno)*serialno=vf->os->serialno;
301*bda690e4SXin Li 
302*bda690e4SXin Li   /* extract the initial header from the first page and verify that the
303*bda690e4SXin Li      Ogg bitstream is in fact Vorbis data */
304*bda690e4SXin Li 
305*bda690e4SXin Li   vorbis_info_init(vi);
306*bda690e4SXin Li   vorbis_comment_init(vc);
307*bda690e4SXin Li 
308*bda690e4SXin Li   i=0;
309*bda690e4SXin Li   while(i<3){
310*bda690e4SXin Li     ogg_stream_pagein(vf->os,og_ptr);
311*bda690e4SXin Li     while(i<3){
312*bda690e4SXin Li       int result=ogg_stream_packetout(vf->os,&op);
313*bda690e4SXin Li       if(result==0)break;
314*bda690e4SXin Li       if(result==-1){
315*bda690e4SXin Li 	ret=OV_EBADHEADER;
316*bda690e4SXin Li 	goto bail_header;
317*bda690e4SXin Li       }
318*bda690e4SXin Li       if((ret=vorbis_dsp_headerin(vi,vc,&op))){
319*bda690e4SXin Li 	goto bail_header;
320*bda690e4SXin Li       }
321*bda690e4SXin Li       i++;
322*bda690e4SXin Li     }
323*bda690e4SXin Li     if(i<3)
324*bda690e4SXin Li       if(_get_next_page(vf,og_ptr,CHUNKSIZE)<0){
325*bda690e4SXin Li 	ret=OV_EBADHEADER;
326*bda690e4SXin Li 	goto bail_header;
327*bda690e4SXin Li       }
328*bda690e4SXin Li   }
329*bda690e4SXin Li 
330*bda690e4SXin Li   ogg_packet_release(&op);
331*bda690e4SXin Li   ogg_page_release(&og);
332*bda690e4SXin Li   vf->ready_state=LINKSET;
333*bda690e4SXin Li   return 0;
334*bda690e4SXin Li 
335*bda690e4SXin Li  bail_header:
336*bda690e4SXin Li   ogg_packet_release(&op);
337*bda690e4SXin Li   ogg_page_release(&og);
338*bda690e4SXin Li   vorbis_info_clear(vi);
339*bda690e4SXin Li   vorbis_comment_clear(vc);
340*bda690e4SXin Li   vf->ready_state=OPENED;
341*bda690e4SXin Li 
342*bda690e4SXin Li   return ret;
343*bda690e4SXin Li }
344*bda690e4SXin Li 
345*bda690e4SXin Li /* we no longer preload all vorbis_info (and the associated
346*bda690e4SXin Li    codec_setup) structs.  Call this to seek and fetch the info from
347*bda690e4SXin Li    the bitstream, if needed */
_set_link_number(OggVorbis_File * vf,int link)348*bda690e4SXin Li static int _set_link_number(OggVorbis_File *vf,int link){
349*bda690e4SXin Li   if(link != vf->current_link) _decode_clear(vf);
350*bda690e4SXin Li   if(vf->ready_state<STREAMSET){
351*bda690e4SXin Li     _seek_helper(vf,vf->offsets[link]);
352*bda690e4SXin Li     ogg_stream_reset_serialno(vf->os,vf->serialnos[link]);
353*bda690e4SXin Li     vf->current_serialno=vf->serialnos[link];
354*bda690e4SXin Li     vf->current_link=link;
355*bda690e4SXin Li     return _fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,NULL);
356*bda690e4SXin Li   }
357*bda690e4SXin Li   return 0;
358*bda690e4SXin Li }
359*bda690e4SXin Li 
_set_link_number_preserve_pos(OggVorbis_File * vf,int link)360*bda690e4SXin Li static int _set_link_number_preserve_pos(OggVorbis_File *vf,int link){
361*bda690e4SXin Li   ogg_int64_t pos=vf->offset;
362*bda690e4SXin Li   int ret=_set_link_number(vf,link);
363*bda690e4SXin Li   if(ret)return ret;
364*bda690e4SXin Li   _seek_helper(vf,pos);
365*bda690e4SXin Li   if(pos<vf->offsets[link] || pos>=vf->offsets[link+1])
366*bda690e4SXin Li     vf->ready_state=STREAMSET;
367*bda690e4SXin Li   return 0;
368*bda690e4SXin Li }
369*bda690e4SXin Li 
370*bda690e4SXin Li /* last step of the OggVorbis_File initialization; get all the offset
371*bda690e4SXin Li    positions.  Only called by the seekable initialization (local
372*bda690e4SXin Li    stream storage is hacked slightly; pay attention to how that's
373*bda690e4SXin Li    done) */
374*bda690e4SXin Li 
375*bda690e4SXin Li /* this is void and does not propogate errors up because we want to be
376*bda690e4SXin Li    able to open and use damaged bitstreams as well as we can.  Just
377*bda690e4SXin Li    watch out for missing information for links in the OggVorbis_File
378*bda690e4SXin Li    struct */
_prefetch_all_offsets(OggVorbis_File * vf,ogg_int64_t dataoffset)379*bda690e4SXin Li static void _prefetch_all_offsets(OggVorbis_File *vf, ogg_int64_t dataoffset){
380*bda690e4SXin Li   ogg_page og={0,0,0,0};
381*bda690e4SXin Li   int i;
382*bda690e4SXin Li   ogg_int64_t ret;
383*bda690e4SXin Li 
384*bda690e4SXin Li   vf->dataoffsets=_ogg_malloc(vf->links*sizeof(*vf->dataoffsets));
385*bda690e4SXin Li   vf->pcmlengths=_ogg_malloc(vf->links*2*sizeof(*vf->pcmlengths));
386*bda690e4SXin Li 
387*bda690e4SXin Li   for(i=0;i<vf->links;i++){
388*bda690e4SXin Li     if(i==0){
389*bda690e4SXin Li       /* we already grabbed the initial header earlier.  Just set the offset */
390*bda690e4SXin Li       vf->dataoffsets[i]=dataoffset;
391*bda690e4SXin Li       _seek_helper(vf,dataoffset);
392*bda690e4SXin Li 
393*bda690e4SXin Li     }else{
394*bda690e4SXin Li 
395*bda690e4SXin Li       /* seek to the location of the initial header */
396*bda690e4SXin Li 
397*bda690e4SXin Li       _seek_helper(vf,vf->offsets[i]);
398*bda690e4SXin Li       if(_fetch_headers(vf,&vf->vi,&vf->vc,NULL,NULL)<0){
399*bda690e4SXin Li     	vf->dataoffsets[i]=-1;
400*bda690e4SXin Li       }else{
401*bda690e4SXin Li 	vf->dataoffsets[i]=vf->offset;
402*bda690e4SXin Li       }
403*bda690e4SXin Li     }
404*bda690e4SXin Li 
405*bda690e4SXin Li     /* fetch beginning PCM offset */
406*bda690e4SXin Li 
407*bda690e4SXin Li     if(vf->dataoffsets[i]!=-1){
408*bda690e4SXin Li       ogg_int64_t accumulated=0,pos;
409*bda690e4SXin Li       long        lastblock=-1;
410*bda690e4SXin Li       int         result;
411*bda690e4SXin Li 
412*bda690e4SXin Li       ogg_stream_reset_serialno(vf->os,vf->serialnos[i]);
413*bda690e4SXin Li 
414*bda690e4SXin Li       while(1){
415*bda690e4SXin Li 	ogg_packet op={0,0,0,0,0,0};
416*bda690e4SXin Li 
417*bda690e4SXin Li 	ret=_get_next_page(vf,&og,-1);
418*bda690e4SXin Li 	if(ret<0)
419*bda690e4SXin Li 	  /* this should not be possible unless the file is
420*bda690e4SXin Li              truncated/mangled */
421*bda690e4SXin Li 	  break;
422*bda690e4SXin Li 
423*bda690e4SXin Li 	if(ogg_page_serialno(&og)!=vf->serialnos[i])
424*bda690e4SXin Li 	  break;
425*bda690e4SXin Li 
426*bda690e4SXin Li 	pos=ogg_page_granulepos(&og);
427*bda690e4SXin Li 
428*bda690e4SXin Li 	/* count blocksizes of all frames in the page */
429*bda690e4SXin Li 	ogg_stream_pagein(vf->os,&og);
430*bda690e4SXin Li 	while((result=ogg_stream_packetout(vf->os,&op))){
431*bda690e4SXin Li 	  if(result>0){ /* ignore holes */
432*bda690e4SXin Li 	    long thisblock=vorbis_packet_blocksize(&vf->vi,&op);
433*bda690e4SXin Li 	    if(lastblock!=-1)
434*bda690e4SXin Li 	      accumulated+=(lastblock+thisblock)>>2;
435*bda690e4SXin Li 	    lastblock=thisblock;
436*bda690e4SXin Li 	  }
437*bda690e4SXin Li 	}
438*bda690e4SXin Li 	ogg_packet_release(&op);
439*bda690e4SXin Li 
440*bda690e4SXin Li 	if(pos!=-1){
441*bda690e4SXin Li 	  /* pcm offset of last packet on the first audio page */
442*bda690e4SXin Li 	  accumulated= pos-accumulated;
443*bda690e4SXin Li 	  break;
444*bda690e4SXin Li 	}
445*bda690e4SXin Li       }
446*bda690e4SXin Li 
447*bda690e4SXin Li       /* less than zero?  This is a stream with samples trimmed off
448*bda690e4SXin Li          the beginning, a normal occurrence; set the offset to zero */
449*bda690e4SXin Li       if(accumulated<0)accumulated=0;
450*bda690e4SXin Li 
451*bda690e4SXin Li       vf->pcmlengths[i*2]=accumulated;
452*bda690e4SXin Li     }
453*bda690e4SXin Li 
454*bda690e4SXin Li     /* get the PCM length of this link. To do this,
455*bda690e4SXin Li        get the last page of the stream */
456*bda690e4SXin Li     {
457*bda690e4SXin Li       ogg_int64_t end=vf->offsets[i+1];
458*bda690e4SXin Li       _seek_helper(vf,end);
459*bda690e4SXin Li 
460*bda690e4SXin Li       while(1){
461*bda690e4SXin Li 	ret=_get_prev_page(vf,&og);
462*bda690e4SXin Li 	if(ret<0){
463*bda690e4SXin Li 	  /* this should not be possible */
464*bda690e4SXin Li 	  vorbis_info_clear(&vf->vi);
465*bda690e4SXin Li 	  vorbis_comment_clear(&vf->vc);
466*bda690e4SXin Li 	  break;
467*bda690e4SXin Li 	}
468*bda690e4SXin Li 	if(ogg_page_granulepos(&og)!=-1){
469*bda690e4SXin Li 	  vf->pcmlengths[i*2+1]=ogg_page_granulepos(&og)-vf->pcmlengths[i*2];
470*bda690e4SXin Li 	  break;
471*bda690e4SXin Li 	}
472*bda690e4SXin Li 	vf->offset=ret;
473*bda690e4SXin Li       }
474*bda690e4SXin Li     }
475*bda690e4SXin Li   }
476*bda690e4SXin Li   ogg_page_release(&og);
477*bda690e4SXin Li }
478*bda690e4SXin Li 
_make_decode_ready(OggVorbis_File * vf)479*bda690e4SXin Li static int _make_decode_ready(OggVorbis_File *vf){
480*bda690e4SXin Li   int i;
481*bda690e4SXin Li   switch(vf->ready_state){
482*bda690e4SXin Li   case OPENED:
483*bda690e4SXin Li   case STREAMSET:
484*bda690e4SXin Li     for(i=0;i<vf->links;i++)
485*bda690e4SXin Li       if(vf->offsets[i+1]>=vf->offset)break;
486*bda690e4SXin Li     if(i==vf->links)return -1;
487*bda690e4SXin Li     i=_set_link_number_preserve_pos(vf,i);
488*bda690e4SXin Li     if(i)return i;
489*bda690e4SXin Li     /* fall through */
490*bda690e4SXin Li   case LINKSET:
491*bda690e4SXin Li     vf->vd=vorbis_dsp_create(&vf->vi);
492*bda690e4SXin Li     vf->ready_state=INITSET;
493*bda690e4SXin Li     vf->bittrack=0;
494*bda690e4SXin Li     vf->samptrack=0;
495*bda690e4SXin Li   case INITSET:
496*bda690e4SXin Li     return 0;
497*bda690e4SXin Li   default:
498*bda690e4SXin Li     return -1;
499*bda690e4SXin Li   }
500*bda690e4SXin Li 
501*bda690e4SXin Li }
502*bda690e4SXin Li 
_open_seekable2(OggVorbis_File * vf)503*bda690e4SXin Li static int _open_seekable2(OggVorbis_File *vf){
504*bda690e4SXin Li   ogg_uint32_t serialno=vf->current_serialno;
505*bda690e4SXin Li   ogg_uint32_t tempserialno;
506*bda690e4SXin Li   ogg_int64_t dataoffset=vf->offset, end;
507*bda690e4SXin Li   ogg_page og={0,0,0,0};
508*bda690e4SXin Li 
509*bda690e4SXin Li   /* we're partially open and have a first link header state in
510*bda690e4SXin Li      storage in vf */
511*bda690e4SXin Li   /* we can seek, so set out learning all about this file */
512*bda690e4SXin Li   (vf->callbacks.seek_func)(vf->datasource,0,SEEK_END);
513*bda690e4SXin Li   vf->offset=vf->end=(vf->callbacks.tell_func)(vf->datasource);
514*bda690e4SXin Li 
515*bda690e4SXin Li   /* We get the offset for the last page of the physical bitstream.
516*bda690e4SXin Li      Most OggVorbis files will contain a single logical bitstream */
517*bda690e4SXin Li   end=_get_prev_page(vf,&og);
518*bda690e4SXin Li   if(end<0)return (int)end;
519*bda690e4SXin Li 
520*bda690e4SXin Li   /* more than one logical bitstream? */
521*bda690e4SXin Li   tempserialno=ogg_page_serialno(&og);
522*bda690e4SXin Li   ogg_page_release(&og);
523*bda690e4SXin Li 
524*bda690e4SXin Li   if(tempserialno!=serialno){
525*bda690e4SXin Li 
526*bda690e4SXin Li     /* Chained bitstream. Bisect-search each logical bitstream
527*bda690e4SXin Li        section.  Do so based on serial number only */
528*bda690e4SXin Li     if(_bisect_forward_serialno(vf,0,0,end+1,serialno,0)<0)return OV_EREAD;
529*bda690e4SXin Li 
530*bda690e4SXin Li   }else{
531*bda690e4SXin Li 
532*bda690e4SXin Li     /* Only one logical bitstream */
533*bda690e4SXin Li     if(_bisect_forward_serialno(vf,0,end,end+1,serialno,0))return OV_EREAD;
534*bda690e4SXin Li 
535*bda690e4SXin Li   }
536*bda690e4SXin Li 
537*bda690e4SXin Li   /* the initial header memory is referenced by vf after; don't free it */
538*bda690e4SXin Li   _prefetch_all_offsets(vf,dataoffset);
539*bda690e4SXin Li   return ov_raw_seek(vf,0);
540*bda690e4SXin Li }
541*bda690e4SXin Li 
542*bda690e4SXin Li /* fetch and process a packet.  Handles the case where we're at a
543*bda690e4SXin Li    bitstream boundary and dumps the decoding machine.  If the decoding
544*bda690e4SXin Li    machine is unloaded, it loads it.  It also keeps pcm_offset up to
545*bda690e4SXin Li    date (seek and read both use this.  seek uses a special hack with
546*bda690e4SXin Li    readp).
547*bda690e4SXin Li 
548*bda690e4SXin Li    return: <0) error, OV_HOLE (lost packet) or OV_EOF
549*bda690e4SXin Li             0) need more data (only if readp==0)
550*bda690e4SXin Li 	    1) got a packet
551*bda690e4SXin Li */
552*bda690e4SXin Li 
_fetch_and_process_packet(OggVorbis_File * vf,int readp,int spanp)553*bda690e4SXin Li static int _fetch_and_process_packet(OggVorbis_File *vf,
554*bda690e4SXin Li 				     int readp,
555*bda690e4SXin Li 				     int spanp){
556*bda690e4SXin Li   ogg_page og={0,0,0,0};
557*bda690e4SXin Li   ogg_packet op={0,0,0,0,0,0};
558*bda690e4SXin Li   int ret=0;
559*bda690e4SXin Li 
560*bda690e4SXin Li   /* handle one packet.  Try to fetch it from current stream state */
561*bda690e4SXin Li   /* extract packets from page */
562*bda690e4SXin Li   while(1){
563*bda690e4SXin Li 
564*bda690e4SXin Li     /* process a packet if we can.  If the machine isn't loaded,
565*bda690e4SXin Li        neither is a page */
566*bda690e4SXin Li     if(vf->ready_state==INITSET){
567*bda690e4SXin Li       while(1) {
568*bda690e4SXin Li 	int result=ogg_stream_packetout(vf->os,&op);
569*bda690e4SXin Li 	ogg_int64_t granulepos;
570*bda690e4SXin Li 
571*bda690e4SXin Li 	if(result<0){
572*bda690e4SXin Li 	  ret=OV_HOLE; /* hole in the data. */
573*bda690e4SXin Li 	  goto cleanup;
574*bda690e4SXin Li 	}
575*bda690e4SXin Li 	if(result>0){
576*bda690e4SXin Li 	  /* got a packet.  process it */
577*bda690e4SXin Li 	  granulepos=op.granulepos;
578*bda690e4SXin Li 	  if(!vorbis_dsp_synthesis(vf->vd,&op,1)){ /* lazy check for lazy
579*bda690e4SXin Li 						      header handling.  The
580*bda690e4SXin Li 						      header packets aren't
581*bda690e4SXin Li 						      audio, so if/when we
582*bda690e4SXin Li 						      submit them,
583*bda690e4SXin Li 						      vorbis_synthesis will
584*bda690e4SXin Li 						      reject them */
585*bda690e4SXin Li 
586*bda690e4SXin Li 	    vf->samptrack+=vorbis_dsp_pcmout(vf->vd,NULL,0);
587*bda690e4SXin Li 	    vf->bittrack+=op.bytes*8;
588*bda690e4SXin Li 
589*bda690e4SXin Li 	    /* update the pcm offset. */
590*bda690e4SXin Li 	    if(granulepos!=-1 && !op.e_o_s){
591*bda690e4SXin Li 	      int link=(vf->seekable?vf->current_link:0);
592*bda690e4SXin Li 	      int i,samples;
593*bda690e4SXin Li 
594*bda690e4SXin Li 	      /* this packet has a pcm_offset on it (the last packet
595*bda690e4SXin Li 	         completed on a page carries the offset) After processing
596*bda690e4SXin Li 	         (above), we know the pcm position of the *last* sample
597*bda690e4SXin Li 	         ready to be returned. Find the offset of the *first*
598*bda690e4SXin Li 
599*bda690e4SXin Li 	         As an aside, this trick is inaccurate if we begin
600*bda690e4SXin Li 	         reading anew right at the last page; the end-of-stream
601*bda690e4SXin Li 	         granulepos declares the last frame in the stream, and the
602*bda690e4SXin Li 	         last packet of the last page may be a partial frame.
603*bda690e4SXin Li 	         So, we need a previous granulepos from an in-sequence page
604*bda690e4SXin Li 	         to have a reference point.  Thus the !op.e_o_s clause
605*bda690e4SXin Li 	         above */
606*bda690e4SXin Li 
607*bda690e4SXin Li 	      if(vf->seekable && link>0)
608*bda690e4SXin Li 		granulepos-=vf->pcmlengths[link*2];
609*bda690e4SXin Li 	      if(granulepos<0)granulepos=0; /* actually, this
610*bda690e4SXin Li 					       shouldn't be possible
611*bda690e4SXin Li 					       here unless the stream
612*bda690e4SXin Li 					       is very broken */
613*bda690e4SXin Li 
614*bda690e4SXin Li 	      samples=vorbis_dsp_pcmout(vf->vd,NULL,0);
615*bda690e4SXin Li 
616*bda690e4SXin Li 	      granulepos-=samples;
617*bda690e4SXin Li 	      for(i=0;i<link;i++)
618*bda690e4SXin Li 	        granulepos+=vf->pcmlengths[i*2+1];
619*bda690e4SXin Li 	      vf->pcm_offset=granulepos;
620*bda690e4SXin Li 	    }
621*bda690e4SXin Li 	    ret=1;
622*bda690e4SXin Li 	    goto cleanup;
623*bda690e4SXin Li 	  }
624*bda690e4SXin Li 	}
625*bda690e4SXin Li 	else
626*bda690e4SXin Li 	  break;
627*bda690e4SXin Li       }
628*bda690e4SXin Li     }
629*bda690e4SXin Li 
630*bda690e4SXin Li     if(vf->ready_state>=OPENED){
631*bda690e4SXin Li       int ret;
632*bda690e4SXin Li       if(!readp){
633*bda690e4SXin Li 	ret=0;
634*bda690e4SXin Li 	goto cleanup;
635*bda690e4SXin Li       }
636*bda690e4SXin Li       ret=(int)_get_next_page(vf,&og,-1);
637*bda690e4SXin Li       if(ret<0){
638*bda690e4SXin Li 	ret=OV_EOF; /* eof. leave unitialized */
639*bda690e4SXin Li 	goto cleanup;
640*bda690e4SXin Li       }
641*bda690e4SXin Li 
642*bda690e4SXin Li 	/* bitrate tracking; add the header's bytes here, the body bytes
643*bda690e4SXin Li 	   are done by packet above */
644*bda690e4SXin Li       vf->bittrack+=og.header_len*8;
645*bda690e4SXin Li 
646*bda690e4SXin Li       /* has our decoding just traversed a bitstream boundary? */
647*bda690e4SXin Li       if(vf->ready_state==INITSET){
648*bda690e4SXin Li 	if(vf->current_serialno!=ogg_page_serialno(&og)){
649*bda690e4SXin Li 	  if(!spanp){
650*bda690e4SXin Li 	    ret=OV_EOF;
651*bda690e4SXin Li 	    goto cleanup;
652*bda690e4SXin Li 	  }
653*bda690e4SXin Li 
654*bda690e4SXin Li 	  _decode_clear(vf);
655*bda690e4SXin Li 	}
656*bda690e4SXin Li       }
657*bda690e4SXin Li     }
658*bda690e4SXin Li 
659*bda690e4SXin Li     /* Do we need to load a new machine before submitting the page? */
660*bda690e4SXin Li     /* This is different in the seekable and non-seekable cases.
661*bda690e4SXin Li 
662*bda690e4SXin Li        In the seekable case, we already have all the header
663*bda690e4SXin Li        information loaded and cached; we just initialize the machine
664*bda690e4SXin Li        with it and continue on our merry way.
665*bda690e4SXin Li 
666*bda690e4SXin Li        In the non-seekable (streaming) case, we'll only be at a
667*bda690e4SXin Li        boundary if we just left the previous logical bitstream and
668*bda690e4SXin Li        we're now nominally at the header of the next bitstream
669*bda690e4SXin Li     */
670*bda690e4SXin Li 
671*bda690e4SXin Li     if(vf->ready_state!=INITSET){
672*bda690e4SXin Li       int link,ret;
673*bda690e4SXin Li 
674*bda690e4SXin Li       if(vf->ready_state<STREAMSET){
675*bda690e4SXin Li 	if(vf->seekable){
676*bda690e4SXin Li 	  vf->current_serialno=ogg_page_serialno(&og);
677*bda690e4SXin Li 
678*bda690e4SXin Li 	  /* match the serialno to bitstream section.  We use this rather than
679*bda690e4SXin Li 	     offset positions to avoid problems near logical bitstream
680*bda690e4SXin Li 	     boundaries */
681*bda690e4SXin Li 	  for(link=0;link<vf->links;link++)
682*bda690e4SXin Li 	    if(vf->serialnos[link]==vf->current_serialno)break;
683*bda690e4SXin Li 	  if(link==vf->links){
684*bda690e4SXin Li 	    ret=OV_EBADLINK; /* sign of a bogus stream.  error out,
685*bda690e4SXin Li 				leave machine uninitialized */
686*bda690e4SXin Li 	    goto cleanup;
687*bda690e4SXin Li 	  }
688*bda690e4SXin Li 
689*bda690e4SXin Li 	  vf->current_link=link;
690*bda690e4SXin Li 	  ret=_fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,&og);
691*bda690e4SXin Li 	  if(ret) goto cleanup;
692*bda690e4SXin Li 
693*bda690e4SXin Li 	}else{
694*bda690e4SXin Li 	  /* we're streaming */
695*bda690e4SXin Li 	  /* fetch the three header packets, build the info struct */
696*bda690e4SXin Li 
697*bda690e4SXin Li 	  int ret=_fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,&og);
698*bda690e4SXin Li 	  if(ret) goto cleanup;
699*bda690e4SXin Li 	  vf->current_link++;
700*bda690e4SXin Li 	}
701*bda690e4SXin Li       }
702*bda690e4SXin Li 
703*bda690e4SXin Li       if(_make_decode_ready(vf)) return OV_EBADLINK;
704*bda690e4SXin Li     }
705*bda690e4SXin Li     ogg_stream_pagein(vf->os,&og);
706*bda690e4SXin Li   }
707*bda690e4SXin Li  cleanup:
708*bda690e4SXin Li   ogg_packet_release(&op);
709*bda690e4SXin Li   ogg_page_release(&og);
710*bda690e4SXin Li   return ret;
711*bda690e4SXin Li }
712*bda690e4SXin Li 
713*bda690e4SXin Li /* if, eg, 64 bit stdio is configured by default, this will build with
714*bda690e4SXin Li    fseek64 */
_fseek64_wrap(FILE * f,ogg_int64_t off,int whence)715*bda690e4SXin Li static int _fseek64_wrap(FILE *f,ogg_int64_t off,int whence){
716*bda690e4SXin Li   if(f==NULL)return -1;
717*bda690e4SXin Li   return fseek(f,(long)off,whence);
718*bda690e4SXin Li }
719*bda690e4SXin Li 
_ov_open1(void * f,OggVorbis_File * vf,char * initial,long ibytes,ov_callbacks callbacks)720*bda690e4SXin Li static int _ov_open1(void *f,OggVorbis_File *vf,char *initial,
721*bda690e4SXin Li 		     long ibytes, ov_callbacks callbacks){
722*bda690e4SXin Li   int offsettest=(f?callbacks.seek_func(f,0,SEEK_CUR):-1);
723*bda690e4SXin Li   int ret;
724*bda690e4SXin Li 
725*bda690e4SXin Li   memset(vf,0,sizeof(*vf));
726*bda690e4SXin Li 
727*bda690e4SXin Li   /* Tremor assumes in multiple places that right shift of a signed
728*bda690e4SXin Li      integer is an arithmetic shift */
729*bda690e4SXin Li   if( (-1>>1) != -1) return OV_EIMPL;
730*bda690e4SXin Li 
731*bda690e4SXin Li   vf->datasource=f;
732*bda690e4SXin Li   vf->callbacks = callbacks;
733*bda690e4SXin Li 
734*bda690e4SXin Li   /* init the framing state */
735*bda690e4SXin Li   vf->oy=ogg_sync_create();
736*bda690e4SXin Li 
737*bda690e4SXin Li   /* perhaps some data was previously read into a buffer for testing
738*bda690e4SXin Li      against other stream types.  Allow initialization from this
739*bda690e4SXin Li      previously read data (as we may be reading from a non-seekable
740*bda690e4SXin Li      stream) */
741*bda690e4SXin Li   if(initial){
742*bda690e4SXin Li     unsigned char *buffer=ogg_sync_bufferin(vf->oy,ibytes);
743*bda690e4SXin Li     memcpy(buffer,initial,ibytes);
744*bda690e4SXin Li     ogg_sync_wrote(vf->oy,ibytes);
745*bda690e4SXin Li   }
746*bda690e4SXin Li 
747*bda690e4SXin Li   /* can we seek? Stevens suggests the seek test was portable */
748*bda690e4SXin Li   if(offsettest!=-1)vf->seekable=1;
749*bda690e4SXin Li 
750*bda690e4SXin Li   /* No seeking yet; Set up a 'single' (current) logical bitstream
751*bda690e4SXin Li      entry for partial open */
752*bda690e4SXin Li   vf->links=1;
753*bda690e4SXin Li   vf->os=ogg_stream_create(-1); /* fill in the serialno later */
754*bda690e4SXin Li 
755*bda690e4SXin Li   /* Try to fetch the headers, maintaining all the storage */
756*bda690e4SXin Li   if((ret=_fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,NULL))<0){
757*bda690e4SXin Li     vf->datasource=NULL;
758*bda690e4SXin Li     ov_clear(vf);
759*bda690e4SXin Li   }else if(vf->ready_state < PARTOPEN)
760*bda690e4SXin Li     vf->ready_state=PARTOPEN;
761*bda690e4SXin Li   return ret;
762*bda690e4SXin Li }
763*bda690e4SXin Li 
_ov_open2(OggVorbis_File * vf)764*bda690e4SXin Li static int _ov_open2(OggVorbis_File *vf){
765*bda690e4SXin Li   if(vf->ready_state < OPENED)
766*bda690e4SXin Li     vf->ready_state=OPENED;
767*bda690e4SXin Li   if(vf->seekable){
768*bda690e4SXin Li     int ret=_open_seekable2(vf);
769*bda690e4SXin Li     if(ret){
770*bda690e4SXin Li       vf->datasource=NULL;
771*bda690e4SXin Li       ov_clear(vf);
772*bda690e4SXin Li     }
773*bda690e4SXin Li     return ret;
774*bda690e4SXin Li   }
775*bda690e4SXin Li   return 0;
776*bda690e4SXin Li }
777*bda690e4SXin Li 
778*bda690e4SXin Li 
779*bda690e4SXin Li /* clear out the OggVorbis_File struct */
ov_clear(OggVorbis_File * vf)780*bda690e4SXin Li int ov_clear(OggVorbis_File *vf){
781*bda690e4SXin Li   if(vf){
782*bda690e4SXin Li     vorbis_dsp_destroy(vf->vd);
783*bda690e4SXin Li     vf->vd=0;
784*bda690e4SXin Li     ogg_stream_destroy(vf->os);
785*bda690e4SXin Li     vorbis_info_clear(&vf->vi);
786*bda690e4SXin Li     vorbis_comment_clear(&vf->vc);
787*bda690e4SXin Li     if(vf->dataoffsets)_ogg_free(vf->dataoffsets);
788*bda690e4SXin Li     if(vf->pcmlengths)_ogg_free(vf->pcmlengths);
789*bda690e4SXin Li     if(vf->serialnos)_ogg_free(vf->serialnos);
790*bda690e4SXin Li     if(vf->offsets)_ogg_free(vf->offsets);
791*bda690e4SXin Li     ogg_sync_destroy(vf->oy);
792*bda690e4SXin Li 
793*bda690e4SXin Li     if(vf->datasource)(vf->callbacks.close_func)(vf->datasource);
794*bda690e4SXin Li     memset(vf,0,sizeof(*vf));
795*bda690e4SXin Li   }
796*bda690e4SXin Li #ifdef DEBUG_LEAKS
797*bda690e4SXin Li   _VDBG_dump();
798*bda690e4SXin Li #endif
799*bda690e4SXin Li   return 0;
800*bda690e4SXin Li }
801*bda690e4SXin Li 
802*bda690e4SXin Li /* inspects the OggVorbis file and finds/documents all the logical
803*bda690e4SXin Li    bitstreams contained in it.  Tries to be tolerant of logical
804*bda690e4SXin Li    bitstream sections that are truncated/woogie.
805*bda690e4SXin Li 
806*bda690e4SXin Li    return: -1) error
807*bda690e4SXin Li             0) OK
808*bda690e4SXin Li */
809*bda690e4SXin Li 
ov_open_callbacks(void * f,OggVorbis_File * vf,char * initial,long ibytes,ov_callbacks callbacks)810*bda690e4SXin Li int ov_open_callbacks(void *f,OggVorbis_File *vf,char *initial,long ibytes,
811*bda690e4SXin Li     ov_callbacks callbacks){
812*bda690e4SXin Li   int ret=_ov_open1(f,vf,initial,ibytes,callbacks);
813*bda690e4SXin Li   if(ret)return ret;
814*bda690e4SXin Li   return _ov_open2(vf);
815*bda690e4SXin Li }
816*bda690e4SXin Li 
ov_open(FILE * f,OggVorbis_File * vf,char * initial,long ibytes)817*bda690e4SXin Li int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes){
818*bda690e4SXin Li   ov_callbacks callbacks = {
819*bda690e4SXin Li     (size_t (*)(void *, size_t, size_t, void *))  fread,
820*bda690e4SXin Li     (int (*)(void *, ogg_int64_t, int))              _fseek64_wrap,
821*bda690e4SXin Li     (int (*)(void *))                             fclose,
822*bda690e4SXin Li     (long (*)(void *))                            ftell
823*bda690e4SXin Li   };
824*bda690e4SXin Li 
825*bda690e4SXin Li   return ov_open_callbacks((void *)f, vf, initial, ibytes, callbacks);
826*bda690e4SXin Li }
827*bda690e4SXin Li 
828*bda690e4SXin Li /* Only partially open the vorbis file; test for Vorbisness, and load
829*bda690e4SXin Li    the headers for the first chain.  Do not seek (although test for
830*bda690e4SXin Li    seekability).  Use ov_test_open to finish opening the file, else
831*bda690e4SXin Li    ov_clear to close/free it. Same return codes as open. */
832*bda690e4SXin Li 
ov_test_callbacks(void * f,OggVorbis_File * vf,char * initial,long ibytes,ov_callbacks callbacks)833*bda690e4SXin Li int ov_test_callbacks(void *f,OggVorbis_File *vf,char *initial,long ibytes,
834*bda690e4SXin Li     ov_callbacks callbacks)
835*bda690e4SXin Li {
836*bda690e4SXin Li   return _ov_open1(f,vf,initial,ibytes,callbacks);
837*bda690e4SXin Li }
838*bda690e4SXin Li 
ov_test(FILE * f,OggVorbis_File * vf,char * initial,long ibytes)839*bda690e4SXin Li int ov_test(FILE *f,OggVorbis_File *vf,char *initial,long ibytes){
840*bda690e4SXin Li   ov_callbacks callbacks = {
841*bda690e4SXin Li     (size_t (*)(void *, size_t, size_t, void *))  fread,
842*bda690e4SXin Li     (int (*)(void *, ogg_int64_t, int))              _fseek64_wrap,
843*bda690e4SXin Li     (int (*)(void *))                             fclose,
844*bda690e4SXin Li     (long (*)(void *))                            ftell
845*bda690e4SXin Li   };
846*bda690e4SXin Li 
847*bda690e4SXin Li   return ov_test_callbacks((void *)f, vf, initial, ibytes, callbacks);
848*bda690e4SXin Li }
849*bda690e4SXin Li 
ov_test_open(OggVorbis_File * vf)850*bda690e4SXin Li int ov_test_open(OggVorbis_File *vf){
851*bda690e4SXin Li   if(vf->ready_state!=PARTOPEN)return OV_EINVAL;
852*bda690e4SXin Li   return _ov_open2(vf);
853*bda690e4SXin Li }
854*bda690e4SXin Li 
855*bda690e4SXin Li /* How many logical bitstreams in this physical bitstream? */
ov_streams(OggVorbis_File * vf)856*bda690e4SXin Li long ov_streams(OggVorbis_File *vf){
857*bda690e4SXin Li   return vf->links;
858*bda690e4SXin Li }
859*bda690e4SXin Li 
860*bda690e4SXin Li /* Is the FILE * associated with vf seekable? */
ov_seekable(OggVorbis_File * vf)861*bda690e4SXin Li long ov_seekable(OggVorbis_File *vf){
862*bda690e4SXin Li   return vf->seekable;
863*bda690e4SXin Li }
864*bda690e4SXin Li 
865*bda690e4SXin Li /* returns the bitrate for a given logical bitstream or the entire
866*bda690e4SXin Li    physical bitstream.  If the file is open for random access, it will
867*bda690e4SXin Li    find the *actual* average bitrate.  If the file is streaming, it
868*bda690e4SXin Li    returns the nominal bitrate (if set) else the average of the
869*bda690e4SXin Li    upper/lower bounds (if set) else -1 (unset).
870*bda690e4SXin Li 
871*bda690e4SXin Li    If you want the actual bitrate field settings, get them from the
872*bda690e4SXin Li    vorbis_info structs */
873*bda690e4SXin Li 
ov_bitrate(OggVorbis_File * vf,int i)874*bda690e4SXin Li long ov_bitrate(OggVorbis_File *vf,int i){
875*bda690e4SXin Li   if(vf->ready_state<OPENED)return OV_EINVAL;
876*bda690e4SXin Li   if(i>=vf->links)return OV_EINVAL;
877*bda690e4SXin Li   if(!vf->seekable && i!=0)return ov_bitrate(vf,0);
878*bda690e4SXin Li   if(i<0){
879*bda690e4SXin Li     ogg_int64_t bits=0;
880*bda690e4SXin Li     int i;
881*bda690e4SXin Li     for(i=0;i<vf->links;i++)
882*bda690e4SXin Li       bits+=(vf->offsets[i+1]-vf->dataoffsets[i])*8;
883*bda690e4SXin Li     /* This once read: return(rint(bits/ov_time_total(vf,-1)));
884*bda690e4SXin Li      * gcc 3.x on x86 miscompiled this at optimisation level 2 and above,
885*bda690e4SXin Li      * so this is slightly transformed to make it work.
886*bda690e4SXin Li      */
887*bda690e4SXin Li     return (long)(bits*1000/ov_time_total(vf,-1));
888*bda690e4SXin Li   }else{
889*bda690e4SXin Li     if(vf->seekable){
890*bda690e4SXin Li       /* return the actual bitrate */
891*bda690e4SXin Li       return (long)((vf->offsets[i+1]-vf->dataoffsets[i])*8000/ov_time_total(vf,i));
892*bda690e4SXin Li     }else{
893*bda690e4SXin Li       /* return nominal if set */
894*bda690e4SXin Li       if(vf->vi.bitrate_nominal>0){
895*bda690e4SXin Li 	return vf->vi.bitrate_nominal;
896*bda690e4SXin Li       }else{
897*bda690e4SXin Li 	if(vf->vi.bitrate_upper>0){
898*bda690e4SXin Li 	  if(vf->vi.bitrate_lower>0){
899*bda690e4SXin Li 	    return (vf->vi.bitrate_upper+vf->vi.bitrate_lower)/2;
900*bda690e4SXin Li 	  }else{
901*bda690e4SXin Li 	    return vf->vi.bitrate_upper;
902*bda690e4SXin Li 	  }
903*bda690e4SXin Li 	}
904*bda690e4SXin Li 	return OV_FALSE;
905*bda690e4SXin Li       }
906*bda690e4SXin Li     }
907*bda690e4SXin Li   }
908*bda690e4SXin Li }
909*bda690e4SXin Li 
910*bda690e4SXin Li /* returns the actual bitrate since last call.  returns -1 if no
911*bda690e4SXin Li    additional data to offer since last call (or at beginning of stream),
912*bda690e4SXin Li    EINVAL if stream is only partially open
913*bda690e4SXin Li */
ov_bitrate_instant(OggVorbis_File * vf)914*bda690e4SXin Li long ov_bitrate_instant(OggVorbis_File *vf){
915*bda690e4SXin Li   long ret;
916*bda690e4SXin Li   if(vf->ready_state<OPENED)return OV_EINVAL;
917*bda690e4SXin Li   if(vf->samptrack==0)return OV_FALSE;
918*bda690e4SXin Li   ret=(long)(vf->bittrack/vf->samptrack*vf->vi.rate);
919*bda690e4SXin Li   vf->bittrack=0;
920*bda690e4SXin Li   vf->samptrack=0;
921*bda690e4SXin Li   return ret;
922*bda690e4SXin Li }
923*bda690e4SXin Li 
924*bda690e4SXin Li /* Guess */
ov_serialnumber(OggVorbis_File * vf,int i)925*bda690e4SXin Li long ov_serialnumber(OggVorbis_File *vf,int i){
926*bda690e4SXin Li   if(i>=vf->links)return ov_serialnumber(vf,vf->links-1);
927*bda690e4SXin Li   if(!vf->seekable && i>=0)return ov_serialnumber(vf,-1);
928*bda690e4SXin Li   if(i<0){
929*bda690e4SXin Li     return vf->current_serialno;
930*bda690e4SXin Li   }else{
931*bda690e4SXin Li     return vf->serialnos[i];
932*bda690e4SXin Li   }
933*bda690e4SXin Li }
934*bda690e4SXin Li 
935*bda690e4SXin Li /* returns: total raw (compressed) length of content if i==-1
936*bda690e4SXin Li             raw (compressed) length of that logical bitstream for i==0 to n
937*bda690e4SXin Li 	    OV_EINVAL if the stream is not seekable (we can't know the length)
938*bda690e4SXin Li 	    or if stream is only partially open
939*bda690e4SXin Li */
ov_raw_total(OggVorbis_File * vf,int i)940*bda690e4SXin Li ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i){
941*bda690e4SXin Li   if(vf->ready_state<OPENED)return OV_EINVAL;
942*bda690e4SXin Li   if(!vf->seekable || i>=vf->links)return OV_EINVAL;
943*bda690e4SXin Li   if(i<0){
944*bda690e4SXin Li     ogg_int64_t acc=0;
945*bda690e4SXin Li     int i;
946*bda690e4SXin Li     for(i=0;i<vf->links;i++)
947*bda690e4SXin Li       acc+=ov_raw_total(vf,i);
948*bda690e4SXin Li     return acc;
949*bda690e4SXin Li   }else{
950*bda690e4SXin Li     return vf->offsets[i+1]-vf->offsets[i];
951*bda690e4SXin Li   }
952*bda690e4SXin Li }
953*bda690e4SXin Li 
954*bda690e4SXin Li /* returns: total PCM length (samples) of content if i==-1 PCM length
955*bda690e4SXin Li 	    (samples) of that logical bitstream for i==0 to n
956*bda690e4SXin Li 	    OV_EINVAL if the stream is not seekable (we can't know the
957*bda690e4SXin Li 	    length) or only partially open
958*bda690e4SXin Li */
ov_pcm_total(OggVorbis_File * vf,int i)959*bda690e4SXin Li ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i){
960*bda690e4SXin Li   if(vf->ready_state<OPENED)return OV_EINVAL;
961*bda690e4SXin Li   if(!vf->seekable || i>=vf->links)return OV_EINVAL;
962*bda690e4SXin Li   if(i<0){
963*bda690e4SXin Li     ogg_int64_t acc=0;
964*bda690e4SXin Li     int i;
965*bda690e4SXin Li     for(i=0;i<vf->links;i++)
966*bda690e4SXin Li       acc+=ov_pcm_total(vf,i);
967*bda690e4SXin Li     return acc;
968*bda690e4SXin Li   }else{
969*bda690e4SXin Li     return vf->pcmlengths[i*2+1];
970*bda690e4SXin Li   }
971*bda690e4SXin Li }
972*bda690e4SXin Li 
973*bda690e4SXin Li /* returns: total milliseconds of content if i==-1
974*bda690e4SXin Li             milliseconds in that logical bitstream for i==0 to n
975*bda690e4SXin Li 	    OV_EINVAL if the stream is not seekable (we can't know the
976*bda690e4SXin Li 	    length) or only partially open
977*bda690e4SXin Li */
ov_time_total(OggVorbis_File * vf,int i)978*bda690e4SXin Li ogg_int64_t ov_time_total(OggVorbis_File *vf,int i){
979*bda690e4SXin Li   if(vf->ready_state<OPENED)return OV_EINVAL;
980*bda690e4SXin Li   if(!vf->seekable || i>=vf->links)return OV_EINVAL;
981*bda690e4SXin Li   if(i<0){
982*bda690e4SXin Li     ogg_int64_t acc=0;
983*bda690e4SXin Li     int i;
984*bda690e4SXin Li     for(i=0;i<vf->links;i++)
985*bda690e4SXin Li       acc+=ov_time_total(vf,i);
986*bda690e4SXin Li     return acc;
987*bda690e4SXin Li   }else{
988*bda690e4SXin Li     return ((ogg_int64_t)vf->pcmlengths[i*2+1])*1000/vf->vi.rate;
989*bda690e4SXin Li   }
990*bda690e4SXin Li }
991*bda690e4SXin Li 
992*bda690e4SXin Li /* seek to an offset relative to the *compressed* data. This also
993*bda690e4SXin Li    scans packets to update the PCM cursor. It will cross a logical
994*bda690e4SXin Li    bitstream boundary, but only if it can't get any packets out of the
995*bda690e4SXin Li    tail of the bitstream we seek to (so no surprises).
996*bda690e4SXin Li 
997*bda690e4SXin Li    returns zero on success, nonzero on failure */
998*bda690e4SXin Li 
ov_raw_seek(OggVorbis_File * vf,ogg_int64_t pos)999*bda690e4SXin Li int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){
1000*bda690e4SXin Li   ogg_stream_state *work_os=NULL;
1001*bda690e4SXin Li   ogg_page og={0,0,0,0};
1002*bda690e4SXin Li   ogg_packet op={0,0,0,0,0,0};
1003*bda690e4SXin Li 
1004*bda690e4SXin Li   if(vf->ready_state<OPENED)return OV_EINVAL;
1005*bda690e4SXin Li   if(!vf->seekable)
1006*bda690e4SXin Li     return OV_ENOSEEK; /* don't dump machine if we can't seek */
1007*bda690e4SXin Li 
1008*bda690e4SXin Li   if(pos<0 || pos>vf->end)return OV_EINVAL;
1009*bda690e4SXin Li 
1010*bda690e4SXin Li   /* don't yet clear out decoding machine (if it's initialized), in
1011*bda690e4SXin Li      the case we're in the same link.  Restart the decode lapping, and
1012*bda690e4SXin Li      let _fetch_and_process_packet deal with a potential bitstream
1013*bda690e4SXin Li      boundary */
1014*bda690e4SXin Li   vf->pcm_offset=-1;
1015*bda690e4SXin Li   ogg_stream_reset_serialno(vf->os,
1016*bda690e4SXin Li 			    vf->current_serialno); /* must set serialno */
1017*bda690e4SXin Li   vorbis_dsp_restart(vf->vd);
1018*bda690e4SXin Li 
1019*bda690e4SXin Li   _seek_helper(vf,pos);
1020*bda690e4SXin Li 
1021*bda690e4SXin Li   /* we need to make sure the pcm_offset is set, but we don't want to
1022*bda690e4SXin Li      advance the raw cursor past good packets just to get to the first
1023*bda690e4SXin Li      with a granulepos.  That's not equivalent behavior to beginning
1024*bda690e4SXin Li      decoding as immediately after the seek position as possible.
1025*bda690e4SXin Li 
1026*bda690e4SXin Li      So, a hack.  We use two stream states; a local scratch state and
1027*bda690e4SXin Li      the shared vf->os stream state.  We use the local state to
1028*bda690e4SXin Li      scan, and the shared state as a buffer for later decode.
1029*bda690e4SXin Li 
1030*bda690e4SXin Li      Unfortuantely, on the last page we still advance to last packet
1031*bda690e4SXin Li      because the granulepos on the last page is not necessarily on a
1032*bda690e4SXin Li      packet boundary, and we need to make sure the granpos is
1033*bda690e4SXin Li      correct.
1034*bda690e4SXin Li   */
1035*bda690e4SXin Li 
1036*bda690e4SXin Li   {
1037*bda690e4SXin Li     int lastblock=0;
1038*bda690e4SXin Li     int accblock=0;
1039*bda690e4SXin Li     int thisblock;
1040*bda690e4SXin Li     int eosflag;
1041*bda690e4SXin Li 
1042*bda690e4SXin Li     work_os=ogg_stream_create(vf->current_serialno); /* get the memory ready */
1043*bda690e4SXin Li     while(1){
1044*bda690e4SXin Li       if(vf->ready_state>=STREAMSET){
1045*bda690e4SXin Li 	/* snarf/scan a packet if we can */
1046*bda690e4SXin Li 	int result=ogg_stream_packetout(work_os,&op);
1047*bda690e4SXin Li 
1048*bda690e4SXin Li 	if(result>0){
1049*bda690e4SXin Li 
1050*bda690e4SXin Li 	  if(vf->vi.codec_setup){
1051*bda690e4SXin Li 	    thisblock=vorbis_packet_blocksize(&vf->vi,&op);
1052*bda690e4SXin Li 	    if(thisblock<0){
1053*bda690e4SXin Li 	      ogg_stream_packetout(vf->os,NULL);
1054*bda690e4SXin Li 	      thisblock=0;
1055*bda690e4SXin Li 	    }else{
1056*bda690e4SXin Li 
1057*bda690e4SXin Li 	      if(eosflag)
1058*bda690e4SXin Li 		ogg_stream_packetout(vf->os,NULL);
1059*bda690e4SXin Li 	      else
1060*bda690e4SXin Li 		if(lastblock)accblock+=(lastblock+thisblock)>>2;
1061*bda690e4SXin Li 	    }
1062*bda690e4SXin Li 
1063*bda690e4SXin Li 	    if(op.granulepos!=-1){
1064*bda690e4SXin Li 	      int i,link=vf->current_link;
1065*bda690e4SXin Li 	      ogg_int64_t granulepos=op.granulepos-vf->pcmlengths[link*2];
1066*bda690e4SXin Li 	      if(granulepos<0)granulepos=0;
1067*bda690e4SXin Li 
1068*bda690e4SXin Li 	      for(i=0;i<link;i++)
1069*bda690e4SXin Li 		granulepos+=vf->pcmlengths[i*2+1];
1070*bda690e4SXin Li 	      vf->pcm_offset=granulepos-accblock;
1071*bda690e4SXin Li 	      break;
1072*bda690e4SXin Li 	    }
1073*bda690e4SXin Li 	    lastblock=thisblock;
1074*bda690e4SXin Li 	    continue;
1075*bda690e4SXin Li 	  }else
1076*bda690e4SXin Li 	    ogg_stream_packetout(vf->os,NULL);
1077*bda690e4SXin Li 	}
1078*bda690e4SXin Li       }
1079*bda690e4SXin Li 
1080*bda690e4SXin Li       if(!lastblock){
1081*bda690e4SXin Li 	if(_get_next_page(vf,&og,-1)<0){
1082*bda690e4SXin Li 	  vf->pcm_offset=ov_pcm_total(vf,-1);
1083*bda690e4SXin Li 	  break;
1084*bda690e4SXin Li 	}
1085*bda690e4SXin Li       }else{
1086*bda690e4SXin Li 	/* huh?  Bogus stream with packets but no granulepos */
1087*bda690e4SXin Li 	vf->pcm_offset=-1;
1088*bda690e4SXin Li 	break;
1089*bda690e4SXin Li       }
1090*bda690e4SXin Li 
1091*bda690e4SXin Li       /* did we just grab a page from other than current link? */
1092*bda690e4SXin Li       if(vf->ready_state>=STREAMSET)
1093*bda690e4SXin Li 	if(vf->current_serialno!=ogg_page_serialno(&og)){
1094*bda690e4SXin Li 	  _decode_clear(vf); /* clear out stream state */
1095*bda690e4SXin Li 	  ogg_stream_destroy(work_os);
1096*bda690e4SXin Li 	}
1097*bda690e4SXin Li 
1098*bda690e4SXin Li       if(vf->ready_state<STREAMSET){
1099*bda690e4SXin Li 	int link;
1100*bda690e4SXin Li 
1101*bda690e4SXin Li 	vf->current_serialno=ogg_page_serialno(&og);
1102*bda690e4SXin Li 	for(link=0;link<vf->links;link++)
1103*bda690e4SXin Li 	  if(vf->serialnos[link]==vf->current_serialno)break;
1104*bda690e4SXin Li 	if(link==vf->links)
1105*bda690e4SXin Li 	  goto seek_error; /* sign of a bogus stream.  error out,
1106*bda690e4SXin Li 			      leave machine uninitialized */
1107*bda690e4SXin Li 
1108*bda690e4SXin Li 	/* need to initialize machine to this link */
1109*bda690e4SXin Li 	{
1110*bda690e4SXin Li 	  int ret=_set_link_number_preserve_pos(vf,link);
1111*bda690e4SXin Li 	  if(ret) goto seek_error;
1112*bda690e4SXin Li 	}
1113*bda690e4SXin Li 	ogg_stream_reset_serialno(vf->os,vf->current_serialno);
1114*bda690e4SXin Li 	ogg_stream_reset_serialno(work_os,vf->current_serialno);
1115*bda690e4SXin Li 
1116*bda690e4SXin Li 
1117*bda690e4SXin Li       }
1118*bda690e4SXin Li 
1119*bda690e4SXin Li       {
1120*bda690e4SXin Li 	ogg_page dup;
1121*bda690e4SXin Li 	ogg_page_dup(&dup,&og);
1122*bda690e4SXin Li 	eosflag=ogg_page_eos(&og);
1123*bda690e4SXin Li 	ogg_stream_pagein(vf->os,&og);
1124*bda690e4SXin Li 	ogg_stream_pagein(work_os,&dup);
1125*bda690e4SXin Li       }
1126*bda690e4SXin Li     }
1127*bda690e4SXin Li   }
1128*bda690e4SXin Li 
1129*bda690e4SXin Li   ogg_packet_release(&op);
1130*bda690e4SXin Li   ogg_page_release(&og);
1131*bda690e4SXin Li   ogg_stream_destroy(work_os);
1132*bda690e4SXin Li   vf->bittrack=0;
1133*bda690e4SXin Li   vf->samptrack=0;
1134*bda690e4SXin Li   return 0;
1135*bda690e4SXin Li 
1136*bda690e4SXin Li  seek_error:
1137*bda690e4SXin Li   ogg_packet_release(&op);
1138*bda690e4SXin Li   ogg_page_release(&og);
1139*bda690e4SXin Li 
1140*bda690e4SXin Li   /* dump the machine so we're in a known state */
1141*bda690e4SXin Li   vf->pcm_offset=-1;
1142*bda690e4SXin Li   ogg_stream_destroy(work_os);
1143*bda690e4SXin Li   _decode_clear(vf);
1144*bda690e4SXin Li   return OV_EBADLINK;
1145*bda690e4SXin Li }
1146*bda690e4SXin Li 
1147*bda690e4SXin Li /* Page granularity seek (faster than sample granularity because we
1148*bda690e4SXin Li    don't do the last bit of decode to find a specific sample).
1149*bda690e4SXin Li 
1150*bda690e4SXin Li    Seek to the last [granule marked] page preceeding the specified pos
1151*bda690e4SXin Li    location, such that decoding past the returned point will quickly
1152*bda690e4SXin Li    arrive at the requested position. */
ov_pcm_seek_page(OggVorbis_File * vf,ogg_int64_t pos)1153*bda690e4SXin Li int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){
1154*bda690e4SXin Li   int link=-1;
1155*bda690e4SXin Li   ogg_int64_t result=0;
1156*bda690e4SXin Li   ogg_int64_t total=ov_pcm_total(vf,-1);
1157*bda690e4SXin Li   ogg_page og={0,0,0,0};
1158*bda690e4SXin Li   ogg_packet op={0,0,0,0,0,0};
1159*bda690e4SXin Li 
1160*bda690e4SXin Li   if(vf->ready_state<OPENED)return OV_EINVAL;
1161*bda690e4SXin Li   if(!vf->seekable)return OV_ENOSEEK;
1162*bda690e4SXin Li   if(pos<0 || pos>total)return OV_EINVAL;
1163*bda690e4SXin Li 
1164*bda690e4SXin Li   /* which bitstream section does this pcm offset occur in? */
1165*bda690e4SXin Li   for(link=vf->links-1;link>=0;link--){
1166*bda690e4SXin Li     total-=vf->pcmlengths[link*2+1];
1167*bda690e4SXin Li     if(pos>=total)break;
1168*bda690e4SXin Li   }
1169*bda690e4SXin Li 
1170*bda690e4SXin Li 
1171*bda690e4SXin Li   if(link!=vf->current_link){
1172*bda690e4SXin Li     int ret=_set_link_number(vf,link);
1173*bda690e4SXin Li     if(ret) goto seek_error;
1174*bda690e4SXin Li   }else{
1175*bda690e4SXin Li     vorbis_dsp_restart(vf->vd);
1176*bda690e4SXin Li   }
1177*bda690e4SXin Li 
1178*bda690e4SXin Li   ogg_stream_reset_serialno(vf->os,vf->serialnos[link]);
1179*bda690e4SXin Li 
1180*bda690e4SXin Li   /* search within the logical bitstream for the page with the highest
1181*bda690e4SXin Li      pcm_pos preceeding (or equal to) pos.  There is a danger here;
1182*bda690e4SXin Li      missing pages or incorrect frame number information in the
1183*bda690e4SXin Li      bitstream could make our task impossible.  Account for that (it
1184*bda690e4SXin Li      would be an error condition) */
1185*bda690e4SXin Li 
1186*bda690e4SXin Li   /* new search algorithm by HB (Nicholas Vinen) */
1187*bda690e4SXin Li   {
1188*bda690e4SXin Li     ogg_int64_t end=vf->offsets[link+1];
1189*bda690e4SXin Li     ogg_int64_t begin=vf->offsets[link];
1190*bda690e4SXin Li     ogg_int64_t begintime = vf->pcmlengths[link*2];
1191*bda690e4SXin Li     ogg_int64_t endtime = vf->pcmlengths[link*2+1]+begintime;
1192*bda690e4SXin Li     ogg_int64_t target=pos-total+begintime;
1193*bda690e4SXin Li     ogg_int64_t best=begin;
1194*bda690e4SXin Li 
1195*bda690e4SXin Li     while(begin<end){
1196*bda690e4SXin Li       ogg_int64_t bisect;
1197*bda690e4SXin Li 
1198*bda690e4SXin Li       if(end-begin<CHUNKSIZE){
1199*bda690e4SXin Li 	bisect=begin;
1200*bda690e4SXin Li       }else{
1201*bda690e4SXin Li 	/* take a (pretty decent) guess. */
1202*bda690e4SXin Li 	bisect=begin +
1203*bda690e4SXin Li 	  (target-begintime)*(end-begin)/(endtime-begintime) - CHUNKSIZE;
1204*bda690e4SXin Li 	if(bisect<=begin)
1205*bda690e4SXin Li 	  bisect=begin+1;
1206*bda690e4SXin Li       }
1207*bda690e4SXin Li 
1208*bda690e4SXin Li       _seek_helper(vf,bisect);
1209*bda690e4SXin Li 
1210*bda690e4SXin Li       while(begin<end){
1211*bda690e4SXin Li 	result=_get_next_page(vf,&og,end-vf->offset);
1212*bda690e4SXin Li 	if(result==OV_EREAD) goto seek_error;
1213*bda690e4SXin Li 	if(result<0){
1214*bda690e4SXin Li 	  if(bisect<=begin+1)
1215*bda690e4SXin Li 	    end=begin; /* found it */
1216*bda690e4SXin Li 	  else{
1217*bda690e4SXin Li 	    if(bisect==0) goto seek_error;
1218*bda690e4SXin Li 	    bisect-=CHUNKSIZE;
1219*bda690e4SXin Li 	    if(bisect<=begin)bisect=begin+1;
1220*bda690e4SXin Li 	    _seek_helper(vf,bisect);
1221*bda690e4SXin Li 	  }
1222*bda690e4SXin Li 	}else{
1223*bda690e4SXin Li 	  ogg_int64_t granulepos=ogg_page_granulepos(&og);
1224*bda690e4SXin Li 	  if(granulepos==-1)continue;
1225*bda690e4SXin Li 	  if(granulepos<target){
1226*bda690e4SXin Li 	    best=result;  /* raw offset of packet with granulepos */
1227*bda690e4SXin Li 	    begin=vf->offset; /* raw offset of next page */
1228*bda690e4SXin Li 	    begintime=granulepos;
1229*bda690e4SXin Li 
1230*bda690e4SXin Li 	    if(target-begintime>44100)break;
1231*bda690e4SXin Li 	    bisect=begin; /* *not* begin + 1 */
1232*bda690e4SXin Li 	  }else{
1233*bda690e4SXin Li 	    if(bisect<=begin+1)
1234*bda690e4SXin Li 	      end=begin;  /* found it */
1235*bda690e4SXin Li 	    else{
1236*bda690e4SXin Li 	      if(end==vf->offset){ /* we're pretty close - we'd be stuck in */
1237*bda690e4SXin Li 		end=result;
1238*bda690e4SXin Li 		bisect-=CHUNKSIZE; /* an endless loop otherwise. */
1239*bda690e4SXin Li 		if(bisect<=begin)bisect=begin+1;
1240*bda690e4SXin Li 		_seek_helper(vf,bisect);
1241*bda690e4SXin Li 	      }else{
1242*bda690e4SXin Li 		end=result;
1243*bda690e4SXin Li 		endtime=granulepos;
1244*bda690e4SXin Li 		break;
1245*bda690e4SXin Li 	      }
1246*bda690e4SXin Li 	    }
1247*bda690e4SXin Li 	  }
1248*bda690e4SXin Li 	}
1249*bda690e4SXin Li       }
1250*bda690e4SXin Li     }
1251*bda690e4SXin Li 
1252*bda690e4SXin Li     /* found our page. seek to it, update pcm offset. Easier case than
1253*bda690e4SXin Li        raw_seek, don't keep packets preceeding granulepos. */
1254*bda690e4SXin Li     {
1255*bda690e4SXin Li 
1256*bda690e4SXin Li       /* seek */
1257*bda690e4SXin Li       _seek_helper(vf,best);
1258*bda690e4SXin Li       vf->pcm_offset=-1;
1259*bda690e4SXin Li 
1260*bda690e4SXin Li       if(_get_next_page(vf,&og,-1)<0){
1261*bda690e4SXin Li 	ogg_page_release(&og);
1262*bda690e4SXin Li 	return OV_EOF; /* shouldn't happen */
1263*bda690e4SXin Li       }
1264*bda690e4SXin Li 
1265*bda690e4SXin Li       ogg_stream_pagein(vf->os,&og);
1266*bda690e4SXin Li 
1267*bda690e4SXin Li       /* pull out all but last packet; the one with granulepos */
1268*bda690e4SXin Li       while(1){
1269*bda690e4SXin Li 	result=ogg_stream_packetpeek(vf->os,&op);
1270*bda690e4SXin Li 	if(result==0){
1271*bda690e4SXin Li 	  /* !!! the packet finishing this page originated on a
1272*bda690e4SXin Li              preceeding page. Keep fetching previous pages until we
1273*bda690e4SXin Li              get one with a granulepos or without the 'continued' flag
1274*bda690e4SXin Li              set.  Then just use raw_seek for simplicity. */
1275*bda690e4SXin Li 
1276*bda690e4SXin Li 	  _seek_helper(vf,best);
1277*bda690e4SXin Li 
1278*bda690e4SXin Li 	  while(1){
1279*bda690e4SXin Li 	    result=_get_prev_page(vf,&og);
1280*bda690e4SXin Li 	    if(result<0) goto seek_error;
1281*bda690e4SXin Li 	    if(ogg_page_granulepos(&og)>-1 ||
1282*bda690e4SXin Li 	       !ogg_page_continued(&og)){
1283*bda690e4SXin Li 	      return ov_raw_seek(vf,result);
1284*bda690e4SXin Li 	    }
1285*bda690e4SXin Li 	    vf->offset=result;
1286*bda690e4SXin Li 	  }
1287*bda690e4SXin Li 	}
1288*bda690e4SXin Li 	if(result<0){
1289*bda690e4SXin Li 	  result = OV_EBADPACKET;
1290*bda690e4SXin Li 	  goto seek_error;
1291*bda690e4SXin Li 	}
1292*bda690e4SXin Li 	if(op.granulepos!=-1){
1293*bda690e4SXin Li 	  vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2];
1294*bda690e4SXin Li 	  if(vf->pcm_offset<0)vf->pcm_offset=0;
1295*bda690e4SXin Li 	  vf->pcm_offset+=total;
1296*bda690e4SXin Li 	  break;
1297*bda690e4SXin Li 	}else
1298*bda690e4SXin Li 	  result=ogg_stream_packetout(vf->os,NULL);
1299*bda690e4SXin Li       }
1300*bda690e4SXin Li     }
1301*bda690e4SXin Li   }
1302*bda690e4SXin Li 
1303*bda690e4SXin Li   /* verify result */
1304*bda690e4SXin Li   if(vf->pcm_offset>pos || pos>ov_pcm_total(vf,-1)){
1305*bda690e4SXin Li     result=OV_EFAULT;
1306*bda690e4SXin Li     goto seek_error;
1307*bda690e4SXin Li   }
1308*bda690e4SXin Li   vf->bittrack=0;
1309*bda690e4SXin Li   vf->samptrack=0;
1310*bda690e4SXin Li 
1311*bda690e4SXin Li   ogg_page_release(&og);
1312*bda690e4SXin Li   ogg_packet_release(&op);
1313*bda690e4SXin Li   return 0;
1314*bda690e4SXin Li 
1315*bda690e4SXin Li  seek_error:
1316*bda690e4SXin Li 
1317*bda690e4SXin Li   ogg_page_release(&og);
1318*bda690e4SXin Li   ogg_packet_release(&op);
1319*bda690e4SXin Li 
1320*bda690e4SXin Li   /* dump machine so we're in a known state */
1321*bda690e4SXin Li   vf->pcm_offset=-1;
1322*bda690e4SXin Li   _decode_clear(vf);
1323*bda690e4SXin Li   return (int)result;
1324*bda690e4SXin Li }
1325*bda690e4SXin Li 
1326*bda690e4SXin Li /* seek to a sample offset relative to the decompressed pcm stream
1327*bda690e4SXin Li    returns zero on success, nonzero on failure */
1328*bda690e4SXin Li 
ov_pcm_seek(OggVorbis_File * vf,ogg_int64_t pos)1329*bda690e4SXin Li int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
1330*bda690e4SXin Li   ogg_packet op={0,0,0,0,0,0};
1331*bda690e4SXin Li   ogg_page og={0,0,0,0};
1332*bda690e4SXin Li   int thisblock,lastblock=0;
1333*bda690e4SXin Li   int ret=ov_pcm_seek_page(vf,pos);
1334*bda690e4SXin Li   if(ret<0)return ret;
1335*bda690e4SXin Li   if(_make_decode_ready(vf))return OV_EBADLINK;
1336*bda690e4SXin Li 
1337*bda690e4SXin Li   /* discard leading packets we don't need for the lapping of the
1338*bda690e4SXin Li      position we want; don't decode them */
1339*bda690e4SXin Li 
1340*bda690e4SXin Li   while(1){
1341*bda690e4SXin Li 
1342*bda690e4SXin Li     int ret=ogg_stream_packetpeek(vf->os,&op);
1343*bda690e4SXin Li     if(ret>0){
1344*bda690e4SXin Li       thisblock=vorbis_packet_blocksize(&vf->vi,&op);
1345*bda690e4SXin Li       if(thisblock<0){
1346*bda690e4SXin Li 	ogg_stream_packetout(vf->os,NULL);
1347*bda690e4SXin Li 	continue; /* non audio packet */
1348*bda690e4SXin Li       }
1349*bda690e4SXin Li       if(lastblock)vf->pcm_offset+=(lastblock+thisblock)>>2;
1350*bda690e4SXin Li 
1351*bda690e4SXin Li       if(vf->pcm_offset+((thisblock+
1352*bda690e4SXin Li 			  vorbis_info_blocksize(&vf->vi,1))>>2)>=pos)break;
1353*bda690e4SXin Li 
1354*bda690e4SXin Li       /* remove the packet from packet queue and track its granulepos */
1355*bda690e4SXin Li       ogg_stream_packetout(vf->os,NULL);
1356*bda690e4SXin Li       vorbis_dsp_synthesis(vf->vd,&op,0);  /* set up a vb with
1357*bda690e4SXin Li 					      only tracking, no
1358*bda690e4SXin Li 					      pcm_decode */
1359*bda690e4SXin Li 
1360*bda690e4SXin Li       /* end of logical stream case is hard, especially with exact
1361*bda690e4SXin Li 	 length positioning. */
1362*bda690e4SXin Li 
1363*bda690e4SXin Li       if(op.granulepos>-1){
1364*bda690e4SXin Li 	int i;
1365*bda690e4SXin Li 	/* always believe the stream markers */
1366*bda690e4SXin Li 	vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2];
1367*bda690e4SXin Li 	if(vf->pcm_offset<0)vf->pcm_offset=0;
1368*bda690e4SXin Li 	for(i=0;i<vf->current_link;i++)
1369*bda690e4SXin Li 	  vf->pcm_offset+=vf->pcmlengths[i*2+1];
1370*bda690e4SXin Li       }
1371*bda690e4SXin Li 
1372*bda690e4SXin Li       lastblock=thisblock;
1373*bda690e4SXin Li 
1374*bda690e4SXin Li     }else{
1375*bda690e4SXin Li       if(ret<0 && ret!=OV_HOLE)break;
1376*bda690e4SXin Li 
1377*bda690e4SXin Li       /* suck in a new page */
1378*bda690e4SXin Li       if(_get_next_page(vf,&og,-1)<0)break;
1379*bda690e4SXin Li       if(vf->current_serialno!=ogg_page_serialno(&og))_decode_clear(vf);
1380*bda690e4SXin Li 
1381*bda690e4SXin Li       if(vf->ready_state<STREAMSET){
1382*bda690e4SXin Li 	int link,ret;
1383*bda690e4SXin Li 
1384*bda690e4SXin Li 	vf->current_serialno=ogg_page_serialno(&og);
1385*bda690e4SXin Li 	for(link=0;link<vf->links;link++)
1386*bda690e4SXin Li 	  if(vf->serialnos[link]==vf->current_serialno)break;
1387*bda690e4SXin Li 	if(link==vf->links){
1388*bda690e4SXin Li 	  ogg_page_release(&og);
1389*bda690e4SXin Li 	  ogg_packet_release(&op);
1390*bda690e4SXin Li 	  return OV_EBADLINK;
1391*bda690e4SXin Li 	}
1392*bda690e4SXin Li 
1393*bda690e4SXin Li 
1394*bda690e4SXin Li 	vf->current_link=link;
1395*bda690e4SXin Li 	ret=_fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,&og);
1396*bda690e4SXin Li 	if(ret) return ret;
1397*bda690e4SXin Li 	if(_make_decode_ready(vf))return OV_EBADLINK;
1398*bda690e4SXin Li 	lastblock=0;
1399*bda690e4SXin Li       }
1400*bda690e4SXin Li 
1401*bda690e4SXin Li       ogg_stream_pagein(vf->os,&og);
1402*bda690e4SXin Li     }
1403*bda690e4SXin Li   }
1404*bda690e4SXin Li 
1405*bda690e4SXin Li   vf->bittrack=0;
1406*bda690e4SXin Li   vf->samptrack=0;
1407*bda690e4SXin Li   /* discard samples until we reach the desired position. Crossing a
1408*bda690e4SXin Li      logical bitstream boundary with abandon is OK. */
1409*bda690e4SXin Li   while(vf->pcm_offset<pos){
1410*bda690e4SXin Li     ogg_int64_t target=pos-vf->pcm_offset;
1411*bda690e4SXin Li     long samples=vorbis_dsp_pcmout(vf->vd,NULL,0);
1412*bda690e4SXin Li 
1413*bda690e4SXin Li     if(samples>target)samples=(long)target;
1414*bda690e4SXin Li     vorbis_dsp_read(vf->vd,samples);
1415*bda690e4SXin Li     vf->pcm_offset+=samples;
1416*bda690e4SXin Li 
1417*bda690e4SXin Li     if(samples<target)
1418*bda690e4SXin Li       if(_fetch_and_process_packet(vf,1,1)<=0)
1419*bda690e4SXin Li 	vf->pcm_offset=ov_pcm_total(vf,-1); /* eof */
1420*bda690e4SXin Li   }
1421*bda690e4SXin Li 
1422*bda690e4SXin Li   ogg_page_release(&og);
1423*bda690e4SXin Li   ogg_packet_release(&op);
1424*bda690e4SXin Li   return 0;
1425*bda690e4SXin Li }
1426*bda690e4SXin Li 
1427*bda690e4SXin Li /* seek to a playback time relative to the decompressed pcm stream
1428*bda690e4SXin Li    returns zero on success, nonzero on failure */
ov_time_seek(OggVorbis_File * vf,ogg_int64_t milliseconds)1429*bda690e4SXin Li int ov_time_seek(OggVorbis_File *vf,ogg_int64_t milliseconds){
1430*bda690e4SXin Li   /* translate time to PCM position and call ov_pcm_seek */
1431*bda690e4SXin Li 
1432*bda690e4SXin Li   int link=-1;
1433*bda690e4SXin Li   ogg_int64_t pcm_total=ov_pcm_total(vf,-1);
1434*bda690e4SXin Li   ogg_int64_t time_total=ov_time_total(vf,-1);
1435*bda690e4SXin Li 
1436*bda690e4SXin Li   if(vf->ready_state<OPENED)return OV_EINVAL;
1437*bda690e4SXin Li   if(!vf->seekable)return OV_ENOSEEK;
1438*bda690e4SXin Li   if(milliseconds<0 || milliseconds>time_total)return OV_EINVAL;
1439*bda690e4SXin Li 
1440*bda690e4SXin Li   /* which bitstream section does this time offset occur in? */
1441*bda690e4SXin Li   for(link=vf->links-1;link>=0;link--){
1442*bda690e4SXin Li     pcm_total-=vf->pcmlengths[link*2+1];
1443*bda690e4SXin Li     time_total-=ov_time_total(vf,link);
1444*bda690e4SXin Li     if(milliseconds>=time_total)break;
1445*bda690e4SXin Li   }
1446*bda690e4SXin Li 
1447*bda690e4SXin Li   /* enough information to convert time offset to pcm offset */
1448*bda690e4SXin Li   {
1449*bda690e4SXin Li     int ret=_set_link_number(vf,link);
1450*bda690e4SXin Li     if(ret)return ret;
1451*bda690e4SXin Li     return
1452*bda690e4SXin Li       ov_pcm_seek(vf,pcm_total+(milliseconds-time_total)*
1453*bda690e4SXin Li 		  vf->vi.rate/1000);
1454*bda690e4SXin Li   }
1455*bda690e4SXin Li }
1456*bda690e4SXin Li 
1457*bda690e4SXin Li /* page-granularity version of ov_time_seek
1458*bda690e4SXin Li    returns zero on success, nonzero on failure */
ov_time_seek_page(OggVorbis_File * vf,ogg_int64_t milliseconds)1459*bda690e4SXin Li int ov_time_seek_page(OggVorbis_File *vf,ogg_int64_t milliseconds){
1460*bda690e4SXin Li   /* translate time to PCM position and call ov_pcm_seek */
1461*bda690e4SXin Li 
1462*bda690e4SXin Li   int link=-1;
1463*bda690e4SXin Li   ogg_int64_t pcm_total=ov_pcm_total(vf,-1);
1464*bda690e4SXin Li   ogg_int64_t time_total=ov_time_total(vf,-1);
1465*bda690e4SXin Li 
1466*bda690e4SXin Li   if(vf->ready_state<OPENED)return OV_EINVAL;
1467*bda690e4SXin Li   if(!vf->seekable)return OV_ENOSEEK;
1468*bda690e4SXin Li   if(milliseconds<0 || milliseconds>time_total)return OV_EINVAL;
1469*bda690e4SXin Li 
1470*bda690e4SXin Li   /* which bitstream section does this time offset occur in? */
1471*bda690e4SXin Li   for(link=vf->links-1;link>=0;link--){
1472*bda690e4SXin Li     pcm_total-=vf->pcmlengths[link*2+1];
1473*bda690e4SXin Li     time_total-=ov_time_total(vf,link);
1474*bda690e4SXin Li     if(milliseconds>=time_total)break;
1475*bda690e4SXin Li   }
1476*bda690e4SXin Li 
1477*bda690e4SXin Li   /* enough information to convert time offset to pcm offset */
1478*bda690e4SXin Li   {
1479*bda690e4SXin Li     int ret=_set_link_number(vf,link);
1480*bda690e4SXin Li     if(ret)return ret;
1481*bda690e4SXin Li     return
1482*bda690e4SXin Li       ov_pcm_seek_page(vf,pcm_total+(milliseconds-time_total)*
1483*bda690e4SXin Li 		       vf->vi.rate/1000);
1484*bda690e4SXin Li   }
1485*bda690e4SXin Li }
1486*bda690e4SXin Li 
1487*bda690e4SXin Li /* tell the current stream offset cursor.  Note that seek followed by
1488*bda690e4SXin Li    tell will likely not give the set offset due to caching */
ov_raw_tell(OggVorbis_File * vf)1489*bda690e4SXin Li ogg_int64_t ov_raw_tell(OggVorbis_File *vf){
1490*bda690e4SXin Li   if(vf->ready_state<OPENED)return OV_EINVAL;
1491*bda690e4SXin Li   return vf->offset;
1492*bda690e4SXin Li }
1493*bda690e4SXin Li 
1494*bda690e4SXin Li /* return PCM offset (sample) of next PCM sample to be read */
ov_pcm_tell(OggVorbis_File * vf)1495*bda690e4SXin Li ogg_int64_t ov_pcm_tell(OggVorbis_File *vf){
1496*bda690e4SXin Li   if(vf->ready_state<OPENED)return OV_EINVAL;
1497*bda690e4SXin Li   return vf->pcm_offset;
1498*bda690e4SXin Li }
1499*bda690e4SXin Li 
1500*bda690e4SXin Li /* return time offset (milliseconds) of next PCM sample to be read */
ov_time_tell(OggVorbis_File * vf)1501*bda690e4SXin Li ogg_int64_t ov_time_tell(OggVorbis_File *vf){
1502*bda690e4SXin Li   int link=0;
1503*bda690e4SXin Li   ogg_int64_t pcm_total=0;
1504*bda690e4SXin Li   ogg_int64_t time_total=0;
1505*bda690e4SXin Li 
1506*bda690e4SXin Li   if(vf->ready_state<OPENED)return OV_EINVAL;
1507*bda690e4SXin Li   if(vf->seekable){
1508*bda690e4SXin Li     pcm_total=ov_pcm_total(vf,-1);
1509*bda690e4SXin Li     time_total=ov_time_total(vf,-1);
1510*bda690e4SXin Li 
1511*bda690e4SXin Li     /* which bitstream section does this time offset occur in? */
1512*bda690e4SXin Li     for(link=vf->links-1;link>=0;link--){
1513*bda690e4SXin Li       pcm_total-=vf->pcmlengths[link*2+1];
1514*bda690e4SXin Li       time_total-=ov_time_total(vf,link);
1515*bda690e4SXin Li       if(vf->pcm_offset>=pcm_total)break;
1516*bda690e4SXin Li     }
1517*bda690e4SXin Li   }
1518*bda690e4SXin Li 
1519*bda690e4SXin Li   return time_total+(1000*vf->pcm_offset-pcm_total)/vf->vi.rate;
1520*bda690e4SXin Li }
1521*bda690e4SXin Li 
1522*bda690e4SXin Li /*  link:   -1) return the vorbis_info struct for the bitstream section
1523*bda690e4SXin Li                 currently being decoded
1524*bda690e4SXin Li            0-n) to request information for a specific bitstream section
1525*bda690e4SXin Li 
1526*bda690e4SXin Li     In the case of a non-seekable bitstream, any call returns the
1527*bda690e4SXin Li     current bitstream.  NULL in the case that the machine is not
1528*bda690e4SXin Li     initialized */
1529*bda690e4SXin Li 
ov_info(OggVorbis_File * vf,int link)1530*bda690e4SXin Li vorbis_info *ov_info(OggVorbis_File *vf,int link){
1531*bda690e4SXin Li   if(vf->seekable){
1532*bda690e4SXin Li     if(link>=vf->links)return NULL;
1533*bda690e4SXin Li     if(link>=0){
1534*bda690e4SXin Li       int ret=_set_link_number_preserve_pos(vf,link);
1535*bda690e4SXin Li       if(ret)return NULL;
1536*bda690e4SXin Li     }
1537*bda690e4SXin Li   }
1538*bda690e4SXin Li   return &vf->vi;
1539*bda690e4SXin Li }
1540*bda690e4SXin Li 
1541*bda690e4SXin Li /* grr, strong typing, grr, no templates/inheritence, grr */
ov_comment(OggVorbis_File * vf,int link)1542*bda690e4SXin Li vorbis_comment *ov_comment(OggVorbis_File *vf,int link){
1543*bda690e4SXin Li   if(vf->seekable){
1544*bda690e4SXin Li     if(link>=vf->links)return NULL;
1545*bda690e4SXin Li     if(link>=0){
1546*bda690e4SXin Li       int ret=_set_link_number_preserve_pos(vf,link);
1547*bda690e4SXin Li       if(ret)return NULL;
1548*bda690e4SXin Li     }
1549*bda690e4SXin Li   }
1550*bda690e4SXin Li   return &vf->vc;
1551*bda690e4SXin Li }
1552*bda690e4SXin Li 
1553*bda690e4SXin Li /* up to this point, everything could more or less hide the multiple
1554*bda690e4SXin Li    logical bitstream nature of chaining from the toplevel application
1555*bda690e4SXin Li    if the toplevel application didn't particularly care.  However, at
1556*bda690e4SXin Li    the point that we actually read audio back, the multiple-section
1557*bda690e4SXin Li    nature must surface: Multiple bitstream sections do not necessarily
1558*bda690e4SXin Li    have to have the same number of channels or sampling rate.
1559*bda690e4SXin Li 
1560*bda690e4SXin Li    ov_read returns the sequential logical bitstream number currently
1561*bda690e4SXin Li    being decoded along with the PCM data in order that the toplevel
1562*bda690e4SXin Li    application can take action on channel/sample rate changes.  This
1563*bda690e4SXin Li    number will be incremented even for streamed (non-seekable) streams
1564*bda690e4SXin Li    (for seekable streams, it represents the actual logical bitstream
1565*bda690e4SXin Li    index within the physical bitstream.  Note that the accessor
1566*bda690e4SXin Li    functions above are aware of this dichotomy).
1567*bda690e4SXin Li 
1568*bda690e4SXin Li    input values: buffer) a buffer to hold packed PCM data for return
1569*bda690e4SXin Li 		 length) the byte length requested to be placed into buffer
1570*bda690e4SXin Li 
1571*bda690e4SXin Li    return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL)
1572*bda690e4SXin Li                    0) EOF
1573*bda690e4SXin Li 		   n) number of bytes of PCM actually returned.  The
1574*bda690e4SXin Li 		   below works on a packet-by-packet basis, so the
1575*bda690e4SXin Li 		   return length is not related to the 'length' passed
1576*bda690e4SXin Li 		   in, just guaranteed to fit.
1577*bda690e4SXin Li 
1578*bda690e4SXin Li 	    *section) set to the logical bitstream number */
1579*bda690e4SXin Li 
ov_read(OggVorbis_File * vf,void * buffer,int bytes_req,int * bitstream)1580*bda690e4SXin Li long ov_read(OggVorbis_File *vf,void *buffer,int bytes_req,int *bitstream){
1581*bda690e4SXin Li 
1582*bda690e4SXin Li   long samples;
1583*bda690e4SXin Li   long channels;
1584*bda690e4SXin Li 
1585*bda690e4SXin Li   if(vf->ready_state<OPENED)return OV_EINVAL;
1586*bda690e4SXin Li 
1587*bda690e4SXin Li   while(1){
1588*bda690e4SXin Li     if(vf->ready_state==INITSET){
1589*bda690e4SXin Li       channels=vf->vi.channels;
1590*bda690e4SXin Li       samples=vorbis_dsp_pcmout(vf->vd,buffer,(bytes_req>>1)/channels);
1591*bda690e4SXin Li       if(samples){
1592*bda690e4SXin Li 	if(samples>0){
1593*bda690e4SXin Li 	  vorbis_dsp_read(vf->vd,samples);
1594*bda690e4SXin Li 	  vf->pcm_offset+=samples;
1595*bda690e4SXin Li 	  if(bitstream)*bitstream=vf->current_link;
1596*bda690e4SXin Li 	  return samples*2*channels;
1597*bda690e4SXin Li 	}
1598*bda690e4SXin Li 	return samples;
1599*bda690e4SXin Li       }
1600*bda690e4SXin Li     }
1601*bda690e4SXin Li 
1602*bda690e4SXin Li     /* suck in another packet */
1603*bda690e4SXin Li     {
1604*bda690e4SXin Li       int ret=_fetch_and_process_packet(vf,1,1);
1605*bda690e4SXin Li       if(ret==OV_EOF)
1606*bda690e4SXin Li 	return 0;
1607*bda690e4SXin Li       if(ret<=0)
1608*bda690e4SXin Li 	return ret;
1609*bda690e4SXin Li     }
1610*bda690e4SXin Li 
1611*bda690e4SXin Li   }
1612*bda690e4SXin Li }
1613