GstPadProbeReturn GstEnginePipeline::EventHandoffCallback(GstPad*, GstPadProbeInfo* info, gpointer self) { GstEnginePipeline* instance = reinterpret_cast<GstEnginePipeline*>(self); GstEvent* e = gst_pad_probe_info_get_event(info); qLog(Debug) << instance->id() << "event" << GST_EVENT_TYPE_NAME(e); if (GST_EVENT_TYPE(e) == GST_EVENT_SEGMENT && !instance->segment_start_received_) { // The segment start time is used to calculate the proper offset of data // buffers from the start of the stream const GstSegment* segment = nullptr; gst_event_parse_segment(e, &segment); instance->segment_start_ = segment->start; instance->segment_start_received_ = true; if (instance->emit_track_ended_on_segment_start_) { qLog(Debug) << "New segment started, EOS will signal on next buffer " "discontinuity"; instance->emit_track_ended_on_segment_start_ = false; instance->emit_track_ended_on_time_discontinuity_ = true; } } return GST_PAD_PROBE_OK; }
static GstPadProbeReturn encoder_appsink_event_probe (GstPad *pad, GstPadProbeInfo *info, gpointer data) { GstEvent *event = gst_pad_probe_info_get_event (info); Encoder *encoder = data; GstClockTime timestamp, running_time, stream_time; gboolean all_headers; guint count; if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { GST_ERROR ("End of Stream of encoder %s", encoder->name); *(encoder->output->eos) = TRUE; return GST_PAD_PROBE_OK; } if (!gst_video_event_is_force_key_unit (event)) { return GST_PAD_PROBE_OK; } /* force key unit event */ gst_video_event_parse_downstream_force_key_unit (event, ×tamp, &stream_time, &running_time, &all_headers, &count); if (encoder->last_segment_duration != 0) { encoder->last_running_time = timestamp; } return GST_PAD_PROBE_OK; }
GstPadProbeReturn GstEnginePipeline::EventHandoffCallback(GstPad*, GstPadProbeInfo* info, gpointer self) { GstEnginePipeline* instance = reinterpret_cast<GstEnginePipeline*>(self); GstEvent* e = gst_pad_probe_info_get_event(info); qLog(Debug) << instance->id() << "event" << GST_EVENT_TYPE_NAME(e); switch (GST_EVENT_TYPE(e)) { case GST_EVENT_SEGMENT: if (!instance->segment_start_received_) { // The segment start time is used to calculate the proper offset of data // buffers from the start of the stream const GstSegment* segment = nullptr; gst_event_parse_segment(e, &segment); instance->segment_start_ = segment->start; instance->segment_start_received_ = true; } break; default: break; } return GST_PAD_PROBE_OK; }
static GstPadProbeReturn accept_eos_probe (GstPad * pad, GstPadProbeInfo * info, gpointer data) { GstEvent *event = gst_pad_probe_info_get_event (info); GstEventType type = GST_EVENT_TYPE (event); if (type == GST_EVENT_EOS || type == GST_EVENT_FLUSH_START || type == GST_EVENT_FLUSH_STOP) { KmsElement *self; gboolean accept; self = KMS_ELEMENT (data); KMS_ELEMENT_LOCK (self); accept = self->priv->accept_eos; KMS_ELEMENT_UNLOCK (self); if (!accept) { GST_DEBUG_OBJECT (pad, "Event %s dropped", gst_event_type_get_name (type)); } return (accept) ? GST_PAD_PROBE_OK : GST_PAD_PROBE_DROP; } return GST_PAD_PROBE_OK; }
static GstPadProbeReturn kms_agnostic_bin2_src_reconfigure_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) { KmsAgnosticBin2 *self = KMS_AGNOSTIC_BIN2 (gst_pad_get_parent_element (pad)); GstPadProbeReturn ret = GST_PAD_PROBE_OK; GstEvent *event; if (self == NULL) { return GST_PAD_PROBE_OK; } if (GST_PAD_PROBE_INFO_TYPE (info) & GST_PAD_PROBE_TYPE_EVENT_BOTH) { event = gst_pad_probe_info_get_event (info); if (GST_EVENT_TYPE (event) == GST_EVENT_RECONFIGURE) { KmsAgnosticBin2 *self = user_data; GST_DEBUG_OBJECT (pad, "Received reconfigure event"); KMS_AGNOSTIC_BIN2_LOCK (self); kms_agnostic_bin2_process_pad (self, pad); ret = GST_PAD_PROBE_DROP; KMS_AGNOSTIC_BIN2_UNLOCK (self); goto end; } } end: g_object_unref (self); return ret; }
static GstPadProbeReturn caps_probe (GstPad * pad, GstPadProbeInfo * info, gpointer data) { GstEvent *event = gst_pad_probe_info_get_event (info); if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) { GstCaps *caps; GstStructure *st; gint width; GST_DEBUG ("Received event: %" GST_PTR_FORMAT, event); gst_event_parse_caps (event, &caps); st = gst_caps_get_structure (caps, 0); if (gst_structure_get_int (st, "width", &width)) { if (width == 320) { GstCaps *new_caps = gst_caps_from_string ("video/x-raw,width=640,height=480"); GST_DEBUG ("Changing caps"); g_object_set (G_OBJECT (data), "caps", new_caps, NULL); gst_caps_unref (new_caps); } else { GST_DEBUG ("Re negotiated OK"); } } } return GST_PAD_PROBE_OK; }
static GstPadProbeReturn kms_agnostic_bin2_sink_caps_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) { KmsAgnosticBin2 *self; GstCaps *current_caps; GstCaps *new_caps = NULL; GstEvent *event = gst_pad_probe_info_get_event (info); if (GST_EVENT_TYPE (event) != GST_EVENT_CAPS) { return GST_PAD_PROBE_OK; } GST_TRACE_OBJECT (pad, "Event: %" GST_PTR_FORMAT, event); self = KMS_AGNOSTIC_BIN2 (user_data); gst_event_parse_caps (event, &new_caps); if (new_caps == NULL) { GST_ERROR_OBJECT (self, "Unexpected NULL caps"); return GST_PAD_PROBE_OK; } KMS_AGNOSTIC_BIN2_LOCK (self); current_caps = self->priv->input_caps; self->priv->input_caps = gst_caps_copy (new_caps); KMS_AGNOSTIC_BIN2_UNLOCK (self); GST_TRACE_OBJECT (user_data, "New caps event: %" GST_PTR_FORMAT, event); if (current_caps != NULL) { GstStructure *st; GST_TRACE_OBJECT (user_data, "Current caps: %" GST_PTR_FORMAT, current_caps); st = gst_caps_get_structure (current_caps, 0); // Remove famerate, width, height, streamheader that make unecessary // agnostic reconstruction happen gst_structure_remove_fields (st, "width", "height", "framerate", "streamheader", "codec_data", NULL); if (!gst_caps_can_intersect (new_caps, current_caps) && !is_raw_caps (current_caps) && !is_raw_caps (new_caps)) { GST_DEBUG_OBJECT (user_data, "Caps differ caps: %" GST_PTR_FORMAT, new_caps); kms_agnostic_bin2_configure_input (self, new_caps); } gst_caps_unref (current_caps); } else { GST_DEBUG_OBJECT (user_data, "No previous caps, starting"); kms_agnostic_bin2_configure_input (self, new_caps); } return GST_PAD_PROBE_OK; }
static GstPadProbeReturn textTrackPrivateEventCallback(GstPad*, GstPadProbeInfo* info, InbandTextTrackPrivateGStreamer* track) { GstEvent* event = gst_pad_probe_info_get_event(info); switch (GST_EVENT_TYPE(event)) { case GST_EVENT_STREAM_START: track->streamChanged(); break; default: break; } return GST_PAD_PROBE_OK; }
static GstPadProbeReturn handle_output (GstPad * pad, GstPadProbeInfo * info, StreamInfo * si) { GstClockTime start, end; GstBuffer *buf; GST_LOG_OBJECT (pad, "Fired probe type 0x%x", info->type); if (info->type & GST_PAD_PROBE_TYPE_BUFFER_LIST) { g_warning ("Buffer list handling not implemented"); return GST_PAD_PROBE_DROP; } if (info->type & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM) { GstEvent *event = gst_pad_probe_info_get_event (info); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEGMENT: gst_event_copy_segment (event, &si->seg); break; case GST_EVENT_EOS: dump_times (si); break; default: break; } return GST_PAD_PROBE_PASS; } buf = gst_pad_probe_info_get_buffer (info); if (!GST_BUFFER_PTS_IS_VALID (buf)) goto done; end = start = GST_BUFFER_PTS (buf); if (GST_BUFFER_DURATION_IS_VALID (buf)) end += GST_BUFFER_DURATION (buf); gst_segment_clip (&si->seg, GST_FORMAT_TIME, start, end, &start, &end); start = gst_segment_to_stream_time (&si->seg, GST_FORMAT_TIME, start); end = gst_segment_to_stream_time (&si->seg, GST_FORMAT_TIME, end); GST_DEBUG_OBJECT (pad, "new buffer %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (end)); /* Now extend measured time range to include new times */ extend_times (si, start, end); done: return GST_PAD_PROBE_PASS; }
static GstPadProbeReturn tee_src_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) { if (GST_PAD_PROBE_INFO_TYPE (info) & GST_PAD_PROBE_TYPE_EVENT_UPSTREAM) { GstEvent *event = gst_pad_probe_info_get_event (info); if (GST_EVENT_TYPE (event) == GST_EVENT_RECONFIGURE) { // Request key frame to upstream elements kms_utils_drop_until_keyframe (pad, TRUE); return GST_PAD_PROBE_DROP; } } return GST_PAD_PROBE_OK; }
/* * FIXME: This is a hack to make x264 work. * * We have notice that x264 doesn't work if width or height is odd, * so we force a rescale increasing one pixel that dimension * when we detect this situation. */ static GstPadProbeReturn check_caps_probe (GstPad * pad, GstPadProbeInfo * info, gpointer data) { int width, height; GstCaps *filter_caps, *caps; GstElement *element; GstStructure *st; gboolean needs_filter = FALSE; GstEvent *event = gst_pad_probe_info_get_event (info); if (GST_EVENT_TYPE (event) != GST_EVENT_CAPS) { return GST_PAD_PROBE_OK; } gst_event_parse_caps (event, &caps); st = gst_caps_get_structure (caps, 0); gst_structure_get (st, "width", G_TYPE_INT, &width, NULL); gst_structure_get (st, "height", G_TYPE_INT, &height, NULL); if (width % 2) { GST_WARNING ("Width is odd"); needs_filter = TRUE; width--; } if (height % 2) { GST_WARNING ("Height is odd"); needs_filter = TRUE; height--; } if (!needs_filter) return GST_PAD_PROBE_OK; filter_caps = gst_caps_from_string ("video/x-raw,format=I420"); gst_caps_set_simple (filter_caps, "width", G_TYPE_INT, width, NULL); gst_caps_set_simple (filter_caps, "height", G_TYPE_INT, height, NULL); element = gst_pad_get_parent_element (pad); g_object_set (element, "caps", filter_caps, NULL); gst_caps_unref (filter_caps); g_object_unref (element); return GST_PAD_PROBE_OK; }
InbandTextTrackPrivateGStreamer::InbandTextTrackPrivateGStreamer(gint index, GRefPtr<GstPad> pad) : InbandTextTrackPrivate(WebVTT), TrackPrivateBaseGStreamer(this, index, pad) { m_eventProbe = gst_pad_add_probe(m_pad.get(), GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, [] (GstPad*, GstPadProbeInfo* info, gpointer userData) -> GstPadProbeReturn { auto* track = static_cast<InbandTextTrackPrivateGStreamer*>(userData); switch (GST_EVENT_TYPE(gst_pad_probe_info_get_event(info))) { case GST_EVENT_STREAM_START: track->streamChanged(); break; default: break; } return GST_PAD_PROBE_OK; }, this, nullptr); notifyTrackOfStreamChanged(); }
static GstPadProbeReturn set_caps (GstPad * pad, GstPadProbeInfo * info, gpointer data) { GstEvent *event = gst_pad_probe_info_get_event (info); GstElement *appsrc = data; GstCaps *caps; if (GST_EVENT_TYPE (event) != GST_EVENT_CAPS) return GST_PAD_PROBE_OK; gst_event_parse_caps (event, &caps); GST_DEBUG_OBJECT (appsrc, "Setting caps to: %" GST_PTR_FORMAT, caps); g_object_set (appsrc, "caps", caps, NULL); return GST_PAD_PROBE_OK; }
static GstPadProbeReturn remb_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) { RembEventManager *manager = user_data; GstEvent *event = gst_pad_probe_info_get_event (info); guint bitrate, ssrc; if (!kms_utils_remb_event_upstream_parse (event, &bitrate, &ssrc)) { return GST_PAD_PROBE_OK; } GST_TRACE_OBJECT (pad, "<%" G_GUINT32_FORMAT ", %" G_GUINT32_FORMAT ">", ssrc, bitrate); remb_event_manager_update_min (manager, bitrate, ssrc); return GST_PAD_PROBE_DROP; }
static GstPadProbeReturn tag_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer data) { GstEvent *event = gst_pad_probe_info_get_event (info); if (GST_EVENT_TYPE (event) == GST_EVENT_TAG) { KmsEncTreeBin *self = data; GstTagList *taglist; guint bitrate; gst_event_parse_tag (event, &taglist); if (gst_tag_list_get_uint (taglist, "bitrate", &bitrate)) { self->priv->tag_bitrate = bitrate; kms_enc_tree_bin_set_target_bitrate (self); } } return GST_PAD_PROBE_OK; }
static GstPadProbeReturn input_bin_src_caps_probe (GstPad * pad, GstPadProbeInfo * info, gpointer bin) { KmsAgnosticBin2 *self = KMS_AGNOSTIC_BIN2 (GST_OBJECT_PARENT (bin)); GstEvent *event = gst_pad_probe_info_get_event (info); GstCaps *current_caps; if (self == NULL) { GST_WARNING_OBJECT (bin, "Parent agnosticbin seems to be released"); return GST_PAD_PROBE_OK; } GST_TRACE_OBJECT (self, "Event in parser pad: %" GST_PTR_FORMAT, event); if (GST_EVENT_TYPE (event) != GST_EVENT_CAPS) { return GST_PAD_PROBE_OK; } KMS_AGNOSTIC_BIN2_LOCK (self); self->priv->started = TRUE; if (self->priv->input_bin_src_caps != NULL) { gst_caps_unref (self->priv->input_bin_src_caps); } gst_event_parse_caps (event, ¤t_caps); self->priv->input_bin_src_caps = gst_caps_copy (current_caps); kms_agnostic_bin2_insert_bin (self, GST_BIN (bin)); GST_INFO_OBJECT (self, "Setting current caps to: %" GST_PTR_FORMAT, current_caps); kms_element_for_each_src_pad (GST_ELEMENT (self), (KmsPadIterationAction) add_linked_pads, self); KMS_AGNOSTIC_BIN2_UNLOCK (self); return GST_PAD_PROBE_REMOVE; }
static GstPadProbeReturn accept_eos_probe (GstPad * pad, GstPadProbeInfo * info, gpointer data) { GstEvent *event = gst_pad_probe_info_get_event (info); if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { KmsElement *self; gboolean accept; self = KMS_ELEMENT (data); KMS_ELEMENT_LOCK (self); accept = self->priv->accept_eos; KMS_ELEMENT_UNLOCK (self); if (!accept) GST_DEBUG_OBJECT (pad, "Eos dropped"); return (accept) ? GST_PAD_PROBE_OK : GST_PAD_PROBE_DROP; } return GST_PAD_PROBE_OK; }
static GstPadProbeReturn gst_hls_sink_ghost_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer data) { GstHlsSink *sink = GST_HLS_SINK_CAST (data); GstEvent *event = gst_pad_probe_info_get_event (info); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEGMENT: { gst_event_copy_segment (event, &sink->segment); break; } case GST_EVENT_FLUSH_STOP: gst_segment_init (&sink->segment, GST_FORMAT_UNDEFINED); break; case GST_EVENT_CUSTOM_DOWNSTREAM: { GstClockTime timestamp; GstClockTime running_time, stream_time; gboolean all_headers; guint count; if (!gst_video_event_is_force_key_unit (event)) break; gst_event_replace (&sink->force_key_unit_event, event); gst_video_event_parse_downstream_force_key_unit (event, ×tamp, &stream_time, &running_time, &all_headers, &count); GST_INFO_OBJECT (sink, "setting index %d", count); sink->index = count; break; } default: break; } return GST_PAD_PROBE_OK; }
static GstPadProbeReturn configure_pipeline_capabilities (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) { GstEvent *event = gst_pad_probe_info_get_event (info); DataEvtProbe *data = user_data; GstElement *appsrc = data->appsrc; GstElement *appsink; GstCaps *caps; if (GST_EVENT_TYPE (event) != GST_EVENT_CAPS) return GST_PAD_PROBE_OK; gst_event_parse_caps (event, &caps); GST_DEBUG_OBJECT (appsrc, "Processing caps event %" GST_PTR_FORMAT, caps); if (gst_caps_get_size (caps) == 0) { GST_ERROR_OBJECT (pad, "Invalid event %" GST_PTR_FORMAT, event); return GST_PAD_PROBE_OK; } if (!gst_caps_is_fixed (caps)) { GST_WARNING_OBJECT (pad, "Not fixed caps in event %" GST_PTR_FORMAT, event); } set_appsrc_caps (appsrc, caps); appsink = gst_pad_get_parent_element (pad); if (appsink) { set_appsink_caps (appsink, caps, data->profile); g_object_unref (appsink); } return GST_PAD_PROBE_OK; }
static GstPadProbeReturn handle_mq_output (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx) { GstSplitMuxSink *splitmux = ctx->splitmux; MqStreamBuf *buf_info = NULL; GST_LOG_OBJECT (pad, "Fired probe type 0x%x\n", info->type); /* FIXME: Handle buffer lists, until then make it clear they won't work */ if (info->type & GST_PAD_PROBE_TYPE_BUFFER_LIST) { g_warning ("Buffer list handling not implemented"); return GST_PAD_PROBE_DROP; } if (info->type & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM) { GstEvent *event = gst_pad_probe_info_get_event (info); GST_LOG_OBJECT (pad, "Event %" GST_PTR_FORMAT, event); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEGMENT: gst_event_copy_segment (event, &ctx->out_segment); break; case GST_EVENT_FLUSH_STOP: GST_SPLITMUX_LOCK (splitmux); gst_segment_init (&ctx->out_segment, GST_FORMAT_UNDEFINED); g_queue_foreach (&ctx->queued_bufs, (GFunc) mq_stream_buf_free, NULL); g_queue_clear (&ctx->queued_bufs); ctx->flushing = FALSE; GST_SPLITMUX_UNLOCK (splitmux); break; case GST_EVENT_FLUSH_START: GST_SPLITMUX_LOCK (splitmux); GST_LOG_OBJECT (pad, "Flush start"); ctx->flushing = TRUE; GST_SPLITMUX_BROADCAST (splitmux); GST_SPLITMUX_UNLOCK (splitmux); break; case GST_EVENT_EOS: GST_SPLITMUX_LOCK (splitmux); if (splitmux->state == SPLITMUX_STATE_STOPPED) goto beach; ctx->out_eos = TRUE; GST_SPLITMUX_UNLOCK (splitmux); break; case GST_EVENT_GAP:{ GstClockTime gap_ts; gst_event_parse_gap (event, &gap_ts, NULL); if (gap_ts == GST_CLOCK_TIME_NONE) break; GST_SPLITMUX_LOCK (splitmux); gap_ts = gst_segment_to_running_time (&ctx->out_segment, GST_FORMAT_TIME, gap_ts); GST_LOG_OBJECT (pad, "Have GAP w/ ts %" GST_TIME_FORMAT, GST_TIME_ARGS (gap_ts)); if (splitmux->state == SPLITMUX_STATE_STOPPED) goto beach; ctx->out_running_time = gap_ts; complete_or_wait_on_out (splitmux, ctx); GST_SPLITMUX_UNLOCK (splitmux); break; } default: break; } return GST_PAD_PROBE_PASS; } /* Allow everything through until the configured next stopping point */ GST_SPLITMUX_LOCK (splitmux); buf_info = g_queue_pop_tail (&ctx->queued_bufs); if (buf_info == NULL) /* Can only happen due to a poorly timed flush */ goto beach; /* If we have popped a keyframe, decrement the queued_gop count */ if (buf_info->keyframe && splitmux->queued_gops > 0) splitmux->queued_gops--; ctx->out_running_time = buf_info->run_ts; GST_LOG_OBJECT (splitmux, "Pad %" GST_PTR_FORMAT " buffer with TS %" GST_TIME_FORMAT " size %" G_GSIZE_FORMAT, pad, GST_TIME_ARGS (ctx->out_running_time), buf_info->buf_size); complete_or_wait_on_out (splitmux, ctx); if (splitmux->muxed_out_time == GST_CLOCK_TIME_NONE || splitmux->muxed_out_time < buf_info->run_ts) splitmux->muxed_out_time = buf_info->run_ts; splitmux->muxed_out_bytes += buf_info->buf_size; #ifndef GST_DISABLE_GST_DEBUG { GstBuffer *buf = gst_pad_probe_info_get_buffer (info); GST_LOG_OBJECT (pad, "Returning to pass buffer %" GST_PTR_FORMAT " run ts %" GST_TIME_FORMAT, buf, GST_TIME_ARGS (ctx->out_running_time)); } #endif GST_SPLITMUX_UNLOCK (splitmux); mq_stream_buf_free (buf_info); return GST_PAD_PROBE_PASS; beach: GST_SPLITMUX_UNLOCK (splitmux); return GST_PAD_PROBE_DROP; }
static GstPadProbeReturn link_to_videomixer (GstPad * pad, GstPadProbeInfo * info, KmsAlphaBlendingData * data) { GstPadTemplate *sink_pad_template; KmsAlphaBlending *mixer = data->mixer; if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info)) != GST_EVENT_CAPS) { return GST_PAD_PROBE_PASS; } GST_DEBUG ("stream start detected"); KMS_ALPHA_BLENDING_LOCK (mixer); data->link_probe_id = 0; sink_pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (mixer-> priv->videomixer), "sink_%u"); if (G_UNLIKELY (sink_pad_template == NULL)) { GST_ERROR_OBJECT (mixer, "Error taking a new pad from videomixer"); KMS_ALPHA_BLENDING_UNLOCK (mixer); return GST_PAD_PROBE_DROP; } if (mixer->priv->master_port == data->id) { //master_port, reconfigurate the output_width and heigth_width //and all the ports already created GstEvent *event; GstCaps *caps; gint width, height; const GstStructure *str; event = gst_pad_probe_info_get_event (info); gst_event_parse_caps (event, &caps); GST_DEBUG ("caps %" GST_PTR_FORMAT, caps); if (caps != NULL) { str = gst_caps_get_structure (caps, 0); if (gst_structure_get_int (str, "width", &width) && gst_structure_get_int (str, "height", &height)) { mixer->priv->output_height = height; mixer->priv->output_width = width; } } } if (mixer->priv->videotestsrc == NULL) { GstCaps *filtercaps; GstPad *pad; mixer->priv->videotestsrc = gst_element_factory_make ("videotestsrc", NULL); mixer->priv->videotestsrc_capsfilter = gst_element_factory_make ("capsfilter", NULL); g_object_set (mixer->priv->videotestsrc, "is-live", TRUE, "pattern", /*black */ 2, NULL); filtercaps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING, "AYUV", "width", G_TYPE_INT, mixer->priv->output_width, "height", G_TYPE_INT, mixer->priv->output_height, "framerate", GST_TYPE_FRACTION, 15, 1, NULL); g_object_set (G_OBJECT (mixer->priv->videotestsrc_capsfilter), "caps", filtercaps, NULL); gst_caps_unref (filtercaps); gst_bin_add_many (GST_BIN (mixer), mixer->priv->videotestsrc, mixer->priv->videotestsrc_capsfilter, NULL); gst_element_link (mixer->priv->videotestsrc, mixer->priv->videotestsrc_capsfilter); /*link capsfilter -> videomixer */ pad = gst_element_request_pad (mixer->priv->videomixer, sink_pad_template, NULL, NULL); gst_element_link_pads (mixer->priv->videotestsrc_capsfilter, NULL, mixer->priv->videomixer, GST_OBJECT_NAME (pad)); g_object_set (pad, "xpos", 0, "ypos", 0, "alpha", 0.0, "zorder", 0, NULL); g_object_unref (pad); gst_element_sync_state_with_parent (mixer->priv->videotestsrc_capsfilter); gst_element_sync_state_with_parent (mixer->priv->videotestsrc); } data->videoscale = gst_element_factory_make ("videoscale", NULL); data->capsfilter = gst_element_factory_make ("capsfilter", NULL); data->videorate = gst_element_factory_make ("videorate", NULL); data->queue = gst_element_factory_make ("queue", NULL); data->videobox = gst_element_factory_make ("videobox", NULL); data->input = TRUE; gst_bin_add_many (GST_BIN (mixer), data->queue, data->videorate, data->videoscale, data->capsfilter, data->videobox, NULL); g_object_set (data->videorate, "average-period", 200 * GST_MSECOND, NULL); g_object_set (data->queue, "flush-on-eos", TRUE, NULL); gst_element_link_many (data->videorate, data->queue, data->videoscale, data->capsfilter, data->videobox, NULL); /*link capsfilter -> videomixer */ data->video_mixer_pad = gst_element_request_pad (mixer->priv->videomixer, sink_pad_template, NULL, NULL); gst_element_link_pads (data->videobox, NULL, mixer->priv->videomixer, GST_OBJECT_NAME (data->video_mixer_pad)); gst_element_link (data->videoconvert, data->videorate); data->probe_id = gst_pad_add_probe (data->video_mixer_pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, (GstPadProbeCallback) cb_EOS_received, KMS_ALPHA_BLENDING_REF (data), (GDestroyNotify) kms_ref_struct_unref); gst_element_sync_state_with_parent (data->videoscale); gst_element_sync_state_with_parent (data->capsfilter); gst_element_sync_state_with_parent (data->videorate); gst_element_sync_state_with_parent (data->queue); gst_element_sync_state_with_parent (data->videobox); /* configure videomixer pad */ mixer->priv->n_elems++; if (mixer->priv->master_port == data->id) { kms_alpha_blending_reconfigure_ports (mixer); } else { configure_port (data); } KMS_ALPHA_BLENDING_UNLOCK (mixer); return GST_PAD_PROBE_REMOVE; }
static GstPadProbeReturn handle_mq_input (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx) { GstSplitMuxSink *splitmux = ctx->splitmux; GstBuffer *buf; MqStreamBuf *buf_info = NULL; GstClockTime ts; gboolean loop_again; gboolean keyframe = FALSE; GST_LOG_OBJECT (pad, "Fired probe type 0x%x\n", info->type); /* FIXME: Handle buffer lists, until then make it clear they won't work */ if (info->type & GST_PAD_PROBE_TYPE_BUFFER_LIST) { g_warning ("Buffer list handling not implemented"); return GST_PAD_PROBE_DROP; } if (info->type & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM) { GstEvent *event = gst_pad_probe_info_get_event (info); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEGMENT: gst_event_copy_segment (event, &ctx->in_segment); break; case GST_EVENT_FLUSH_STOP: GST_SPLITMUX_LOCK (splitmux); gst_segment_init (&ctx->in_segment, GST_FORMAT_UNDEFINED); ctx->in_eos = FALSE; ctx->in_bytes = 0; ctx->in_running_time = 0; GST_SPLITMUX_UNLOCK (splitmux); break; case GST_EVENT_EOS: GST_SPLITMUX_LOCK (splitmux); ctx->in_eos = TRUE; if (splitmux->state == SPLITMUX_STATE_STOPPED) goto beach; if (ctx->is_video) { GST_INFO_OBJECT (splitmux, "Got Video EOS. Finishing up"); /* Act as if this is a new keyframe with infinite timestamp */ splitmux->max_in_running_time = GST_CLOCK_TIME_NONE; splitmux->state = SPLITMUX_STATE_WAITING_GOP_COMPLETE; /* Wake up other input pads to collect this GOP */ GST_SPLITMUX_BROADCAST (splitmux); check_completed_gop (splitmux, ctx); } else if (splitmux->state == SPLITMUX_STATE_WAITING_GOP_COMPLETE) { /* If we are waiting for a GOP to be completed (ie, for aux * pads to catch up), then this pad is complete, so check * if the whole GOP is. */ check_completed_gop (splitmux, ctx); } GST_SPLITMUX_UNLOCK (splitmux); break; default: break; } return GST_PAD_PROBE_PASS; } buf = gst_pad_probe_info_get_buffer (info); ctx->in_running_time = gst_segment_to_running_time (&ctx->in_segment, GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buf)); buf_info = mq_stream_buf_new (); if (GST_BUFFER_PTS_IS_VALID (buf)) ts = GST_BUFFER_PTS (buf); else ts = GST_BUFFER_DTS (buf); GST_SPLITMUX_LOCK (splitmux); if (splitmux->state == SPLITMUX_STATE_STOPPED) goto beach; /* If this buffer has a timestamp, advance the input timestamp of the * stream */ if (GST_CLOCK_TIME_IS_VALID (ts)) { GstClockTime running_time = gst_segment_to_running_time (&ctx->in_segment, GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buf)); if (GST_CLOCK_TIME_IS_VALID (running_time) && (ctx->in_running_time == GST_CLOCK_TIME_NONE || running_time > ctx->in_running_time)) ctx->in_running_time = running_time; } /* Try to make sure we have a valid running time */ if (!GST_CLOCK_TIME_IS_VALID (ctx->in_running_time)) { ctx->in_running_time = gst_segment_to_running_time (&ctx->in_segment, GST_FORMAT_TIME, ctx->in_segment.start); } buf_info->run_ts = ctx->in_running_time; buf_info->buf_size = gst_buffer_get_size (buf); /* Update total input byte counter for overflow detect */ ctx->in_bytes += buf_info->buf_size; GST_DEBUG_OBJECT (pad, "Buf TS %" GST_TIME_FORMAT " total in_bytes %" G_GSIZE_FORMAT, GST_TIME_ARGS (buf_info->run_ts), ctx->in_bytes); loop_again = TRUE; do { if (ctx->flushing) break; switch (splitmux->state) { case SPLITMUX_STATE_COLLECTING_GOP_START: if (ctx->is_video) { /* If a keyframe, we have a complete GOP */ if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) || !GST_CLOCK_TIME_IS_VALID (ctx->in_running_time) || splitmux->max_in_running_time >= ctx->in_running_time) { /* Pass this buffer through */ loop_again = FALSE; break; } GST_INFO_OBJECT (pad, "Have keyframe with running time %" GST_TIME_FORMAT, GST_TIME_ARGS (ctx->in_running_time)); keyframe = TRUE; splitmux->state = SPLITMUX_STATE_WAITING_GOP_COMPLETE; splitmux->max_in_running_time = ctx->in_running_time; /* Wake up other input pads to collect this GOP */ GST_SPLITMUX_BROADCAST (splitmux); check_completed_gop (splitmux, ctx); } else { /* We're still waiting for a keyframe on the video pad, sleep */ GST_LOG_OBJECT (pad, "Sleeping for GOP start"); GST_SPLITMUX_WAIT (splitmux); GST_LOG_OBJECT (pad, "Done sleeping for GOP start state now %d", splitmux->state); } break; case SPLITMUX_STATE_WAITING_GOP_COMPLETE: /* After a GOP start is found, this buffer might complete the GOP */ /* If we overran the target timestamp, it might be time to process * the GOP, otherwise bail out for more data */ GST_LOG_OBJECT (pad, "Checking TS %" GST_TIME_FORMAT " against max %" GST_TIME_FORMAT, GST_TIME_ARGS (ctx->in_running_time), GST_TIME_ARGS (splitmux->max_in_running_time)); if (ctx->in_running_time < splitmux->max_in_running_time) { loop_again = FALSE; break; } GST_LOG_OBJECT (pad, "Collected last packet of GOP. Checking other pads"); check_completed_gop (splitmux, ctx); break; case SPLITMUX_STATE_ENDING_FILE: case SPLITMUX_STATE_START_NEXT_FRAGMENT: /* A fragment is ending, wait until that's done before continuing */ GST_DEBUG_OBJECT (pad, "Sleeping for fragment restart"); GST_SPLITMUX_WAIT (splitmux); GST_DEBUG_OBJECT (pad, "Done sleeping for fragment restart state now %d", splitmux->state); break; default: loop_again = FALSE; break; } } while (loop_again); if (keyframe) { splitmux->queued_gops++; buf_info->keyframe = TRUE; } /* Now add this buffer to the queue just before returning */ g_queue_push_head (&ctx->queued_bufs, buf_info); /* Check the buffer will fit in the mq */ check_queue_length (splitmux, ctx); GST_LOG_OBJECT (pad, "Returning to queue buffer %" GST_PTR_FORMAT " run ts %" GST_TIME_FORMAT, buf, GST_TIME_ARGS (ctx->in_running_time)); GST_SPLITMUX_UNLOCK (splitmux); return GST_PAD_PROBE_PASS; beach: GST_SPLITMUX_UNLOCK (splitmux); if (buf_info) mq_stream_buf_free (buf_info); return GST_PAD_PROBE_PASS; }