2008-08-27 Wim Taymans <wim.taymans@collabora.co.uk>
+ * gst/realmedia/rdtdepay.c: (gst_rdt_depay_init),
+ (gst_rdt_depay_setcaps), (gst_rdt_depay_sink_event),
+ (create_segment_event), (gst_rdt_depay_push),
+ (gst_rdt_depay_handle_data), (gst_rdt_depay_change_state):
+ * gst/realmedia/rdtdepay.h:
+ Parse other values from the incomming caps.
+ Add event handler to handle flushing and segments.
+ Create segment events.
+
+ * gst/realmedia/rdtjitterbuffer.c: (rdt_jitter_buffer_insert):
+ Do skew correction based on RDT timestamps.
+
+ * gst/realmedia/rdtmanager.c: (activate_session),
+ (gst_rdt_manager_parse_caps), (gst_rdt_manager_setcaps),
+ (create_recv_rtp):
+ Parse caps to get the clockrate needed for the jitterbuffer.
+
+ * gst/realmedia/rmdemux.c: (gst_rmdemux_parse_video_packet):
+ Apply timestamp fixup after correcting for initial timestamp and
+ internal base timestamp corrections.
+
+2008-08-27 Wim Taymans <wim.taymans@collabora.co.uk>
+
* gst/realmedia/rdtdepay.c: (gst_rdt_depay_handle_data),
(gst_rdt_depay_change_state):
* gst/realmedia/rdtdepay.h:
element, GstStateChange transition);
static gboolean gst_rdt_depay_setcaps (GstPad * pad, GstCaps * caps);
+static gboolean gst_rdt_depay_sink_event (GstPad * pad, GstEvent * event);
static GstFlowReturn gst_rdt_depay_chain (GstPad * pad, GstBuffer * buf);
static void
rdtdepay->sinkpad =
gst_pad_new_from_static_template (&gst_rdt_depay_sink_template, "sink");
gst_pad_set_chain_function (rdtdepay->sinkpad, gst_rdt_depay_chain);
+ gst_pad_set_event_function (rdtdepay->sinkpad, gst_rdt_depay_sink_event);
gst_pad_set_setcaps_function (rdtdepay->sinkpad, gst_rdt_depay_setcaps);
gst_element_add_pad (GST_ELEMENT_CAST (rdtdepay), rdtdepay->sinkpad);
GstRDTDepay *rdtdepay;
GstCaps *srccaps;
gint clock_rate = 1000; /* default */
- const GValue *config;
+ const GValue *value;
GstBuffer *header;
rdtdepay = GST_RDT_DEPAY (GST_PAD_PARENT (pad));
gst_structure_get_int (structure, "clock-rate", &clock_rate);
/* config contains the RealMedia header as a buffer. */
- config = gst_structure_get_value (structure, "config");
- if (!config)
+ value = gst_structure_get_value (structure, "config");
+ if (!value)
goto no_header;
- header = gst_value_get_buffer (config);
+ header = gst_value_get_buffer (value);
if (!header)
goto no_header;
+ /* get other values for newsegment */
+ value = gst_structure_get_value (structure, "npt-start");
+ if (value && G_VALUE_HOLDS_UINT64 (value))
+ rdtdepay->npt_start = g_value_get_uint64 (value);
+ else
+ rdtdepay->npt_start = 0;
+ GST_DEBUG_OBJECT (rdtdepay, "NPT start %" G_GUINT64_FORMAT,
+ rdtdepay->npt_start);
+
+ value = gst_structure_get_value (structure, "npt-stop");
+ if (value && G_VALUE_HOLDS_UINT64 (value))
+ rdtdepay->npt_stop = g_value_get_uint64 (value);
+ else
+ rdtdepay->npt_stop = -1;
+
+ GST_DEBUG_OBJECT (rdtdepay, "NPT stop %" G_GUINT64_FORMAT,
+ rdtdepay->npt_stop);
+
+ value = gst_structure_get_value (structure, "play-speed");
+ if (value && G_VALUE_HOLDS_DOUBLE (value))
+ rdtdepay->play_speed = g_value_get_double (value);
+ else
+ rdtdepay->play_speed = 1.0;
+
+ value = gst_structure_get_value (structure, "play-scale");
+ if (value && G_VALUE_HOLDS_DOUBLE (value))
+ rdtdepay->play_scale = g_value_get_double (value);
+ else
+ rdtdepay->play_scale = 1.0;
+
/* caps seem good, configure element */
rdtdepay->clock_rate = clock_rate;
}
}
+static gboolean
+gst_rdt_depay_sink_event (GstPad * pad, GstEvent * event)
+{
+ GstRDTDepay *depay;
+ gboolean res = TRUE;
+
+ depay = GST_RDT_DEPAY (GST_OBJECT_PARENT (pad));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_FLUSH_STOP:
+ res = gst_pad_push_event (depay->srcpad, event);
+
+ gst_segment_init (&depay->segment, GST_FORMAT_UNDEFINED);
+ depay->need_newsegment = TRUE;
+ depay->next_seqnum = -1;
+ break;
+ case GST_EVENT_NEWSEGMENT:
+ {
+ gboolean update;
+ gdouble rate;
+ GstFormat fmt;
+ gint64 start, stop, position;
+
+ gst_event_parse_new_segment (event, &update, &rate, &fmt, &start, &stop,
+ &position);
+
+ gst_segment_set_newsegment (&depay->segment, update, rate, fmt,
+ start, stop, position);
+
+ /* don't pass the event downstream, we generate our own segment
+ * including the NTP time and other things we receive in caps */
+ gst_event_unref (event);
+ break;
+ }
+ default:
+ /* pass other events forward */
+ res = gst_pad_push_event (depay->srcpad, event);
+ break;
+ }
+ return res;
+}
+
+static GstEvent *
+create_segment_event (GstRDTDepay * depay, gboolean update,
+ GstClockTime position)
+{
+ GstEvent *event;
+ GstClockTime stop;
+
+ if (depay->npt_stop != -1)
+ stop = depay->npt_stop - depay->npt_start;
+ else
+ stop = -1;
+
+ event = gst_event_new_new_segment_full (update, depay->play_speed,
+ depay->play_scale, GST_FORMAT_TIME, position, stop,
+ position + depay->npt_start);
+
+ return event;
+}
+
static GstFlowReturn
gst_rdt_depay_push (GstRDTDepay * rdtdepay, GstBuffer * buffer)
{
GstFlowReturn ret;
+ if (rdtdepay->need_newsegment) {
+ GstEvent *event;
+
+ event = create_segment_event (rdtdepay, FALSE, 0);
+ gst_pad_push_event (rdtdepay->srcpad, event);
+
+ rdtdepay->need_newsegment = FALSE;
+ }
+
gst_buffer_set_caps (buffer, GST_PAD_CAPS (rdtdepay->srcpad));
if (rdtdepay->discont) {
guint32 timestamp;
gint gap;
guint16 seqnum;
- gboolean discont;
/* get pointers to the packet data */
gst_rdt_packet_data_peek_data (packet, &data, &size);
case GST_STATE_CHANGE_NULL_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
+ gst_segment_init (&rdtdepay->segment, GST_FORMAT_UNDEFINED);
rdtdepay->next_seqnum = -1;
+ rdtdepay->need_newsegment = TRUE;
break;
default:
break;
GstPad *srcpad;
guint clock_rate;
+ GstClockTime npt_start;
+ GstClockTime npt_stop;
+ gdouble play_speed;
+ gdouble play_scale;
+
guint32 next_seqnum;
gboolean discont;
+ gboolean need_newsegment;
+ GstSegment segment;
GstBuffer *header;
};
g_return_val_if_fail (more == TRUE, FALSE);
seqnum = gst_rdt_packet_data_get_seq (&packet);
+ /* do skew calculation by measuring the difference between rtptime and the
+ * receive time, this function will retimestamp @buf with the skew corrected
+ * running time. */
+ rtptime = gst_rdt_packet_data_get_timestamp (&packet);
/* loop the list to skip strictly smaller seqnum buffers */
for (list = jbuf->packets->head; list; list = g_list_next (list)) {
break;
}
- /* do skew calculation by measuring the difference between rtptime and the
- * receive time, this function will retimestamp @buf with the skew corrected
- * running time. */
- //rtptime = gst_rtp_buffer_get_timestamp (buf);
- rtptime = 0;
+
if (clock_rate) {
time = calculate_skew (jbuf, rtptime, time, clock_rate);
GST_BUFFER_TIMESTAMP (buf) = time;
GstPadTemplate * templ, const gchar * name);
static void gst_rdt_manager_release_pad (GstElement * element, GstPad * pad);
+static gboolean gst_rdt_manager_parse_caps (GstRDTManager * rdtmanager,
+ GstRDTManagerSession * session, GstCaps * caps);
+static gboolean gst_rdt_manager_setcaps (GstPad * pad, GstCaps * caps);
+
static GstFlowReturn gst_rdt_manager_chain_rdt (GstPad * pad,
GstBuffer * buffer);
static GstFlowReturn gst_rdt_manager_chain_rtcp (GstPad * pad,
guint8 pt;
gint clock_rate;
GstCaps *caps;
+ gint64 clock_base;
GstSegment segment;
g_signal_emitv (args, gst_rdt_manager_signals[SIGNAL_REQUEST_PT_MAP], 0,
&ret);
- caps = (GstCaps *) g_value_get_boxed (&ret);
+ g_value_unset (&args[0]);
+ g_value_unset (&args[1]);
+ g_value_unset (&args[2]);
+ caps = (GstCaps *) g_value_dup_boxed (&ret);
+ g_value_unset (&ret);
+
+ if (caps)
+ gst_rdt_manager_parse_caps (rdtmanager, session, caps);
name = g_strdup_printf ("recv_rtp_src_%d_%u_%d", session->id, ssrc, pt);
klass = GST_ELEMENT_GET_CLASS (rdtmanager);
g_free (name);
gst_pad_set_caps (session->recv_rtp_src, caps);
+ gst_caps_unref (caps);
gst_pad_set_element_private (session->recv_rtp_src, session);
gst_pad_set_query_function (session->recv_rtp_src, gst_rdt_manager_query_src);
}
}
+static gboolean
+gst_rdt_manager_parse_caps (GstRDTManager * rdtmanager,
+ GstRDTManagerSession * session, GstCaps * caps)
+{
+ GstStructure *caps_struct;
+ guint val;
+
+ /* first parse the caps */
+ caps_struct = gst_caps_get_structure (caps, 0);
+
+ GST_DEBUG_OBJECT (rdtmanager, "got caps");
+
+ /* we need a clock-rate to convert the rtp timestamps to GStreamer time and to
+ * measure the amount of data in the buffer */
+ if (!gst_structure_get_int (caps_struct, "clock-rate", &session->clock_rate))
+ session->clock_rate = 1000;
+
+ if (session->clock_rate <= 0)
+ goto wrong_rate;
+
+ GST_DEBUG_OBJECT (rdtmanager, "got clock-rate %d", session->clock_rate);
+
+ /* gah, clock-base is uint. If we don't have a base, we will use the first
+ * buffer timestamp as the base time. This will screw up sync but it's better
+ * than nothing. */
+ if (gst_structure_get_uint (caps_struct, "clock-base", &val))
+ session->clock_base = val;
+ else
+ session->clock_base = -1;
+
+ GST_DEBUG_OBJECT (rdtmanager, "got clock-base %" G_GINT64_FORMAT,
+ session->clock_base);
+
+ /* first expected seqnum */
+ if (gst_structure_get_uint (caps_struct, "seqnum-base", &val))
+ session->next_seqnum = val;
+ else
+ session->next_seqnum = -1;
+
+ GST_DEBUG_OBJECT (rdtmanager, "got seqnum-base %d", session->next_seqnum);
+
+ return TRUE;
+
+ /* ERRORS */
+wrong_rate:
+ {
+ GST_DEBUG_OBJECT (rdtmanager, "Invalid clock-rate %d", session->clock_rate);
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_rdt_manager_setcaps (GstPad * pad, GstCaps * caps)
+{
+ GstRDTManager *rdtmanager;
+ GstRDTManagerSession *session;
+ gboolean res;
+
+ rdtmanager = GST_RDT_MANAGER (GST_PAD_PARENT (pad));
+ /* find session */
+ session = gst_pad_get_element_private (pad);
+
+ res = gst_rdt_manager_parse_caps (rdtmanager, session, caps);
+
+ return res;
+}
+
static GstFlowReturn
gst_rdt_manager_chain_rdt (GstPad * pad, GstBuffer * buffer)
{
session->recv_rtp_sink = gst_pad_new_from_template (templ, name);
gst_pad_set_element_private (session->recv_rtp_sink, session);
+ gst_pad_set_setcaps_function (session->recv_rtp_sink,
+ gst_rdt_manager_setcaps);
gst_pad_set_chain_function (session->recv_rtp_sink,
gst_rdt_manager_chain_rdt);
gst_pad_set_active (session->recv_rtp_sink, TRUE);
stream->frag_length = 0;
gst_buffer_set_caps (out, GST_PAD_CAPS (stream->pad));
- timestamp =
- gst_rmdemux_fix_timestamp (rmdemux, stream, outdata, timestamp);
- if (rmdemux->first_ts != -1 && timestamp > rmdemux->first_ts)
- timestamp -= rmdemux->first_ts;
- else
- timestamp = 0;
+ if (timestamp != -1) {
+ if (rmdemux->first_ts != -1 && timestamp > rmdemux->first_ts)
+ timestamp -= rmdemux->first_ts;
+ else
+ timestamp = 0;
- if (rmdemux->base_ts != -1)
- timestamp += rmdemux->base_ts;
+ if (rmdemux->base_ts != -1)
+ timestamp += rmdemux->base_ts;
+ }
+ timestamp =
+ gst_rmdemux_fix_timestamp (rmdemux, stream, outdata, timestamp);
GST_BUFFER_TIMESTAMP (out) = timestamp;