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