static GstFlowReturn splitmux_part_pad_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) { GstSplitMuxPartPad *part_pad = SPLITMUX_PART_PAD_CAST (pad); GstSplitMuxPartReader *reader = part_pad->reader; GstDataQueueItem *item; GstClockTimeDiff offset; GST_LOG_OBJECT (reader, "Pad %" GST_PTR_FORMAT " %" GST_PTR_FORMAT, pad, buf); SPLITMUX_PART_LOCK (reader); if (reader->prep_state == PART_STATE_PREPARING_COLLECT_STREAMS || reader->prep_state == PART_STATE_PREPARING_MEASURE_STREAMS) { handle_buffer_measuring (reader, part_pad, buf); gst_buffer_unref (buf); SPLITMUX_PART_UNLOCK (reader); return GST_FLOW_OK; } if (!block_until_can_push (reader)) { /* Flushing */ SPLITMUX_PART_UNLOCK (reader); gst_buffer_unref (buf); return GST_FLOW_FLUSHING; } /* Adjust buffer timestamps */ offset = reader->start_offset + part_pad->segment.base; offset -= part_pad->initial_ts_offset; if (GST_BUFFER_PTS_IS_VALID (buf)) GST_BUFFER_PTS (buf) += offset; if (GST_BUFFER_DTS_IS_VALID (buf)) GST_BUFFER_DTS (buf) += offset; /* We are active, and one queue is empty, place this buffer in * the dataqueue */ GST_LOG_OBJECT (reader, "Enqueueing buffer %" GST_PTR_FORMAT, buf); item = g_slice_new (GstDataQueueItem); item->destroy = (GDestroyNotify) splitmux_part_free_queue_item; item->object = GST_MINI_OBJECT (buf); item->size = gst_buffer_get_size (buf); item->duration = GST_BUFFER_DURATION (buf); if (item->duration == GST_CLOCK_TIME_NONE) item->duration = 0; item->visible = TRUE; gst_object_ref (part_pad); SPLITMUX_PART_UNLOCK (reader); if (!gst_data_queue_push (part_pad->queue, item)) { splitmux_part_free_queue_item (item); gst_object_unref (part_pad); return GST_FLOW_FLUSHING; } gst_object_unref (part_pad); return GST_FLOW_OK; }
static GstFlowReturn gst_scream_queue_sink_chain(GstPad *pad, GstObject *parent, GstBuffer *buffer) { GstScreamQueue *self = GST_SCREAM_QUEUE(parent); GstRTPBuffer rtp_buffer = GST_RTP_BUFFER_INIT; GstFlowReturn flow_ret = GST_FLOW_OK; GstScreamDataQueueRtpItem *rtp_item; if (GST_PAD_IS_FLUSHING(pad)) { flow_ret = GST_FLOW_FLUSHING; goto end; } if (!gst_rtp_buffer_map(buffer, GST_MAP_READ, &rtp_buffer)) { flow_ret = GST_FLOW_ERROR; goto end; } rtp_item = g_slice_new(GstScreamDataQueueRtpItem); ((GstDataQueueItem *)rtp_item)->object = GST_MINI_OBJECT(buffer); ((GstDataQueueItem *)rtp_item)->size = gst_buffer_get_size(buffer); ((GstDataQueueItem *)rtp_item)->visible = TRUE; ((GstDataQueueItem *)rtp_item)->duration = GST_BUFFER_DURATION(buffer); ((GstDataQueueItem *)rtp_item)->destroy = (GDestroyNotify) gst_scream_data_queue_rtp_item_free; ((GstScreamDataQueueItem *)rtp_item)->type = GST_SCREAM_DATA_QUEUE_ITEM_TYPE_RTP; ((GstScreamDataQueueItem *)rtp_item)->rtp_ssrc = gst_rtp_buffer_get_ssrc(&rtp_buffer); rtp_item->rtp_pt = gst_rtp_buffer_get_payload_type(&rtp_buffer); rtp_item->gst_ts = GST_BUFFER_PTS(buffer); rtp_item->rtp_seq = gst_rtp_buffer_get_seq(&rtp_buffer); rtp_item->rtp_ts = gst_rtp_buffer_get_timestamp(&rtp_buffer); rtp_item->rtp_marker = gst_rtp_buffer_get_marker(&rtp_buffer); rtp_item->rtp_payload_size = gst_rtp_buffer_get_payload_len(&rtp_buffer); rtp_item->enqueued_time = get_gst_time_us(self); gst_rtp_buffer_unmap(&rtp_buffer); if (self->pass_through) { rtp_item->adapted = FALSE; GST_LOG_OBJECT(self, "passing through: pt = %u, seq: %u, pass: %u", rtp_item->rtp_pt, rtp_item->rtp_seq, self->pass_through); gst_data_queue_push(self->approved_packets, (GstDataQueueItem *)rtp_item); goto end; } GST_LOG_OBJECT(self, "queuing: pt = %u, seq: %u, pass: %u", rtp_item->rtp_pt, rtp_item->rtp_seq, self->pass_through); g_async_queue_push(self->incoming_packets, (gpointer)rtp_item); end: return flow_ret; }
static void approve_transmit_cb(guint stream_id, GstScreamQueue *self) { GstScreamDataQueueRtpItem *item; GstScreamStream *stream; g_rw_lock_reader_lock(&self->lock); stream = g_hash_table_lookup(self->streams, GUINT_TO_POINTER(stream_id)); g_rw_lock_reader_unlock(&self->lock); if ((item = gst_atomic_queue_pop(stream->packet_queue))) { stream->enqueued_payload_size -= item->rtp_payload_size; stream->enqueued_packets--; GST_LOG_OBJECT(self, "approving: pt = %u, seq: %u, pass: %u", item->rtp_pt, item->rtp_seq, self->pass_through); gst_data_queue_push(self->approved_packets, (GstDataQueueItem *)item); } else GST_LOG_OBJECT(self, "Got approve callback on an empty queue, or flushing"); }
static GstFlowReturn gst_eglglessink_queue_buffer (GstEglGlesSink * eglglessink, GstBuffer * buf) { GstDataQueueItem *item; GstFlowReturn last_flow; g_mutex_lock (eglglessink->render_lock); last_flow = eglglessink->last_flow; g_mutex_unlock (eglglessink->render_lock); if (last_flow != GST_FLOW_OK) return last_flow; item = g_slice_new0 (GstDataQueueItem); item->object = GST_MINI_OBJECT_CAST (buf); item->size = (buf ? GST_BUFFER_SIZE (buf) : 0); item->duration = (buf ? GST_BUFFER_DURATION (buf) : GST_CLOCK_TIME_NONE); item->visible = (buf ? TRUE : FALSE); item->destroy = (GDestroyNotify) queue_item_destroy; GST_DEBUG_OBJECT (eglglessink, "Queueing buffer %" GST_PTR_FORMAT, buf); if (buf) g_mutex_lock (eglglessink->render_lock); if (!gst_data_queue_push (eglglessink->queue, item)) { item->destroy (item); g_mutex_unlock (eglglessink->render_lock); GST_DEBUG_OBJECT (eglglessink, "Flushing"); return GST_FLOW_WRONG_STATE; } if (buf) { GST_DEBUG_OBJECT (eglglessink, "Waiting for buffer to be rendered"); g_cond_wait (eglglessink->render_cond, eglglessink->render_lock); GST_DEBUG_OBJECT (eglglessink, "Buffer rendered: %s", gst_flow_get_name (eglglessink->last_flow)); last_flow = eglglessink->last_flow; g_mutex_unlock (eglglessink->render_lock); } return (buf ? last_flow : GST_FLOW_OK); }
static gboolean splitmux_part_pad_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstSplitMuxPartPad *part_pad = SPLITMUX_PART_PAD_CAST (pad); GstSplitMuxPartReader *reader = part_pad->reader; gboolean ret = TRUE; SplitMuxSrcPad *target; GstDataQueueItem *item; SPLITMUX_PART_LOCK (reader); target = gst_object_ref (part_pad->target); GST_LOG_OBJECT (reader, "Pad %" GST_PTR_FORMAT " event %" GST_PTR_FORMAT, pad, event); if (part_pad->flushing && GST_EVENT_TYPE (event) != GST_EVENT_FLUSH_STOP) goto drop_event; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEGMENT:{ GstSegment *seg = &part_pad->segment; GST_LOG_OBJECT (pad, "Received segment %" GST_PTR_FORMAT, event); gst_event_copy_segment (event, seg); gst_event_copy_segment (event, &part_pad->orig_segment); if (seg->format != GST_FORMAT_TIME) goto wrong_segment; /* Adjust segment */ /* Adjust start/stop so the overall file is 0 + start_offset based */ if (seg->stop != -1) { seg->stop -= seg->start; seg->stop += seg->time + reader->start_offset; } seg->start = seg->time + reader->start_offset; seg->time += reader->start_offset; seg->position += reader->start_offset; GST_LOG_OBJECT (pad, "Adjusted segment now %" GST_PTR_FORMAT, event); /* Replace event */ gst_event_unref (event); event = gst_event_new_segment (seg); if (reader->prep_state != PART_STATE_PREPARING_COLLECT_STREAMS && reader->prep_state != PART_STATE_PREPARING_MEASURE_STREAMS) break; /* Only do further stuff with segments during initial measuring */ /* Take the first segment from the first part */ if (target->segment.format == GST_FORMAT_UNDEFINED) { gst_segment_copy_into (seg, &target->segment); GST_DEBUG_OBJECT (reader, "Target pad segment now %" GST_SEGMENT_FORMAT, &target->segment); } if (seg->stop != -1 && target->segment.stop != -1) { GstClockTime stop = seg->base + seg->stop; if (stop > target->segment.stop) { target->segment.stop = stop; GST_DEBUG_OBJECT (reader, "Adjusting segment stop by %" GST_TIME_FORMAT " output now %" GST_SEGMENT_FORMAT, GST_TIME_ARGS (reader->start_offset), &target->segment); } } GST_LOG_OBJECT (pad, "Forwarding segment %" GST_PTR_FORMAT, event); break; } case GST_EVENT_EOS:{ GST_DEBUG_OBJECT (part_pad, "State %u EOS event. MaxTS seen %" GST_TIME_FORMAT, reader->prep_state, GST_TIME_ARGS (part_pad->max_ts)); if (reader->prep_state == PART_STATE_PREPARING_COLLECT_STREAMS || reader->prep_state == PART_STATE_PREPARING_MEASURE_STREAMS) { /* Mark this pad as EOS */ part_pad->is_eos = TRUE; if (splitmux_part_is_eos_locked (reader)) { /* Finished measuring things, set state and tell the state change func * so it can seek back to the start */ GST_LOG_OBJECT (reader, "EOS while measuring streams. Resetting for ready"); reader->prep_state = PART_STATE_PREPARING_RESET_FOR_READY; SPLITMUX_PART_BROADCAST (reader); } goto drop_event; } break; } case GST_EVENT_FLUSH_START: reader->flushing = TRUE; part_pad->flushing = TRUE; GST_LOG_OBJECT (reader, "Pad %" GST_PTR_FORMAT " flushing dataqueue", part_pad); gst_data_queue_set_flushing (part_pad->queue, TRUE); SPLITMUX_PART_BROADCAST (reader); break; case GST_EVENT_FLUSH_STOP:{ gst_data_queue_set_flushing (part_pad->queue, FALSE); gst_data_queue_flush (part_pad->queue); part_pad->seen_buffer = FALSE; part_pad->flushing = FALSE; part_pad->is_eos = FALSE; reader->flushing = splitmux_is_flushing (reader); GST_LOG_OBJECT (reader, "%s pad %" GST_PTR_FORMAT " flush_stop. Overall flushing=%d", reader->path, pad, reader->flushing); SPLITMUX_PART_BROADCAST (reader); break; } default: break; } /* Don't send events downstream while preparing */ if (reader->prep_state != PART_STATE_READY) goto drop_event; /* Don't pass flush events - those are done by the parent */ if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_START || GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) goto drop_event; if (!block_until_can_push (reader)) { SPLITMUX_PART_UNLOCK (reader); gst_object_unref (target); gst_event_unref (event); return FALSE; } switch (GST_EVENT_TYPE (event)) { case GST_EVENT_GAP:{ /* FIXME: Drop initial gap (if any) in each segment, not all GAPs */ goto drop_event; } default: break; } /* We are active, and one queue is empty, place this buffer in * the dataqueue */ gst_object_ref (part_pad->queue); SPLITMUX_PART_UNLOCK (reader); GST_LOG_OBJECT (reader, "Enqueueing event %" GST_PTR_FORMAT, event); item = g_slice_new (GstDataQueueItem); item->destroy = (GDestroyNotify) splitmux_part_free_queue_item; item->object = GST_MINI_OBJECT (event); item->size = 0; item->duration = 0; if (item->duration == GST_CLOCK_TIME_NONE) item->duration = 0; item->visible = FALSE; if (!gst_data_queue_push (part_pad->queue, item)) { splitmux_part_free_queue_item (item); ret = FALSE; } gst_object_unref (part_pad->queue); gst_object_unref (target); return ret; wrong_segment: gst_event_unref (event); gst_object_unref (target); SPLITMUX_PART_UNLOCK (reader); GST_ELEMENT_ERROR (reader, STREAM, FAILED, (NULL), ("Received non-time segment - reader %s pad %" GST_PTR_FORMAT, reader->path, pad)); return FALSE; drop_event: GST_LOG_OBJECT (pad, "Dropping event %" GST_PTR_FORMAT " from %" GST_PTR_FORMAT " on %" GST_PTR_FORMAT, event, pad, target); gst_event_unref (event); gst_object_unref (target); SPLITMUX_PART_UNLOCK (reader); return TRUE; }
static void gst_scream_queue_srcpad_loop(GstScreamQueue *self) { GstScreamDataQueueItem *item; GstScreamDataQueueRtpItem *rtp_item; GstScreamStream *stream; guint stream_id; guint64 time_now_us, time_until_next_approve = 0; GstBuffer *buffer; time_now_us = get_gst_time_us(self); if (G_UNLIKELY(time_now_us == 0)) { goto end; } if (time_now_us >= self->next_approve_time) { time_until_next_approve = gst_scream_controller_approve_transmits(self->scream_controller, time_now_us); } else { GST_LOG_OBJECT(self, "Time is %" G_GUINT64_FORMAT ", waiting %" G_GUINT64_FORMAT, time_now_us, self->next_approve_time); } /* Send all approved packets */ while (!gst_data_queue_is_empty(self->approved_packets)) { if (G_UNLIKELY(!gst_data_queue_pop(self->approved_packets, (GstDataQueueItem **)&rtp_item))) { GST_WARNING_OBJECT(self, "Failed to pop from approved packets queue. Flushing?"); goto end; /* flushing */ } buffer = GST_BUFFER(((GstDataQueueItem *)rtp_item)->object); gst_pad_push(self->src_pad, buffer); GST_LOG_OBJECT(self, "pushing: pt = %u, seq: %u, pass: %u", rtp_item->rtp_pt, rtp_item->rtp_seq, self->pass_through); if (rtp_item->adapted) { guint tmp_time; stream_id = ((GstScreamDataQueueItem *)rtp_item)->rtp_ssrc; tmp_time = gst_scream_controller_packet_transmitted(self->scream_controller, stream_id, rtp_item->rtp_payload_size, rtp_item->rtp_seq, time_now_us); time_until_next_approve = MIN(time_until_next_approve, tmp_time); } g_slice_free(GstScreamDataQueueRtpItem, rtp_item); } self->next_approve_time = time_now_us + time_until_next_approve; GST_LOG_OBJECT(self, "Popping or waiting %" G_GUINT64_FORMAT, time_until_next_approve); item = (GstScreamDataQueueItem *)g_async_queue_timeout_pop(self->incoming_packets, time_until_next_approve); if (!item) { goto end; } stream_id = item->rtp_ssrc; if (item->type == GST_SCREAM_DATA_QUEUE_ITEM_TYPE_RTP) { GstScreamDataQueueRtpItem *rtp_item = (GstScreamDataQueueRtpItem *)item; stream = get_stream(self, item->rtp_ssrc, rtp_item->rtp_pt); if (!stream) { rtp_item->adapted = FALSE; GST_LOG_OBJECT(self, "!adapted, approving: pt = %u, seq: %u, pass: %u", rtp_item->rtp_pt, rtp_item->rtp_seq, self->pass_through); gst_data_queue_push(self->approved_packets, (GstDataQueueItem *)item); } else { gst_atomic_queue_push(stream->packet_queue, rtp_item); stream->enqueued_payload_size += rtp_item->rtp_payload_size; stream->enqueued_packets++; rtp_item->adapted = TRUE; self->next_approve_time = 0; gst_scream_controller_new_rtp_packet(self->scream_controller, stream_id, rtp_item->rtp_ts, rtp_item->enqueued_time, stream->enqueued_payload_size, rtp_item->rtp_payload_size); } } else { /* item->type == GST_SCREAM_DATA_QUEUE_ITEM_TYPE_RTCP */ GstScreamDataQueueRtcpItem *rtcp_item = (GstScreamDataQueueRtcpItem *)item; gst_scream_controller_incoming_feedback(self->scream_controller, stream_id, time_now_us, rtcp_item->timestamp, rtcp_item->highest_seq, rtcp_item->n_loss, rtcp_item->n_ecn, rtcp_item->qbit); ((GstDataQueueItem *)item)->destroy(item); } end: return; }