static GstStateChangeReturn gst_video_rate_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn ret; GstVideoRate *videorate; videorate = GST_VIDEO_RATE (element); switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: videorate->discont = TRUE; videorate->last_ts = -1; break; default: break; } ret = parent_class->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: gst_video_rate_reset (videorate); break; default: break; } return ret; }
static void gst_video_rate_init (GstVideoRate * videorate, GstVideoRateClass * klass) { videorate->sinkpad = gst_pad_new_from_static_template (&gst_video_rate_sink_template, "sink"); gst_pad_set_event_function (videorate->sinkpad, GST_DEBUG_FUNCPTR (gst_video_rate_event)); gst_pad_set_chain_function (videorate->sinkpad, GST_DEBUG_FUNCPTR (gst_video_rate_chain)); gst_pad_set_getcaps_function (videorate->sinkpad, GST_DEBUG_FUNCPTR (gst_video_rate_getcaps)); gst_pad_set_setcaps_function (videorate->sinkpad, GST_DEBUG_FUNCPTR (gst_video_rate_setcaps)); gst_element_add_pad (GST_ELEMENT (videorate), videorate->sinkpad); videorate->srcpad = gst_pad_new_from_static_template (&gst_video_rate_src_template, "src"); gst_pad_set_query_function (videorate->srcpad, GST_DEBUG_FUNCPTR (gst_video_rate_query)); gst_pad_set_getcaps_function (videorate->srcpad, GST_DEBUG_FUNCPTR (gst_video_rate_getcaps)); gst_pad_set_setcaps_function (videorate->srcpad, GST_DEBUG_FUNCPTR (gst_video_rate_setcaps)); gst_element_add_pad (GST_ELEMENT (videorate), videorate->srcpad); gst_video_rate_reset (videorate); videorate->silent = DEFAULT_SILENT; videorate->new_pref = DEFAULT_NEW_PREF; videorate->from_rate_numerator = 0; videorate->from_rate_denominator = 0; videorate->to_rate_numerator = 0; videorate->to_rate_denominator = 0; }
static void gst_video_rate_init (GstVideoRate * videorate) { gst_video_rate_reset (videorate); videorate->silent = DEFAULT_SILENT; videorate->new_pref = DEFAULT_NEW_PREF; videorate->drop_only = DEFAULT_DROP_ONLY; videorate->average_period = DEFAULT_AVERAGE_PERIOD; videorate->average_period_set = DEFAULT_AVERAGE_PERIOD; videorate->max_rate = DEFAULT_MAX_RATE; videorate->from_rate_numerator = 0; videorate->from_rate_denominator = 0; videorate->to_rate_numerator = 0; videorate->to_rate_denominator = 0; gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (videorate), TRUE); }
static gboolean gst_video_rate_sink_event (GstBaseTransform * trans, GstEvent * event) { GstVideoRate *videorate; videorate = GST_VIDEO_RATE (trans); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEGMENT: { const GstSegment *segment; gst_event_parse_segment (event, &segment); if (segment->format != GST_FORMAT_TIME) goto format_error; GST_DEBUG_OBJECT (videorate, "handle NEWSEGMENT"); /* close up the previous segment, if appropriate */ if (videorate->prevbuf) { gint count = 0; GstFlowReturn res; res = GST_FLOW_OK; /* fill up to the end of current segment, * or only send out the stored buffer if there is no specific stop. * regardless, prevent going loopy in strange cases */ while (res == GST_FLOW_OK && count <= MAGIC_LIMIT && ((GST_CLOCK_TIME_IS_VALID (videorate->segment.stop) && videorate->next_ts - videorate->segment.base < videorate->segment.stop) || count < 1)) { res = gst_video_rate_flush_prev (videorate, count > 0); count++; } if (count > 1) { videorate->dup += count - 1; if (!videorate->silent) gst_video_rate_notify_duplicate (videorate); } else if (count == 0) { videorate->drop++; if (!videorate->silent) gst_video_rate_notify_drop (videorate); } /* clean up for the new one; _chain will resume from the new start */ gst_video_rate_swap_prev (videorate, NULL, 0); } videorate->base_ts = 0; videorate->out_frame_count = 0; videorate->next_ts = GST_CLOCK_TIME_NONE; /* We just want to update the accumulated stream_time */ gst_segment_copy_into (segment, &videorate->segment); GST_DEBUG_OBJECT (videorate, "updated segment: %" GST_SEGMENT_FORMAT, &videorate->segment); break; } case GST_EVENT_EOS:{ gint count = 0; GstFlowReturn res = GST_FLOW_OK; GST_DEBUG_OBJECT (videorate, "Got EOS"); /* If the segment has a stop position, fill the segment */ if (GST_CLOCK_TIME_IS_VALID (videorate->segment.stop)) { /* fill up to the end of current segment, * or only send out the stored buffer if there is no specific stop. * regardless, prevent going loopy in strange cases */ while (res == GST_FLOW_OK && count <= MAGIC_LIMIT && ((videorate->next_ts - videorate->segment.base < videorate->segment.stop) || count < 1)) { res = gst_video_rate_flush_prev (videorate, count > 0); count++; } } else if (videorate->prevbuf) { /* Output at least one frame but if the buffer duration is valid, output * enough frames to use the complete buffer duration */ if (GST_BUFFER_DURATION_IS_VALID (videorate->prevbuf)) { GstClockTime end_ts = videorate->next_ts + GST_BUFFER_DURATION (videorate->prevbuf); while (res == GST_FLOW_OK && count <= MAGIC_LIMIT && ((videorate->next_ts - videorate->segment.base < end_ts) || count < 1)) { res = gst_video_rate_flush_prev (videorate, count > 0); count++; } } else { res = gst_video_rate_flush_prev (videorate, FALSE); count = 1; } } if (count > 1) { videorate->dup += count - 1; if (!videorate->silent) gst_video_rate_notify_duplicate (videorate); } else if (count == 0) { videorate->drop++; if (!videorate->silent) gst_video_rate_notify_drop (videorate); } break; } case GST_EVENT_FLUSH_STOP: /* also resets the segment */ GST_DEBUG_OBJECT (videorate, "Got FLUSH_STOP"); gst_video_rate_reset (videorate); break; case GST_EVENT_GAP: /* no gaps after videorate, ignore the event */ gst_event_unref (event); return TRUE; default: break; } return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event); /* ERRORS */ format_error: { GST_WARNING_OBJECT (videorate, "Got segment but doesn't have GST_FORMAT_TIME value"); return FALSE; } }
static gboolean gst_video_rate_stop (GstBaseTransform * trans) { gst_video_rate_reset (GST_VIDEO_RATE (trans)); return TRUE; }
static gboolean gst_video_rate_event (GstPad * pad, GstEvent * event) { GstVideoRate *videorate; gboolean ret; videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_NEWSEGMENT: { gint64 start, stop, time; gdouble rate, arate; gboolean update; GstFormat format; gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, &start, &stop, &time); if (format != GST_FORMAT_TIME) goto format_error; GST_DEBUG_OBJECT (videorate, "handle NEWSEGMENT"); /* close up the previous segment, if appropriate */ if (!update && videorate->prevbuf) { gint count = 0; GstFlowReturn res; res = GST_FLOW_OK; /* fill up to the end of current segment, * or only send out the stored buffer if there is no specific stop. * regardless, prevent going loopy in strange cases */ while (res == GST_FLOW_OK && count <= MAGIC_LIMIT && ((GST_CLOCK_TIME_IS_VALID (videorate->segment.stop) && videorate->next_ts - videorate->segment.accum < videorate->segment.stop) || count < 1)) { res = gst_video_rate_flush_prev (videorate, count > 0); count++; } if (count > 1) { videorate->dup += count - 1; if (!videorate->silent) gst_video_rate_notify_duplicate (videorate); } else if (count == 0) { videorate->drop++; if (!videorate->silent) gst_video_rate_notify_drop (videorate); } /* clean up for the new one; _chain will resume from the new start */ videorate->base_ts = 0; videorate->out_frame_count = 0; gst_video_rate_swap_prev (videorate, NULL, 0); videorate->next_ts = GST_CLOCK_TIME_NONE; } /* We just want to update the accumulated stream_time */ gst_segment_set_newsegment_full (&videorate->segment, update, rate, arate, format, start, stop, time); GST_DEBUG_OBJECT (videorate, "updated segment: %" GST_SEGMENT_FORMAT, &videorate->segment); break; } case GST_EVENT_EOS:{ gint count = 0; GstFlowReturn res = GST_FLOW_OK; GST_DEBUG_OBJECT (videorate, "Got EOS"); /* If the segment has a stop position, fill the segment */ if (GST_CLOCK_TIME_IS_VALID (videorate->segment.stop)) { /* fill up to the end of current segment, * or only send out the stored buffer if there is no specific stop. * regardless, prevent going loopy in strange cases */ while (res == GST_FLOW_OK && count <= MAGIC_LIMIT && ((videorate->next_ts - videorate->segment.accum < videorate->segment.stop) || count < 1)) { res = gst_video_rate_flush_prev (videorate, count > 0); count++; } } else if (videorate->prevbuf) { /* Output at least one frame but if the buffer duration is valid, output * enough frames to use the complete buffer duration */ if (GST_BUFFER_DURATION_IS_VALID (videorate->prevbuf)) { GstClockTime end_ts = videorate->next_ts + GST_BUFFER_DURATION (videorate->prevbuf); while (res == GST_FLOW_OK && count <= MAGIC_LIMIT && ((videorate->next_ts - videorate->segment.accum < end_ts) || count < 1)) { res = gst_video_rate_flush_prev (videorate, count > 0); count++; } } else { res = gst_video_rate_flush_prev (videorate, FALSE); count = 1; } } if (count > 1) { videorate->dup += count - 1; if (!videorate->silent) gst_video_rate_notify_duplicate (videorate); } else if (count == 0) { videorate->drop++; if (!videorate->silent) gst_video_rate_notify_drop (videorate); } break; } case GST_EVENT_FLUSH_STOP: /* also resets the segment */ GST_DEBUG_OBJECT (videorate, "Got FLUSH_STOP"); gst_video_rate_reset (videorate); break; default: break; } ret = gst_pad_push_event (videorate->srcpad, event); done: gst_object_unref (videorate); return ret; /* ERRORS */ format_error: { GST_WARNING_OBJECT (videorate, "Got segment but doesn't have GST_FORMAT_TIME value"); gst_event_unref (event); ret = FALSE; goto done; } }