ext/ogg/gstoggdemux.c: Generate correct disconts for live chained oggs.
authorWim Taymans <wim.taymans@gmail.com>
Thu, 21 Jul 2005 17:25:40 +0000 (17:25 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Thu, 21 Jul 2005 17:25:40 +0000 (17:25 +0000)
Original commit message from CVS:
* ext/ogg/gstoggdemux.c: (gst_ogg_pad_event),
(gst_ogg_pad_internal_chain), (gst_ogg_pad_typefind),
(gst_ogg_demux_chain_elem_pad), (gst_ogg_demux_queue_data),
(gst_ogg_demux_chain_peer), (gst_ogg_pad_submit_packet),
(gst_ogg_pad_submit_page), (gst_ogg_chain_new),
(gst_ogg_demux_init), (gst_ogg_demux_activate_chain),
(gst_ogg_demux_perform_seek), (gst_ogg_demux_collect_chain_info),
(gst_ogg_demux_collect_info), (gst_ogg_demux_chain),
(gst_ogg_demux_send_event), (gst_ogg_demux_loop):
Generate correct disconts for live chained oggs.

* gst-libs/gst/audio/gstbaseaudiosink.c:
(gst_base_audio_sink_render),
(gst_base_audio_sink_create_ringbuffer),
(gst_base_audio_sink_change_state):
Handle discont math correctly.

* gst/playback/gstplaybin.c: (add_sink):
Some small debug cleanup.

ChangeLog
ext/ogg/gstoggdemux.c
gst-libs/gst/audio/gstbaseaudiosink.c
gst/playback/gstplaybin.c

index e2c6005..8ef330e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,27 @@
 2005-07-21  Wim Taymans  <wim@fluendo.com>
 
+       * ext/ogg/gstoggdemux.c: (gst_ogg_pad_event),
+       (gst_ogg_pad_internal_chain), (gst_ogg_pad_typefind),
+       (gst_ogg_demux_chain_elem_pad), (gst_ogg_demux_queue_data),
+       (gst_ogg_demux_chain_peer), (gst_ogg_pad_submit_packet),
+       (gst_ogg_pad_submit_page), (gst_ogg_chain_new),
+       (gst_ogg_demux_init), (gst_ogg_demux_activate_chain),
+       (gst_ogg_demux_perform_seek), (gst_ogg_demux_collect_chain_info),
+       (gst_ogg_demux_collect_info), (gst_ogg_demux_chain),
+       (gst_ogg_demux_send_event), (gst_ogg_demux_loop):
+       Generate correct disconts for live chained oggs.
+
+       * gst-libs/gst/audio/gstbaseaudiosink.c:
+       (gst_base_audio_sink_render),
+       (gst_base_audio_sink_create_ringbuffer),
+       (gst_base_audio_sink_change_state):
+       Handle discont math correctly.
+
+       * gst/playback/gstplaybin.c: (add_sink):
+       Some small debug cleanup.
+
+2005-07-21  Wim Taymans  <wim@fluendo.com>
+
        * ext/ogg/gstoggdemux.c: (gst_ogg_pad_init), (gst_ogg_pad_event),
        (gst_ogg_pad_internal_chain), (gst_ogg_pad_typefind),
        (gst_ogg_demux_chain_elem_pad), (gst_ogg_demux_queue_data),
index b6112ac..a505618 100644 (file)
@@ -131,12 +131,6 @@ struct _GstOggPadClass
   PARENTCLASS parent_class;
 };
 
-typedef enum
-{
-  OGG_STATE_NEW_CHAIN,
-  OGG_STATE_STREAMING,
-} OggState;
-
 #define GST_CHAIN_LOCK(ogg)    g_mutex_lock((ogg)->chain_lock)
 #define GST_CHAIN_UNLOCK(ogg)  g_mutex_unlock((ogg)->chain_lock)
 
@@ -149,7 +143,6 @@ struct _GstOggDemux
   gint64 length;
   gint64 offset;
 
-  OggState state;
   gboolean seekable;
   gboolean running;
 
@@ -169,6 +162,9 @@ struct _GstOggDemux
   GstClockTime segment_stop;
   gboolean segment_play;
 
