Index: gstmpegaudioparse.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-ugly/gst/mpegaudioparse/gstmpegaudioparse.c,v
retrieving revision 1.66
diff -u -p -u -p -r1.66 gstmpegaudioparse.c
--- gstmpegaudioparse.c	1 Jul 2007 19:12:32 -0000	1.66
+++ gstmpegaudioparse.c	7 Jul 2007 19:35:45 -0000
@@ -244,7 +244,6 @@ gst_mp3parse_reset (GstMPEGAudioParse * 
   mp3parse->skip = 0;
   mp3parse->resyncing = TRUE;
   mp3parse->cur_offset = -1;
-  mp3parse->next_ts = GST_CLOCK_TIME_NONE;
 
   mp3parse->tracked_offset = 0;
   mp3parse->pending_ts = GST_CLOCK_TIME_NONE;
@@ -263,6 +262,22 @@ gst_mp3parse_reset (GstMPEGAudioParse * 
 
   mp3parse->xing_flags = 0;
   mp3parse->xing_bitrate = 0;
+
+  if (mp3parse->seek_table) {
+    g_list_foreach (mp3parse->seek_table, (GFunc) g_free, NULL);
+    mp3parse->seek_table = NULL;
+  }
+
+  g_mutex_lock (mp3parse->pending_accurate_seeks_lock);
+  if (mp3parse->pending_accurate_seeks) {
+    g_list_foreach (mp3parse->pending_accurate_seeks, (GFunc) g_free, NULL);
+    mp3parse->pending_accurate_seeks = NULL;
+  }
+  g_mutex_unlock (mp3parse->pending_accurate_seeks_lock);
+
+  mp3parse->exact_position = FALSE;
+  gst_segment_init (&mp3parse->segment, GST_FORMAT_TIME);
+  gst_segment_init (&mp3parse->downstream_segment, GST_FORMAT_TIME);
 }
 
 static void
@@ -283,6 +298,7 @@ gst_mp3parse_init (GstMPEGAudioParse * m
   gst_element_add_pad (GST_ELEMENT (mp3parse), mp3parse->srcpad);
 
   mp3parse->adapter = gst_adapter_new ();
+  mp3parse->pending_accurate_seeks_lock = g_mutex_new ();
 
   gst_mp3parse_reset (mp3parse);
 }
@@ -296,6 +312,8 @@ gst_mp3parse_dispose (GObject * object)
     g_object_unref (mp3parse->adapter);
     mp3parse->adapter = NULL;
   }
+  g_mutex_free (mp3parse->pending_accurate_seeks_lock);
+  mp3parse->pending_accurate_seeks_lock = NULL;
 
   G_OBJECT_CLASS (parent_class)->dispose (object);
 }
@@ -319,6 +337,58 @@ gst_mp3parse_sink_event (GstPad * pad, G
       gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
           &format, &start, &stop, &pos);
 
