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