summaryrefslogtreecommitdiff
path: root/gst/videorate/gstvideorate.c
diff options
context:
space:
mode:
Diffstat (limited to 'gst/videorate/gstvideorate.c')
-rw-r--r--gst/videorate/gstvideorate.c920
1 files changed, 0 insertions, 920 deletions
diff --git a/gst/videorate/gstvideorate.c b/gst/videorate/gstvideorate.c
deleted file mode 100644
index b3481338..00000000
--- a/gst/videorate/gstvideorate.c
+++ /dev/null
@@ -1,920 +0,0 @@
-/* GStreamer
- * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/**
- * SECTION:element-videorate
- *
- * This element takes an incoming stream of timestamped video frames.
- * It will produce a perfect stream that matches the source pad's framerate.
- *
- * The correction is performed by dropping and duplicating frames, no fancy
- * algorithm is used to interpolate frames (yet).
- *
- * By default the element will simply negotiate the same framerate on its
- * source and sink pad.
- *
- * This operation is useful to link to elements that require a perfect stream.
- * Typical examples are formats that do not store timestamps for video frames,
- * but only store a framerate, like Ogg and AVI.
- *
- * A conversion to a specific framerate can be forced by using filtered caps on
- * the source pad.
- *
- * The properties #GstVideoRate:in, #GstVideoRate:out, #GstVideoRate:duplicate
- * and #GstVideoRate:drop can be read to obtain information about number of
- * input frames, output frames, dropped frames (i.e. the number of unused input
- * frames) and duplicated frames (i.e. the number of times an input frame was
- * duplicated, beside being used normally).
- *
- * An input stream that needs no adjustments will thus never have dropped or
- * duplicated frames.
- *
- * When the #GstVideoRate:silent property is set to FALSE, a GObject property
- * notification will be emitted whenever one of the #GstVideoRate:duplicate or
- * #GstVideoRate:drop values changes.
- * This can potentially cause performance degradation.
- * Note that property notification will happen from the streaming thread, so
- * applications should be prepared for this.
- *
- * <refsect2>
- * <title>Example pipelines</title>
- * |[
- * gst-launch -v filesrc location=videotestsrc.ogg ! oggdemux ! theoradec ! videorate ! video/x-raw-yuv,framerate=15/1 ! xvimagesink
- * ]| Decode an Ogg/Theora file and adjust the framerate to 15 fps before playing.
- * To create the test Ogg/Theora file refer to the documentation of theoraenc.
- * |[
- * gst-launch -v v4lsrc ! videorate ! video/x-raw-yuv,framerate=25/2 ! theoraenc ! oggmux ! filesink location=v4l.ogg
- * ]| Capture video from a V4L device, and adjust the stream to 12.5 fps before
- * encoding to Ogg/Theora.
- * </refsect2>
- *
- * Last reviewed on 2006-09-02 (0.10.11)
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "gstvideorate.h"
-
-GST_DEBUG_CATEGORY_STATIC (video_rate_debug);
-#define GST_CAT_DEFAULT video_rate_debug
-
-/* elementfactory information */
-static const GstElementDetails video_rate_details =
-GST_ELEMENT_DETAILS ("Video rate adjuster",
- "Filter/Effect/Video",
- "Drops/duplicates/adjusts timestamps on video frames to make a perfect stream",
- "Wim Taymans <wim@fluendo.com>");
-
-/* GstVideoRate signals and args */
-enum
-{
- /* FILL ME */
- LAST_SIGNAL
-};
-
-#define DEFAULT_SILENT TRUE
-#define DEFAULT_NEW_PREF 1.0
-#define DEFAULT_SKIP_TO_FIRST FALSE
-
-enum
-{
- ARG_0,
- ARG_IN,
- ARG_OUT,
- ARG_DUP,
- ARG_DROP,
- ARG_SILENT,
- ARG_NEW_PREF,
- ARG_SKIP_TO_FIRST
- /* FILL ME */
-};
-
-static GstStaticPadTemplate gst_video_rate_src_template =
- GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("video/x-raw-yuv; video/x-raw-rgb; image/jpeg; image/png")
- );
-
-static GstStaticPadTemplate gst_video_rate_sink_template =
- GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("video/x-raw-yuv; video/x-raw-rgb; image/jpeg; image/png")
- );
-
-static void gst_video_rate_swap_prev (GstVideoRate * videorate,
- GstBuffer * buffer, gint64 time);
-static gboolean gst_video_rate_event (GstPad * pad, GstEvent * event);
-static gboolean gst_video_rate_query (GstPad * pad, GstQuery * query);
-static GstFlowReturn gst_video_rate_chain (GstPad * pad, GstBuffer * buffer);
-
-static void gst_video_rate_set_property (GObject * object,
- guint prop_id, const GValue * value, GParamSpec * pspec);
-static void gst_video_rate_get_property (GObject * object,
- guint prop_id, GValue * value, GParamSpec * pspec);
-
-static GstStateChangeReturn gst_video_rate_change_state (GstElement * element,
- GstStateChange transition);
-
-/*static guint gst_video_rate_signals[LAST_SIGNAL] = { 0 }; */
-
-GST_BOILERPLATE (GstVideoRate, gst_video_rate, GstElement, GST_TYPE_ELEMENT);
-
-static void
-gst_video_rate_base_init (gpointer g_class)
-{
- GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
-
- gst_element_class_set_details (element_class, &video_rate_details);
-
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&gst_video_rate_sink_template));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&gst_video_rate_src_template));
-}
-
-static void
-gst_video_rate_class_init (GstVideoRateClass * klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
-
- parent_class = g_type_class_peek_parent (klass);
-
- object_class->set_property = gst_video_rate_set_property;
- object_class->get_property = gst_video_rate_get_property;
-
- g_object_class_install_property (object_class, ARG_IN,
- g_param_spec_uint64 ("in", "In",
- "Number of input frames", 0, G_MAXUINT64, 0,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (object_class, ARG_OUT,
- g_param_spec_uint64 ("out", "Out", "Number of output frames", 0,
- G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (object_class, ARG_DUP,
- g_param_spec_uint64 ("duplicate", "Duplicate",
- "Number of duplicated frames", 0, G_MAXUINT64, 0,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (object_class, ARG_DROP,
- g_param_spec_uint64 ("drop", "Drop", "Number of dropped frames", 0,
- G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (object_class, ARG_SILENT,
- g_param_spec_boolean ("silent", "silent",
- "Don't emit notify for dropped and duplicated frames", DEFAULT_SILENT,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (object_class, ARG_NEW_PREF,
- g_param_spec_double ("new-pref", "New Pref",
- "Value indicating how much to prefer new frames (unused)", 0.0, 1.0,
- DEFAULT_NEW_PREF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- /**
- * GstVideoRate:skip-to-first:
- *
- * Don't produce buffers before the first one we receive.
- *
- * Since: 0.10.25
- */
- g_object_class_install_property (object_class, ARG_SKIP_TO_FIRST,
- g_param_spec_boolean ("skip-to-first", "Skip to first buffer",
- "Don't produce buffers before the first one we receive",
- DEFAULT_SKIP_TO_FIRST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- element_class->change_state = gst_video_rate_change_state;
-}
-
-/* return the caps that can be used on out_pad given in_caps on in_pad */
-static gboolean
-gst_video_rate_transformcaps (GstPad * in_pad, GstCaps * in_caps,
- GstPad * out_pad, GstCaps ** out_caps)
-{
- GstCaps *intersect;
- const GstCaps *in_templ;
- gint i;
- GSList *extra_structures = NULL;
- GSList *iter;
-
- in_templ = gst_pad_get_pad_template_caps (in_pad);
- intersect = gst_caps_intersect (in_caps, in_templ);
-
- /* all possible framerates are allowed */
- for (i = 0; i < gst_caps_get_size (intersect); i++) {
- GstStructure *structure;
-
- structure = gst_caps_get_structure (intersect, i);
-
- if (gst_structure_has_field (structure, "framerate")) {
- GstStructure *copy_structure;
-
- copy_structure = gst_structure_copy (structure);
- gst_structure_set (copy_structure,
- "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
- extra_structures = g_slist_append (extra_structures, copy_structure);
- }
- }
-
- /* append the extra structures */
- for (iter = extra_structures; iter != NULL; iter = g_slist_next (iter)) {
- gst_caps_append_structure (intersect, (GstStructure *) iter->data);
- }
- g_slist_free (extra_structures);
-
- *out_caps = intersect;
-
- return TRUE;
-}
-
-static GstCaps *
-gst_video_rate_getcaps (GstPad * pad)
-{
- GstVideoRate *videorate;
- GstPad *otherpad;
- GstCaps *caps;
-
- videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
-
- otherpad = (pad == videorate->srcpad) ? videorate->sinkpad :
- videorate->srcpad;
-
- /* we can do what the peer can */
- caps = gst_pad_peer_get_caps (otherpad);
- if (caps) {
- GstCaps *transform;
-
- gst_video_rate_transformcaps (otherpad, caps, pad, &transform);
- gst_caps_unref (caps);
- caps = transform;
- } else {
- /* no peer, our padtemplate is enough then */
- caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
- }
-
- return caps;
-}
-
-static gboolean
-gst_video_rate_setcaps (GstPad * pad, GstCaps * caps)
-{
- GstVideoRate *videorate;
- GstStructure *structure;
- gboolean ret = TRUE;
- GstPad *otherpad, *opeer;
- gint rate_numerator, rate_denominator;
-
- videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad));
-
- GST_DEBUG ("setcaps called %" GST_PTR_FORMAT, caps);
-
- structure = gst_caps_get_structure (caps, 0);
- if (!gst_structure_get_fraction (structure, "framerate",
- &rate_numerator, &rate_denominator))
- goto no_framerate;
-
- if (pad == videorate->srcpad) {
- videorate->to_rate_numerator = rate_numerator;
- videorate->to_rate_denominator = rate_denominator;
- otherpad = videorate->sinkpad;
- } else {
- videorate->from_rate_numerator = rate_numerator;
- videorate->from_rate_denominator = rate_denominator;
- otherpad = videorate->srcpad;
- }
-
- /* now try to find something for the peer */
- opeer = gst_pad_get_peer (otherpad);
- if (opeer) {
- if (gst_pad_accept_caps (opeer, caps)) {
- /* the peer accepts the caps as they are */
- gst_pad_set_caps (otherpad, caps);
-
- ret = TRUE;
- } else {
- GstCaps *peercaps;
- GstCaps *intersect;
- GstCaps *transform = NULL;
-
- ret = FALSE;
-
- /* see how we can transform the input caps */
- if (!gst_video_rate_transformcaps (pad, caps, otherpad, &transform))
- goto no_transform;
-
- /* see what the peer can do */
- peercaps = gst_pad_get_caps (opeer);
-
- GST_DEBUG ("icaps %" GST_PTR_FORMAT, peercaps);
- GST_DEBUG ("transform %" GST_PTR_FORMAT, transform);
-
- /* filter against our possibilities */
- intersect = gst_caps_intersect (peercaps, transform);
- gst_caps_unref (peercaps);
- gst_caps_unref (transform);
-
- GST_DEBUG ("intersect %" GST_PTR_FORMAT, intersect);
-
- /* take first possibility */
- caps = gst_caps_copy_nth (intersect, 0);
- gst_caps_unref (intersect);
- structure = gst_caps_get_structure (caps, 0);
-
- /* and fixate */
- gst_structure_fixate_field_nearest_fraction (structure, "framerate",
- rate_numerator, rate_denominator);
-
- gst_structure_get_fraction (structure, "framerate",
- &rate_numerator, &rate_denominator);
-
- if (otherpad == videorate->srcpad) {
- videorate->to_rate_numerator = rate_numerator;
- videorate->to_rate_denominator = rate_denominator;
- } else {
- videorate->from_rate_numerator = rate_numerator;
- videorate->from_rate_denominator = rate_denominator;
- }
- gst_pad_set_caps (otherpad, caps);
- gst_caps_unref (caps);
- ret = TRUE;
- }
- gst_object_unref (opeer);
- }
-done:
- /* After a setcaps, our caps may have changed. In that case, we can't use
- * the old buffer, if there was one (it might have different dimensions) */
- GST_DEBUG ("swapping old buffers");
- gst_video_rate_swap_prev (videorate, NULL, GST_CLOCK_TIME_NONE);
-
- gst_object_unref (videorate);
- return ret;
-
-no_framerate:
- {
- GST_DEBUG_OBJECT (videorate, "no framerate specified");
- goto done;
- }
-no_transform:
- {
- GST_DEBUG_OBJECT (videorate, "no framerate transform possible");
- ret = FALSE;
- goto done;
- }
-}
-
-static void
-gst_video_rate_reset (GstVideoRate * videorate)
-{
- GST_DEBUG ("resetting internal variables");
-
- videorate->in = 0;
- videorate->out = 0;
- videorate->segment_out = 0;
- videorate->drop = 0;
- videorate->dup = 0;
- videorate->next_ts = GST_CLOCK_TIME_NONE;
- videorate->last_ts = GST_CLOCK_TIME_NONE;
- videorate->discont = TRUE;
- gst_video_rate_swap_prev (videorate, NULL, 0);
-
- gst_segment_init (&videorate->segment, GST_FORMAT_TIME);
-}
-
-static void
-gst_video_rate_init (GstVideoRate * videorate, GstVideoRateClass * klass)
-{
- GST_DEBUG ("gst_video_rate_init");
- videorate->sinkpad =
- gst_pad_new_from_static_template (&gst_video_rate_sink_template, "sink");
- gst_pad_set_event_function (videorate->sinkpad, gst_video_rate_event);
- gst_pad_set_chain_function (videorate->sinkpad, gst_video_rate_chain);
- gst_pad_set_getcaps_function (videorate->sinkpad, gst_video_rate_getcaps);
- gst_pad_set_setcaps_function (videorate->sinkpad, 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_video_rate_query);
- gst_pad_set_getcaps_function (videorate->srcpad, gst_video_rate_getcaps);
- gst_pad_set_setcaps_function (videorate->srcpad, 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;
-}
-
-/* flush the oldest buffer */
-static GstFlowReturn
-gst_video_rate_flush_prev (GstVideoRate * videorate)
-{
- GstFlowReturn res;
- GstBuffer *outbuf;
- GstClockTime push_ts;
-
- if (!videorate->prevbuf)
- goto eos_before_buffers;
-
- /* make sure we can write to the metadata */
- outbuf = gst_buffer_make_metadata_writable
- (gst_buffer_ref (videorate->prevbuf));
-
- GST_BUFFER_OFFSET (outbuf) = videorate->out;
- GST_BUFFER_OFFSET_END (outbuf) = videorate->out + 1;
-
- if (videorate->discont) {
- GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
- videorate->discont = FALSE;
- } else
- GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DISCONT);
-
- /* this is the timestamp we put on the buffer */
- push_ts = videorate->next_ts;
-
- videorate->out++;
- videorate->segment_out++;
- if (videorate->to_rate_numerator) {
- /* interpolate next expected timestamp in the segment */
- videorate->next_ts = videorate->segment.accum + videorate->segment.start +
- gst_util_uint64_scale (videorate->segment_out,
- videorate->to_rate_denominator * GST_SECOND,
- videorate->to_rate_numerator);
- GST_BUFFER_DURATION (outbuf) = videorate->next_ts - push_ts;
- }
-
- /* adapt for looping, bring back to time in current segment. */
- GST_BUFFER_TIMESTAMP (outbuf) = push_ts - videorate->segment.accum;
-
- gst_buffer_set_caps (outbuf, GST_PAD_CAPS (videorate->srcpad));
-
- GST_LOG_OBJECT (videorate,
- "old is best, dup, pushing buffer outgoing ts %" GST_TIME_FORMAT,
- GST_TIME_ARGS (push_ts));
-
- res = gst_pad_push (videorate->srcpad, outbuf);
-
- return res;
-
- /* WARNINGS */
-eos_before_buffers:
- {
- GST_INFO_OBJECT (videorate, "got EOS before any buffer was received");
- return GST_FLOW_OK;
- }
-}
-
-static void
-gst_video_rate_swap_prev (GstVideoRate * videorate, GstBuffer * buffer,
- gint64 time)
-{
- GST_LOG_OBJECT (videorate, "swap_prev: storing buffer %p in prev", buffer);
- if (videorate->prevbuf)
- gst_buffer_unref (videorate->prevbuf);
- videorate->prevbuf = buffer;
- videorate->prev_ts = time;
-}
-
-#define MAGIC_LIMIT 25
-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)) {
- gst_video_rate_flush_prev (videorate);
- count++;
- }
- if (count > 1) {
- videorate->dup += count - 1;
- if (!videorate->silent)
- g_object_notify (G_OBJECT (videorate), "duplicate");
- } else if (count == 0) {
- videorate->drop++;
- if (!videorate->silent)
- g_object_notify (G_OBJECT (videorate), "drop");
- }
- /* clean up for the new one; _chain will resume from the new start */
- videorate->segment_out = 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:
- /* flush last queued frame */
- GST_DEBUG_OBJECT (videorate, "Got EOS");
- gst_video_rate_flush_prev (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;
- }
-}
-
-static gboolean
-gst_video_rate_query (GstPad * pad, GstQuery * query)
-{
- GstVideoRate *videorate;
- gboolean res = FALSE;
-
- videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad));
-
- switch (GST_QUERY_TYPE (query)) {
- case GST_QUERY_LATENCY:
- {
- GstClockTime min, max;
- gboolean live;
- guint64 latency;
- GstPad *peer;
-
- if ((peer = gst_pad_get_peer (videorate->sinkpad))) {
- if ((res = gst_pad_query (peer, query))) {
- gst_query_parse_latency (query, &live, &min, &max);
-
- GST_DEBUG_OBJECT (videorate, "Peer latency: min %"
- GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
- GST_TIME_ARGS (min), GST_TIME_ARGS (max));
-
- if (videorate->from_rate_numerator != 0) {
- /* add latency. We don't really know since we hold on to the frames
- * until we get a next frame, which can be anything. We assume
- * however that this will take from_rate time. */
- latency = gst_util_uint64_scale (GST_SECOND,
- videorate->from_rate_denominator,
- videorate->from_rate_numerator);
- } else {
- /* no input framerate, we don't know */
- latency = 0;
- }
-
- GST_DEBUG_OBJECT (videorate, "Our latency: %"
- GST_TIME_FORMAT, GST_TIME_ARGS (latency));
-
- min += latency;
- if (max != -1)
- max += latency;
-
- GST_DEBUG_OBJECT (videorate, "Calculated total latency : min %"
- GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
- GST_TIME_ARGS (min), GST_TIME_ARGS (max));
-
- gst_query_set_latency (query, live, min, max);
- }
- gst_object_unref (peer);
- }
- break;
- }
- default:
- res = gst_pad_query_default (pad, query);
- break;
- }
- gst_object_unref (videorate);
-
- return res;
-}
-
-static GstFlowReturn
-gst_video_rate_chain (GstPad * pad, GstBuffer * buffer)
-{
- GstVideoRate *videorate;
- GstFlowReturn res = GST_FLOW_OK;
- GstClockTime intime, in_ts, in_dur;
-
- videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
-
- /* make sure the denominators are not 0 */
- if (videorate->from_rate_denominator == 0 ||
- videorate->to_rate_denominator == 0)
- goto not_negotiated;
-
- in_ts = GST_BUFFER_TIMESTAMP (buffer);
- in_dur = GST_BUFFER_DURATION (buffer);
-
- if (G_UNLIKELY (in_ts == GST_CLOCK_TIME_NONE)) {
- in_ts = videorate->last_ts;
- if (G_UNLIKELY (in_ts == GST_CLOCK_TIME_NONE))
- goto invalid_buffer;
- }
-
- /* get the time of the next expected buffer timestamp, we use this when the
- * next buffer has -1 as a timestamp */
- videorate->last_ts = in_ts;
- if (in_dur != GST_CLOCK_TIME_NONE)
- videorate->last_ts += in_dur;
-
- GST_DEBUG_OBJECT (videorate, "got buffer with timestamp %" GST_TIME_FORMAT,
- GST_TIME_ARGS (in_ts));
-
- /* the input time is the time in the segment + all previously accumulated
- * segments */
- intime = in_ts + videorate->segment.accum;
-
- /* we need to have two buffers to compare */
- if (videorate->prevbuf == NULL) {
- gst_video_rate_swap_prev (videorate, buffer, intime);
- videorate->in++;
- if (!GST_CLOCK_TIME_IS_VALID (videorate->next_ts)) {
- /* new buffer, we expect to output a buffer that matches the first
- * timestamp in the segment */
- if (videorate->skip_to_first) {
- videorate->next_ts = in_ts;
- videorate->segment_out = gst_util_uint64_scale (in_ts,
- videorate->to_rate_numerator,
- videorate->to_rate_denominator * GST_SECOND) -
- (videorate->segment.accum + videorate->segment.start);
- } else {
- videorate->next_ts =
- videorate->segment.start + videorate->segment.accum;
- }
- }
- } else {
- GstClockTime prevtime;
- gint count = 0;
- gint64 diff1, diff2;
-
- prevtime = videorate->prev_ts;
-
- GST_LOG_OBJECT (videorate,
- "BEGINNING prev buf %" GST_TIME_FORMAT " new buf %" GST_TIME_FORMAT
- " outgoing ts %" GST_TIME_FORMAT, GST_TIME_ARGS (prevtime),
- GST_TIME_ARGS (intime), GST_TIME_ARGS (videorate->next_ts));
-
- videorate->in++;
-
- /* drop new buffer if it's before previous one */
- if (intime < prevtime) {
- GST_DEBUG_OBJECT (videorate,
- "The new buffer (%" GST_TIME_FORMAT
- ") is before the previous buffer (%"
- GST_TIME_FORMAT "). Dropping new buffer.",
- GST_TIME_ARGS (intime), GST_TIME_ARGS (prevtime));
- videorate->drop++;
- if (!videorate->silent)
- g_object_notify (G_OBJECT (videorate), "drop");
- gst_buffer_unref (buffer);
- goto done;
- }
-
- /* got 2 buffers, see which one is the best */
- do {
-
- diff1 = prevtime - videorate->next_ts;
- diff2 = intime - videorate->next_ts;
-
- /* take absolute values, beware: abs and ABS don't work for gint64 */
- if (diff1 < 0)
- diff1 = -diff1;
- if (diff2 < 0)
- diff2 = -diff2;
-
- GST_LOG_OBJECT (videorate,
- "diff with prev %" GST_TIME_FORMAT " diff with new %"
- GST_TIME_FORMAT " outgoing ts %" GST_TIME_FORMAT,
- GST_TIME_ARGS (diff1), GST_TIME_ARGS (diff2),
- GST_TIME_ARGS (videorate->next_ts));
-
- /* output first one when its the best */
- if (diff1 <= diff2) {
- count++;
-
- /* on error the _flush function posted a warning already */
- if ((res = gst_video_rate_flush_prev (videorate)) != GST_FLOW_OK) {
- gst_buffer_unref (buffer);
- goto done;
- }
- }
- /* continue while the first one was the best, if they were equal avoid
- * going into an infinite loop */
- }
- while (diff1 < diff2);
-
- /* if we outputed the first buffer more then once, we have dups */
- if (count > 1) {
- videorate->dup += count - 1;
- if (!videorate->silent)
- g_object_notify (G_OBJECT (videorate), "duplicate");
- }
- /* if we didn't output the first buffer, we have a drop */
- else if (count == 0) {
- videorate->drop++;
-
- if (!videorate->silent)
- g_object_notify (G_OBJECT (videorate), "drop");
-
- GST_LOG_OBJECT (videorate,
- "new is best, old never used, drop, outgoing ts %"
- GST_TIME_FORMAT, GST_TIME_ARGS (videorate->next_ts));
- }
- GST_LOG_OBJECT (videorate,
- "END, putting new in old, diff1 %" GST_TIME_FORMAT
- ", diff2 %" GST_TIME_FORMAT ", next_ts %" GST_TIME_FORMAT
- ", in %" G_GUINT64_FORMAT ", out %" G_GUINT64_FORMAT ", drop %"
- G_GUINT64_FORMAT ", dup %" G_GUINT64_FORMAT, GST_TIME_ARGS (diff1),
- GST_TIME_ARGS (diff2), GST_TIME_ARGS (videorate->next_ts),
- videorate->in, videorate->out, videorate->drop, videorate->dup);
-
- /* swap in new one when it's the best */
- gst_video_rate_swap_prev (videorate, buffer, intime);
- }
-done:
- return res;
-
- /* ERRORS */
-not_negotiated:
- {
- GST_WARNING_OBJECT (videorate, "no framerate negotiated");
- gst_buffer_unref (buffer);
- res = GST_FLOW_NOT_NEGOTIATED;
- goto done;
- }
-
-invalid_buffer:
- {
- GST_WARNING_OBJECT (videorate,
- "Got buffer with GST_CLOCK_TIME_NONE timestamp, discarding it");
- gst_buffer_unref (buffer);
- goto done;
- }
-}
-
-static void
-gst_video_rate_set_property (GObject * object,
- guint prop_id, const GValue * value, GParamSpec * pspec)
-{
- GstVideoRate *videorate = GST_VIDEO_RATE (object);
-
- switch (prop_id) {
- case ARG_SILENT:
- videorate->silent = g_value_get_boolean (value);
- break;
- case ARG_NEW_PREF:
- videorate->new_pref = g_value_get_double (value);
- break;
- case ARG_SKIP_TO_FIRST:
- videorate->skip_to_first = g_value_get_boolean (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-gst_video_rate_get_property (GObject * object,
- guint prop_id, GValue * value, GParamSpec * pspec)
-{
- GstVideoRate *videorate = GST_VIDEO_RATE (object);
-
- switch (prop_id) {
- case ARG_IN:
- g_value_set_uint64 (value, videorate->in);
- break;
- case ARG_OUT:
- g_value_set_uint64 (value, videorate->out);
- break;
- case ARG_DUP:
- g_value_set_uint64 (value, videorate->dup);
- break;
- case ARG_DROP:
- g_value_set_uint64 (value, videorate->drop);
- break;
- case ARG_SILENT:
- g_value_set_boolean (value, videorate->silent);
- break;
- case ARG_NEW_PREF:
- g_value_set_double (value, videorate->new_pref);
- break;
- case ARG_SKIP_TO_FIRST:
- g_value_set_boolean (value, videorate->skip_to_first);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-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 gboolean
-plugin_init (GstPlugin * plugin)
-{
- GST_DEBUG_CATEGORY_INIT (video_rate_debug, "videorate", 0,
- "VideoRate stream fixer");
-
- return gst_element_register (plugin, "videorate", GST_RANK_NONE,
- GST_TYPE_VIDEO_RATE);
-}
-
-GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
- GST_VERSION_MINOR,
- "videorate",
- "Adjusts video frames",
- plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)