]> git.sesse.net Git - vlc/blob - modules/demux/oggseek.c
ogg: fix computation of vorbis stream length (close #1859)
[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 = xmalloc( 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 /* add a theora entry to our list; format is highest granulepos -> page offset of
90    keyframe start */
91
92 const demux_index_entry_t *oggseek_theora_index_entry_add ( logical_stream_t *p_stream,
93                                                             int64_t i_granule,
94                                                             int64_t i_pagepos)
95 {
96     /* add or update entry for keyframe */
97     demux_index_entry_t *idx;
98     demux_index_entry_t *oidx;
99     demux_index_entry_t *last_idx = NULL;
100     int64_t i_gpos;
101     int64_t i_frame;
102     int64_t i_kframe;
103     int64_t i_tframe;
104     int64_t i_tkframe;
105
106     if ( p_stream == NULL ) return NULL;
107
108     oidx = idx = p_stream->idx;
109
110     i_tkframe = i_granule >> p_stream->i_granule_shift;
111     i_tframe = i_tkframe + i_granule - ( i_tkframe << p_stream->i_granule_shift );
112
113     if ( i_tkframe < 1 ) return NULL;
114
115     if ( idx == NULL )
116     {
117         demux_index_entry_t *ie = index_entry_new();
118         ie->i_value = i_granule;
119         ie->i_pagepos = i_pagepos;
120         p_stream->idx = ie;
121         return ie;
122     }
123
124
125     while ( idx != NULL )
126     {
127         i_gpos = idx->i_value;
128
129         i_kframe = i_gpos >> p_stream->i_granule_shift;
130         if ( i_kframe > i_tframe ) break;
131
132         if ( i_kframe == i_tkframe )
133         {
134             /* entry exists, update it if applicable, and return it */
135             i_frame = i_kframe + i_gpos - ( i_kframe << p_stream->i_granule_shift );
136             if ( i_frame < i_tframe )
137             {
138                 idx->i_value = i_granule;
139                 idx->i_pagepos = i_pagepos;
140             }
141
142             return idx;
143         }
144
145         last_idx = idx;
146         idx = idx->p_next;
147     }
148
149
150     /* new entry; insert after last_idx */
151
152     idx = index_entry_new();
153
154     if ( last_idx != NULL )
155     {
156         idx->p_next = last_idx->p_next;
157         last_idx->p_next = idx;
158         idx->p_prev = last_idx;
159     }
160     else
161     {
162         idx->p_next = oidx;
163         oidx = idx;
164     }
165
166     if ( idx->p_next != NULL )
167     {
168         idx->p_next->p_prev = idx;
169     }
170
171     idx->i_value = i_granule;
172     idx->i_pagepos = i_pagepos;
173
174     return idx;
175 }
176
177
178
179
180 /*********************************************************************
181  * private functions
182  **********************************************************************/
183
184 /* seek in ogg file to offset i_pos and update the sync */
185
186 static void seek_byte( demux_t *p_demux, int64_t i_pos )
187 {
188     demux_sys_t *p_sys  = p_demux->p_sys;
189
190     if ( ! stream_Seek( p_demux->s, i_pos ) )
191     {
192         ogg_sync_reset( &p_sys->oy );
193
194         p_sys->i_input_position = i_pos;
195         p_sys->b_page_waiting = false;
196     }
197 }
198
199
200
201 /* read bytes from the ogg file to try to find a page start */
202
203 static int64_t get_data( demux_t *p_demux, int64_t i_bytes_to_read )
204 {
205     demux_sys_t *p_sys  = p_demux->p_sys;
206
207     char *buf;
208     int64_t i_result;
209
210     if ( p_sys->i_total_length > 0 )
211     {
212         if ( p_sys->i_input_position + i_bytes_to_read > p_sys->i_total_length )
213         {
214             i_bytes_to_read = p_sys->i_total_length - p_sys->i_input_position;
215             if ( i_bytes_to_read <= 0 ) {
216                 return 0;
217             }
218         }
219     }
220
221     seek_byte ( p_demux, p_sys->i_input_position );
222
223     buf = ogg_sync_buffer( &p_sys->oy, i_bytes_to_read );
224
225     i_result = stream_Read( p_demux->s, buf, i_bytes_to_read );
226
227     p_sys->b_page_waiting = false;
228
229     ogg_sync_wrote( &p_sys->oy, i_result );
230     return i_result;
231 }
232
233
234
235
236
237 /* Find the first first ogg page for p_stream between offsets i_pos1 and i_pos2,
238    return file offset in bytes; -1 is returned on failure */
239
240 static int64_t find_first_page( demux_t *p_demux, int64_t i_pos1, int64_t i_pos2,
241                                 logical_stream_t *p_stream,
242                                 int64_t *pi_kframe, int64_t *pi_frame )
243 {
244     int64_t i_result;
245     int64_t i_granulepos;
246     int64_t i_bytes_to_read = i_pos2 - i_pos1 + 1;
247     int64_t i_bytes_read;
248     int64_t i_pages_checked = 0;
249     int64_t i_packets_checked;
250
251     demux_sys_t *p_sys  = p_demux->p_sys;
252
253     ogg_packet op;
254
255     seek_byte( p_demux, i_pos1 );
256
257     if ( i_pos1 == p_stream->i_data_start )
258     {
259         /* set a dummy granulepos at data_start */
260         *pi_kframe = p_stream->i_keyframe_offset;
261         *pi_frame = p_stream->i_keyframe_offset;
262
263         p_sys->b_page_waiting = true;
264         return p_sys->i_input_position;
265     }
266
267     if ( i_bytes_to_read > OGGSEEK_BYTES_TO_READ ) i_bytes_to_read = OGGSEEK_BYTES_TO_READ;
268
269     while ( 1 )
270     {
271
272         if ( p_sys->i_input_position >= i_pos2 )
273         {
274             /* we reached the end and found no pages */
275             *pi_frame=-1;
276             return -1;
277         }
278
279         /* read next chunk */
280         if ( ! ( i_bytes_read = get_data( p_demux, i_bytes_to_read ) ) )
281         {
282             /* EOF */
283             *pi_frame = -1;
284             return -1;
285         }
286
287         i_bytes_to_read = OGGSEEK_BYTES_TO_READ;
288
289         i_result = ogg_sync_pageseek( &p_sys->oy, &p_sys->current_page );
290
291         if ( i_result < 0 )
292         {
293             /* found a page, sync to page start */
294             p_sys->i_input_position -= i_result;
295             i_pos1 = p_sys->i_input_position;
296             continue;
297         }
298
299         if ( i_result > 0 || ( i_result == 0 && p_sys->oy.fill > 3 &&
300                                ! strncmp( (char *)p_sys->oy.data, "OggS" , 4 ) ) )
301         {
302             i_pos1 = p_sys->i_input_position;
303             break;
304         }
305
306         p_sys->i_input_position += i_bytes_read;
307
308     };
309
310     seek_byte( p_demux, p_sys->i_input_position );
311     ogg_stream_reset( &p_stream->os );
312
313     while( 1 )
314     {
315
316         if ( p_sys->i_input_position >= i_pos2 )
317         {
318             /* reached the end of the search region and nothing was found */
319             *pi_frame = -1;
320             return p_sys->i_input_position;
321         }
322
323         p_sys->b_page_waiting = false;
324
325         if ( ! ( i_result = oggseek_read_page( p_demux ) ) )
326         {
327             /* EOF */
328             *pi_frame = -1;
329             return p_sys->i_input_position;
330         }
331
332         // found a page
333         if ( p_stream->os.serialno != ogg_page_serialno( &p_sys->current_page ) )
334         {
335             /* page is not for this stream */
336             p_sys->i_input_position += i_result;
337             if ( ! i_pages_checked ) i_pos1 = p_sys->i_input_position;
338             continue;
339         }
340
341
342         ogg_stream_pagein( &p_stream->os, &p_sys->current_page );
343
344         i_pages_checked++;
345         i_packets_checked = 0;
346
347         if ( ogg_stream_packetout( &p_stream->os, &op ) > 0 )
348         {
349             i_packets_checked++;
350         }
351
352         if ( i_packets_checked )
353         {
354             i_granulepos = ogg_page_granulepos( &p_sys->current_page );
355
356             oggseek_theora_index_entry_add( p_stream, i_granulepos, i_pos1 );
357
358             *pi_kframe =
359                 i_granulepos >> p_stream->i_granule_shift;
360
361             *pi_frame = *pi_kframe +
362                 i_granulepos - ( *pi_kframe << p_stream->i_granule_shift );
363
364             p_sys->b_page_waiting = true;
365             return i_pos1;
366
367         }
368
369         /*  -> start of next page */
370         p_sys->i_input_position += i_result;
371     }
372 }
373
374
375
376
377
378
379
380
381 /* Find the last frame for p_stream,
382    -1 is returned on failure */
383
384 static int64_t find_last_frame (demux_t *p_demux, logical_stream_t *p_stream)
385 {
386
387     int64_t i_page_pos;
388     int64_t i_start_pos;
389     int64_t i_frame = -1;
390     int64_t i_last_frame = -1;
391     int64_t i_kframe = 0;
392     int64_t i_pos1;
393     int64_t i_pos2;
394     int64_t i_serialno;
395
396     demux_sys_t *p_sys  = p_demux->p_sys;
397
398     i_pos1 = p_stream->i_data_start;
399     i_pos2 = p_sys->i_total_length;
400     i_serialno = p_stream->os.serialno;
401
402     i_start_pos = i_pos2 - OGGSEEK_BYTES_TO_READ;
403
404
405     while( 1 )
406     {
407         if ( i_start_pos < i_pos1 ) i_start_pos = i_pos1;
408
409         i_page_pos = find_first_page( p_demux, i_start_pos, i_pos2, p_stream, &i_kframe, &i_frame );
410
411         if ( i_frame == -1 )
412         {
413             /* no pages found in range */
414             if ( i_last_frame >= 0 )
415             {
416                 /* No more pages in range -> return last one */
417                 return i_last_frame;
418             }
419             if ( i_start_pos <= i_pos1 )
420             {
421                 return -1;
422             }
423
424             /* Go back a bit */
425             i_pos2 -= i_start_pos;
426             i_start_pos -= OGGSEEK_BYTES_TO_READ;
427             if ( i_start_pos < i_pos1 ) i_start_pos = i_pos1;
428             i_pos2 += i_start_pos;
429         }
430         else
431         {
432             /* found a page, see if we can find another one */
433             i_last_frame = i_frame;
434             i_start_pos = i_page_pos + 1;
435         }
436     }
437     return -1;
438 }
439
440
441
442
443
444
445 /* convert a theora frame to a granulepos */
446
447 static inline int64_t frame_to_gpos( logical_stream_t *p_stream, int64_t i_kframe,
448                                      int64_t i_frame )
449 {
450     if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
451     {
452         return ( i_kframe << p_stream->i_granule_shift ) + ( i_frame - i_kframe );
453     }
454
455     return i_kframe;
456 }
457
458
459
460
461
462 /* seek to a suitable point to begin decoding for i_tframe. We can pre-set bounding positions
463    i_pos_lower and i_pos_higher to narrow the search domain. */
464
465
466 static int64_t ogg_seek( demux_t *p_demux, logical_stream_t *p_stream, int64_t i_tframe,
467                          int64_t i_pos_lower, int64_t i_pos_upper, int64_t *pi_pagepos,
468                          bool b_exact )
469 {
470     /* For theora:
471      * We do two passes here, first with b_exact set, then with b_exact unset.
472      *
473      * If b_exact is set, we find the highest granulepos <= the target granulepos
474      * from this we extract an estimate of the keyframe (note that there could be other
475      * "hidden" keyframes between the found granulepos and the target).
476      *
477      * On the second pass we find the highest granulepos < target. This places us just before or
478      * at the start of the target keyframe.
479      *
480      * When we come to decode, we start from this second position, discarding any completed
481      * packets on that page, and read pages discarding packets until we get to the target frame.
482      *
483      * The function returns the granulepos which is found,
484      * sets the page offset in pi_pagepos. -1 is returned on error.
485      *
486      * for dirac:
487      *
488      * we find the highest sync frame <= target frame, and return the sync_frame number
489      * b_exact should be set to true
490      *
491      *
492      * the method used is bi-sections:
493      *  - we check the lower keyframe
494      * if this is == target we return
495      * if > target, or we find no keyframes, we go to the lower segment
496      * if < target we divide the segment in two and check the upper half
497      *
498      * This is then repeated until the segment size is too small to hold a packet,
499      * at which point we return our best match
500      *
501      * Two optimisations are made: - anything we discover about keyframes is added to our index
502      * - before calling this function we get approximate bounds from the index
503      *
504      * therefore, subsequent searches become more rapid.
505      *
506      */
507
508     int64_t i_start_pos;
509     int64_t i_end_pos;
510     int64_t i_pagepos;
511     int64_t i_segsize;
512     int64_t i_frame;
513     int64_t i_kframe;
514
515     int64_t i_best_kframe = -1;
516     int64_t i_best_frame = -1;
517     int64_t i_best_pagepos = -1;
518
519     demux_sys_t *p_sys  = p_demux->p_sys;
520
521     if ( i_tframe < p_stream->i_keyframe_offset )
522     {
523         *pi_pagepos = p_stream->i_data_start;
524
525         if ( ! b_exact ) {
526             seek_byte( p_demux, p_stream->i_data_start );
527             return frame_to_gpos( p_stream, p_stream->i_keyframe_offset, 1 );
528         }
529         return frame_to_gpos( p_stream, p_stream->i_keyframe_offset, 0 );
530     }
531
532     if ( i_pos_lower < p_stream->i_data_start )
533     {
534         i_pos_lower = p_stream->i_data_start;
535     }
536
537     if ( i_pos_upper < 0 )
538     {
539         i_pos_upper = p_sys->i_total_length;
540     }
541
542     if ( i_pos_upper > p_sys->i_total_length )
543     {
544         i_pos_upper = p_sys->i_total_length;
545     }
546
547     i_start_pos = i_pos_lower;
548     i_end_pos = i_pos_upper;
549
550     i_segsize = ( i_end_pos - i_start_pos + 1 ) >> 1;
551
552     do
553     {
554         /* see if the frame lies in current segment */
555         if ( i_start_pos < i_pos_lower )
556         {
557             i_start_pos = i_pos_lower;
558         }
559         if ( i_end_pos > i_pos_upper )
560         {
561             i_end_pos = i_pos_upper;
562         }
563
564         if ( i_start_pos >= i_end_pos )
565         {
566             if ( i_start_pos == i_pos_lower)
567             {
568                 if ( ! b_exact ) seek_byte( p_demux, i_start_pos );
569                 *pi_pagepos = i_start_pos;
570                 return frame_to_gpos( p_stream, p_stream->i_keyframe_offset, 1 );
571             }
572             break;
573         }
574
575         if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
576         {
577             i_pagepos = find_first_page( p_demux, i_start_pos, i_end_pos, p_stream,
578                                          &i_kframe, &i_frame );
579         }
580         else return -1;
581
582         if ( i_pagepos != -1 && i_kframe != -1 )
583         {
584             /* found a page */
585
586             if ( b_exact && i_frame >= i_tframe && i_kframe <= i_tframe )
587             {
588                 /* got it ! */
589                 *pi_pagepos = i_start_pos;
590                 return frame_to_gpos( p_stream, i_kframe, i_frame );
591             }
592
593             if ( ( i_kframe < i_tframe || ( b_exact && i_kframe == i_tframe ) )
594                  && i_kframe > i_best_kframe )
595             {
596                 i_best_kframe = i_kframe;
597                 i_best_frame = i_frame;
598                 i_best_pagepos = i_pagepos;
599             }
600
601             if ( i_frame >= i_tframe )
602             {
603                 /* check lower half of segment */
604                 i_start_pos -= i_segsize;
605                 i_end_pos -= i_segsize;
606             }
607
608             else i_start_pos = i_pagepos;
609
610         }
611         else
612         {
613             /* no keyframe found, check lower segment */
614             i_end_pos -= i_segsize;
615             i_start_pos -= i_segsize;
616         }
617
618         i_segsize = ( i_end_pos - i_start_pos + 1 ) >> 1;
619         i_start_pos += i_segsize;
620
621     } while ( i_segsize > 64 );
622
623     if ( i_best_kframe >- 1 )
624     {
625         if ( !b_exact )
626         {
627             seek_byte( p_demux, i_best_pagepos );
628         }
629         *pi_pagepos = i_best_pagepos;
630         return frame_to_gpos( p_stream, i_best_kframe, i_best_frame );
631     }
632
633     return -1;
634 }
635
636
637
638
639
640
641 /* find upper and lower pagepos for i_tframe; if we find an exact match, we return it */
642
643 static demux_index_entry_t *get_bounds_for ( logical_stream_t *p_stream, int64_t i_tframe,
644                                              int64_t *pi_pos_lower, int64_t *pi_pos_upper)
645 {
646     int64_t i_kframe;
647     int64_t i_frame;
648     int64_t i_gpos;
649
650     demux_index_entry_t *idx = p_stream->idx;
651
652     *pi_pos_lower = *pi_pos_upper = -1;
653
654     while ( idx != NULL )
655     {
656
657         if ( idx-> i_pagepos < 0 )
658         {
659             /* kframe was found to be invalid */
660             idx = idx->p_next;
661             continue;
662         }
663
664         if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
665         {
666             i_gpos = idx->i_value;
667             i_kframe = i_gpos >> p_stream->i_granule_shift;
668             i_frame = i_kframe + i_gpos - ( i_kframe << p_stream->i_granule_shift );
669         }
670         else return NULL;
671
672
673         if ( i_kframe > i_tframe )
674         {
675             *pi_pos_upper = idx->i_pagepos;
676             return NULL;
677         }
678
679         if ( i_frame < i_tframe )
680         {
681             *pi_pos_lower = idx->i_pagepos;
682             idx = idx->p_next;
683             continue;
684         }
685
686         return idx;
687     }
688
689     return NULL;
690 }
691
692
693 /* get highest frame in theora and opus streams */
694
695 static int64_t get_last_frame ( demux_t *p_demux, logical_stream_t *p_stream )
696 {
697     demux_sys_t *p_sys  = p_demux->p_sys;
698     int64_t i_frame;
699
700     i_frame = find_last_frame ( p_demux, p_stream );
701
702     /* We need to reset back to the start here, otherwise packets cannot be decoded.
703      * I think this is due to the fact that we seek to the end and then we must reset
704      * all logical streams, which causes remaining headers not to be read correctly.
705      * Seeking to 0 is the only value which seems to work, and it appears to have no
706      * adverse effects. */
707
708     seek_byte( p_demux, 0 );
709     /* Reset stream states */
710     p_sys->i_streams = 0;
711     p_stream->i_serial_no = ogg_page_serialno( &p_sys->current_page );
712     ogg_stream_init( &p_stream->os, p_stream->i_serial_no );
713     ogg_stream_pagein( &p_stream->os, &p_sys->current_page );
714
715     return i_frame;
716 }
717
718
719
720 /************************************************************************
721  * public functions
722  *************************************************************************/
723
724
725
726
727 /* return highest frame number for p_stream (which must be a theora, dirac or opus stream) */
728
729 int64_t oggseek_get_last_frame ( demux_t *p_demux, logical_stream_t *p_stream )
730 {
731     int64_t i_frame = -1;
732
733     if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA ||
734          p_stream->fmt.i_codec == VLC_CODEC_VORBIS ||
735          p_stream->fmt.i_codec == VLC_CODEC_OPUS )
736     {
737         i_frame = get_last_frame ( p_demux, p_stream );
738
739         if ( i_frame < 0 ) return -1;
740         return i_frame;
741     }
742
743     /* unhandled video format */
744     return -1;
745 }
746
747
748
749
750
751
752 /* seek to target frame in p_stream; actually we will probably end up just before it
753  *   (so we set skip)
754  *
755  * range for i_tframe is 0 -> p_sys->i_total_frames - 1
756  */
757
758 int oggseek_find_frame ( demux_t *p_demux, logical_stream_t *p_stream, int64_t i_tframe )
759 {
760
761     const demux_index_entry_t *fidx;
762
763     /* lower and upper bounds for search domain */
764     int64_t i_pos_lower;
765     int64_t i_pos_upper;
766
767     int64_t i_granulepos;
768     int64_t i_pagepos;
769
770     /* keyframe for i_tframe ( <= i_tframe ) */
771     int64_t i_kframe;
772
773     /* keyframe for i_kframe ( <= i_kframe ) */
774     int64_t i_xkframe;
775
776     /* next frame to be decoded ( >= i_xkframe ) */
777     int64_t i_cframe;
778
779     demux_sys_t *p_sys  = p_demux->p_sys;
780
781     i_tframe += p_stream->i_keyframe_offset;
782
783     i_cframe = i_tframe;
784     /* For Opus, seek back 80 ms before the target playback position. */
785     if ( p_stream->fmt.i_codec == VLC_CODEC_OPUS )
786     {
787         if ( i_tframe <= p_stream->i_pre_skip )
788             i_cframe = 0;
789         else if ( i_tframe < 80*48 )
790             i_cframe = 0;
791         else
792             i_cframe = i_tframe - 80*48;
793     }
794
795     /* reduce the search domain */
796     fidx = get_bounds_for( p_stream, i_cframe, &i_pos_lower, &i_pos_upper );
797
798     if ( fidx == NULL )
799     {
800         /* no exact match found; search the domain for highest keyframe <= i_cframe */
801
802         i_granulepos = ogg_seek ( p_demux, p_stream, i_cframe, i_pos_lower, i_pos_upper,
803                                   &i_pagepos, true );
804         if ( i_granulepos == -1 )
805         {
806             return VLC_EGENERIC;
807         }
808
809     }
810     else {
811         i_granulepos = fidx->i_value;
812     }
813
814     if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
815     {
816         i_kframe = i_granulepos >> p_stream->i_granule_shift;
817         if ( i_kframe < p_stream->i_keyframe_offset )
818         {
819             i_kframe = p_stream->i_keyframe_offset;
820         }
821
822         /* we found a keyframe, but we don't know where its packet starts, so search for a
823            frame just before it */
824
825         /* reduce search domain */
826         get_bounds_for( p_stream, i_kframe-1, &i_pos_lower, &i_pos_upper );
827
828         i_granulepos = ogg_seek( p_demux, p_stream, i_kframe-1, i_pos_lower, i_pos_upper,
829                                  &i_pagepos, false );
830
831         /* i_cframe will be the next frame we decode */
832         i_xkframe = i_granulepos >> p_stream->i_granule_shift;
833         i_cframe = i_xkframe + i_granulepos - ( i_xkframe << p_stream->i_granule_shift) + 1;
834
835         if ( p_sys->i_input_position == p_stream->i_data_start )
836         {
837             i_cframe = i_kframe = p_stream->i_keyframe_offset;
838         }
839         else
840         {
841             oggseek_theora_index_entry_add( p_stream, i_granulepos, p_sys->i_input_position );
842         }
843
844     }
845     else return VLC_EGENERIC;
846
847     p_stream->i_skip_frames = i_tframe - i_cframe;
848
849     ogg_stream_reset( &p_stream->os );
850
851     return VLC_SUCCESS;
852 }
853
854
855
856
857
858
859 /****************************************************************************
860  * oggseek_read_page: Read a full Ogg page from the physical bitstream.
861  ****************************************************************************
862  * Returns number of bytes read. This should always be > 0
863  * unless we are at the end of stream.
864  *
865  ****************************************************************************/
866 int64_t oggseek_read_page( demux_t *p_demux )
867 {
868     demux_sys_t *p_ogg = p_demux->p_sys  ;
869     uint8_t header[PAGE_HEADER_BYTES+255];
870     int i_nsegs;
871     int i_in_pos;
872     int i;
873     int64_t i_result;
874     int i_page_size;
875     char *buf;
876
877     demux_sys_t *p_sys  = p_demux->p_sys;
878
879     /* store position of this page */
880     i_in_pos = p_ogg->i_input_position = stream_Tell( p_demux->s );
881
882     if ( p_sys->b_page_waiting) {
883         msg_Warn( p_demux, "Ogg page already loaded" );
884         return 0;
885     }
886
887     if ( stream_Read ( p_demux->s, header, PAGE_HEADER_BYTES ) < PAGE_HEADER_BYTES )
888     {
889         stream_Seek( p_demux->s, i_in_pos );
890         msg_Dbg ( p_demux, "Reached clean EOF in ogg file" );
891         return 0;
892     }
893
894     i_nsegs = header[ PAGE_HEADER_BYTES - 1 ];
895
896     if ( stream_Read ( p_demux->s, header+PAGE_HEADER_BYTES, i_nsegs ) < i_nsegs )
897     {
898         stream_Seek( p_demux->s, i_in_pos );
899         msg_Warn ( p_demux, "Reached broken EOF in ogg file" );
900         return 0;
901     }
902
903     i_page_size = PAGE_HEADER_BYTES + i_nsegs;
904
905     for ( i = 0; i < i_nsegs; i++ )
906     {
907         i_page_size += header[ PAGE_HEADER_BYTES + i ];
908     }
909
910     ogg_sync_reset( &p_ogg->oy );
911
912     buf = ogg_sync_buffer( &p_ogg->oy, i_page_size );
913
914     memcpy( buf, header, PAGE_HEADER_BYTES + i_nsegs );
915
916     i_result = stream_Read ( p_demux->s, (uint8_t*)buf + PAGE_HEADER_BYTES + i_nsegs,
917                              i_page_size - PAGE_HEADER_BYTES - i_nsegs );
918
919     ogg_sync_wrote( &p_ogg->oy, i_result + PAGE_HEADER_BYTES + i_nsegs );
920
921
922
923
924     if ( ogg_sync_pageout( &p_ogg->oy, &p_ogg->current_page ) != 1 )
925     {
926         msg_Err( p_demux , "Got invalid packet, read %"PRId64" of %i: %s",i_result,i_page_size,
927                  buf );
928         return 0;
929     }
930
931     p_sys->b_page_waiting = false;
932
933     return i_result + PAGE_HEADER_BYTES + i_nsegs;
934 }
935
936