1 /*****************************************************************************
2 * oggseek.c : ogg seeking functions for ogg demuxer vlc
3 *****************************************************************************
4 * Copyright (C) 2008 - 2010 Gabriel Finch <salsaman@gmail.com>
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
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.
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.
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 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_demux.h>
45 #define THEORA_FTYPE_NOTDATA 0x80
46 #define THEORA_FTYPE_INTERFRAME 0x40
48 #define SEGMENT_NOT_FOUND -1
50 #define MAX_PAGE_SIZE 65307
51 typedef struct packetStartCoordinates
56 } packetStartCoordinates;
58 //#define OGG_SEEK_DEBUG 1
60 #define OggDebug(code) code
61 #define OggNoDebug(code)
63 #define OggDebug(code)
64 #define OggNoDebug(code) code
66 /************************************************************
68 *************************************************************/
70 /* free all entries in index list */
72 void oggseek_index_entries_free ( demux_index_entry_t *idx )
74 demux_index_entry_t *idx_next;
78 idx_next = idx->p_next;
85 /* internal function to create a new list member */
87 static demux_index_entry_t *index_entry_new( void )
89 demux_index_entry_t *idx = xmalloc( sizeof( demux_index_entry_t ) );
90 if ( !idx ) return NULL;
91 idx->p_next = idx->p_prev = NULL;
92 idx->i_pagepos_end = -1;
96 /* We insert into index, sorting by pagepos (as a page can match multiple
98 const demux_index_entry_t *OggSeek_IndexAdd ( logical_stream_t *p_stream,
102 demux_index_entry_t *idx;
103 demux_index_entry_t *oidx;
104 demux_index_entry_t *last_idx = NULL;
106 if ( p_stream == NULL ) return NULL;
108 oidx = idx = p_stream->idx;
110 if ( i_timestamp < 1 || i_pagepos < 1 ) return NULL;
114 demux_index_entry_t *ie = index_entry_new();
115 if ( !ie ) return NULL;
116 ie->i_value = i_timestamp;
117 ie->i_pagepos = i_pagepos;
122 while ( idx != NULL )
124 if ( idx->i_pagepos > i_pagepos ) break;
129 /* new entry; insert after last_idx */
130 idx = index_entry_new();
131 if ( !idx ) return NULL;
132 if ( last_idx != NULL )
134 idx->p_next = last_idx->p_next;
135 last_idx->p_next = idx;
136 idx->p_prev = last_idx;
144 if ( idx->p_next != NULL )
146 idx->p_next->p_prev = idx;
149 idx->i_value = i_timestamp;
150 idx->i_pagepos = i_pagepos;
155 static bool OggSeekIndexFind ( logical_stream_t *p_stream, int64_t i_timestamp,
156 int64_t *pi_pos_lower, int64_t *pi_pos_upper )
158 demux_index_entry_t *idx = p_stream->idx;
160 while ( idx != NULL )
162 if ( idx->i_value <= i_timestamp )
164 if ( !idx->p_next ) /* found on last index */
166 *pi_pos_lower = idx->i_pagepos;
169 if ( idx->p_next->i_value > i_timestamp )
171 *pi_pos_lower = idx->i_pagepos;
172 *pi_pos_upper = idx->p_next->i_pagepos;
182 /*********************************************************************
184 **********************************************************************/
186 /* seek in ogg file to offset i_pos and update the sync */
188 static void seek_byte( demux_t *p_demux, int64_t i_pos )
190 demux_sys_t *p_sys = p_demux->p_sys;
192 if ( ! stream_Seek( p_demux->s, i_pos ) )
194 ogg_sync_reset( &p_sys->oy );
196 p_sys->i_input_position = i_pos;
197 p_sys->b_page_waiting = false;
203 /* read bytes from the ogg file to try to find a page start */
205 static int64_t get_data( demux_t *p_demux, int64_t i_bytes_to_read )
207 demux_sys_t *p_sys = p_demux->p_sys;
212 if ( p_sys->i_total_length > 0 )
214 if ( p_sys->i_input_position + i_bytes_to_read > p_sys->i_total_length )
216 i_bytes_to_read = p_sys->i_total_length - p_sys->i_input_position;
217 if ( i_bytes_to_read <= 0 ) {
223 i_bytes_to_read = __MIN( i_bytes_to_read, INT_MAX );
225 seek_byte ( p_demux, p_sys->i_input_position );
227 buf = ogg_sync_buffer( &p_sys->oy, i_bytes_to_read );
229 i_result = stream_Read( p_demux->s, buf, i_bytes_to_read );
231 ogg_sync_wrote( &p_sys->oy, i_result );
236 void Oggseek_ProbeEnd( demux_t *p_demux )
238 /* Temporary state */
242 demux_sys_t *p_sys = p_demux->p_sys;
243 int64_t i_pos, i_startpos, i_result, i_granule, i_lowerbound;
244 int64_t i_length = 0;
245 int64_t i_backup_pos = stream_Tell( p_demux->s );
246 int64_t i_upperbound = stream_Size( p_demux->s );
247 unsigned int i_backoffset = OGGSEEK_BYTES_TO_READ;
248 assert( OGGSEEK_BYTES_TO_READ < UINT_MAX );
252 ogg_stream_init( &os, -1 );
253 ogg_sync_init( &oy );
255 /* Try to lookup last granule from each logical stream */
256 i_lowerbound = stream_Size( p_demux->s ) - p_sys->i_streams * MAX_PAGE_SIZE * 2;
257 i_lowerbound = __MAX( 0, i_lowerbound );
259 i_pos = i_startpos = __MAX( i_lowerbound, i_upperbound - i_backoffset );
261 if ( stream_Seek( p_demux->s, i_pos ) )
263 ogg_sync_clear( &oy );
264 ogg_stream_clear( &os );
268 while( i_pos >= i_lowerbound )
271 while( i_pos < i_upperbound )
274 i_result = ogg_sync_pageseek( &oy, &page );
276 buffer = ogg_sync_buffer( &oy, OGGSEEK_BYTES_TO_READ );
277 if ( buffer == NULL ) goto clean;
278 i_result = stream_Read( p_demux->s, (void*) buffer, OGGSEEK_BYTES_TO_READ );
279 if ( i_result < 1 ) goto clean;
281 ogg_sync_wrote( &oy, i_result );
283 while( ogg_sync_pageout( &oy, &page ) == 1 )
285 i_granule = ogg_page_granulepos( &page );
286 if ( i_granule == -1 ) continue;
288 for ( int i=0; i< p_sys->i_streams; i++ )
290 if ( p_sys->pp_stream[i]->i_serial_no != ogg_page_serialno( &page ) )
293 i_length = Oggseek_GranuleToAbsTimestamp( p_sys->pp_stream[i], i_granule, false );
294 p_sys->i_length = __MAX( p_sys->i_length, i_length / 1000000 );
298 if ( i_length > 0 ) break;
301 /* We found at least a page with valid granule */
302 if ( i_length > 0 ) break;
304 /* Otherwise increase read size, starting earlier */
305 if ( i_backoffset <= ( UINT_MAX >> 1 ) )
308 i_startpos = i_upperbound - i_backoffset;
312 i_startpos -= i_backoffset;
316 if ( stream_Seek( p_demux->s, i_pos ) )
321 stream_Seek( p_demux->s, i_backup_pos );
323 ogg_sync_clear( &oy );
324 ogg_stream_clear( &os );
328 static int64_t find_first_page_granule( demux_t *p_demux,
329 int64_t i_pos1, int64_t i_pos2,
330 logical_stream_t *p_stream,
331 int64_t *i_granulepos )
335 int64_t i_bytes_to_read = i_pos2 - i_pos1 + 1;
336 int64_t i_bytes_read;
337 int64_t i_pages_checked = 0;
338 int64_t i_packets_checked;
340 demux_sys_t *p_sys = p_demux->p_sys;
344 seek_byte( p_demux, i_pos1 );
346 if ( i_pos1 == p_stream->i_data_start )
347 return p_sys->i_input_position;
349 if ( i_bytes_to_read > OGGSEEK_BYTES_TO_READ ) i_bytes_to_read = OGGSEEK_BYTES_TO_READ;
354 if ( p_sys->i_input_position >= i_pos2 )
356 /* we reached the end and found no pages */
360 /* read next chunk */
361 if ( ! ( i_bytes_read = get_data( p_demux, i_bytes_to_read ) ) )
367 i_bytes_to_read = OGGSEEK_BYTES_TO_READ;
369 i_result = ogg_sync_pageseek( &p_sys->oy, &p_sys->current_page );
373 /* found a page, sync to page start */
374 p_sys->i_input_position -= i_result;
375 i_pos1 = p_sys->i_input_position;
379 if ( i_result > 0 || ( i_result == 0 && p_sys->oy.fill > 3 &&
380 ! strncmp( (char *)p_sys->oy.data, "OggS" , 4 ) ) )
382 i_pos1 = p_sys->i_input_position;
386 p_sys->i_input_position += i_bytes_read;
390 seek_byte( p_demux, p_sys->i_input_position );
391 ogg_stream_reset( &p_stream->os );
396 if ( p_sys->i_input_position >= i_pos2 )
398 /* reached the end of the search region and nothing was found */
399 return p_sys->i_input_position;
402 p_sys->b_page_waiting = false;
404 if ( ! ( i_result = oggseek_read_page( p_demux ) ) )
407 return p_sys->i_input_position;
410 if ( ogg_page_granulepos( &p_sys->current_page ) <= 0 )
412 p_sys->i_input_position += i_result;
413 i_bytes_to_read += OGGSEEK_BYTES_TO_READ;
418 if ( ogg_stream_pagein( &p_stream->os, &p_sys->current_page ) != 0 )
420 /* page is not for this stream or incomplete */
421 p_sys->i_input_position += i_result;
422 if ( ! i_pages_checked ) i_pos1 = p_sys->i_input_position;
427 i_packets_checked = 0;
429 while ( ogg_stream_packetout( &p_stream->os, &op ) > 0 )
434 if ( i_packets_checked )
436 *i_granulepos = ogg_page_granulepos( &p_sys->current_page );
440 /* -> start of next page */
441 p_sys->i_input_position += i_result;
445 /* Checks if current packet matches codec keyframe */
446 bool Ogg_IsKeyFrame( logical_stream_t *p_stream, ogg_packet *p_packet )
448 if ( p_stream->b_oggds )
450 return ( p_packet->bytes > 0 && p_packet->packet[0] & PACKET_IS_SYNCPOINT );
452 else switch ( p_stream->fmt.i_codec )
454 case VLC_CODEC_THEORA:
455 if ( p_packet->bytes <= 0 || p_packet->packet[0] & THEORA_FTYPE_NOTDATA )
458 return !( p_packet->packet[0] & THEORA_FTYPE_INTERFRAME );
460 return ( ( ( p_packet->granulepos >> 3 ) & 0x07FFFFFF ) == 0 );
461 case VLC_CODEC_DIRAC:
462 return ( p_packet->granulepos & 0xFF8000FF );
468 int64_t Ogg_GetKeyframeGranule( logical_stream_t *p_stream, int64_t i_granule )
470 if ( p_stream->b_oggds )
472 return -1; /* We have no way to know */
474 else if( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
476 return ( i_granule >> p_stream->i_granule_shift ) << p_stream->i_granule_shift;
478 else if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC )
480 return ( i_granule >> 31 ) << 31;
482 /* No change, that's keyframe or it can't be shifted out (oggds) */
486 static bool OggSeekToPacket( demux_t *p_demux, logical_stream_t *p_stream,
487 int64_t i_granulepos, packetStartCoordinates *p_lastpacketcoords,
491 demux_sys_t *p_sys = p_demux->p_sys;
492 if ( ogg_stream_pagein( &p_stream->os, &p_sys->current_page ) != 0 )
494 p_sys->b_page_waiting = true;
497 int64_t itarget_frame = Ogg_GetKeyframeGranule( p_stream, i_granulepos );
498 int64_t iframe = Ogg_GetKeyframeGranule( p_stream, ogg_page_granulepos( &p_sys->current_page ) );
500 if ( ! ogg_page_continued( &p_sys->current_page ) )
502 /* Start of frame, not continued page, but no packet. */
503 p_lastpacketcoords->i_pageno = ogg_page_pageno( &p_sys->current_page );
504 p_lastpacketcoords->i_pos = p_sys->i_input_position;
505 p_lastpacketcoords->i_skip = 0;
508 if ( b_exact && iframe > itarget_frame )
510 while( ogg_stream_packetout( &p_stream->os, &op ) > 0 ) {};
511 p_sys->b_page_waiting = false;
515 while( ogg_stream_packetpeek( &p_stream->os, &op ) > 0 )
517 if ( ( !b_exact || itarget_frame == iframe ) && Ogg_IsKeyFrame( p_stream, &op ) )
520 msg_Dbg(p_demux, "** KEYFRAME **" );
521 msg_Dbg(p_demux, "** KEYFRAME PACKET START pageno %"PRId64" OFFSET %"PRId64" skip %"PRId64" **", p_lastpacketcoords->i_pageno, p_lastpacketcoords->i_pos, p_lastpacketcoords->i_skip );
522 msg_Dbg(p_demux, "KEYFRAME PACKET IS at pageno %"PRId64" OFFSET %"PRId64" with skip %d packet (%d / %d) ",
523 ogg_page_pageno( &p_sys->current_page ), p_sys->i_input_position, i, i+1, ogg_page_packets( &p_sys->current_page ) );
524 DemuxDebug( p_sys->b_seeked = true; )
527 if ( i != 0 ) /* Not continued packet */
529 /* We need to handle the case when the packet spans onto N
530 previous page(s). packetout() will be valid only when
531 all segments are assembled.
532 Keyframe flag is only available after assembling last part
533 (when packetout() becomes valid). We have no way to guess
534 keyframe at earlier time.
536 p_lastpacketcoords->i_pageno = ogg_page_pageno( &p_sys->current_page );
537 p_lastpacketcoords->i_pos = p_sys->i_input_position;
538 p_lastpacketcoords->i_skip = i;
543 p_lastpacketcoords->i_pageno = ogg_page_pageno( &p_sys->current_page );
544 p_lastpacketcoords->i_pos = p_sys->i_input_position;
545 p_lastpacketcoords->i_skip = i + 1;
547 /* remove that packet and go sync to next */
548 ogg_stream_packetout( &p_stream->os, &op );
554 static int64_t OggForwardSeekToFrame( demux_t *p_demux, int64_t i_pos1, int64_t i_pos2,
555 logical_stream_t *p_stream, int64_t i_granulepos, bool b_fastseek )
558 int64_t i_bytes_to_read;
559 int64_t i_bytes_read;
561 demux_sys_t *p_sys = p_demux->p_sys;
563 i_bytes_to_read = i_pos2 - i_pos1 + 1;
564 seek_byte( p_demux, i_pos1 );
565 if ( i_bytes_to_read > OGGSEEK_BYTES_TO_READ ) i_bytes_to_read = OGGSEEK_BYTES_TO_READ;
568 msg_Dbg( p_demux, "Probing Fwd %"PRId64" %"PRId64" for granule %"PRId64,
569 i_pos1, i_pos2, i_granulepos );
575 if ( p_sys->i_input_position >= i_pos2 )
576 return SEGMENT_NOT_FOUND;
578 /* read next chunk */
579 if ( ! ( i_bytes_read = get_data( p_demux, i_bytes_to_read ) ) )
580 return SEGMENT_NOT_FOUND;
582 i_bytes_to_read = OGGSEEK_BYTES_TO_READ;
584 i_result = ogg_sync_pageseek( &p_sys->oy, &p_sys->current_page );
588 /* found a page, sync to page start */
589 p_sys->i_input_position -= i_result;
590 i_pos1 = p_sys->i_input_position;
594 if ( i_result > 0 || ( i_result == 0 && p_sys->oy.fill > 3 &&
595 ! strncmp( (char *)p_sys->oy.data, "OggS" , 4 ) ) )
597 i_pos1 = p_sys->i_input_position;
601 p_sys->i_input_position += i_bytes_read;
604 seek_byte( p_demux, p_sys->i_input_position );
605 ogg_stream_reset( &p_stream->os );
608 while( ogg_stream_packetout( &p_stream->os, &op ) > 0 ) {};
610 packetStartCoordinates lastpacket = { -1, -1, -1 };
615 if ( p_sys->i_input_position >= i_pos2 )
617 /* reached the end of the search region and nothing was found */
621 p_sys->b_page_waiting = false;
623 if ( ! ( i_result = oggseek_read_page( p_demux ) ) )
630 if ( p_stream->os.serialno != ogg_page_serialno( &p_sys->current_page ) )
632 /* page is not for this stream */
633 p_sys->i_input_position += i_result;
637 if ( OggSeekToPacket( p_demux, p_stream, i_granulepos, &lastpacket, b_fastseek ) )
639 p_sys->i_input_position = lastpacket.i_pos;
640 p_stream->i_skip_frames = 0;
641 return p_sys->i_input_position;
644 /* -> start of next page */
645 p_sys->i_input_position += i_result;
648 return SEGMENT_NOT_FOUND;
651 static int64_t OggBackwardSeekToFrame( demux_t *p_demux, int64_t i_pos1, int64_t i_pos2,
652 logical_stream_t *p_stream, int64_t i_granulepos )
655 int64_t i_offset = __MAX( 1 + ( (i_pos2 - i_pos1) >> 1 ), OGGSEEK_BYTES_TO_READ );
660 msg_Dbg( p_demux, "Probing Back %"PRId64" %"PRId64" for granule %"PRId64,
661 i_pos1, i_pos2, i_granulepos );
664 i_result = OggForwardSeekToFrame( p_demux, i_pos1, i_pos2, p_stream,
665 i_granulepos, true );
667 if ( i_result == SEGMENT_NOT_FOUND && i_pos1 > p_stream->i_data_start )
669 i_pos1 = __MAX( p_stream->i_data_start, i_pos1 - i_offset );
676 /* Dont use b_presentation with frames granules ! */
677 int64_t Oggseek_GranuleToAbsTimestamp( logical_stream_t *p_stream,
678 int64_t i_granule, bool b_presentation )
680 int64_t i_timestamp = -1;
684 if ( p_stream->b_oggds )
686 if ( b_presentation ) i_granule--;
687 i_timestamp = i_granule * CLOCK_FREQ / p_stream->f_rate;
689 else switch( p_stream->fmt.i_codec )
691 case VLC_CODEC_THEORA:
694 ogg_int64_t iframe = i_granule >> p_stream->i_granule_shift;
695 ogg_int64_t pframe = i_granule - ( iframe << p_stream->i_granule_shift );
696 /* See Theora A.2.3 */
697 if ( b_presentation ) pframe -= p_stream->i_keyframe_offset;
698 i_timestamp = ( iframe + pframe ) * CLOCK_FREQ / p_stream->f_rate;
703 ogg_int64_t frame = i_granule >> p_stream->i_granule_shift;
704 if ( b_presentation ) frame--;
705 i_timestamp = frame * CLOCK_FREQ / p_stream->f_rate;
708 case VLC_CODEC_DIRAC:
710 ogg_int64_t i_dts = i_granule >> 31;
711 ogg_int64_t delay = (i_granule >> 9) & 0x1fff;
712 /* NB, OggDirac granulepos values are in units of 2*picturerate */
713 double f_rate = p_stream->f_rate;
714 if ( !p_stream->special.dirac.b_interlaced ) f_rate *= 2;
715 if ( b_presentation ) i_dts += delay;
716 i_timestamp = i_dts * CLOCK_FREQ / f_rate;
721 if ( b_presentation ) return VLC_TS_INVALID;
722 i_timestamp = ( i_granule - p_stream->i_pre_skip ) * CLOCK_FREQ / 48000;
725 case VLC_CODEC_VORBIS:
728 if ( b_presentation ) return VLC_TS_INVALID;
729 i_timestamp = i_granule * CLOCK_FREQ / p_stream->f_rate;
732 case VLC_CODEC_SPEEX:
734 if ( b_presentation )
735 i_granule -= p_stream->special.speex.i_framesize *
736 p_stream->special.speex.i_framesperpacket;
737 i_timestamp = i_granule * CLOCK_FREQ / p_stream->f_rate;
746 static int64_t OggBisectSearchByTime( demux_t *p_demux, logical_stream_t *p_stream,
747 int64_t i_targettime, int64_t i_pos_lower, int64_t i_pos_upper)
758 } bestlower = { p_stream->i_data_start, -1, -1 },
759 current = { -1, -1, -1 },
760 lowestupper = { -1, -1, -1 };
762 demux_sys_t *p_sys = p_demux->p_sys;
764 i_pos_lower = __MAX( i_pos_lower, p_stream->i_data_start );
765 i_pos_upper = __MIN( i_pos_upper, p_sys->i_total_length );
766 if ( i_pos_upper < 0 ) i_pos_upper = p_sys->i_total_length;
768 i_start_pos = i_pos_lower;
769 i_end_pos = i_pos_upper;
771 i_segsize = ( i_end_pos - i_start_pos + 1 ) >> 1;
772 i_start_pos += i_segsize;
774 OggDebug( msg_Dbg(p_demux, "Bisecting for time=%"PRId64" between %"PRId64" and %"PRId64,
775 i_targettime, i_pos_lower, i_pos_upper ) );
779 /* see if the frame lies in current segment */
780 i_start_pos = __MAX( i_start_pos, i_pos_lower );
781 i_end_pos = __MIN( i_end_pos, i_pos_upper );
783 if ( i_start_pos >= i_end_pos )
785 if ( i_start_pos == i_pos_lower)
793 current.i_pos = find_first_page_granule( p_demux,
794 i_start_pos, i_end_pos,
796 ¤t.i_granule );
798 current.i_timestamp = Oggseek_GranuleToAbsTimestamp( p_stream,
799 current.i_granule, false );
801 if ( current.i_timestamp == -1 && current.i_granule > 0 )
803 msg_Err( p_demux, "Unmatched granule. New codec ?" );
806 else if ( current.i_timestamp < -1 ) /* due to preskip with some codecs */
808 current.i_timestamp = 0;
811 if ( current.i_pos != -1 && current.i_granule != -1 )
815 if ( current.i_timestamp <= i_targettime )
817 /* set our lower bound */
818 if ( current.i_timestamp > bestlower.i_timestamp )
820 i_start_pos = current.i_pos;
822 else if ( current.i_timestamp > i_targettime )
824 if ( lowestupper.i_timestamp == -1 || current.i_timestamp < lowestupper.i_timestamp )
825 lowestupper = current;
826 /* check lower half of segment */
827 i_start_pos -= i_segsize;
828 i_end_pos -= i_segsize;
833 /* no keyframe found, check lower segment */
834 i_end_pos -= i_segsize;
835 i_start_pos -= i_segsize;
838 OggDebug( msg_Dbg(p_demux, "Bisect restart i_segsize=%"PRId64" between %"PRId64
839 " and %"PRId64 " bl %"PRId64" lu %"PRId64,
840 i_segsize, i_start_pos, i_end_pos, bestlower.i_granule, lowestupper.i_granule ) );
842 i_segsize = ( i_end_pos - i_start_pos + 1 ) >> 1;
843 i_start_pos += i_segsize;
845 } while ( i_segsize > 64 );
847 if ( bestlower.i_granule == -1 )
849 if ( lowestupper.i_granule == -1 )
852 bestlower = lowestupper;
855 if ( p_stream->b_oggds )
857 int64_t a = OggBackwardSeekToFrame( p_demux,
858 __MAX ( bestlower.i_pos - OGGSEEK_BYTES_TO_READ, p_stream->i_data_start ),
860 p_stream, bestlower.i_granule /* unused */ );
863 /* If not each packet is usable as keyframe, query the codec for keyframe */
864 else if ( Ogg_GetKeyframeGranule( p_stream, bestlower.i_granule ) != bestlower.i_granule )
866 int64_t i_keyframegranule = Ogg_GetKeyframeGranule( p_stream, bestlower.i_granule );
868 OggDebug( msg_Dbg( p_demux, "Need to reseek to keyframe (%"PRId64") granule (%"PRId64"!=%"PRId64") to t=%"PRId64,
869 i_keyframegranule >> p_stream->i_granule_shift,
872 Oggseek_GranuleToAbsTimestamp( p_stream, i_keyframegranule, false ) ) );
874 OggDebug( msg_Dbg( p_demux, "Seeking back to %"PRId64, __MAX ( bestlower.i_pos - OGGSEEK_BYTES_TO_READ, p_stream->i_data_start ) ) );
876 int64_t a = OggBackwardSeekToFrame( p_demux,
877 __MAX ( bestlower.i_pos - OGGSEEK_BYTES_TO_READ, p_stream->i_data_start ),
878 stream_Size( p_demux->s ), p_stream, i_keyframegranule );
882 return bestlower.i_pos;
886 /************************************************************************
888 *************************************************************************/
890 int Oggseek_BlindSeektoAbsoluteTime( demux_t *p_demux, logical_stream_t *p_stream,
891 int64_t i_time, bool b_fastseek )
893 demux_sys_t *p_sys = p_demux->p_sys;
894 int64_t i_lowerpos = -1;
895 int64_t i_upperpos = -1;
896 bool b_found = false;
898 /* Search in skeleton */
899 Ogg_GetBoundsUsingSkeletonIndex( p_stream, i_time, &i_lowerpos, &i_upperpos );
900 if ( i_lowerpos != -1 ) b_found = true;
902 /* And also search in our own index */
903 if ( !b_found && OggSeekIndexFind( p_stream, i_time, &i_lowerpos, &i_upperpos ) )
908 /* Or try to be smart with audio fixed bitrate streams */
909 if ( !b_found && p_stream->fmt.i_cat == AUDIO_ES && p_sys->i_streams == 1
910 && p_sys->i_bitrate && Ogg_GetKeyframeGranule( p_stream, 0xFF00FF00 ) == 0xFF00FF00 )
912 /* But only if there's no keyframe/preload requirements */
913 /* FIXME: add function to get preload time by codec, ex: opus */
914 i_lowerpos = i_time * p_sys->i_bitrate / INT64_C(8000000);
919 if ( !b_found && b_fastseek )
921 i_lowerpos = OggBisectSearchByTime( p_demux, p_stream, i_time,
922 p_stream->i_data_start, p_sys->i_total_length );
923 b_found = ( i_lowerpos != -1 );
926 if ( !b_found ) return -1;
928 if ( i_lowerpos < p_stream->i_data_start || i_upperpos > p_sys->i_total_length )
931 /* And really do seek */
932 p_sys->i_input_position = i_lowerpos;
933 seek_byte( p_demux, p_sys->i_input_position );
934 ogg_stream_reset( &p_stream->os );
939 int Oggseek_BlindSeektoPosition( demux_t *p_demux, logical_stream_t *p_stream,
940 double f, bool b_canfastseek )
942 OggDebug( msg_Dbg( p_demux, "=================== Seeking To Blind Pos" ) );
943 int64_t i_size = stream_Size( p_demux->s );
947 i_size = find_first_page_granule( p_demux,
952 OggDebug( msg_Dbg( p_demux, "Seek start pos is %"PRId64" granule %"PRId64, i_size, i_granule ) );
954 i_granule = Ogg_GetKeyframeGranule( p_stream, i_granule );
958 /* Peek back until we meet a keyframe to start our decoding up to our
960 i_pagepos = OggBackwardSeekToFrame( p_demux,
961 __MAX ( i_size - MAX_PAGE_SIZE, p_stream->i_data_start ),
962 __MIN ( i_size + MAX_PAGE_SIZE, p_demux->p_sys->i_total_length ),
963 p_stream, i_granule );
967 /* Otherwise, we just sync to the next keyframe we meet */
968 i_pagepos = OggForwardSeekToFrame( p_demux,
969 __MAX ( i_size - MAX_PAGE_SIZE, p_stream->i_data_start ),
970 stream_Size( p_demux->s ),
971 p_stream, i_granule, false );
974 OggDebug( msg_Dbg( p_demux, "=================== Seeked To %"PRId64" granule %"PRId64, i_pagepos, i_granule ) );
978 int Oggseek_SeektoAbsolutetime( demux_t *p_demux, logical_stream_t *p_stream,
981 demux_sys_t *p_sys = p_demux->p_sys;
983 OggDebug( msg_Dbg( p_demux, "=================== Seeking To Absolute Time %"PRId64, i_time ) );
984 int64_t i_offset_lower = -1;
985 int64_t i_offset_upper = -1;
987 if ( Ogg_GetBoundsUsingSkeletonIndex( p_stream, i_time, &i_offset_lower, &i_offset_upper ) )
990 OggDebug( msg_Dbg( p_demux, "Found keyframe at %"PRId64" using skeleton index", i_offset_lower ) );
991 if ( i_offset_lower == -1 ) i_offset_lower = p_stream->i_data_start;
992 p_sys->i_input_position = i_offset_lower;
993 seek_byte( p_demux, p_sys->i_input_position );
994 ogg_stream_reset( &p_stream->os );
995 return i_offset_lower;
997 OggDebug( msg_Dbg( p_demux, "Search bounds set to %"PRId64" %"PRId64" using skeleton index", i_offset_lower, i_offset_upper ) );
1000 OggSeekIndexFind( p_stream, i_time, &i_offset_lower, &i_offset_upper )
1003 i_offset_lower = __MAX( i_offset_lower, p_stream->i_data_start );
1004 i_offset_upper = __MIN( i_offset_upper, p_sys->i_total_length );
1006 int64_t i_pagepos = OggBisectSearchByTime( p_demux, p_stream, i_time,
1007 i_offset_lower, i_offset_upper);
1008 if ( i_pagepos >= 0 )
1010 /* be sure to clear any state or read+pagein() will fail on same # */
1011 ogg_stream_reset( &p_stream->os );
1012 seek_byte( p_demux, p_sys->i_input_position );
1014 /* Insert keyframe position into index */
1016 if ( i_pagepos >= p_stream->i_data_start )
1017 OggSeek_IndexAdd( p_stream, i_time, i_pagepos )
1020 OggDebug( msg_Dbg( p_demux, "=================== Seeked To %"PRId64" time %"PRId64, i_pagepos, i_time ) );
1024 /****************************************************************************
1025 * oggseek_read_page: Read a full Ogg page from the physical bitstream.
1026 ****************************************************************************
1027 * Returns number of bytes read. This should always be > 0
1028 * unless we are at the end of stream.
1030 ****************************************************************************/
1032 int64_t oggseek_read_page( demux_t *p_demux )
1034 demux_sys_t *p_ogg = p_demux->p_sys ;
1035 uint8_t header[PAGE_HEADER_BYTES+255];
1043 demux_sys_t *p_sys = p_demux->p_sys;
1045 /* store position of this page */
1046 i_in_pos = p_ogg->i_input_position = stream_Tell( p_demux->s );
1048 if ( p_sys->b_page_waiting) {
1049 msg_Warn( p_demux, "Ogg page already loaded" );
1053 if ( stream_Read ( p_demux->s, header, PAGE_HEADER_BYTES ) < PAGE_HEADER_BYTES )
1055 stream_Seek( p_demux->s, i_in_pos );
1056 msg_Dbg ( p_demux, "Reached clean EOF in ogg file" );
1060 i_nsegs = header[ PAGE_HEADER_BYTES - 1 ];
1062 if ( stream_Read ( p_demux->s, header+PAGE_HEADER_BYTES, i_nsegs ) < i_nsegs )
1064 stream_Seek( p_demux->s, i_in_pos );
1065 msg_Warn ( p_demux, "Reached broken EOF in ogg file" );
1069 i_page_size = PAGE_HEADER_BYTES + i_nsegs;
1071 for ( i = 0; i < i_nsegs; i++ )
1073 i_page_size += header[ PAGE_HEADER_BYTES + i ];
1076 ogg_sync_reset( &p_ogg->oy );
1078 buf = ogg_sync_buffer( &p_ogg->oy, i_page_size );
1080 memcpy( buf, header, PAGE_HEADER_BYTES + i_nsegs );
1082 i_result = stream_Read ( p_demux->s, (uint8_t*)buf + PAGE_HEADER_BYTES + i_nsegs,
1083 i_page_size - PAGE_HEADER_BYTES - i_nsegs );
1085 ogg_sync_wrote( &p_ogg->oy, i_result + PAGE_HEADER_BYTES + i_nsegs );
1090 if ( ogg_sync_pageout( &p_ogg->oy, &p_ogg->current_page ) != 1 )
1092 msg_Err( p_demux , "Got invalid packet, read %"PRId64" of %i: %s %"PRId64,
1093 i_result, i_page_size, buf, i_in_pos );
1097 return i_result + PAGE_HEADER_BYTES + i_nsegs;