1 /*
2  * GStreamer
3  * Copyright (C) 2017 Collabora Inc.
4  *   Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 /**
23  * SECTION:element-fakevideosink
24  * @title: fakevideosink
25  *
26  * This element is the same as fakesink but will pretend to support various
27  * allocation meta API like GstVideoMeta in order to prevent memory copies.
28  * This is useful for throughput testing and testing zero-copy path while
29  * creating a new pipeline.
30  *
31  * ## Example launch lines
32  * |[
33  * gst-launch-1.0 videotestsrc ! fakevideosink
34  * gst-launch-1.0 videotestsrc ! fpsdisplaysink text-overlay=false video-sink=fakevideosink
35  * ]|
36  *
37  * Since 1.14
38  */
39 
40 #include "gstfakevideosink.h"
41 
42 #include <gst/video/video.h>
43 
44 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
45     GST_PAD_SINK,
46     GST_PAD_ALWAYS,
47     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES ("ANY",
48             GST_VIDEO_FORMATS_ALL)));
49 
50 G_DEFINE_TYPE (GstFakeVideoSink, gst_fake_video_sink, GST_TYPE_BIN);
51 
52 static gboolean
gst_fake_video_sink_query(GstPad * pad,GstObject * parent,GstQuery * query)53 gst_fake_video_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
54 {
55   GstFakeVideoSink *self = GST_FAKE_VIDEO_SINK (parent);
56   GstCaps *caps;
57   GstVideoInfo info;
58   guint min_buffers = 1;
59 
60   if (GST_QUERY_TYPE (query) != GST_QUERY_ALLOCATION)
61     return gst_pad_query_default (pad, parent, query);
62 
63   gst_query_parse_allocation (query, &caps, NULL);
64   if (!gst_video_info_from_caps (&info, caps))
65     return FALSE;
66 
67   /* Request an extra buffer if we are keeping a ref on the last rendered buffer */
68   if (gst_base_sink_is_last_sample_enabled (GST_BASE_SINK (self->child)))
69     min_buffers++;
70 
71   gst_query_add_allocation_pool (query, NULL, info.size, min_buffers, 0);
72   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
73   gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
74   gst_query_add_allocation_meta (query,
75       GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
76   /* add here any meta API that would help support zero-copy */
77 
78   return TRUE;
79 }
80 
81 /* TODO complete the types and make this an utility */
82 static void
gst_fake_video_sink_proxy_properties(GstFakeVideoSink * self,GstElement * child)83 gst_fake_video_sink_proxy_properties (GstFakeVideoSink * self,
84     GstElement * child)
85 {
86   static gsize initialized = 0;
87 
88   if (g_once_init_enter (&initialized)) {
89     GObjectClass *object_class;
90     GParamSpec **properties;
91     guint n_properties, i;
92 
93     object_class = G_OBJECT_CLASS (GST_FAKE_VIDEO_SINK_GET_CLASS (self));
94     properties = g_object_class_list_properties (G_OBJECT_GET_CLASS (child),
95         &n_properties);
96 
97     for (i = 0; i < n_properties; i++) {
98       if (properties[i]->owner_type != G_OBJECT_TYPE (child) &&
99           properties[i]->owner_type != GST_TYPE_BASE_SINK)
100         continue;
101 
102       if (G_IS_PARAM_SPEC_BOOLEAN (properties[i])) {
103         GParamSpecBoolean *prop = G_PARAM_SPEC_BOOLEAN (properties[i]);
104         g_object_class_install_property (object_class, i + 1,
105             g_param_spec_boolean (g_param_spec_get_name (properties[i]),
106                 g_param_spec_get_nick (properties[i]),
107                 g_param_spec_get_blurb (properties[i]),
108                 prop->default_value, properties[i]->flags));
109       } else if (G_IS_PARAM_SPEC_INT (properties[i])) {
110         GParamSpecInt *prop = G_PARAM_SPEC_INT (properties[i]);
111         g_object_class_install_property (object_class, i + 1,
112             g_param_spec_int (g_param_spec_get_name (properties[i]),
113                 g_param_spec_get_nick (properties[i]),
114                 g_param_spec_get_blurb (properties[i]),
115                 prop->minimum, prop->maximum, prop->default_value,
116                 properties[i]->flags));
117       } else if (G_IS_PARAM_SPEC_UINT (properties[i])) {
118         GParamSpecUInt *prop = G_PARAM_SPEC_UINT (properties[i]);
119         g_object_class_install_property (object_class, i + 1,
120             g_param_spec_uint (g_param_spec_get_name (properties[i]),
121                 g_param_spec_get_nick (properties[i]),
122                 g_param_spec_get_blurb (properties[i]),
123                 prop->minimum, prop->maximum, prop->default_value,
124                 properties[i]->flags));
125       } else if (G_IS_PARAM_SPEC_INT64 (properties[i])) {
126         GParamSpecInt64 *prop = G_PARAM_SPEC_INT64 (properties[i]);
127         g_object_class_install_property (object_class, i + 1,
128             g_param_spec_int64 (g_param_spec_get_name (properties[i]),
129                 g_param_spec_get_nick (properties[i]),
130                 g_param_spec_get_blurb (properties[i]),
131                 prop->minimum, prop->maximum, prop->default_value,
132                 properties[i]->flags));
133       } else if (G_IS_PARAM_SPEC_UINT64 (properties[i])) {
134         GParamSpecUInt64 *prop = G_PARAM_SPEC_UINT64 (properties[i]);
135         g_object_class_install_property (object_class, i + 1,
136             g_param_spec_uint64 (g_param_spec_get_name (properties[i]),
137                 g_param_spec_get_nick (properties[i]),
138                 g_param_spec_get_blurb (properties[i]),
139                 prop->minimum, prop->maximum, prop->default_value,
140                 properties[i]->flags));
141       } else if (G_IS_PARAM_SPEC_ENUM (properties[i])) {
142         GParamSpecEnum *prop = G_PARAM_SPEC_ENUM (properties[i]);
143         g_object_class_install_property (object_class, i + 1,
144             g_param_spec_enum (g_param_spec_get_name (properties[i]),
145                 g_param_spec_get_nick (properties[i]),
146                 g_param_spec_get_blurb (properties[i]),
147                 properties[i]->value_type, prop->default_value,
148                 properties[i]->flags));
149       } else if (G_IS_PARAM_SPEC_STRING (properties[i])) {
150         GParamSpecString *prop = G_PARAM_SPEC_STRING (properties[i]);
151         g_object_class_install_property (object_class, i + 1,
152             g_param_spec_string (g_param_spec_get_name (properties[i]),
153                 g_param_spec_get_nick (properties[i]),
154                 g_param_spec_get_blurb (properties[i]),
155                 prop->default_value, properties[i]->flags));
156       } else if (G_IS_PARAM_SPEC_BOXED (properties[i])) {
157         g_object_class_install_property (object_class, i + 1,
158             g_param_spec_boxed (g_param_spec_get_name (properties[i]),
159                 g_param_spec_get_nick (properties[i]),
160                 g_param_spec_get_blurb (properties[i]),
161                 properties[i]->value_type, properties[i]->flags));
162       }
163     }
164 
165     g_free (properties);
166     g_once_init_leave (&initialized, 1);
167   }
168 }
169 
170 static void
gst_fake_video_sink_init(GstFakeVideoSink * self)171 gst_fake_video_sink_init (GstFakeVideoSink * self)
172 {
173   GstElement *child;
174 
175   child = gst_element_factory_make ("fakesink", "sink");
176 
177   if (child) {
178     GstPad *sink_pad = gst_element_get_static_pad (child, "sink");
179     GstPad *ghost_pad;
180 
181     /* mimic GstVideoSink base class */
182     g_object_set (child, "max-lateness", G_GINT64_CONSTANT (20000000), "qos",
183         TRUE, "sync", TRUE, NULL);
184 
185     gst_bin_add (GST_BIN (self), child);
186 
187     ghost_pad = gst_ghost_pad_new ("sink", sink_pad);
188     gst_element_add_pad (GST_ELEMENT (self), ghost_pad);
189     gst_object_unref (sink_pad);
190 
191     gst_pad_set_query_function (ghost_pad, gst_fake_video_sink_query);
192 
193     self->child = child;
194 
195     gst_fake_video_sink_proxy_properties (self, child);
196   } else {
197     g_warning ("Check your GStreamer installation, "
198         "core element 'fakesink' is missing.");
199   }
200 }
201 
202 static void
gst_fake_video_sink_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)203 gst_fake_video_sink_get_property (GObject * object, guint property_id,
204     GValue * value, GParamSpec * pspec)
205 {
206   GstFakeVideoSink *self = GST_FAKE_VIDEO_SINK (object);
207   g_object_get_property (G_OBJECT (self->child), pspec->name, value);
208 }
209 
210 static void
gst_fake_video_sink_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)211 gst_fake_video_sink_set_property (GObject * object, guint property_id,
212     const GValue * value, GParamSpec * pspec)
213 {
214   GstFakeVideoSink *self = GST_FAKE_VIDEO_SINK (object);
215   g_object_set_property (G_OBJECT (self->child), pspec->name, value);
216 }
217 
218 static void
gst_fake_video_sink_class_init(GstFakeVideoSinkClass * klass)219 gst_fake_video_sink_class_init (GstFakeVideoSinkClass * klass)
220 {
221   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
222   GObjectClass *object_class = G_OBJECT_CLASS (klass);
223 
224   object_class->get_property = gst_fake_video_sink_get_property;
225   object_class->set_property = gst_fake_video_sink_set_property;
226 
227   gst_element_class_add_static_pad_template (element_class, &sink_factory);
228   gst_element_class_set_static_metadata (element_class, "Fake Video Sink",
229       "Video/Sink", "Fake video display that allow zero-copy",
230       "Nicolas Dufresne <nicolas.dufresne@collabora.com>");
231 }
232