Index: ext/ffmpeg/gstffmpegdec.c
===================================================================
RCS file: /cvs/gstreamer/gst-ffmpeg/ext/ffmpeg/gstffmpegdec.c,v
retrieving revision 1.168
diff -u -r1.168 gstffmpegdec.c
--- ext/ffmpeg/gstffmpegdec.c	27 Nov 2006 18:58:38 -0000	1.168
+++ ext/ffmpeg/gstffmpegdec.c	6 Dec 2006 14:20:47 -0000
@@ -85,6 +85,14 @@
   gdouble proportion;
   GstClockTime earliest_time;
 
+  /* indicates this stream should generate a clock from the PCR */
+  gboolean provide_clock;
+
+  /* MPEG TS clock provider */
+  GstSystemClock *provided_clock;
+
+  GstClockTime start_timestamp;
+
   /* clipping segment */
   GstSegment segment;
 };
@@ -122,7 +130,8 @@
 {
   ARG_0,
   ARG_LOWRES,
-  ARG_SKIPFRAME
+  ARG_SKIPFRAME,
+  ARG_PROVIDE_CLOCK
 };
 
 static GHashTable *global_plugins;
@@ -142,6 +151,7 @@
 
 static GstStateChangeReturn gst_ffmpegdec_change_state (GstElement * element,
     GstStateChange transition);
+static GstClock* gst_ffmpegdec_provide_clock (GstElement *element);
 
 static void gst_ffmpegdec_set_property (GObject * object,
     guint prop_id, const GValue * value, GParamSpec * pspec);
@@ -266,9 +276,14 @@
         g_param_spec_enum ("lowres", "Low resolution",
             "At which resolution to decode images",
             GST_FFMPEGDEC_TYPE_LOWRES, 0, G_PARAM_READWRITE));
+    g_object_class_install_property (gobject_class, ARG_PROVIDE_CLOCK,
+        g_param_spec_boolean ("provide-clock", "Provide clock",
+            "Provide a clock for untimestamped streams",
+            FALSE, G_PARAM_READWRITE));
   }
 
   gstelement_class->change_state = gst_ffmpegdec_change_state;
+  gstelement_class->provide_clock = gst_ffmpegdec_provide_clock;
 }
 
 static void
@@ -310,6 +325,13 @@
 
   ffmpegdec->format.video.fps_n = -1;
   ffmpegdec->format.video.old_fps_n = -1;
+
+  ffmpegdec->provide_clock = FALSE;
+  ffmpegdec->provided_clock = GST_SYSTEM_CLOCK(g_object_new(GST_TYPE_SYSTEM_CLOCK, NULL));
+  /* make clock get_time return 0 until we have correct data to set
+   * the calibration*/
+  gst_clock_set_calibration (GST_CLOCK (ffmpegdec->provided_clock), 0, 0, 0, 1);
+
   gst_segment_init (&ffmpegdec->segment, GST_FORMAT_TIME);
 }
 
@@ -1190,6 +1212,10 @@
   gint have_data;
   gboolean iskeyframe;
   gboolean mode_switch;
+  gboolean got_first_keyframe = FALSE;
+  gdouble r_squared;
+  GstClockTime internal;
+  GstClock *clock = GST_CLOCK(ffmpegdec->provided_clock);
 
   *ret = GST_FLOW_OK;
   *outbuf = NULL;
@@ -1227,6 +1253,8 @@
   /* check if we are dealing with a keyframe here */
   iskeyframe = check_keyframe (ffmpegdec);
 
+  GST_DEBUG_OBJECT (ffmpegdec, "waiting_for_key %d, iskeyframe %d", ffmpegdec->waiting_for_key, iskeyframe);
+
   /* when we're waiting for a keyframe, see if we have one or drop the current
    * non-keyframe */
   if (G_UNLIKELY (ffmpegdec->waiting_for_key)) {
@@ -1235,6 +1263,7 @@
 
     /* we have a keyframe, we can stop waiting for one */
     ffmpegdec->waiting_for_key = FALSE;
+    got_first_keyframe = TRUE;
   }
 
   /* get a handle to the output buffer */