+  gint64 current_granule;
+  GstClockTime current_time;
+
   /* ogg stuff */
   ogg_sync_state sync;
 };
@@ -529,16 +525,20 @@ static GstFlowReturn
 gst_ogg_pad_internal_chain (GstPad * pad, GstBuffer * buffer)
 {
   GstOggPad *oggpad;
+  GstOggDemux *ogg;
   GstClockTime timestamp;
 
   oggpad = gst_pad_get_element_private (pad);
+  ogg = GST_OGG_DEMUX (oggpad->ogg);
 
   timestamp = GST_BUFFER_TIMESTAMP (buffer);
   GST_DEBUG_OBJECT (oggpad, "received buffer from iternal pad, TS=%lld",
       timestamp);
 
-  if (oggpad->start_time == GST_CLOCK_TIME_NONE)
+  if (oggpad->start_time == GST_CLOCK_TIME_NONE) {
     oggpad->start_time = timestamp;
+    ogg->current_time = timestamp;
+  }
 
   gst_buffer_unref (buffer);
 
@@ -701,6 +701,21 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet)
     GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
 
     ret = gst_pad_push (GST_PAD (pad), buf);
+
+    if (packet->granulepos != -1) {
+      GstFormat format;
+
+      ogg->current_granule = packet->granulepos;
+      format = GST_FORMAT_TIME;
+      if (!gst_pad_query_convert (pad->elem_pad,
+              GST_FORMAT_DEFAULT, packet->granulepos, &format,
+              (gint64 *) & ogg->current_time)) {
+        g_warning ("could not convert granulepos to time");
+      } else {
+        GST_DEBUG ("ogg current time %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (ogg->current_time));
+      }
+    }
   } else {
     GST_DEBUG_OBJECT (ogg,
         "%p could not get buffer from peer %08lx", pad, pad->serialno);
@@ -725,18 +740,20 @@ gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
 
   GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08lx", pad, pad->serialno);
 
+  /* first packet */
+  if (!pad->have_type) {
+    gst_ogg_pad_typefind (pad, packet);
+    pad->have_type = TRUE;
+  }
+
   granule = packet->granulepos;
   if (granule != -1) {
+    ogg->current_granule = granule;
     pad->current_granule = granule;
     if (pad->first_granule == -1 && granule != 0) {
       pad->first_granule = granule;
     }
   }
-  /* first packet */
-  if (!pad->have_type) {
-    gst_ogg_pad_typefind (pad, packet);
-    pad->have_type = TRUE;
-  }
 
   /* no start time known, stream to internal plugin to
    * get time */
@@ -747,17 +764,21 @@ gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
    * can activate the complete chain if this is a dynamic
    * chain. */
   if (pad->start_time != GST_CLOCK_TIME_NONE) {
+    GstOggChain *chain = pad->chain;
+
     /* check if complete chain has start time */
-    if (pad->chain == ogg->building_chain) {
-      if (gst_ogg_demux_collect_chain_info (ogg, pad->chain)) {
+    if (chain == ogg->building_chain) {
+
+      /* see if we have enough info to activate the chain */
+      if (gst_ogg_demux_collect_chain_info (ogg, chain)) {
         GstEvent *event;
 
         /* create the discont event we are going to send out */
         event = gst_event_new_discontinuous (1.0,
-            GST_FORMAT_TIME, (gint64) pad->chain->start_time,
-            (gint64) pad->chain->last_time, NULL);
+            GST_FORMAT_TIME, (gint64) chain->start_time - chain->begin_time,
+            (gint64) chain->last_time - chain->begin_time, NULL);
 
-        gst_ogg_demux_activate_chain (ogg, pad->chain, event);
+        gst_ogg_demux_activate_chain (ogg, chain, event);
 
         ogg->building_chain = NULL;
       }
@@ -848,6 +869,7 @@ gst_ogg_chain_new (GstOggDemux * ogg)
   chain->bytes = -1;
   chain->have_bos = FALSE;
   chain->streams = g_array_new (FALSE, TRUE, sizeof (GstOggPad *));
+  chain->begin_time = GST_CLOCK_TIME_NONE;
   chain->start_time = GST_CLOCK_TIME_NONE;
   chain->total_time = GST_CLOCK_TIME_NONE;
 
@@ -1029,7 +1051,9 @@ gst_ogg_demux_init (GstOggDemux * ogg)
 
   ogg->chain_lock = g_mutex_new ();
   ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *));
-  ogg->state = OGG_STATE_NEW_CHAIN;
+
+  ogg->current_granule = -1;
+  ogg->current_time = 0;
 
   ogg->segment_start = GST_CLOCK_TIME_NONE;
   ogg->segment_stop = GST_CLOCK_TIME_NONE;
@@ -1287,9 +1311,6 @@ gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain,
 
   GST_DEBUG ("starting chain");
 
-  /* we are streaming now */
-  ogg->state = OGG_STATE_STREAMING;
-
   /* then send out any queued buffers */
   for (i = 0; i < chain->streams->len; i++) {
     GList *headers;
@@ -1512,36 +1533,30 @@ gst_ogg_demux_perform_seek (GstOggDemux * ogg, gboolean flush)
 
   ogg->offset = best;
 
-  /* switch to different chain */
-  if (chain != ogg->current_chain) {
-    gst_ogg_demux_activate_chain (ogg, chain, NULL);
-  }
+  /* current time starts from 0 again after a flush */
+  if (flush)
+    ogg->current_time = 0;
+
   /* now we have a new position, prepare for streaming again */
   {
-    gint i;
     GstEvent *event;
 
+    /* we have to send the flush to the old chain, not the new one */
+    if (flush)
+      gst_ogg_demux_send_event (ogg, gst_event_new_flush (TRUE));
+
     /* create the discont event we are going to send out */
     event = gst_event_new_discontinuous (1.0,
         GST_FORMAT_TIME, (gint64) ogg->segment_start,
         (gint64) ogg->segment_stop, NULL);
 
-    for (i = 0; i < ogg->chains->len; i++) {
-      GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
-      gint j;
-
-      for (j = 0; j < chain->streams->len; j++) {
-        GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j);
-
-        if (flush)
-          gst_pad_push_event (GST_PAD (pad), gst_event_new_flush (TRUE));
-
-        /* and the discont */
-        gst_event_ref (event);
-        gst_pad_push_event (GST_PAD (pad), event);
-      }
+    if (chain != ogg->current_chain) {
+      /* switch to different chain, send discont on new chain */
+      gst_ogg_demux_activate_chain (ogg, chain, event);
+    } else {
+      /* send discont on old chain */
+      gst_ogg_demux_send_event (ogg, event);
     }
-    gst_event_unref (event);
 
     /* notify start of new segment */
     if (ogg->segment_play) {
@@ -1987,6 +2002,7 @@ gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
   GstOggDemux *ogg;
   gint ret = -1;
   GstFlowReturn result = GST_FLOW_OK;
+  guint serialno;
 
   ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
 
@@ -2004,14 +2020,14 @@ gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
       /* discontinuity in the pages */
     } else {
       GstOggPad *pad;
-      guint serialno;
+      gint64 granule;
 
       serialno = ogg_page_serialno (&page);
+      granule = ogg_page_granulepos (&page);
 
       GST_LOG_OBJECT (ogg,
           "processing ogg page (serial %08lx, pageno %ld, granule pos %llu, bos %d)",
-          serialno, ogg_page_pageno (&page),
-          ogg_page_granulepos (&page), ogg_page_bos (&page));
+          serialno, ogg_page_pageno (&page), granule, ogg_page_bos (&page));
 
       if (ogg_page_bos (&page)) {
         GstOggChain *chain;
@@ -2024,16 +2040,40 @@ gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
           gst_ogg_demux_activate_chain (ogg, chain, NULL);
           pad = gst_ogg_demux_find_pad (ogg, serialno);
         } else {
-          /* chain is unknown */
-          if (ogg->state == OGG_STATE_STREAMING) {
+          GstClockTime chain_time;
+          GstOggChain *current_chain;
+
+          /* this can only happen in non-seekabe mode */
+          if (ogg->seekable)
+            goto unknown_chain;
+
+          current_chain = ogg->current_chain;
+          if (current_chain) {
+            GstClockTime duration;
+
+            /* this was the duration of the previous chain */
+            duration = ogg->current_time - current_chain->start_time;
+            /* the new chain time starts at duration + begin_time */
+            chain_time = duration + current_chain->begin_time;
+
             /* remove existing pads */
             gst_ogg_demux_deactivate_current_chain (ogg);
-            /* switch to new-chain mode */
-            ogg->state = OGG_STATE_NEW_CHAIN;
+          } else {
+            /* non previous chain, start at configured current time */
+            chain_time = ogg->current_time;
           }
           if (ogg->building_chain == NULL) {
-            ogg->building_chain = gst_ogg_chain_new (ogg);
-            ogg->building_chain->offset = 0;
+            GstOggChain *newchain;
+
+            newchain = gst_ogg_chain_new (ogg);
+            newchain->offset = 0;
+            /* set new chain begin time aligned with end time of old chain */
+            newchain->begin_time = chain_time;
+            GST_DEBUG ("new chain, begin time %" GST_TIME_FORMAT,
+                GST_TIME_ARGS (chain_time));
+
+            /* and this is the one we are building now */
+            ogg->building_chain = newchain;
           }
           pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno);
         }
@@ -2043,12 +2083,31 @@ gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
       if (pad) {
         result = gst_ogg_pad_submit_page (pad, &page);
       } else {
-        GST_LOG_OBJECT (ogg, "cannot find pad for serial %08lx", serialno);
+        /* no pad, this is pretty fatal. This means an ogg page without bos
+         * has been seen for this serialno. could just ignore it too... */
+        goto unknown_pad;
       }
     }
   }
 
   return result;
+
+unknown_chain:
+  {
+    GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
+        ("unknown ogg chain for serial %08x detected", serialno),
+        ("unknown ogg chain for serial %08x detected", serialno));
+    gst_ogg_demux_send_event (ogg, gst_event_new_eos ());
+    return GST_FLOW_ERROR;
+  }
+unknown_pad:
+  {
+    GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
+        ("unknown ogg pad for serial %08d detected", serialno),
+        ("unknown ogg pad for serial %08d detected", serialno));
+    gst_ogg_demux_send_event (ogg, gst_event_new_eos ());
+    return GST_FLOW_ERROR;
+  }
 }
 
 static void
