]> git.sesse.net Git - vlc/blobdiff - modules/demux/oggseek.c
CC: use c99, removed dummy Eia608Exit
[vlc] / modules / demux / oggseek.c
index e9d993c4fe4963a2a4e9e450e094727547c0a6d2..42d08ee2fc8c9c6f21ca6ffeeddb55b6725c7c0a 100644 (file)
@@ -78,7 +78,7 @@ static demux_index_entry_t *index_entry_delete( demux_index_entry_t *idx )
 
 static demux_index_entry_t *index_entry_new( void )
 {
-    demux_index_entry_t *idx = (demux_index_entry_t *)malloc( sizeof( demux_index_entry_t ) );
+    demux_index_entry_t *idx = xmalloc( sizeof( demux_index_entry_t ) );
     idx->p_next = idx->p_prev = NULL;
     idx->i_pagepos_end = -1;
     return idx;
@@ -86,11 +86,11 @@ static demux_index_entry_t *index_entry_new( void )
 
 
 
-/* add a theora entry to our list; format is highest granulepos -> page offset of 
+/* add a theora entry to our list; format is highest granulepos -> page offset of
    keyframe start */
 
-const demux_index_entry_t *oggseek_theora_index_entry_add ( logical_stream_t *p_stream, 
-                                                            int64_t i_granule, 
+const demux_index_entry_t *oggseek_theora_index_entry_add ( logical_stream_t *p_stream,
+                                                            int64_t i_granule,
                                                             int64_t i_pagepos)
 {
     /* add or update entry for keyframe */
@@ -237,8 +237,8 @@ static int64_t get_data( demux_t *p_demux, int64_t i_bytes_to_read )
 /* Find the first first ogg page for p_stream between offsets i_pos1 and i_pos2,
    return file offset in bytes; -1 is returned on failure */
 
-static int64_t find_first_page( demux_t *p_demux, int64_t i_pos1, int64_t i_pos2, 
-                                logical_stream_t *p_stream, 
+static int64_t find_first_page( demux_t *p_demux, int64_t i_pos1, int64_t i_pos2,
+                                logical_stream_t *p_stream,
                                 int64_t *pi_kframe, int64_t *pi_frame )
 {
     int64_t i_result;
@@ -296,7 +296,7 @@ static int64_t find_first_page( demux_t *p_demux, int64_t i_pos1, int64_t i_pos2
             continue;
         }
 
-        if ( i_result > 0 || ( i_result == 0 && p_sys->oy.fill > 3 && 
+        if ( i_result > 0 || ( i_result == 0 && p_sys->oy.fill > 3 &&
                                ! strncmp( (char *)p_sys->oy.data, "OggS" , 4 ) ) )
         {
             i_pos1 = p_sys->i_input_position;
@@ -444,7 +444,7 @@ static int64_t find_last_frame (demux_t *p_demux, logical_stream_t *p_stream)
 
 /* convert a theora frame to a granulepos */
 
-static inline int64_t frame_to_gpos( logical_stream_t *p_stream, int64_t i_kframe, 
+static inline int64_t frame_to_gpos( logical_stream_t *p_stream, int64_t i_kframe,
                                      int64_t i_frame )
 {
     if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
@@ -459,28 +459,28 @@ static inline int64_t frame_to_gpos( logical_stream_t *p_stream, int64_t i_kfram
 
 
 
-/* seek to a suitable point to begin decoding for i_tframe. We can pre-set bounding positions 
+/* seek to a suitable point to begin decoding for i_tframe. We can pre-set bounding positions
    i_pos_lower and i_pos_higher to narrow the search domain. */
 
 
-static int64_t ogg_seek( demux_t *p_demux, logical_stream_t *p_stream, int64_t i_tframe, 
-                         int64_t i_pos_lower, int64_t i_pos_upper, int64_t *pi_pagepos, 
+static int64_t ogg_seek( demux_t *p_demux, logical_stream_t *p_stream, int64_t i_tframe,
+                         int64_t i_pos_lower, int64_t i_pos_upper, int64_t *pi_pagepos,
                          bool b_exact )
 {
     /* For theora:
      * We do two passes here, first with b_exact set, then with b_exact unset.
      *
      * If b_exact is set, we find the highest granulepos <= the target granulepos
-     * from this we extract an estimate of the keyframe (note that there could be other 
+     * from this we extract an estimate of the keyframe (note that there could be other
      * "hidden" keyframes between the found granulepos and the target).
      *
-     * On the second pass we find the highest granulepos < target. This places us just before or 
+     * On the second pass we find the highest granulepos < target. This places us just before or
      * at the start of the target keyframe.
      *
-     * When we come to decode, we start from this second position, discarding any completed 
+     * When we come to decode, we start from this second position, discarding any completed
      * packets on that page, and read pages discarding packets until we get to the target frame.
      *
-     * The function returns the granulepos which is found, 
+     * The function returns the granulepos which is found,
      * sets the page offset in pi_pagepos. -1 is returned on error.
      *
      * for dirac:
@@ -495,7 +495,7 @@ static int64_t ogg_seek( demux_t *p_demux, logical_stream_t *p_stream, int64_t i
      * if > target, or we find no keyframes, we go to the lower segment
      * if < target we divide the segment in two and check the upper half
      *
-     * This is then repeated until the segment size is too small to hold a packet, 
+     * This is then repeated until the segment size is too small to hold a packet,
      * at which point we return our best match
      *
      * Two optimisations are made: - anything we discover about keyframes is added to our index
@@ -574,7 +574,7 @@ static int64_t ogg_seek( demux_t *p_demux, logical_stream_t *p_stream, int64_t i
 
         if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
         {
-            i_pagepos = find_first_page( p_demux, i_start_pos, i_end_pos, p_stream, 
+            i_pagepos = find_first_page( p_demux, i_start_pos, i_end_pos, p_stream,
                                          &i_kframe, &i_frame );
         }
         else return -1;
@@ -590,7 +590,7 @@ static int64_t ogg_seek( demux_t *p_demux, logical_stream_t *p_stream, int64_t i
                 return frame_to_gpos( p_stream, i_kframe, i_frame );
             }
 
-            if ( ( i_kframe < i_tframe || ( b_exact && i_kframe == i_tframe ) ) 
+            if ( ( i_kframe < i_tframe || ( b_exact && i_kframe == i_tframe ) )
                  && i_kframe > i_best_kframe )
             {
                 i_best_kframe = i_kframe;
@@ -640,7 +640,7 @@ static int64_t ogg_seek( demux_t *p_demux, logical_stream_t *p_stream, int64_t i
 
 /* find upper and lower pagepos for i_tframe; if we find an exact match, we return it */
 
-static demux_index_entry_t *get_bounds_for ( logical_stream_t *p_stream, int64_t i_tframe, 
+static demux_index_entry_t *get_bounds_for ( logical_stream_t *p_stream, int64_t i_tframe,
                                              int64_t *pi_pos_lower, int64_t *pi_pos_upper)
 {
     int64_t i_kframe;
@@ -678,12 +678,9 @@ static demux_index_entry_t *get_bounds_for ( logical_stream_t *p_stream, int64_t
 
         if ( i_frame < i_tframe )
         {
-            if ( pi_pos_lower != NULL )
-            {
-                *pi_pos_lower = idx->i_pagepos;
-                idx = idx->p_next;
-                continue;
-            }
+            *pi_pos_lower = idx->i_pagepos;
+            idx = idx->p_next;
+            continue;
         }
 
         return idx;
@@ -693,21 +690,27 @@ static demux_index_entry_t *get_bounds_for ( logical_stream_t *p_stream, int64_t
 }
 
 
-/* get highest frame in theora stream */
+/* get highest frame in theora and opus streams */
 
-static int64_t find_last_theora_frame ( demux_t *p_demux, logical_stream_t *p_stream )
+static int64_t get_last_frame ( demux_t *p_demux, logical_stream_t *p_stream )
 {
+    demux_sys_t *p_sys  = p_demux->p_sys;
     int64_t i_frame;
 
     i_frame = find_last_frame ( p_demux, p_stream );
 
     /* We need to reset back to the start here, otherwise packets cannot be decoded.
-     * I think this is due to the fact that we seek to the end and then we must reset 
+     * I think this is due to the fact that we seek to the end and then we must reset
      * all logical streams, which causes remaining headers not to be read correctly.
      * Seeking to 0 is the only value which seems to work, and it appears to have no
      * adverse effects. */
 
     seek_byte( p_demux, 0 );
+    /* Reset stream states */
+    p_sys->i_streams = 0;
+    p_stream->i_serial_no = ogg_page_serialno( &p_sys->current_page );
+    ogg_stream_init( &p_stream->os, p_stream->i_serial_no );
+    ogg_stream_pagein( &p_stream->os, &p_sys->current_page );
 
     return i_frame;
 }
@@ -721,15 +724,17 @@ static int64_t find_last_theora_frame ( demux_t *p_demux, logical_stream_t *p_st
 
 
 
-/* return highest frame number for p_stream (which must be a theora or dirac video stream) */
+/* return highest frame number for p_stream (which must be a theora, dirac or opus stream) */
 
 int64_t oggseek_get_last_frame ( demux_t *p_demux, logical_stream_t *p_stream )
 {
     int64_t i_frame = -1;
 
-    if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
+    if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA ||
+         p_stream->fmt.i_codec == VLC_CODEC_VORBIS ||
+         p_stream->fmt.i_codec == VLC_CODEC_OPUS )
     {
-        i_frame = find_last_theora_frame ( p_demux, p_stream );
+        i_frame = get_last_frame ( p_demux, p_stream );
 
         if ( i_frame < 0 ) return -1;
         return i_frame;
@@ -775,14 +780,26 @@ int oggseek_find_frame ( demux_t *p_demux, logical_stream_t *p_stream, int64_t i
 
     i_tframe += p_stream->i_keyframe_offset;
 
+    i_cframe = i_tframe;
+    /* For Opus, seek back 80 ms before the target playback position. */
+    if ( p_stream->fmt.i_codec == VLC_CODEC_OPUS )
+    {
+        if ( i_tframe <= p_stream->i_pre_skip )
+            i_cframe = 0;
+        else if ( i_tframe < 80*48 )
+            i_cframe = 0;
+        else
+            i_cframe = i_tframe - 80*48;
+    }
+
     /* reduce the search domain */
-    fidx = get_bounds_for( p_stream, i_tframe, &i_pos_lower, &i_pos_upper );
+    fidx = get_bounds_for( p_stream, i_cframe, &i_pos_lower, &i_pos_upper );
 
     if ( fidx == NULL )
     {
-        /* no exact match found; search the domain for highest keyframe <= i_tframe */
+        /* no exact match found; search the domain for highest keyframe <= i_cframe */
 
-        i_granulepos = ogg_seek ( p_demux, p_stream, i_tframe, i_pos_lower, i_pos_upper, 
+        i_granulepos = ogg_seek ( p_demux, p_stream, i_cframe, i_pos_lower, i_pos_upper,
                                   &i_pagepos, true );
         if ( i_granulepos == -1 )
         {
@@ -802,13 +819,13 @@ int oggseek_find_frame ( demux_t *p_demux, logical_stream_t *p_stream, int64_t i
             i_kframe = p_stream->i_keyframe_offset;
         }
 
-        /* we found a keyframe, but we don't know where its packet starts, so search for a 
+        /* we found a keyframe, but we don't know where its packet starts, so search for a
            frame just before it */
 
         /* reduce search domain */
         get_bounds_for( p_stream, i_kframe-1, &i_pos_lower, &i_pos_upper );
 
-        i_granulepos = ogg_seek( p_demux, p_stream, i_kframe-1, i_pos_lower, i_pos_upper, 
+        i_granulepos = ogg_seek( p_demux, p_stream, i_kframe-1, i_pos_lower, i_pos_upper,
                                  &i_pagepos, false );
 
         /* i_cframe will be the next frame we decode */
@@ -896,7 +913,7 @@ int64_t oggseek_read_page( demux_t *p_demux )
 
     memcpy( buf, header, PAGE_HEADER_BYTES + i_nsegs );
 
-    i_result = stream_Read ( p_demux->s, (uint8_t*)buf + PAGE_HEADER_BYTES + i_nsegs, 
+    i_result = stream_Read ( p_demux->s, (uint8_t*)buf + PAGE_HEADER_BYTES + i_nsegs,
                              i_page_size - PAGE_HEADER_BYTES - i_nsegs );
 
     ogg_sync_wrote( &p_ogg->oy, i_result + PAGE_HEADER_BYTES + i_nsegs );