+      g_mutex_lock (mp3parse->pending_accurate_seeks_lock);
+      if (format == GST_FORMAT_BYTES && mp3parse->pending_accurate_seeks) {
+        MPEGAudioPendingAccurateSeek *seek = NULL;
+        GList *node;
+
+        for (node = mp3parse->pending_accurate_seeks; node; node = node->next) {
+          MPEGAudioPendingAccurateSeek *tmp = node->data;
+
+          if (tmp->upstream_start == pos) {
+            seek = tmp;
+            break;
+          }
+        }
+        if (seek) {
+          GstSegment *s = &seek->downstream_segment;
+
+          event =
+              gst_event_new_new_segment_full (FALSE, s->rate, s->applied_rate,
+              GST_FORMAT_TIME, s->start, s->stop, s->last_stop);
+
+          mp3parse->segment = seek->segment;
+
+          mp3parse->resyncing = FALSE;
+          mp3parse->cur_offset = pos;
+          mp3parse->pending_ts = GST_CLOCK_TIME_NONE;
+          mp3parse->tracked_offset = 0;
+
+          gst_event_parse_new_segment_full (event, &update, &rate,
+              &applied_rate, &format, &start, &stop, &pos);
+
+          GST_DEBUG_OBJECT (mp3parse,
+              "Pushing accurate newseg rate %g, applied rate %g, "
+              "format %d, start %lld, stop %lld, pos %lld\n", rate,
+              applied_rate, format, start, stop, pos);
+
+          g_free (seek);
+          mp3parse->pending_accurate_seeks =
+              g_list_delete_link (mp3parse->pending_accurate_seeks, node);
+
+          g_mutex_unlock (mp3parse->pending_accurate_seeks_lock);
+          res = gst_pad_push_event (mp3parse->srcpad, event);
+
+          return res;
+        } else {
+          GST_WARNING_OBJECT (mp3parse,
+              "Accurate seek not possible, didn't get an appropiate upstream segment");
+        }
+      }
+      g_mutex_unlock (mp3parse->pending_accurate_seeks_lock);
+
+      mp3parse->exact_position = FALSE;
+
       if (format == GST_FORMAT_BYTES) {
         GstClockTime seg_start, seg_stop, seg_pos;
 
@@ -332,8 +402,9 @@ gst_mp3parse_sink_event (GstPad * pad, G
               GST_FORMAT_TIME, seg_start, seg_stop, seg_pos);
           format = GST_FORMAT_TIME;
           GST_DEBUG_OBJECT (mp3parse, "Converted incoming segment to TIME. "
-              "start = %" G_GINT64_FORMAT ", stop = %" G_GINT64_FORMAT
-              "pos = %" G_GINT64_FORMAT, seg_start, seg_stop, seg_pos);
+              "start = %" GST_TIME_FORMAT ", stop = %" GST_TIME_FORMAT
+              ", pos = %" GST_TIME_FORMAT, GST_TIME_ARGS (seg_start),
+              GST_TIME_ARGS (seg_stop), GST_TIME_ARGS (seg_pos));
         }
       }
 
@@ -346,7 +417,6 @@ gst_mp3parse_sink_event (GstPad * pad, G
       }
       mp3parse->resyncing = TRUE;
       mp3parse->cur_offset = -1;
-      mp3parse->next_ts = GST_CLOCK_TIME_NONE;
       mp3parse->pending_ts = GST_CLOCK_TIME_NONE;
       mp3parse->tracked_offset = 0;
 
@@ -355,6 +425,16 @@ gst_mp3parse_sink_event (GstPad * pad, G
       GST_DEBUG_OBJECT (mp3parse, "Pushing newseg rate %g, applied rate %g, "
           "format %d, start %lld, stop %lld, pos %lld\n",
           rate, applied_rate, format, start, stop, pos);
+
+      mp3parse->downstream_segment.rate = rate;
+      mp3parse->downstream_segment.rate = rate;
+      mp3parse->downstream_segment.rate = rate;
+      gst_segment_set_newsegment_full (&mp3parse->downstream_segment, update,
+          rate, applied_rate, format, start, stop, pos);
+      gst_segment_set_newsegment_full (&mp3parse->segment, update, rate,
+          applied_rate, format, start, stop, pos);
+      mp3parse->segment.last_stop = GST_CLOCK_TIME_NONE;
+
       res = gst_pad_push_event (mp3parse->srcpad, event);
       break;
     }
@@ -373,14 +453,25 @@ gst_mp3parse_sink_event (GstPad * pad, G
   return res;
 }
 
