theoraenc: implement upstream keyframe force
authorOlivier Crete <tester at tester.ca>
Tue, 14 Apr 2009 16:16:37 +0000 (18:16 +0200)
committerWim Taymans <wim.taymans@collabora.co.uk>
Tue, 14 Apr 2009 16:21:45 +0000 (18:21 +0200)
Implement handling of upstream keyframe forcing.
Update the design documents too.
Fixes #578656

docs/design/draft-keyframe-force.txt
ext/theora/gsttheoraenc.h
ext/theora/theoraenc.c

index d315e8d..c1d408a 100644 (file)
@@ -48,7 +48,7 @@ Implementation:
                                         triggered the event.
         (G_TYPE_UINT64)"stream-time"  : the stream position that triggered the
                                         event.
-        (G_TYPE_UINT64)"running_time" : the running time of the stream when the 
+        (G_TYPE_UINT64)"running-time" : the running time of the stream when the 
                                         event was triggered.
         ....                          : optional other data fields.
 
@@ -70,3 +70,18 @@ Implementation:
   3) The application receives the GstForceKeyUnit on a sink padprobe of the sink
      and reconfigures the sink to make it perform new actions after receiving
      the next buffer. 
+
+
+Upstream
+--------
+
+When using RTP packets can get lost or receivers can be added at any time,
+they may request a new key frame.
+
+An downstream element sends an upstream "GstForceKeyUnit" event up the
+pipeline.
+
+When an element produces some kind of key unit in output, but has
+no such concept in its input (like an encoder that takes raw frames),
+it consumes the event (doesn't pass it upstream), and instead sends
+a downstream GstForceKeyUnit event and a new keyframe.
index cad5496..9d45414 100644 (file)
@@ -99,6 +99,8 @@ struct _GstTheoraEnc
   GstClockTime expected_ts;
   gboolean next_discont;
 
+  gboolean force_keyframe;
+
   guint packetno;
   guint64 bytes_out;
   guint64 granulepos_offset;
index 7d79091..6704a57 100644 (file)
@@ -184,6 +184,7 @@ GST_STATIC_PAD_TEMPLATE ("src",
 GST_BOILERPLATE (GstTheoraEnc, gst_theora_enc, GstElement, GST_TYPE_ELEMENT);
 
 static gboolean theora_enc_sink_event (GstPad * pad, GstEvent * event);
+static gboolean theora_enc_src_event (GstPad * pad, GstEvent * event);
 static GstFlowReturn theora_enc_chain (GstPad * pad, GstBuffer * buffer);
 static GstStateChangeReturn theora_enc_change_state (GstElement * element,
     GstStateChange transition);
@@ -292,6 +293,7 @@ gst_theora_enc_init (GstTheoraEnc * enc, GstTheoraEncClass * g_class)
 
   enc->srcpad =
       gst_pad_new_from_static_template (&theora_enc_src_factory, "src");
+  gst_pad_set_event_function (enc->srcpad, theora_enc_src_event);
   gst_element_add_pad (GST_ELEMENT (enc), enc->srcpad);
 
   gst_segment_init (&enc->segment, GST_FORMAT_UNDEFINED);
@@ -668,6 +670,41 @@ theora_enc_sink_event (GstPad * pad, GstEvent * event)
 }
 
 static gboolean
+theora_enc_src_event (GstPad * pad, GstEvent * event)
+{
+  GstTheoraEnc *enc;
+  gboolean res = TRUE;
+
+  enc = GST_THEORA_ENC (GST_PAD_PARENT (pad));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_CUSTOM_UPSTREAM:
+    {
+      const GstStructure *s;
+
+      s = gst_event_get_structure (event);
+
+      if (gst_structure_has_name (s, "GstForceKeyUnit")) {
+        GST_OBJECT_LOCK (enc);
+        enc->force_keyframe = TRUE;
+        GST_OBJECT_UNLOCK (enc);
+        /* consume the event */
+        res = TRUE;
+        gst_event_unref (event);
+      } else {
+        res = gst_pad_push_event (enc->sinkpad, event);
+      }
+      break;
+    }
+    default:
+      res = gst_pad_push_event (enc->sinkpad, event);
+      break;
+  }
+
+  return res;
+}
+
+static gboolean
 theora_enc_is_discontinuous (GstTheoraEnc * enc, GstClockTime timestamp,
     GstClockTime duration)
 {
@@ -704,6 +741,7 @@ theora_enc_chain (GstPad * pad, GstBuffer * buffer)
   ogg_packet op;
   GstClockTime timestamp, duration, running_time;
   GstFlowReturn ret;
+  gboolean force_keyframe;
 
   enc = GST_THEORA_ENC (GST_PAD_PARENT (pad));
 
@@ -716,9 +754,34 @@ theora_enc_chain (GstPad * pad, GstBuffer * buffer)
    */
   timestamp = GST_BUFFER_TIMESTAMP (buffer);
   duration = GST_BUFFER_DURATION (buffer);
+
   running_time =
       gst_segment_to_running_time (&enc->segment, GST_FORMAT_TIME, timestamp);
 
+  /* see if we need to schedule a keyframe */
+  GST_OBJECT_LOCK (enc);
+  force_keyframe = enc->force_keyframe;
+  enc->force_keyframe = FALSE;
+  GST_OBJECT_UNLOCK (enc);
+
+  if (force_keyframe) {
+    GstClockTime stream_time;
+    GstStructure *s;
+
+    stream_time = gst_segment_to_stream_time (&enc->segment,
+        GST_FORMAT_TIME, timestamp);
+
+    s = gst_structure_new ("GstForceKeyUnit",
+        "timestamp", G_TYPE_UINT64, timestamp,
+        "stream-time", G_TYPE_UINT64, stream_time,
+        "running-time", G_TYPE_UINT64, running_time, NULL);
+
+    theora_enc_force_keyframe (enc);
+
+    gst_pad_push_event (enc->srcpad,
+        gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s));
+  }
+
   /* make sure we copy the discont flag to the next outgoing buffer when it's
    * set on the incomming buffer */
   if (GST_BUFFER_IS_DISCONT (buffer)) {
@@ -1044,6 +1107,7 @@ theora_enc_change_state (GstElement * element, GstStateChange transition)
       theora_info_init (&enc->info);
       theora_comment_init (&enc->comment);
       enc->packetno = 0;
+      enc->force_keyframe = FALSE;
       break;
     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
       break;