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