+static MPEGAudioSeekEntry *
+mp3parse_seek_table_last_entry (GstMPEGAudioParse * mp3parse)
+{
+  MPEGAudioSeekEntry *ret = NULL;
+
+  if (mp3parse->seek_table) {
+    ret = mp3parse->seek_table->data;
+  }
+
+  return ret;
+}
+
 /* Prepare a buffer of the indicated size, timestamp it and output */
 static GstFlowReturn
 gst_mp3parse_emit_frame (GstMPEGAudioParse * mp3parse, guint size)
 {
   GstBuffer *outbuf;
   guint bitrate;
-
-  GST_DEBUG_OBJECT (mp3parse, "pushing buffer of %d bytes", size);
+  GstFlowReturn ret = GST_FLOW_OK;
 
   outbuf = gst_adapter_take_buffer (mp3parse->adapter, size);
 
@@ -397,29 +488,30 @@ gst_mp3parse_emit_frame (GstMPEGAudioPar
        * 90khz MPEG ticks, then take it and, if needed, set the discont flag. 
        * This avoids creating imperfect streams just because of 
        * quantization in the MPEG clock sampling */
-      GstClockTimeDiff diff = mp3parse->next_ts - mp3parse->pending_ts;
+      GstClockTimeDiff diff =
+          mp3parse->segment.last_stop - mp3parse->pending_ts;
 
       if (diff < -2 * (GST_SECOND / 90000) || diff > 2 * (GST_SECOND / 90000)) {
         GST_DEBUG_OBJECT (mp3parse, "Updating next_ts from %" GST_TIME_FORMAT
             " to pending ts %" GST_TIME_FORMAT
             " at offset %lld (pending offset was %lld)",
-            GST_TIME_ARGS (mp3parse->next_ts),
+            GST_TIME_ARGS (mp3parse->segment.last_stop),
             GST_TIME_ARGS (mp3parse->pending_ts), mp3parse->tracked_offset,
             mp3parse->pending_offset);
 
         /* Only set discont if we sent out some timestamps already and we're
          * adjusting */
-        if (GST_CLOCK_TIME_IS_VALID (mp3parse->next_ts))
+        if (GST_CLOCK_TIME_IS_VALID (mp3parse->segment.last_stop))
           GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
-        mp3parse->next_ts = mp3parse->pending_ts;
+        mp3parse->segment.last_stop = mp3parse->pending_ts;
       }
       mp3parse->pending_ts = GST_CLOCK_TIME_NONE;
     }
   }
 
   /* Decide what timestamp we're going to apply */
-  if (GST_CLOCK_TIME_IS_VALID (mp3parse->next_ts)) {
-    GST_BUFFER_TIMESTAMP (outbuf) = mp3parse->next_ts;
+  if (GST_CLOCK_TIME_IS_VALID (mp3parse->segment.last_stop)) {
+    GST_BUFFER_TIMESTAMP (outbuf) = mp3parse->segment.last_stop;
   } else {
     GstClockTime ts;
 
@@ -432,13 +524,33 @@ gst_mp3parse_emit_frame (GstMPEGAudioPar
     }
   }
 
+  if (GST_BUFFER_TIMESTAMP (outbuf) == 0)
+    mp3parse->exact_position = TRUE;
+
+  if (mp3parse->exact_position && (!mp3parse->seek_table ||
+          (mp3parse_seek_table_last_entry (mp3parse))->byte <
+          GST_BUFFER_OFFSET (outbuf))) {
+    MPEGAudioSeekEntry *entry = g_new0 (MPEGAudioSeekEntry, 1);
+
+    entry->byte = mp3parse->cur_offset;
+    entry->byte_end = mp3parse->cur_offset + size;
+    entry->timestamp = GST_BUFFER_TIMESTAMP (outbuf);
+    entry->timestamp_end =
+        GST_BUFFER_TIMESTAMP (outbuf) + GST_BUFFER_DURATION (outbuf);
+    mp3parse->seek_table = g_list_prepend (mp3parse->seek_table, entry);
+    GST_DEBUG_OBJECT (mp3parse, "Adding index entry %" GST_TIME_FORMAT " - %"
+        GST_TIME_FORMAT " @ offset 0x%08" G_GINT64_MODIFIER "x",
+        GST_TIME_ARGS (entry->timestamp), GST_TIME_ARGS (entry->timestamp_end),
+        entry->byte);
+  }
+
   /* Update our byte offset tracking */
   if (mp3parse->cur_offset != -1) {
     mp3parse->cur_offset += size;
   }
   mp3parse->tracked_offset += size;
 
-  mp3parse->next_ts =
+  mp3parse->segment.last_stop =
       GST_BUFFER_TIMESTAMP (outbuf) + GST_BUFFER_DURATION (outbuf);
 
   gst_buffer_set_caps (outbuf, GST_PAD_CAPS (mp3parse->srcpad));
@@ -459,7 +571,44 @@ gst_mp3parse_emit_frame (GstMPEGAudioPar
         mp3parse->srcpad, taglist);
   }
 
