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>
42 /************************************************************
44 *************************************************************/
46 /* free all entries in index list */
48 void oggseek_index_entries_free ( demux_index_entry_t *idx )
50 demux_index_entry_t *idx_next;
54 idx_next = idx->p_next;
61 /* internal function to create a new list member */
63 static demux_index_entry_t *index_entry_new( void )
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;
73 /* add a theora entry to our list; format is highest granulepos -> page offset of
76 const demux_index_entry_t *oggseek_theora_index_entry_add ( logical_stream_t *p_stream,
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;
90 if ( p_stream == NULL ) return NULL;
92 oidx = idx = p_stream->idx;
94 i_tkframe = i_granule >> p_stream->i_granule_shift;
95 i_tframe = i_tkframe + i_granule - ( i_tkframe << p_stream->i_granule_shift );
97 if ( i_tkframe < 1 ) return NULL;
101 demux_index_entry_t *ie = index_entry_new();
102 ie->i_value = i_granule;
103 ie->i_pagepos = i_pagepos;
109 while ( idx != NULL )
111 i_gpos = idx->i_value;
113 i_kframe = i_gpos >> p_stream->i_granule_shift;
114 if ( i_kframe > i_tframe ) break;
116 if ( i_kframe == i_tkframe )
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 )
122 idx->i_value = i_granule;
123 idx->i_pagepos = i_pagepos;
134 /* new entry; insert after last_idx */
136 idx = index_entry_new();
138 if ( last_idx != NULL )
140 idx->p_next = last_idx->p_next;
141 last_idx->p_next = idx;
142 idx->p_prev = last_idx;
150 if ( idx->p_next != NULL )
152 idx->p_next->p_prev = idx;
155 idx->i_value = i_granule;
156 idx->i_pagepos = i_pagepos;
164 /*********************************************************************
166 **********************************************************************/
168 /* seek in ogg file to offset i_pos and update the sync */
170 static void seek_byte( demux_t *p_demux, int64_t i_pos )
172 demux_sys_t *p_sys = p_demux->p_sys;
174 if ( ! stream_Seek( p_demux->s, i_pos ) )
176 ogg_sync_reset( &p_sys->oy );
178 p_sys->i_input_position = i_pos;
179 p_sys->b_page_waiting = false;
185 /* read bytes from the ogg file to try to find a page start */
187 static int64_t get_data( demux_t *p_demux, int64_t i_bytes_to_read )
189 demux_sys_t *p_sys = p_demux->p_sys;
194 if ( p_sys->i_total_length > 0 )
196 if ( p_sys->i_input_position + i_bytes_to_read > p_sys->i_total_length )
198 i_bytes_to_read = p_sys->i_total_length - p_sys->i_input_position;
199 if ( i_bytes_to_read <= 0 ) {
205 seek_byte ( p_demux, p_sys->i_input_position );
207 buf = ogg_sync_buffer( &p_sys->oy, i_bytes_to_read );
209 i_result = stream_Read( p_demux->s, buf, i_bytes_to_read );
211 p_sys->b_page_waiting = false;
213 ogg_sync_wrote( &p_sys->oy, i_result );
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 */
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 )
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;
235 demux_sys_t *p_sys = p_demux->p_sys;
239 seek_byte( p_demux, i_pos1 );
241 if ( i_pos1 == p_stream->i_data_start )
243 /* set a dummy granulepos at data_start */
244 *pi_kframe = p_stream->i_keyframe_offset;
245 *pi_frame = p_stream->i_keyframe_offset;
247 p_sys->b_page_waiting = true;
248 return p_sys->i_input_position;
251 if ( i_bytes_to_read > OGGSEEK_BYTES_TO_READ ) i_bytes_to_read = OGGSEEK_BYTES_TO_READ;
256 if ( p_sys->i_input_position >= i_pos2 )
258 /* we reached the end and found no pages */
263 /* read next chunk */
264 if ( ! ( i_bytes_read = get_data( p_demux, i_bytes_to_read ) ) )
271 i_bytes_to_read = OGGSEEK_BYTES_TO_READ;
273 i_result = ogg_sync_pageseek( &p_sys->oy, &p_sys->current_page );
277 /* found a page, sync to page start */
278 p_sys->i_input_position -= i_result;
279 i_pos1 = p_sys->i_input_position;
283 if ( i_result > 0 || ( i_result == 0 && p_sys->oy.fill > 3 &&
284 ! strncmp( (char *)p_sys->oy.data, "OggS" , 4 ) ) )
286 i_pos1 = p_sys->i_input_position;
290 p_sys->i_input_position += i_bytes_read;
294 seek_byte( p_demux, p_sys->i_input_position );
295 ogg_stream_reset( &p_stream->os );
300 if ( p_sys->i_input_position >= i_pos2 )
302 /* reached the end of the search region and nothing was found */
304 return p_sys->i_input_position;
307 p_sys->b_page_waiting = false;
309 if ( ! ( i_result = oggseek_read_page( p_demux ) ) )
313 return p_sys->i_input_position;
317 if ( p_stream->os.serialno != ogg_page_serialno( &p_sys->current_page ) )
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;
326 ogg_stream_pagein( &p_stream->os, &p_sys->current_page );
329 i_packets_checked = 0;
331 if ( ogg_stream_packetout( &p_stream->os, &op ) > 0 )
336 if ( i_packets_checked )
338 i_granulepos = ogg_page_granulepos( &p_sys->current_page );
340 oggseek_theora_index_entry_add( p_stream, i_granulepos, i_pos1 );
343 i_granulepos >> p_stream->i_granule_shift;
345 *pi_frame = *pi_kframe +
346 i_granulepos - ( *pi_kframe << p_stream->i_granule_shift );
348 p_sys->b_page_waiting = true;
353 /* -> start of next page */
354 p_sys->i_input_position += i_result;
365 /* Find the last frame for p_stream,
366 -1 is returned on failure */
368 static int64_t find_last_frame (demux_t *p_demux, logical_stream_t *p_stream)
373 int64_t i_frame = -1;
374 int64_t i_last_frame = -1;
375 int64_t i_kframe = 0;
379 demux_sys_t *p_sys = p_demux->p_sys;
381 i_pos1 = p_stream->i_data_start;
382 i_pos2 = p_sys->i_total_length;
384 i_start_pos = i_pos2 - OGGSEEK_BYTES_TO_READ;
389 if ( i_start_pos < i_pos1 ) i_start_pos = i_pos1;
391 i_page_pos = find_first_page( p_demux, i_start_pos, i_pos2, p_stream, &i_kframe, &i_frame );
395 /* no pages found in range */
396 if ( i_last_frame >= 0 )
398 /* No more pages in range -> return last one */
401 if ( i_start_pos <= i_pos1 )
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;
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;
427 /* convert a theora frame to a granulepos */
429 static inline int64_t frame_to_gpos( logical_stream_t *p_stream, int64_t i_kframe,
432 if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
434 return ( i_kframe << p_stream->i_granule_shift ) + ( i_frame - i_kframe );
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. */
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,
453 * We do two passes here, first with b_exact set, then with b_exact unset.
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).
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.
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.
465 * The function returns the granulepos which is found,
466 * sets the page offset in pi_pagepos. -1 is returned on error.
470 * we find the highest sync frame <= target frame, and return the sync_frame number
471 * b_exact should be set to true
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
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
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
486 * therefore, subsequent searches become more rapid.
497 int64_t i_best_kframe = -1;
498 int64_t i_best_frame = -1;
499 int64_t i_best_pagepos = -1;
501 demux_sys_t *p_sys = p_demux->p_sys;
503 if ( i_tframe < p_stream->i_keyframe_offset )
505 *pi_pagepos = p_stream->i_data_start;
508 seek_byte( p_demux, p_stream->i_data_start );
509 return frame_to_gpos( p_stream, p_stream->i_keyframe_offset, 1 );
511 return frame_to_gpos( p_stream, p_stream->i_keyframe_offset, 0 );
514 if ( i_pos_lower < p_stream->i_data_start )
516 i_pos_lower = p_stream->i_data_start;
519 if ( i_pos_upper < 0 )
521 i_pos_upper = p_sys->i_total_length;
524 if ( i_pos_upper > p_sys->i_total_length )
526 i_pos_upper = p_sys->i_total_length;
529 i_start_pos = i_pos_lower;
530 i_end_pos = i_pos_upper;
532 i_segsize = ( i_end_pos - i_start_pos + 1 ) >> 1;
536 /* see if the frame lies in current segment */
537 if ( i_start_pos < i_pos_lower )
539 i_start_pos = i_pos_lower;
541 if ( i_end_pos > i_pos_upper )
543 i_end_pos = i_pos_upper;
546 if ( i_start_pos >= i_end_pos )
548 if ( i_start_pos == i_pos_lower)
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 );
557 if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
559 i_pagepos = find_first_page( p_demux, i_start_pos, i_end_pos, p_stream,
560 &i_kframe, &i_frame );
564 if ( i_pagepos != -1 && i_kframe != -1 )
568 if ( b_exact && i_frame >= i_tframe && i_kframe <= i_tframe )
571 *pi_pagepos = i_start_pos;
572 return frame_to_gpos( p_stream, i_kframe, i_frame );
575 if ( ( i_kframe < i_tframe || ( b_exact && i_kframe == i_tframe ) )
576 && i_kframe > i_best_kframe )
578 i_best_kframe = i_kframe;
579 i_best_frame = i_frame;
580 i_best_pagepos = i_pagepos;
583 if ( i_frame >= i_tframe )
585 /* check lower half of segment */
586 i_start_pos -= i_segsize;
587 i_end_pos -= i_segsize;
590 else i_start_pos = i_pagepos;
595 /* no keyframe found, check lower segment */
596 i_end_pos -= i_segsize;
597 i_start_pos -= i_segsize;
600 i_segsize = ( i_end_pos - i_start_pos + 1 ) >> 1;
601 i_start_pos += i_segsize;
603 } while ( i_segsize > 64 );
605 if ( i_best_kframe >- 1 )
609 seek_byte( p_demux, i_best_pagepos );
611 *pi_pagepos = i_best_pagepos;
612 return frame_to_gpos( p_stream, i_best_kframe, i_best_frame );
623 /* find upper and lower pagepos for i_tframe; if we find an exact match, we return it */
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)
632 demux_index_entry_t *idx = p_stream->idx;
634 *pi_pos_lower = *pi_pos_upper = -1;
636 while ( idx != NULL )
639 if ( idx-> i_pagepos < 0 )
641 /* kframe was found to be invalid */
646 if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
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 );
655 if ( i_kframe > i_tframe )
657 *pi_pos_upper = idx->i_pagepos;
661 if ( i_frame < i_tframe )
663 *pi_pos_lower = idx->i_pagepos;
675 /* get highest frame in theora and opus streams */
677 static int64_t get_last_frame ( demux_t *p_demux, logical_stream_t *p_stream )
679 demux_sys_t *p_sys = p_demux->p_sys;
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 );
690 i_frame = find_last_frame ( p_demux, p_stream );
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. */
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) );
709 /************************************************************************
711 *************************************************************************/
716 /* return highest frame number for p_stream (which must be a theora, dirac or opus stream) */
718 int64_t oggseek_get_last_frame ( demux_t *p_demux, logical_stream_t *p_stream )
720 int64_t i_frame = -1;
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 )
726 i_frame = get_last_frame ( p_demux, p_stream );
728 if ( i_frame < 0 ) return -1;
732 /* unhandled video format */
741 /* seek to target frame in p_stream; actually we will probably end up just before it
744 * range for i_tframe is 0 -> p_sys->i_total_frames - 1
747 int oggseek_find_frame ( demux_t *p_demux, logical_stream_t *p_stream, int64_t i_tframe )
750 const demux_index_entry_t *fidx;
752 /* lower and upper bounds for search domain */
756 int64_t i_granulepos;
759 /* keyframe for i_tframe ( <= i_tframe ) */
762 /* keyframe for i_kframe ( <= i_kframe ) */
765 /* next frame to be decoded ( >= i_xkframe ) */
768 demux_sys_t *p_sys = p_demux->p_sys;
770 i_tframe += p_stream->i_keyframe_offset;
773 /* For Opus, seek back 80 ms before the target playback position. */
774 if ( p_stream->fmt.i_codec == VLC_CODEC_OPUS )
776 if ( i_tframe <= p_stream->i_pre_skip )
778 else if ( i_tframe < 80*48 )
781 i_cframe = i_tframe - 80*48;
784 /* reduce the search domain */
785 fidx = get_bounds_for( p_stream, i_cframe, &i_pos_lower, &i_pos_upper );
789 /* no exact match found; search the domain for highest keyframe <= i_cframe */
791 i_granulepos = ogg_seek ( p_demux, p_stream, i_cframe, i_pos_lower, i_pos_upper,
793 if ( i_granulepos == -1 )
800 i_granulepos = fidx->i_value;
803 if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
805 i_kframe = i_granulepos >> p_stream->i_granule_shift;
806 if ( i_kframe < p_stream->i_keyframe_offset )
808 i_kframe = p_stream->i_keyframe_offset;
811 /* we found a keyframe, but we don't know where its packet starts, so search for a
812 frame just before it */
814 /* reduce search domain */
815 get_bounds_for( p_stream, i_kframe-1, &i_pos_lower, &i_pos_upper );
817 i_granulepos = ogg_seek( p_demux, p_stream, i_kframe-1, i_pos_lower, i_pos_upper,
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;
824 if ( p_sys->i_input_position == p_stream->i_data_start )
826 i_cframe = i_kframe = p_stream->i_keyframe_offset;
830 oggseek_theora_index_entry_add( p_stream, i_granulepos, p_sys->i_input_position );
834 else return VLC_EGENERIC;
836 p_stream->i_skip_frames = i_tframe - i_cframe;
838 ogg_stream_reset( &p_stream->os );
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.
854 ****************************************************************************/
855 int64_t oggseek_read_page( demux_t *p_demux )
857 demux_sys_t *p_ogg = p_demux->p_sys ;
858 uint8_t header[PAGE_HEADER_BYTES+255];
866 demux_sys_t *p_sys = p_demux->p_sys;
868 /* store position of this page */
869 i_in_pos = p_ogg->i_input_position = stream_Tell( p_demux->s );
871 if ( p_sys->b_page_waiting) {
872 msg_Warn( p_demux, "Ogg page already loaded" );
876 if ( stream_Read ( p_demux->s, header, PAGE_HEADER_BYTES ) < PAGE_HEADER_BYTES )
878 stream_Seek( p_demux->s, i_in_pos );
879 msg_Dbg ( p_demux, "Reached clean EOF in ogg file" );
883 i_nsegs = header[ PAGE_HEADER_BYTES - 1 ];
885 if ( stream_Read ( p_demux->s, header+PAGE_HEADER_BYTES, i_nsegs ) < i_nsegs )
887 stream_Seek( p_demux->s, i_in_pos );
888 msg_Warn ( p_demux, "Reached broken EOF in ogg file" );
892 i_page_size = PAGE_HEADER_BYTES + i_nsegs;
894 for ( i = 0; i < i_nsegs; i++ )
896 i_page_size += header[ PAGE_HEADER_BYTES + i ];
899 ogg_sync_reset( &p_ogg->oy );
901 buf = ogg_sync_buffer( &p_ogg->oy, i_page_size );
903 memcpy( buf, header, PAGE_HEADER_BYTES + i_nsegs );
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 );
908 ogg_sync_wrote( &p_ogg->oy, i_result + PAGE_HEADER_BYTES + i_nsegs );
913 if ( ogg_sync_pageout( &p_ogg->oy, &p_ogg->current_page ) != 1 )
915 msg_Err( p_demux , "Got invalid packet, read %"PRId64" of %i: %s",i_result,i_page_size,
920 p_sys->b_page_waiting = false;
922 return i_result + PAGE_HEADER_BYTES + i_nsegs;