@@ -1243,33 +1272,6 @@
     goto no_output;
 
   /*
-   * Timestamps:
-   *
-   *  1) Copy parse context timestamp if present and valid (FIXME)
-   *  2) Copy input timestamp if valid
-   *  3) else interpolate from previous input timestamp
-   */
-#if 0
-  /* this does not work reliably, for some files this works fine, for other
-   * files it returns the same timestamp twice. Leaving the code here for when
-   * the parsers are improved in ffmpeg. */
-  if (ffmpegdec->pctx) {
-    GST_DEBUG_OBJECT (ffmpegdec, "picture: ffpts %"G_GUINT64_FORMAT, ffmpegdec->pctx->pts);
-    if (ffmpegdec->pctx->pts != AV_NOPTS_VALUE) {
-      in_timestamp = gst_ffmpeg_time_ff_to_gst (ffmpegdec->pctx->pts,
-            ffmpegdec->context->time_base);
-    }
-  }
-#endif
-  if (!GST_CLOCK_TIME_IS_VALID (in_timestamp)) {
-    GST_LOG_OBJECT (ffmpegdec, "using timestamp returned by ffmpeg");
-    /* Get (interpolated) timestamp from FFMPEG */
-    in_timestamp = gst_ffmpeg_time_ff_to_gst ((guint64) ffmpegdec->picture->pts,
-        ffmpegdec->context->time_base);
-  }
-  GST_BUFFER_TIMESTAMP (*outbuf) = in_timestamp;
-
-  /*
    * Duration:
    *
    *  1) Copy input duration if valid
@@ -1298,6 +1300,64 @@
     }
   }
 
+  /*
+   * Timestamps:
+   *
+   *  1) Copy parse context timestamp if present and valid (FIXME)
+   *  2) Copy input timestamp if valid
+   *  3) else interpolate from previous input timestamp
+   */
+#if 0
+  /* this does not work reliably, for some files this works fine, for other
+   * files it returns the same timestamp twice. Leaving the code here for when
+   * the parsers are improved in ffmpeg. */
+  if (ffmpegdec->pctx) {
+    GST_DEBUG_OBJECT (ffmpegdec, "picture: ffpts %"G_GUINT64_FORMAT, ffmpegdec->pctx->pts);
+    if (ffmpegdec->pctx->pts != AV_NOPTS_VALUE) {
+      in_timestamp = gst_ffmpeg_time_ff_to_gst (ffmpegdec->pctx->pts,
+            ffmpegdec->context->time_base);
+    }
+  }
+#endif
+  if (!GST_CLOCK_TIME_IS_VALID (in_timestamp)) {
+    /* Get (interpolated) timestamp from FFMPEG */
+    in_timestamp = in_duration * ffmpegdec->picture->coded_picture_number;
+    GST_LOG_OBJECT (ffmpegdec,
+                    "Incoming clock not valid, simulating clock from picture"
+                    "number and duration: "
+                    GST_TIME_FORMAT,
+                    GST_TIME_ARGS(in_timestamp));
+    GST_LOG_OBJECT (ffmpegdec, "adding observation %"G_GUINT64_FORMAT
+                      " at time %"G_GUINT64_FORMAT, in_timestamp,
+                       gst_clock_get_internal_time (clock));
+    if (got_first_keyframe)
+      {
+        internal = gst_clock_get_internal_time (clock);
+        gst_clock_set_calibration (clock,
+                                   internal, 0,
+                                   1, 1);
+        ffmpegdec->start_timestamp = in_timestamp;
+
+        GST_LOG_OBJECT (ffmpegdec, "pushing newsegment for first buffer as "
+                                   "using timestamp returned by ffmpeg");
+        gst_pad_push_event (ffmpegdec->srcpad,
+          gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0,
+                                     -1, 0));
+      }
+    else
+      {
+        gst_clock_add_observation (clock,
+          gst_clock_get_internal_time (clock),
+          in_timestamp-ffmpegdec->start_timestamp, &r_squared);
+        GST_DEBUG_OBJECT(ffmpegdec, "gst_clock_add_observation return r_squared %f",
+                         r_squared);
+
+      }
+    in_timestamp = in_timestamp - ffmpegdec->start_timestamp;
+  }
+  GST_BUFFER_TIMESTAMP (*outbuf) = in_timestamp;
+
+
   /* Take repeat_pict into account */
   if (GST_CLOCK_TIME_IS_VALID (in_duration)) {
     in_duration += in_duration * ffmpegdec->picture->repeat_pict / 2;
@@ -1805,9 +1865,13 @@
 
   oclass = (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
 
-  /* do early keyframe check pretty bad to rely on the keyframe flag in the
+  /* Keyframe checking is done in gst_ffmpegdec_video_frame, so why is this
+   * code still here? commenting out as it clashes with the keyframe check
+   * in gst_ffmpegdec_video_frame
+   *
+   * do early keyframe check pretty bad to rely on the keyframe flag in the
    * source for this as it might not even be parsed (UDP/file/..).  */
-  if (G_UNLIKELY (ffmpegdec->waiting_for_key)) {
+/*  if (G_UNLIKELY (ffmpegdec->waiting_for_key)) {
     if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DELTA_UNIT) &&
 	oclass->in_plugin->type != CODEC_TYPE_AUDIO)
       goto skip_keyframe;
@@ -1815,7 +1879,7 @@
     GST_DEBUG_OBJECT (ffmpegdec, "got keyframe");
     ffmpegdec->waiting_for_key = FALSE;
   }
-
+*/
   pending_timestamp = GST_BUFFER_TIMESTAMP (inbuf);
   pending_duration  = GST_BUFFER_DURATION (inbuf);
 
@@ -1982,12 +2046,13 @@
     gst_buffer_unref (inbuf);
     return GST_FLOW_NOT_NEGOTIATED;
   }
-skip_keyframe:
+/*skip_keyframe:
   {
     GST_DEBUG_OBJECT (ffmpegdec, "skipping non keyframe");
     gst_buffer_unref (inbuf);
     return GST_FLOW_OK;
   }
+  */
 }
 
 static GstStateChangeReturn
@@ -2029,6 +2094,9 @@
       ffmpegdec->hurry_up = ffmpegdec->context->hurry_up =
           g_value_get_enum (value);
       break;
+    case ARG_PROVIDE_CLOCK:
+      ffmpegdec->provide_clock = g_value_get_boolean (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -2048,6 +2116,9 @@
     case ARG_SKIPFRAME:
       g_value_set_enum (value, ffmpegdec->context->hurry_up);
       break;
+    case ARG_PROVIDE_CLOCK:
+      g_value_set_boolean (value, ffmpegdec->provide_clock);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -2179,3 +2250,29 @@
 
   return TRUE;
 }
+
+static GstClock*
+gst_ffmpegdec_provide_clock (GstElement *element)
+{
+  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) element;
+  GstClock *clock;
+
+  GST_OBJECT_LOCK (ffmpegdec);
+
+  if (!ffmpegdec->provide_clock)
+    goto clock_disabled;
+
+  clock=GST_CLOCK_CAST (gst_object_ref (ffmpegdec->provided_clock));
+
+  GST_OBJECT_UNLOCK (ffmpegdec);
+
+  return clock;
+
+clock_disabled:
+  {
+    GST_DEBUG_OBJECT (ffmpegdec, "provide clock disabled");
+    GST_OBJECT_UNLOCK (ffmpegdec);
+    return NULL;
+  }
+}
+
