]> git.sesse.net Git - vlc/blob - modules/demux/oggseek.c
Use var_Inherit* instead of var_CreateGet*.
[vlc] / modules / demux / oggseek.c
1 /*****************************************************************************
2  * oggseek.c : ogg seeking functions for ogg demuxer vlc
3  *****************************************************************************
4  * Copyright (C) 2008 - 2010 Gabriel Finch <salsaman@gmail.com>
5  *
6  * Authors: Gabriel Finch <salsaman@gmail.com>
7  * adapted from: http://lives.svn.sourceforge.net/viewvc/lives/trunk/lives-plugins
8  * /plugins/decoders/ogg_theora_decoder.c
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34 #include <vlc_demux.h>
35
36 #include <ogg/ogg.h>
37
38 #include "ogg.h"
39 #include "oggseek.h"
40
41
42 /************************************************************
43 * index entries
44 *************************************************************/
45
46 /* free all entries in index list */
47
48 void oggseek_index_entries_free ( demux_index_entry_t *idx )
49 {
50     demux_index_entry_t *idx_next;
51
52     while ( idx != NULL )
53     {
54         idx_next = idx->p_next;
55         free( idx );
56         idx = idx_next;
57     }
58 }
59
60
61 /* unlink and free idx. If idx is head of list, return new head */
62
63 static demux_index_entry_t *index_entry_delete( demux_index_entry_t *idx )
64 {
65     demux_index_entry_t *xidx = idx;
66
67     if ( idx->p_prev != NULL ) idx->p_prev->p_next = idx->p_next;
68     else xidx = idx->p_next;
69
70     if ( idx->p_next != NULL ) idx->p_next->p_prev = idx->p_prev;
71     free( idx );
72
73     return xidx;
74 }
75
76
77 /* internal function to create a new list member */
78
79 static demux_index_entry_t *index_entry_new( void )
80 {
81     demux_index_entry_t *idx = (demux_index_entry_t *)malloc( sizeof( demux_index_entry_t ) );
82     idx->p_next = idx->p_prev = NULL;
83     idx->i_pagepos_end = -1;
84     return idx;
85 }
86
87
88
89
90 /*********************************************************************
91  * private functions
92  **********************************************************************/
93
94 /* seek in ogg file to offset i_pos and update the sync */
95
96 static void seek_byte( demux_t *p_demux, int64_t i_pos )
97 {
98     demux_sys_t *p_sys  = p_demux->p_sys;
99
100     if ( ! stream_Seek( p_demux->s, i_pos ) )
101     {
102         ogg_sync_reset( &p_sys->oy );
103
104         p_sys->i_input_position = i_pos;
105         p_sys->b_page_waiting = false;
106     }
107 }
108
109
110
111 /* read bytes from the ogg file to try to find a page start */
112
113 static int64_t get_data( demux_t *p_demux, int64_t i_bytes_to_read )
114 {
115     demux_sys_t *p_sys  = p_demux->p_sys;
116
117     char *buf;
118     int64_t i_result;
119
120     if ( p_sys->i_total_length > 0 )
121     {
122         if ( p_sys->i_input_position + i_bytes_to_read > p_sys->i_total_length )
123         {
124             i_bytes_to_read = p_sys->i_total_length - p_sys->i_input_position;
125             if ( i_bytes_to_read <= 0 ) {
126                 return 0;
127             }
128         }
129     }
130
131     seek_byte ( p_demux, p_sys->i_input_position );
132
133     buf = ogg_sync_buffer( &p_sys->oy, i_bytes_to_read );
134
135     i_result = stream_Read( p_demux->s, buf, i_bytes_to_read );
136
137     p_sys->b_page_waiting = false;
138
139     ogg_sync_wrote( &p_sys->oy, i_result );
140     return i_result;
141 }
142
143
144
145
146
147
148 /* Find the last frame for p_stream,
149    -1 is returned on failure */
150
151 static int64_t find_last_frame (demux_t *p_demux, logical_stream_t *p_stream)
152 {
153
154     return -1;
155 }
156
157
158
159
160 /* seek to a suitable point to begin decoding for i_tframe. We can pre-set bounding positions 
161    i_pos_lower and i_pos_higher to narrow the search domain. */
162
163
164 static int64_t ogg_seek( demux_t *p_demux, logical_stream_t *p_stream, int64_t i_tframe, 
165                          int64_t i_pos_lower, int64_t i_pos_upper, int64_t *pi_pagepos, 
166                          bool b_exact )
167 {
168     /* For theora:
169      * We do two passes here, first with b_exact set, then with b_exact unset.
170      *
171      * If b_exact is set, we find the highest granulepos <= the target granulepos
172      * from this we extract an estimate of the keyframe (note that there could be other 
173      * "hidden" keyframes between the found granulepos and the target).
174      *
175      * On the second pass we find the highest granulepos < target. This places us just before or 
176      * at the start of the target keyframe.
177      *
178      * When we come to decode, we start from this second position, discarding any completed 
179      * packets on that page, and read pages discarding packets until we get to the target frame.
180      *
181      * The function returns the granulepos which is found, 
182      * sets the page offset in pi_pagepos. -1 is returned on error.
183      *
184      * for dirac:
185      *
186      * we find the highest sync frame <= target frame, and return the sync_frame number
187      * b_exact should be set to true
188      *
189      *
190      * the method used is bi-sections:
191      *  - we check the lower keyframe
192      * if this is == target we return
193      * if > target, or we find no keyframes, we go to the lower segment
194      * if < target we divide the segment in two and check the upper half
195      *
196      * This is then repeated until the segment size is too small to hold a packet, 
197      * at which point we return our best match
198      *
199      * Two optimisations are made: - anything we discover about keyframes is added to our index
200      * - before calling this function we get approximate bounds from the index
201      *
202      * therefore, subsequent searches become more rapid.
203      *
204      */
205
206  
207     return -1;
208 }
209
210
211
212
213
214
215 /* find upper and lower pagepos for i_tframe; if we find an exact match, we return it */
216
217 static demux_index_entry_t *get_bounds_for ( logical_stream_t *p_stream, int64_t i_tframe, 
218                                              int64_t *pi_pos_lower, int64_t *pi_pos_upper)
219 {
220
221     return NULL;
222 }
223
224
225 /************************************************************************
226  * public functions
227  *************************************************************************/
228
229
230
231 /* return highest frame number for p_stream (which must be a theora or dirac video stream) */
232
233 int64_t oggseek_get_last_frame ( demux_t *p_demux, logical_stream_t *p_stream )
234 {
235
236     /* unhandled video format */
237     return -1;
238 }
239
240
241
242
243
244
245 /* seek to target frame in p_stream; actually we will probably end up just before it
246  *   (so we set skip)
247  *
248  * range for i_tframe is 0 -> p_sys->i_total_frames - 1
249  */
250
251 int oggseek_find_frame ( demux_t *p_demux, logical_stream_t *p_stream, int64_t i_tframe )
252 {
253
254     const demux_index_entry_t *fidx;
255
256     /* lower and upper bounds for search domain */
257     int64_t i_pos_lower;
258     int64_t i_pos_upper;
259
260     int64_t i_granulepos;
261     int64_t i_pagepos;
262
263     /* keyframe for i_tframe ( <= i_tframe ) */
264     int64_t i_kframe;
265
266     /* keyframe for i_kframe ( <= i_kframe ) */
267     int64_t i_xkframe;
268
269     /* next frame to be decoded ( >= i_xkframe ) */
270     int64_t i_cframe;
271
272     demux_sys_t *p_sys  = p_demux->p_sys;
273
274     i_tframe += p_stream->i_keyframe_offset;
275
276     /* reduce the search domain */
277     fidx = get_bounds_for( p_stream, i_tframe, &i_pos_lower, &i_pos_upper );
278
279     if ( fidx == NULL )
280     {
281         /* no exact match found; search the domain for highest keyframe <= i_tframe */
282
283         i_granulepos = ogg_seek ( p_demux, p_stream, i_tframe, i_pos_lower, i_pos_upper, 
284                                   &i_pagepos, true );
285         if ( i_granulepos == -1 )
286         {
287             return VLC_EGENERIC;
288         }
289
290     }
291     else {
292         i_granulepos = fidx->i_value;
293     }
294
295     return VLC_EGENERIC;
296
297 }
298
299
300
301
302
303
304 /****************************************************************************
305  * oggseek_read_page: Read a full Ogg page from the physical bitstream.
306  ****************************************************************************
307  * Returns number of bytes read. This should always be > 0
308  * unless we are at the end of stream.
309  *
310  ****************************************************************************/
311 int64_t oggseek_read_page( demux_t *p_demux )
312 {
313     demux_sys_t *p_ogg = p_demux->p_sys  ;
314     uint8_t header[PAGE_HEADER_BYTES+255];
315     int i_nsegs;
316     int i_in_pos;
317     int i;
318     int64_t i_result;
319     int i_page_size;
320     char *buf;
321
322     demux_sys_t *p_sys  = p_demux->p_sys;
323
324     /* store position of this page */
325     i_in_pos = p_ogg->i_input_position = stream_Tell( p_demux->s );
326
327     if ( p_sys->b_page_waiting) {
328         msg_Warn( p_demux, "Ogg page already loaded" );
329         return 0;
330     }
331
332     if ( stream_Read ( p_demux->s, header, PAGE_HEADER_BYTES ) < PAGE_HEADER_BYTES )
333     {
334         stream_Seek( p_demux->s, i_in_pos );
335         msg_Dbg ( p_demux, "Reached clean EOF in ogg file" );
336         return 0;
337     }
338
339     i_nsegs = header[ PAGE_HEADER_BYTES - 1 ];
340
341     if ( stream_Read ( p_demux->s, header+PAGE_HEADER_BYTES, i_nsegs ) < i_nsegs )
342     {
343         stream_Seek( p_demux->s, i_in_pos );
344         msg_Warn ( p_demux, "Reached broken EOF in ogg file" );
345         return 0;
346     }
347
348     i_page_size = PAGE_HEADER_BYTES + i_nsegs;
349
350     for ( i = 0; i < i_nsegs; i++ )
351     {
352         i_page_size += header[ PAGE_HEADER_BYTES + i ];
353     }
354
355     ogg_sync_reset( &p_ogg->oy );
356
357     buf = ogg_sync_buffer( &p_ogg->oy, i_page_size );
358
359     memcpy( buf, header, PAGE_HEADER_BYTES + i_nsegs );
360
361     i_result = stream_Read ( p_demux->s, (uint8_t*)buf + PAGE_HEADER_BYTES + i_nsegs, 
362                              i_page_size - PAGE_HEADER_BYTES - i_nsegs );
363
364     ogg_sync_wrote( &p_ogg->oy, i_result + PAGE_HEADER_BYTES + i_nsegs );
365
366
367
368
369     if ( ogg_sync_pageout( &p_ogg->oy, &p_ogg->current_page ) != 1 )
370     {
371         msg_Err( p_demux , "Got invalid packet, read %"PRId64" of %i: %s",i_result,i_page_size,
372                  buf );
373         return 0;
374     }
375
376     p_sys->b_page_waiting = false;
377
378     return i_result + PAGE_HEADER_BYTES + i_nsegs;
379 }
380
381