-  return gst_pad_push (mp3parse->srcpad, outbuf);
+  if (G_UNLIKELY ((GST_CLOCK_TIME_IS_VALID (mp3parse->segment.start) &&
+              GST_BUFFER_TIMESTAMP (outbuf) + GST_BUFFER_DURATION (outbuf)
+              < mp3parse->segment.start)
+          || (GST_CLOCK_TIME_IS_VALID (mp3parse->segment.stop)
+              && GST_BUFFER_TIMESTAMP (outbuf) >= mp3parse->segment.stop))) {
+    GST_DEBUG_OBJECT (mp3parse,
+        "Buffer outside of configured segment, dropping, timestamp %"
+        GST_TIME_FORMAT ", offset 0x%08" G_GINT64_MODIFIER "x",
+        GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
+        GST_BUFFER_OFFSET (outbuf));
+    gst_buffer_unref (outbuf);
+    ret = GST_FLOW_OK;
+  } else {
+    GST_DEBUG_OBJECT (mp3parse,
+        "pushing buffer of %d bytes, timestamp %" GST_TIME_FORMAT
+        ", offset 0x%08" G_GINT64_MODIFIER "x", size,
+        GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
+        GST_BUFFER_OFFSET (outbuf));
+    ret = gst_pad_push (mp3parse->srcpad, outbuf);
+  }
+  if (ret == GST_FLOW_UNEXPECTED
+      && mp3parse->downstream_segment.flags & GST_SEEK_FLAG_SEGMENT) {
+    GstClockTime stop;
+
+    GST_LOG_OBJECT (mp3parse, "Sending segment done");
+
+    if ((stop = mp3parse->segment.stop) == -1)
+      stop = mp3parse->segment.duration;
+
+    gst_element_post_message (GST_ELEMENT_CAST (mp3parse),
+        gst_message_new_segment_done (GST_OBJECT_CAST (mp3parse),
+            mp3parse->segment.format, stop));
+  } else if (GST_CLOCK_TIME_IS_VALID (mp3parse->segment.stop)
+      && mp3parse->segment.last_stop >= mp3parse->segment.stop) {
+    GST_DEBUG_OBJECT (mp3parse, "Sending EOS");
+    gst_pad_push_event (mp3parse->srcpad, gst_event_new_eos ());
+  }
+  return ret;
 }
 
 #define XING_FRAMES_FLAG     0x0001
@@ -1020,6 +1169,44 @@ no_bitrate:
   return FALSE;
 }
 
+#if 0
+static MPEGAudioSeekEntry *
+mp3parse_time_to_bytepos_indexed (GstMPEGAudioParse * mp3parse, GstClockTime ts)
+{
+  MPEGAudioSeekEntry *entry;
+  GList *node;
+
+  for (node = mp3parse->seek_table; node; node = node->next) {
+    entry = node->data;
+
+    if (ts >= entry->timestamp && ts < entry->timestamp_end)
+      return entry;
+
+    if (ts >= entry->timestamp_end)
+      return NULL;
+  }
+  return NULL;
+}
+
+static MPEGAudioSeekEntry *
+mp3parse_bytepos_to_time_indexed (GstMPEGAudioParse * mp3parse, gint64 bytepos)
+{
+  MPEGAudioSeekEntry *entry;
+  GList *node;
+
+  for (node = mp3parse->seek_table; node; node = node->next) {
+    entry = node->data;
+
+    if (bytepos >= entry->byte_start && bytepos < entry->byte_end)
+      return entry;
+
+    if (ts >= entry->byte_offset)
+      return NULL;
+  }
+  return ret;
+}
+#endif
+
 static gboolean
 mp3parse_bytepos_to_time (GstMPEGAudioParse * mp3parse,
     gint64 bytepos, GstClockTime * ts)
