📄 gstbasesink.c
字号:
/* GStreamer * Copyright (C) 2005-2007 Wim Taymans <wim.taymans@gmail.com> * * gstbasesink.c: Base class for sink elements * * 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:gstbasesink * @short_description: Base class for sink elements * @see_also: #GstBaseTransform, #GstBaseSource * * #GstBaseSink is the base class for sink elements in GStreamer, such as * xvimagesink or filesink. It is a layer on top of #GstElement that provides a * simplified interface to plugin writers. #GstBaseSink handles many details * for you, for example: preroll, clock synchronization, state changes, * activation in push or pull mode, and queries. * * In most cases, when writing sink elements, there is no need to implement * class methods from #GstElement or to set functions on pads, because the * #GstBaseSink infrastructure should be sufficient. * * #GstBaseSink provides support for exactly one sink pad, which should be * named "sink". A sink implementation (subclass of #GstBaseSink) should * install a pad template in its base_init function, like so: * <programlisting> * static void * my_element_base_init (gpointer g_class) * { * GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); * * // sinktemplate should be a #GstStaticPadTemplate with direction * // #GST_PAD_SINK and name "sink" * gst_element_class_add_pad_template (gstelement_class, * gst_static_pad_template_get (&sinktemplate)); * // see #GstElementDetails * gst_element_class_set_details (gstelement_class, &details); * } * </programlisting> * * #GstBaseSink will handle the prerolling correctly. This means that it will * return #GST_STATE_CHANGE_ASYNC from a state change to PAUSED until the first * buffer arrives in this element. The base class will call the * #GstBaseSink::preroll vmethod with this preroll buffer and will then commit * the state change to the next asynchronously pending state. * * When the element is set to PLAYING, #GstBaseSink will synchronise on the * clock using the times returned from ::get_times. If this function returns * #GST_CLOCK_TIME_NONE for the start time, no synchronisation will be done. * Synchronisation can be disabled entirely by setting the object "sync" * property to %FALSE. * * After synchronisation the virtual method #GstBaseSink::render will be called. * Subclasses should minimally implement this method. * * Since 0.10.3 subclasses that synchronise on the clock in the ::render method * are supported as well. These classes typically receive a buffer in the render * method and can then potentially block on the clock while rendering. A typical * example is an audiosink. Since 0.10.11 these subclasses can use * gst_base_sink_wait_preroll() to perform the blocking wait. * * Upon receiving the EOS event in the PLAYING state, #GstBaseSink will wait * for the clock to reach the time indicated by the stop time of the last * ::get_times call before posting an EOS message. When the element receives * EOS in PAUSED, preroll completes, the event is queued and an EOS message is * posted when going to PLAYING. * * #GstBaseSink will internally use the #GST_EVENT_NEWSEGMENT events to schedule * synchronisation and clipping of buffers. Buffers that fall completely outside * of the current segment are dropped. Buffers that fall partially in the * segment are rendered (and prerolled). Subclasses should do any subbuffer * clipping themselves when needed. * * #GstBaseSink will by default report the current playback position in * #GST_FORMAT_TIME based on the current clock time and segment information. * If no clock has been set on the element, the query will be forwarded * upstream. * * The ::set_caps function will be called when the subclass should configure * itself to process a specific media type. * * The ::start and ::stop virtual methods will be called when resources should * be allocated. Any ::preroll, ::render and ::set_caps function will be * called between the ::start and ::stop calls. * * The ::event virtual method will be called when an event is received by * #GstBaseSink. Normally this method should only be overriden by very specific * elements (such as file sinks) which need to handle the newsegment event * specially. * * #GstBaseSink provides an overridable ::buffer_alloc function that can be * used by sinks that want to do reverse negotiation or to provide * custom buffers (hardware buffers for example) to upstream elements. * * The ::unlock method is called when the elements should unblock any blocking * operations they perform in the ::render method. This is mostly useful when * the ::render method performs a blocking write on a file descriptor, for * example. * * The max-lateness property affects how the sink deals with buffers that * arrive too late in the sink. A buffer arrives too late in the sink when * the presentation time (as a combination of the last segment, buffer * timestamp and element base_time) plus the duration is before the current * time of the clock. * If the frame is later than max-lateness, the sink will drop the buffer * without calling the render method. * This feature is disabled if sync is disabled, the ::get-times method does * not return a valid start time or max-lateness is set to -1 (the default). * Subclasses can use gst_base_sink_set_max_lateness() to configure the * max-lateness value. * * The qos property will enable the quality-of-service features of the basesink * which gather statistics about the real-time performance of the clock * synchronisation. For each buffer received in the sink, statistics are * gathered and a QOS event is sent upstream with these numbers. This * information can then be used by upstream elements to reduce their processing * rate, for example. * * Since 0.10.15 the async property can be used to instruct the sink to never * perform an ASYNC state change. This feature is mostly usable when dealing * with non-synchronized streams or sparse streams. * * Last reviewed on 2007-08-29 (0.10.15) */#ifdef HAVE_CONFIG_H# include "config.h"#endif#include "gstbasesink.h"#include <gst/gstmarshal.h>#include <gst/gst_private.h>#include <gst/gst-i18n-lib.h>GST_DEBUG_CATEGORY_STATIC (gst_base_sink_debug);#define GST_CAT_DEFAULT gst_base_sink_debug#define GST_BASE_SINK_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BASE_SINK, GstBaseSinkPrivate))/* FIXME, some stuff in ABI.data and other in Private... * Make up your mind please. */struct _GstBaseSinkPrivate{ gint qos_enabled; /* ATOMIC */ gboolean async_enabled; GstClockTimeDiff ts_offset; /* start, stop of current buffer, stream time, used to report position */ GstClockTime current_sstart; GstClockTime current_sstop; /* start, stop and jitter of current buffer, running time */ GstClockTime current_rstart; GstClockTime current_rstop; GstClockTimeDiff current_jitter; /* EOS sync time in running time */ GstClockTime eos_rtime; /* last buffer that arrived in time, running time */ GstClockTime last_in_time; /* when the last buffer left the sink, running time */ GstClockTime last_left; /* running averages go here these are done on running time */ GstClockTime avg_pt; GstClockTime avg_duration; gdouble avg_rate; /* these are done on system time. avg_jitter and avg_render are * compared to eachother to see if the rendering time takes a * huge amount of the processing, If so we are flooded with * buffers. */ GstClockTime last_left_systime; GstClockTime avg_jitter; GTimeVal start, stop; GstClockTime avg_render; /* number of rendered and dropped frames */ guint64 rendered; guint64 dropped; /* latency stuff */ GstClockTime latency; /* if we already commited the state */ gboolean commited; /* when we received EOS */ gboolean received_eos; /* when we are prerolled and able to report latency */ gboolean have_latency; /* the last buffer we prerolled or rendered. Useful for making snapshots */ GstBuffer *last_buffer;};#define DO_RUNNING_AVG(avg,val,size) (((val) + ((size)-1) * (avg)) / (size))/* generic running average, this has a neutral window size */#define UPDATE_RUNNING_AVG(avg,val) DO_RUNNING_AVG(avg,val,8)/* the windows for these running averages are experimentally obtained. * possitive values get averaged more while negative values use a small * window so we can react faster to badness. */#define UPDATE_RUNNING_AVG_P(avg,val) DO_RUNNING_AVG(avg,val,16)#define UPDATE_RUNNING_AVG_N(avg,val) DO_RUNNING_AVG(avg,val,4)/* BaseSink properties */#define DEFAULT_SIZE 1024#define DEFAULT_CAN_ACTIVATE_PULL FALSE /* fixme: enable me */#define DEFAULT_CAN_ACTIVATE_PUSH TRUE#define DEFAULT_PREROLL_QUEUE_LEN 0#define DEFAULT_SYNC TRUE#define DEFAULT_MAX_LATENESS -1#define DEFAULT_QOS FALSE#define DEFAULT_ASYNC TRUE#define DEFAULT_TS_OFFSET 0enum{ PROP_0, PROP_PREROLL_QUEUE_LEN, PROP_SYNC, PROP_MAX_LATENESS, PROP_QOS, PROP_ASYNC, PROP_TS_OFFSET, PROP_LAST_BUFFER, PROP_LAST};static GstElementClass *parent_class = NULL;static void gst_base_sink_base_init (gpointer g_class);static void gst_base_sink_class_init (GstBaseSinkClass * klass);static void gst_base_sink_init (GstBaseSink * trans, gpointer g_class);static void gst_base_sink_finalize (GObject * object);GTypegst_base_sink_get_type (void){ static GType base_sink_type = 0; if (G_UNLIKELY (base_sink_type == 0)) { static const GTypeInfo base_sink_info = { sizeof (GstBaseSinkClass), (GBaseInitFunc) gst_base_sink_base_init, NULL, (GClassInitFunc) gst_base_sink_class_init, NULL, NULL, sizeof (GstBaseSink), 0, (GInstanceInitFunc) gst_base_sink_init, }; base_sink_type = g_type_register_static (GST_TYPE_ELEMENT, "GstBaseSink", &base_sink_info, G_TYPE_FLAG_ABSTRACT); } return base_sink_type;}static void gst_base_sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);static void gst_base_sink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);static gboolean gst_base_sink_send_event (GstElement * element, GstEvent * event);static gboolean gst_base_sink_query (GstElement * element, GstQuery * query);static GstCaps *gst_base_sink_get_caps (GstBaseSink * sink);static gboolean gst_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps);static GstFlowReturn gst_base_sink_buffer_alloc (GstBaseSink * sink, guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);static void gst_base_sink_get_times (GstBaseSink * basesink, GstBuffer * buffer, GstClockTime * start, GstClockTime * end);static gboolean gst_base_sink_set_flushing (GstBaseSink * basesink, GstPad * pad, gboolean flushing);static gboolean gst_base_sink_default_activate_pull (GstBaseSink * basesink, gboolean active);static GstStateChangeReturn gst_base_sink_change_state (GstElement * element, GstStateChange transition);static GstFlowReturn gst_base_sink_chain (GstPad * pad, GstBuffer * buffer);static void gst_base_sink_loop (GstPad * pad);static gboolean gst_base_sink_pad_activate (GstPad * pad);static gboolean gst_base_sink_pad_activate_push (GstPad * pad, gboolean active);static gboolean gst_base_sink_pad_activate_pull (GstPad * pad, gboolean active);static gboolean gst_base_sink_event (GstPad * pad, GstEvent * event);static gboolean gst_base_sink_peer_query (GstBaseSink * sink, GstQuery * query);/* check if an object was too late */static gboolean gst_base_sink_is_too_late (GstBaseSink * basesink, GstMiniObject * obj, GstClockTime start, GstClockTime stop, GstClockReturn status, GstClockTimeDiff jitter);static voidgst_base_sink_base_init (gpointer g_class){ GST_DEBUG_CATEGORY_INIT (gst_base_sink_debug, "basesink", 0, "basesink element");}static voidgst_base_sink_class_init (GstBaseSinkClass * klass){ GObjectClass *gobject_class; GstElementClass *gstelement_class; gobject_class = G_OBJECT_CLASS (klass); gstelement_class = GST_ELEMENT_CLASS (klass); g_type_class_add_private (klass, sizeof (GstBaseSinkPrivate)); parent_class = g_type_class_peek_parent (klass); gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_base_sink_finalize); gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_base_sink_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_base_sink_get_property); /* FIXME, this next value should be configured using an event from the * upstream element, ie, the BUFFER_SIZE event. */ g_object_class_install_property (gobject_class, PROP_PREROLL_QUEUE_LEN, g_param_spec_uint ("preroll-queue-len", "Preroll queue length", "Number of buffers to queue during preroll", 0, G_MAXUINT, DEFAULT_PREROLL_QUEUE_LEN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_SYNC, g_param_spec_boolean ("sync", "Sync", "Sync on the clock", DEFAULT_SYNC, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_MAX_LATENESS, g_param_spec_int64 ("max-lateness", "Max Lateness", "Maximum number of nanoseconds that a buffer can be late before it " "is dropped (-1 unlimited)", -1, G_MAXINT64, DEFAULT_MAX_LATENESS, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_QOS, g_param_spec_boolean ("qos", "Qos", "Generate Quality-of-Service events upstream", DEFAULT_QOS, G_PARAM_READWRITE)); /** * GstBaseSink:async * * If set to #TRUE, the basesink will perform asynchronous state changes. * When set to #FALSE, the sink will not signal the parent when it prerolls. * Use this option when dealing with sparse streams or when synchronisation is * not required. * * Since: 0.10.15 */ g_object_class_install_property (gobject_class, PROP_ASYNC, g_param_spec_boolean ("async", "Async", "Go asynchronously to PAUSED", DEFAULT_ASYNC, G_PARAM_READWRITE)); /** * GstBaseSink:ts-offset * * Controls the final synchronisation, a negative value will render the buffer * earlier while a positive value delays playback. This property can be * used to fix synchronisation in bad files. * * Since: 0.10.15 */ g_object_class_install_property (gobject_class, PROP_TS_OFFSET, g_param_spec_int64 ("ts-offset", "TS Offset", "Timestamp offset in nanoseconds", G_MININT64, G_MAXINT64, DEFAULT_TS_OFFSET, G_PARAM_READWRITE)); /** * GstBaseSink:last-buffer * * The last buffer that arrived in the sink and was used for preroll or for * rendering. This property can be used to generate thumbnails. * * Since: 0.10.15 */ g_object_class_install_property (gobject_class, PROP_LAST_BUFFER, gst_param_spec_mini_object ("last-buffer", "Last Buffer", "The last buffer received in the sink", GST_TYPE_BUFFER, G_PARAM_READABLE)); gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_base_sink_change_state); gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_base_sink_send_event); gstelement_class->query = GST_DEBUG_FUNCPTR (gst_base_sink_query); klass->get_caps = GST_DEBUG_FUNCPTR (gst_base_sink_get_caps); klass->set_caps = GST_DEBUG_FUNCPTR (gst_base_sink_set_caps); klass->buffer_alloc = GST_DEBUG_FUNCPTR (gst_base_sink_buffer_alloc); klass->get_times = GST_DEBUG_FUNCPTR (gst_base_sink_get_times); klass->activate_pull = GST_DEBUG_FUNCPTR (gst_base_sink_default_activate_pull);}static GstCaps *gst_base_sink_pad_getcaps (GstPad * pad){ GstBaseSinkClass *bclass; GstBaseSink *bsink; GstCaps *caps = NULL; bsink = GST_BASE_SINK (gst_pad_get_parent (pad)); bclass = GST_BASE_SINK_GET_CLASS (bsink); if (bclass->get_caps) caps = bclass->get_caps (bsink); if (caps == NULL) { GstPadTemplate *pad_template; pad_template = gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "sink"); if (pad_template != NULL) { caps = gst_caps_ref (gst_pad_template_get_caps (pad_template)); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -