From ff1503f5cf8bfc172a15b4f7e02e6d4e8ad77494 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 27 Aug 2008 15:55:05 +0000 Subject: [PATCH] gst/realmedia/rdtdepay.*: Parse other values from the incomming caps. Original commit message from CVS: * 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. --- ChangeLog | 23 +++++++ gst/realmedia/rdtdepay.c | 113 ++++++++++++++++++++++++++++++-- gst/realmedia/rdtdepay.h | 7 ++ gst/realmedia/rdtjitterbuffer.c | 10 +-- gst/realmedia/rdtmanager.c | 84 +++++++++++++++++++++++- gst/realmedia/rmdemux.c | 18 ++--- 6 files changed, 236 insertions(+), 19 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1e93d01f96..12a0604150 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2008-08-27 Wim Taymans + + * 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 * gst/realmedia/rdtdepay.c: (gst_rdt_depay_handle_data), diff --git a/gst/realmedia/rdtdepay.c b/gst/realmedia/rdtdepay.c index 3668da2f76..552b75987e 100644 --- a/gst/realmedia/rdtdepay.c +++ b/gst/realmedia/rdtdepay.c @@ -84,6 +84,7 @@ static GstStateChangeReturn gst_rdt_depay_change_state (GstElement * 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 @@ -127,6 +128,7 @@ gst_rdt_depay_init (GstRDTDepay * rdtdepay, GstRDTDepayClass * klass) 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); @@ -155,7 +157,7 @@ gst_rdt_depay_setcaps (GstPad * pad, GstCaps * caps) 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)); @@ -166,14 +168,44 @@ gst_rdt_depay_setcaps (GstPad * pad, GstCaps * caps) 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; @@ -196,11 +228,81 @@ no_header: } } +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) { @@ -224,7 +326,6 @@ gst_rdt_depay_handle_data (GstRDTDepay * rdtdepay, GstClockTime outtime, guint32 timestamp; gint gap; guint16 seqnum; - gboolean discont; /* get pointers to the packet data */ gst_rdt_packet_data_peek_data (packet, &data, &size); @@ -399,7 +500,9 @@ gst_rdt_depay_change_state (GstElement * element, GstStateChange transition) 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; diff --git a/gst/realmedia/rdtdepay.h b/gst/realmedia/rdtdepay.h index 9099a4100f..7bacae6018 100644 --- a/gst/realmedia/rdtdepay.h +++ b/gst/realmedia/rdtdepay.h @@ -47,9 +47,16 @@ struct _GstRDTDepay 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; }; diff --git a/gst/realmedia/rdtjitterbuffer.c b/gst/realmedia/rdtjitterbuffer.c index 5e622a6b93..1e34c5062c 100644 --- a/gst/realmedia/rdtjitterbuffer.c +++ b/gst/realmedia/rdtjitterbuffer.c @@ -360,6 +360,10 @@ rdt_jitter_buffer_insert (RDTJitterBuffer * jbuf, GstBuffer * buf, 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)) { @@ -385,11 +389,7 @@ rdt_jitter_buffer_insert (RDTJitterBuffer * jbuf, GstBuffer * buf, 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; diff --git a/gst/realmedia/rdtmanager.c b/gst/realmedia/rdtmanager.c index 1d4540ef6a..6dd0b41a50 100644 --- a/gst/realmedia/rdtmanager.c +++ b/gst/realmedia/rdtmanager.c @@ -136,6 +136,10 @@ static GstPad *gst_rdt_manager_request_new_pad (GstElement * element, 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, @@ -181,6 +185,7 @@ struct _GstRDTManagerSession guint8 pt; gint clock_rate; GstCaps *caps; + gint64 clock_base; GstSegment segment; @@ -279,7 +284,14 @@ activate_session (GstRDTManager * rdtmanager, GstRDTManagerSession * session, 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); @@ -288,6 +300,7 @@ activate_session (GstRDTManager * rdtmanager, GstRDTManagerSession * session, 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); @@ -651,6 +664,73 @@ duplicate: } } +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) { @@ -1072,6 +1152,8 @@ create_recv_rtp (GstRDTManager * rdtmanager, GstPadTemplate * templ, 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); diff --git a/gst/realmedia/rmdemux.c b/gst/realmedia/rmdemux.c index bf9b1dfcba..6384b12bad 100644 --- a/gst/realmedia/rmdemux.c +++ b/gst/realmedia/rmdemux.c @@ -2311,17 +2311,19 @@ gst_rmdemux_parse_video_packet (GstRMDemux * rmdemux, GstRMDemuxStream * stream, stream->frag_length = 0; gst_buffer_set_caps (out, GST_PAD_CAPS (stream->pad)); + + 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; + } 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 (rmdemux->base_ts != -1) - timestamp += rmdemux->base_ts; - GST_BUFFER_TIMESTAMP (out) = timestamp; GST_LOG_OBJECT (rmdemux, "pushing timestamp %" GST_TIME_FORMAT,