@@ -1082,6 +1269,9 @@ mp3parse_handle_seek (GstMPEGAudioParse 
   gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur,
       &stop_type, &stop);
 
+  GST_DEBUG_OBJECT (mp3parse, "Performing seek to %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (cur));
+
   /* For any format other than TIME, see if upstream handles
    * it directly or fail. For TIME, try upstream, but do it ourselves if
    * it fails upstream */
@@ -1096,6 +1286,121 @@ mp3parse_handle_seek (GstMPEGAudioParse 
 
   /* Handle TIME based seeks by converting to a BYTE position */
 
+  /* For accurate seeking get the frame 9 (MPEG1) or 29 (MPEG2) frames
+   * before the one we want to seek to and push them all to the decoder.
+   *
+   * This is necessary because of the bit reservoir. See
+   * http://www.mars.org/mailman/public/mad-dev/2002-May/000634.html
+   *
+   * mp3parse->segment contains the segment in time which
+   * we will push out of the pad. This will be 9/29 frames before
+   * the one we want.
+   *
+   * mp3parse->downstream_segment contains the segment in time
+   * which the app wanted to seek to. This will be pushed
+   * downstream and the decoder will care for any necessary clipping.
+   */
+
+  if (flags & GST_SEEK_FLAG_ACCURATE) {
+    MPEGAudioPendingAccurateSeek *seek =
+        g_new0 (MPEGAudioPendingAccurateSeek, 1);
+    guint64 length =
+        gst_util_uint64_scale (GST_SECOND, mp3parse->spf, mp3parse->rate);
+    gint64 start;
+    gint frame_offset = (mp3parse->version == 1) ? 9 : 29;
+
+    seek->downstream_segment = mp3parse->downstream_segment;
+    seek->segment = mp3parse->segment;
+
+    gst_segment_set_seek (&seek->downstream_segment, rate, GST_FORMAT_TIME,
+        flags, cur_type, cur, stop_type, stop, NULL);
+
+    if (!mp3parse->seek_table) {
+      byte_cur = 0;
+      byte_stop = -1;
+      start = MAX (0, cur - (frame_offset + 1) * length);
+      cur = 0;
+      stop = GST_CLOCK_TIME_NONE;
+    } else {
+      MPEGAudioSeekEntry *entry = NULL, *start_entry = NULL, *stop_entry = NULL;
+      GList *start_node, *stop_node;
+
+      for (start_node = mp3parse->seek_table; start_node;
+          start_node = start_node->next) {
+        entry = start_node->data;
+
+        if (cur >= entry->timestamp && cur < entry->timestamp_end) {
+          start_entry = entry;
+          break;
+        }
+
+        if (cur >= entry->timestamp_end)
+          break;
+      }
+
+      if (!start_entry) {
+        start_entry = mp3parse->seek_table->data;
+        byte_cur = start_entry->byte;
+        start = MAX (start_entry->timestamp, cur - (frame_offset + 1) * length);
+        cur = start_entry->timestamp;
+      } else {
+        int i = frame_offset;
+
+        while (start_node->next && i > 1) {
+          start_node = start_node->next;
+          i--;
+        }
+        start_entry = start_node->data;
+        byte_cur = start_entry->byte;
+        start = MAX (start_entry->timestamp, cur - (frame_offset + 1) * length);
+        cur = start_entry->timestamp;
+      }
+
+      for (stop_node = mp3parse->seek_table; stop_node;
+          stop_node = stop_node->next) {
+        entry = stop_node->data;
+
+        if (stop >= entry->timestamp && stop < entry->timestamp_end) {
+          stop_entry = entry;
+          break;
+        }
+
+        if (stop >= entry->timestamp_end)
+          break;
+      }
+
+      if (!stop_entry) {
+        byte_stop = -1;
+        stop = GST_CLOCK_TIME_NONE;
+      } else {
+        byte_stop = stop_entry->byte_end;
+        stop = stop_entry->timestamp_end;
+      }
+
+    }
+    g_mutex_lock (mp3parse->pending_accurate_seeks_lock);
+    event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type,
+        byte_cur, stop_type, byte_stop);
+    if (gst_pad_push_event (mp3parse->sinkpad, event)) {
+      mp3parse->exact_position = TRUE;
+      gst_segment_set_seek (&seek->segment, rate, GST_FORMAT_TIME, flags,
+          cur_type, start, stop_type, stop, NULL);
+      seek->segment.last_stop = cur;
+      seek->upstream_start = byte_cur;
+      mp3parse->pending_accurate_seeks =
+          g_list_prepend (mp3parse->pending_accurate_seeks, seek);
+      g_mutex_unlock (mp3parse->pending_accurate_seeks_lock);
+      return TRUE;
+    } else {
+      g_mutex_unlock (mp3parse->pending_accurate_seeks_lock);
+      mp3parse->exact_position = TRUE;
+      g_free (seek);
+      return TRUE;
+    }
+  }
+
+  mp3parse->exact_position = FALSE;
+
   /* Convert the TIME to the appropriate BYTE position at which to resume
    * decoding. */
   if (!mp3parse_time_to_bytepos (mp3parse, (GstClockTime) cur, &byte_cur))
@@ -1161,9 +1466,10 @@ mp3parse_src_query (GstPad * pad, GstQue
           res = TRUE;
         }
       } else if (format == GST_FORMAT_TIME) {
-        if (mp3parse->next_ts == GST_CLOCK_TIME_NONE)
+        if (mp3parse->segment.last_stop == GST_CLOCK_TIME_NONE)
           goto out;
-        gst_query_set_position (query, GST_FORMAT_TIME, mp3parse->next_ts);
+        gst_query_set_position (query, GST_FORMAT_TIME,
+            mp3parse->segment.last_stop);
         res = TRUE;
       }
 
