static gboolean gst_rdt_manager_src_activate_mode (GstPad * pad, GstObject * parent, GstPadMode mode, gboolean active) { gboolean result; GstRDTManager *rdtmanager; GstRDTManagerSession *session; session = gst_pad_get_element_private (pad); rdtmanager = session->dec; switch (mode) { case GST_PAD_MODE_PUSH: if (active) { /* allow data processing */ JBUF_LOCK (session); GST_DEBUG_OBJECT (rdtmanager, "Enabling pop on queue"); /* Mark as non flushing */ session->srcresult = GST_FLOW_OK; gst_segment_init (&session->segment, GST_FORMAT_TIME); session->last_popped_seqnum = -1; session->last_out_time = -1; session->next_seqnum = -1; session->eos = FALSE; JBUF_UNLOCK (session); /* start pushing out buffers */ GST_DEBUG_OBJECT (rdtmanager, "Starting task on srcpad"); result = gst_pad_start_task (pad, (GstTaskFunction) gst_rdt_manager_loop, pad, NULL); } else { /* make sure all data processing stops ASAP */ JBUF_LOCK (session); /* mark ourselves as flushing */ session->srcresult = GST_FLOW_FLUSHING; GST_DEBUG_OBJECT (rdtmanager, "Disabling pop on queue"); /* this unblocks any waiting pops on the src pad task */ JBUF_SIGNAL (session); /* unlock clock, we just unschedule, the entry will be released by * the locking streaming thread. */ if (session->clock_id) gst_clock_id_unschedule (session->clock_id); JBUF_UNLOCK (session); /* NOTE this will hardlock if the state change is called from the src pad * task thread because we will _join() the thread. */ GST_DEBUG_OBJECT (rdtmanager, "Stopping task on srcpad"); result = gst_pad_stop_task (pad); } break; default: result = FALSE; break; } return result; }
static GstFlowReturn gst_rdt_manager_handle_data_packet (GstRDTManagerSession * session, GstClockTime timestamp, GstRDTPacket * packet) { GstRDTManager *rdtmanager; guint16 seqnum; gboolean tail; GstFlowReturn res; GstBuffer *buffer; rdtmanager = session->dec; res = GST_FLOW_OK; seqnum = 0; GST_DEBUG_OBJECT (rdtmanager, "Received packet #%d at time %" GST_TIME_FORMAT, seqnum, GST_TIME_ARGS (timestamp)); buffer = gst_rdt_packet_to_buffer (packet); JBUF_LOCK_CHECK (session, out_flushing); /* insert the packet into the queue now, FIXME, use seqnum */ if (!rdt_jitter_buffer_insert (session->jbuf, buffer, timestamp, session->clock_rate, &tail)) goto duplicate; /* signal addition of new buffer when the _loop is waiting. */ if (session->waiting) JBUF_SIGNAL (session); finished: JBUF_UNLOCK (session); return res; /* ERRORS */ out_flushing: { res = session->srcresult; GST_DEBUG_OBJECT (rdtmanager, "flushing %s", gst_flow_get_name (res)); gst_buffer_unref (buffer); goto finished; } duplicate: { GST_WARNING_OBJECT (rdtmanager, "Duplicate packet #%d detected, dropping", seqnum); session->num_duplicates++; gst_buffer_unref (buffer); goto finished; } }
static void gst_rtp_jitter_buffer_flush_start (GstRtpJitterBuffer * jitterbuffer) { GstRtpJitterBufferPrivate *priv; priv = jitterbuffer->priv; JBUF_LOCK (priv); /* mark ourselves as flushing */ priv->srcresult = GST_FLOW_WRONG_STATE; GST_DEBUG_OBJECT (jitterbuffer, "Disabling pop on queue"); /* this unblocks any waiting pops on the src pad task */ JBUF_SIGNAL (priv); /* unlock clock, we just unschedule, the entry will be released by the * locking streaming thread. */ if (priv->clock_id) gst_clock_id_unschedule (priv->clock_id); JBUF_UNLOCK (priv); }
static GstFlowReturn gst_rtp_jitter_buffer_chain (GstPad * pad, GstBuffer * buffer) { GstRtpJitterBuffer *jitterbuffer; GstRtpJitterBufferPrivate *priv; guint16 seqnum; GstFlowReturn ret = GST_FLOW_OK; GstClockTime timestamp; guint64 latency_ts; gboolean tail; jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad)); if (!gst_rtp_buffer_validate (buffer)) goto invalid_buffer; priv = jitterbuffer->priv; if (priv->last_pt != gst_rtp_buffer_get_payload_type (buffer)) { GstCaps *caps; priv->last_pt = gst_rtp_buffer_get_payload_type (buffer); /* reset clock-rate so that we get a new one */ priv->clock_rate = -1; /* Try to get the clock-rate from the caps first if we can. If there are no * caps we must fire the signal to get the clock-rate. */ if ((caps = GST_BUFFER_CAPS (buffer))) { gst_jitter_buffer_sink_parse_caps (jitterbuffer, caps); } } if (priv->clock_rate == -1) { guint8 pt; /* no clock rate given on the caps, try to get one with the signal */ pt = gst_rtp_buffer_get_payload_type (buffer); gst_rtp_jitter_buffer_get_clock_rate (jitterbuffer, pt); if (priv->clock_rate == -1) goto not_negotiated; } /* take the timestamp of the buffer. This is the time when the packet was * received and is used to calculate jitter and clock skew. We will adjust * this timestamp with the smoothed value after processing it in the * jitterbuffer. */ timestamp = GST_BUFFER_TIMESTAMP (buffer); /* bring to running time */ timestamp = gst_segment_to_running_time (&priv->segment, GST_FORMAT_TIME, timestamp); seqnum = gst_rtp_buffer_get_seq (buffer); GST_DEBUG_OBJECT (jitterbuffer, "Received packet #%d at time %" GST_TIME_FORMAT, seqnum, GST_TIME_ARGS (timestamp)); JBUF_LOCK_CHECK (priv, out_flushing); /* don't accept more data on EOS */ if (priv->eos) goto have_eos; /* let's check if this buffer is too late, we can only accept packets with * bigger seqnum than the one we last pushed. */ if (priv->last_popped_seqnum != -1) { gint gap; gap = gst_rtp_buffer_compare_seqnum (priv->last_popped_seqnum, seqnum); if (gap <= 0) { /* priv->last_popped_seqnum >= seqnum, this packet is too late or the * sender might have been restarted with different seqnum. */ if (gap < -100) { GST_DEBUG_OBJECT (jitterbuffer, "reset: buffer too old %d", gap); priv->last_popped_seqnum = -1; priv->next_seqnum = -1; } else { goto too_late; } } else { /* priv->last_popped_seqnum < seqnum, this is a new packet */ if (gap > 3000) { GST_DEBUG_OBJECT (jitterbuffer, "reset: too many dropped packets %d", gap); priv->last_popped_seqnum = -1; priv->next_seqnum = -1; } } } /* let's drop oldest packet if the queue is already full and drop-on-latency * is set. We can only do this when there actually is a latency. When no * latency is set, we just pump it in the queue and let the other end push it * out as fast as possible. */ if (priv->latency_ms && priv->drop_on_latency) { latency_ts = gst_util_uint64_scale_int (priv->latency_ms, priv->clock_rate, 1000); if (rtp_jitter_buffer_get_ts_diff (priv->jbuf) >= latency_ts) { GstBuffer *old_buf; GST_DEBUG_OBJECT (jitterbuffer, "Queue full, dropping old packet #%d", seqnum); old_buf = rtp_jitter_buffer_pop (priv->jbuf); gst_buffer_unref (old_buf); } } /* now insert the packet into the queue in sorted order. This function returns * FALSE if a packet with the same seqnum was already in the queue, meaning we * have a duplicate. */ if (!rtp_jitter_buffer_insert (priv->jbuf, buffer, timestamp, priv->clock_rate, &tail)) goto duplicate; /* signal addition of new buffer when the _loop is waiting. */ if (priv->waiting) JBUF_SIGNAL (priv); /* let's unschedule and unblock any waiting buffers. We only want to do this * when the tail buffer changed */ if (priv->clock_id && tail) { GST_DEBUG_OBJECT (jitterbuffer, "Unscheduling waiting buffer, new tail buffer"); gst_clock_id_unschedule (priv->clock_id); } GST_DEBUG_OBJECT (jitterbuffer, "Pushed packet #%d, now %d packets", seqnum, rtp_jitter_buffer_num_packets (priv->jbuf)); finished: JBUF_UNLOCK (priv); gst_object_unref (jitterbuffer); return ret; /* ERRORS */ invalid_buffer: { /* this is not fatal but should be filtered earlier */ GST_ELEMENT_WARNING (jitterbuffer, STREAM, DECODE, (NULL), ("Received invalid RTP payload, dropping")); gst_buffer_unref (buffer); gst_object_unref (jitterbuffer); return GST_FLOW_OK; } not_negotiated: { GST_WARNING_OBJECT (jitterbuffer, "No clock-rate in caps!"); gst_buffer_unref (buffer); gst_object_unref (jitterbuffer); return GST_FLOW_OK; } out_flushing: { ret = priv->srcresult; GST_DEBUG_OBJECT (jitterbuffer, "flushing %s", gst_flow_get_name (ret)); gst_buffer_unref (buffer); goto finished; } have_eos: { ret = GST_FLOW_UNEXPECTED; GST_WARNING_OBJECT (jitterbuffer, "we are EOS, refusing buffer"); gst_buffer_unref (buffer); goto finished; } too_late: { GST_WARNING_OBJECT (jitterbuffer, "Packet #%d too late as #%d was already" " popped, dropping", seqnum, priv->last_popped_seqnum); priv->num_late++; gst_buffer_unref (buffer); goto finished; } duplicate: { GST_WARNING_OBJECT (jitterbuffer, "Duplicate packet #%d detected, dropping", seqnum); priv->num_duplicates++; gst_buffer_unref (buffer); goto finished; } }
static gboolean gst_rtp_jitter_buffer_sink_event (GstPad * pad, GstEvent * event) { gboolean ret = TRUE; GstRtpJitterBuffer *jitterbuffer; GstRtpJitterBufferPrivate *priv; jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad)); priv = jitterbuffer->priv; GST_DEBUG_OBJECT (jitterbuffer, "received %s", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_NEWSEGMENT: { GstFormat format; gdouble rate, arate; gint64 start, stop, time; gboolean update; gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, &start, &stop, &time); /* we need time for now */ if (format != GST_FORMAT_TIME) goto newseg_wrong_format; GST_DEBUG_OBJECT (jitterbuffer, "newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time)); /* now configure the values, we need these to time the release of the * buffers on the srcpad. */ gst_segment_set_newsegment_full (&priv->segment, update, rate, arate, format, start, stop, time); /* FIXME, push SEGMENT in the queue. Sorting order might be difficult. */ ret = gst_pad_push_event (priv->srcpad, event); break; } case GST_EVENT_FLUSH_START: gst_rtp_jitter_buffer_flush_start (jitterbuffer); ret = gst_pad_push_event (priv->srcpad, event); break; case GST_EVENT_FLUSH_STOP: ret = gst_pad_push_event (priv->srcpad, event); ret = gst_rtp_jitter_buffer_src_activate_push (priv->srcpad, TRUE); break; case GST_EVENT_EOS: { /* push EOS in queue. We always push it at the head */ JBUF_LOCK (priv); /* check for flushing, we need to discard the event and return FALSE when * we are flushing */ ret = priv->srcresult == GST_FLOW_OK; if (ret && !priv->eos) { GST_DEBUG_OBJECT (jitterbuffer, "queuing EOS"); priv->eos = TRUE; JBUF_SIGNAL (priv); } else if (priv->eos) { GST_DEBUG_OBJECT (jitterbuffer, "dropping EOS, we are already EOS"); } else { GST_DEBUG_OBJECT (jitterbuffer, "dropping EOS, reason %s", gst_flow_get_name (priv->srcresult)); } JBUF_UNLOCK (priv); gst_event_unref (event); break; } default: ret = gst_pad_push_event (priv->srcpad, event); break; } done: gst_object_unref (jitterbuffer); return ret; /* ERRORS */ newseg_wrong_format: { GST_DEBUG_OBJECT (jitterbuffer, "received non TIME newsegment"); ret = FALSE; goto done; } }
static GstStateChangeReturn gst_rtp_jitter_buffer_change_state (GstElement * element, GstStateChange transition) { GstRtpJitterBuffer *jitterbuffer; GstRtpJitterBufferPrivate *priv; GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; jitterbuffer = GST_RTP_JITTER_BUFFER (element); priv = jitterbuffer->priv; switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: break; case GST_STATE_CHANGE_READY_TO_PAUSED: JBUF_LOCK (priv); /* reset negotiated values */ priv->clock_rate = -1; priv->clock_base = -1; priv->peer_latency = 0; priv->last_pt = -1; /* block until we go to PLAYING */ priv->blocked = TRUE; /* reset skew detection initialy */ rtp_jitter_buffer_reset_skew (priv->jbuf); JBUF_UNLOCK (priv); break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: JBUF_LOCK (priv); /* unblock to allow streaming in PLAYING */ priv->blocked = FALSE; JBUF_SIGNAL (priv); JBUF_UNLOCK (priv); break; default: break; } ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: /* we are a live element because we sync to the clock, which we can only * do in the PLAYING state */ if (ret != GST_STATE_CHANGE_FAILURE) ret = GST_STATE_CHANGE_NO_PREROLL; break; case GST_STATE_CHANGE_PLAYING_TO_PAUSED: JBUF_LOCK (priv); /* block to stop streaming when PAUSED */ priv->blocked = TRUE; JBUF_UNLOCK (priv); if (ret != GST_STATE_CHANGE_FAILURE) ret = GST_STATE_CHANGE_NO_PREROLL; break; case GST_STATE_CHANGE_PAUSED_TO_READY: break; case GST_STATE_CHANGE_READY_TO_NULL: break; default: break; } return ret; }