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 "gstdebugutilsbadelements.h"
41 #include "gstfakevideosink.h"
42 #include "gstfakesinkutils.h"
43
44 #include <gst/video/video.h>
45
46 #define C_FLAGS(v) ((guint) v)
47
48 GType
gst_fake_video_sink_allocation_meta_flags_get_type(void)49 gst_fake_video_sink_allocation_meta_flags_get_type (void)
50 {
51 static const GFlagsValue values[] = {
52 {C_FLAGS (GST_ALLOCATION_FLAG_CROP_META),
53 "Expose the crop meta as supported", "crop"},
54 {C_FLAGS (GST_ALLOCATION_FLAG_OVERLAY_COMPOSITION_META),
55 "Expose the overlay composition meta as supported",
56 "overlay-composition"},
57 {0, NULL, NULL}
58 };
59 static GType id = 0;
60
61 if (g_once_init_enter ((gsize *) & id)) {
62 GType _id;
63
64 _id =
65 g_flags_register_static ("GstFakeVideoSinkAllocationMetaFlags", values);
66
67 g_once_init_leave ((gsize *) & id, _id);
68 }
69
70 return id;
71 }
72
73 enum
74 {
75 PROP_0,
76 PROP_ALLOCATION_META_FLAGS,
77 PROP_LAST
78 };
79
80 #define ALLOCATION_META_DEFAULT_FLAGS GST_ALLOCATION_FLAG_CROP_META | GST_ALLOCATION_FLAG_OVERLAY_COMPOSITION_META
81
82 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
83 GST_PAD_SINK,
84 GST_PAD_ALWAYS,
85 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES ("ANY",
86 GST_VIDEO_FORMATS_ALL)));
87
88 G_DEFINE_TYPE (GstFakeVideoSink, gst_fake_video_sink, GST_TYPE_BIN);
89 GST_ELEMENT_REGISTER_DEFINE (fakevideosink, "fakevideosink",
90 GST_RANK_NONE, gst_fake_video_sink_get_type ());
91
92 static gboolean
gst_fake_video_sink_query(GstPad * pad,GstObject * parent,GstQuery * query)93 gst_fake_video_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
94 {
95 GstFakeVideoSink *self = GST_FAKE_VIDEO_SINK (parent);
96 GstCaps *caps;
97 GstVideoInfo info;
98 guint min_buffers = 1;
99
100 if (GST_QUERY_TYPE (query) != GST_QUERY_ALLOCATION)
101 return gst_pad_query_default (pad, parent, query);
102
103 gst_query_parse_allocation (query, &caps, NULL);
104 if (!gst_video_info_from_caps (&info, caps))
105 return FALSE;
106
107 /* Request an extra buffer if we are keeping a ref on the last rendered buffer */
108 if (gst_base_sink_is_last_sample_enabled (GST_BASE_SINK (self->child)))
109 min_buffers++;
110
111 gst_query_add_allocation_pool (query, NULL, info.size, min_buffers, 0);
112 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
113
114 GST_OBJECT_LOCK (self);
115 if (self->allocation_meta_flags & GST_ALLOCATION_FLAG_CROP_META)
116 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
117
118 if (self->allocation_meta_flags &
119 GST_ALLOCATION_FLAG_OVERLAY_COMPOSITION_META)
120 gst_query_add_allocation_meta (query,
121 GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
122
123 GST_OBJECT_UNLOCK (self);
124
125 /* add here any meta API that would help support zero-copy */
126
127 return TRUE;
128 }
129
130 static void
gst_fake_video_sink_proxy_properties(GstFakeVideoSink * self,GstElement * child)131 gst_fake_video_sink_proxy_properties (GstFakeVideoSink * self,
132 GstElement * child)
133 {
134 static gsize initialized = 0;
135
136 if (g_once_init_enter (&initialized)) {
137 gst_fake_sink_proxy_properties (GST_ELEMENT_CAST (self), child, PROP_LAST);
138 g_once_init_leave (&initialized, 1);
139 }
140 }
141
142 static void
gst_fake_video_sink_init(GstFakeVideoSink * self)143 gst_fake_video_sink_init (GstFakeVideoSink * self)
144 {
145 GstElement *child;
146 GstPadTemplate *template = gst_static_pad_template_get (&sink_factory);
147
148 child = gst_element_factory_make ("fakesink", "sink");
149
150 self->allocation_meta_flags = ALLOCATION_META_DEFAULT_FLAGS;
151
152 if (child) {
153 GstPad *sink_pad = gst_element_get_static_pad (child, "sink");
154 GstPad *ghost_pad;
155
156 /* mimic GstVideoSink base class */
157 g_object_set (child, "max-lateness", 5 * GST_MSECOND,
158 "processing-deadline", 15 * GST_MSECOND, "qos", TRUE, "sync", TRUE,
159 NULL);
160
161 gst_bin_add (GST_BIN (self), child);
162
163 ghost_pad = gst_ghost_pad_new_from_template ("sink", sink_pad, template);
164 gst_object_unref (template);
165 gst_element_add_pad (GST_ELEMENT (self), ghost_pad);
166 gst_object_unref (sink_pad);
167
168 gst_pad_set_query_function (ghost_pad, gst_fake_video_sink_query);
169
170 self->child = child;
171
172 gst_fake_video_sink_proxy_properties (self, child);
173 } else {
174 g_warning ("Check your GStreamer installation, "
175 "core element 'fakesink' is missing.");
176 }
177 }
178
179 static void
gst_fake_video_sink_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)180 gst_fake_video_sink_get_property (GObject * object, guint property_id,
181 GValue * value, GParamSpec * pspec)
182 {
183 GstFakeVideoSink *self = GST_FAKE_VIDEO_SINK (object);
184
185 switch (property_id) {
186 case PROP_ALLOCATION_META_FLAGS:
187 GST_OBJECT_LOCK (self);
188 g_value_set_flags (value, self->allocation_meta_flags);
189 GST_OBJECT_UNLOCK (self);
190 break;
191 default:
192 g_object_get_property (G_OBJECT (self->child), pspec->name, value);
193 break;
194 }
195 }
196
197 static void
gst_fake_video_sink_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)198 gst_fake_video_sink_set_property (GObject * object, guint property_id,
199 const GValue * value, GParamSpec * pspec)
200 {
201 GstFakeVideoSink *self = GST_FAKE_VIDEO_SINK (object);
202
203 switch (property_id) {
204 case PROP_ALLOCATION_META_FLAGS:
205 GST_OBJECT_LOCK (self);
206 self->allocation_meta_flags = g_value_get_flags (value);
207 GST_OBJECT_UNLOCK (self);
208 break;
209 default:
210 g_object_set_property (G_OBJECT (self->child), pspec->name, value);
211 break;
212 }
213 }
214
215 static void
gst_fake_video_sink_class_init(GstFakeVideoSinkClass * klass)216 gst_fake_video_sink_class_init (GstFakeVideoSinkClass * klass)
217 {
218 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
219 GObjectClass *object_class = G_OBJECT_CLASS (klass);
220
221 object_class->get_property = gst_fake_video_sink_get_property;
222 object_class->set_property = gst_fake_video_sink_set_property;
223
224 gst_element_class_add_static_pad_template (element_class, &sink_factory);
225 gst_element_class_set_static_metadata (element_class, "Fake Video Sink",
226 "Video/Sink", "Fake video display that allows zero-copy",
227 "Nicolas Dufresne <nicolas.dufresne@collabora.com>");
228
229 /**
230 * GstFakeVideoSink:allocation-meta-flags
231 *
232 * Control the behaviour of the sink allocation query handler.
233 *
234 * Since: 1.18
235 */
236 g_object_class_install_property (object_class, PROP_ALLOCATION_META_FLAGS,
237 g_param_spec_flags ("allocation-meta-flags", "Flags",
238 "Flags to control behaviour",
239 GST_TYPE_FAKE_VIDEO_SINK_ALLOCATION_META_FLAGS,
240 ALLOCATION_META_DEFAULT_FLAGS,
241 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
242
243 gst_type_mark_as_plugin_api (GST_TYPE_FAKE_VIDEO_SINK_ALLOCATION_META_FLAGS,
244 0);
245 }
246