Index: gstmpegaudioparse.h
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-ugly/gst/mpegaudioparse/gstmpegaudioparse.h,v
retrieving revision 1.17
diff -u -p -u -p -r1.17 gstmpegaudioparse.h
--- gstmpegaudioparse.h	1 Jul 2007 19:12:32 -0000	1.17
+++ gstmpegaudioparse.h	7 Jul 2007 19:35:45 -0000
@@ -40,13 +40,30 @@ G_BEGIN_DECLS
 
 typedef struct _GstMPEGAudioParse GstMPEGAudioParse;
 typedef struct _GstMPEGAudioParseClass GstMPEGAudioParseClass;
+typedef struct _MPEGAudioSeekEntry MPEGAudioSeekEntry;
+typedef struct _MPEGAudioPendingAccurateSeek MPEGAudioPendingAccurateSeek;
+
+
+struct _MPEGAudioSeekEntry {
+  gint64 byte;
+  gint64 byte_end;
+  GstClockTime timestamp;
+  GstClockTime timestamp_end;
+};
+
+struct _MPEGAudioPendingAccurateSeek {
+  GstSegment downstream_segment;
+  GstSegment segment;
+  gint64 upstream_start;
+};
 
 struct _GstMPEGAudioParse {
   GstElement element;
 
   GstPad *sinkpad, *srcpad;
 
-  GstClockTime next_ts;
+  GstSegment segment;
+
   /* Offset as supplied by incoming buffers */
   gint64 cur_offset;
 
@@ -85,6 +102,13 @@ struct _GstMPEGAudioParse {
   guint16 xing_seek_table_inverse[256];
   guint32 xing_vbr_scale;
   guint   xing_bitrate;
+
+  /* Accurate seeking */
+  GList *seek_table;
+  GMutex *pending_accurate_seeks_lock;
+  GList *pending_accurate_seeks;
+  GstSegment downstream_segment;
+  gboolean exact_position;
 };
 
 struct _GstMPEGAudioParseClass {