index f440f19..1841002 100644 (file)
@@ -331,6 +331,7 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
 {
   guint64 render_offset, in_offset;
   GstClockTime time, render_time;
+  GstClockTimeDiff render_diff;
   GstBaseAudioSink *sink = GST_BASE_AUDIO_SINK (bsink);
   gint64 diff;
 
@@ -344,15 +345,16 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
   GST_DEBUG ("time %" GST_TIME_FORMAT ", offset %llu, start %" GST_TIME_FORMAT,
       GST_TIME_ARGS (time), in_offset, GST_TIME_ARGS (bsink->discont_start));
 
+  render_diff = time - bsink->discont_start;
   /* samples should be rendered based on their timestamp. All samples
    * arriving before the discont_start are to be thrown away */
   /* FIXME, for now we drop the sample completely, we should
    * in fact clip the sample. Same for the segment_stop, actually. */
-  if (time < bsink->discont_start)
+  if (render_diff < 0)
     return GST_FLOW_OK;
 
   /* bring buffer timestamp to stream time */
-  render_time = time - bsink->discont_start;
+  render_time = render_diff;
   /* add base time to get absolute clock time */
   render_time += gst_element_get_base_time (GST_ELEMENT (bsink));
   /* and bring the time to the offset in the buffer */
index 3f46c1a..7053081 100644 (file)
@@ -729,7 +729,7 @@ add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad)
   /* this is only for debugging */
   parent = gst_pad_get_parent_element (srcpad);
   if (parent) {
-    GST_DEBUG ("Adding sink with state %d (parent: %d, peer: %d)\n",
+    GST_DEBUG ("Adding sink with state %d (parent: %d, peer: %d)",
         GST_STATE (sink), GST_STATE (play_bin), GST_STATE (parent));
     gst_object_unref (parent);
   }