1 /* GStreamer
2 * Copyright (C) 2011 David A. Schleef <ds@schleef.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
17 * Boston, MA 02110-1335, USA.
18 */
19 /**
20 * SECTION:element-gstintervideosink
21 * @title: gstintervideosink
22 *
23 * The intervideosink element is a video sink element. It is used
24 * in connection with an intervideosrc element in a different pipeline,
25 * similar to interaudiosink and interaudiosrc.
26 *
27 * ## Example launch line
28 * |[
29 * gst-launch-1.0 -v videotestsrc ! intervideosink
30 * ]|
31 *
32 * The intervideosink element cannot be used effectively with gst-launch-1.0,
33 * as it requires a second pipeline in the application to send video to.
34 * See the gstintertest.c example in the gst-plugins-bad source code for
35 * more details.
36 *
37 */
38
39 #ifdef HAVE_CONFIG_H
40 #include "config.h"
41 #endif
42
43 #include "gstintervideosink.h"
44
45 #include <string.h>
46
47 GST_DEBUG_CATEGORY_STATIC (gst_inter_video_sink_debug_category);
48 #define GST_CAT_DEFAULT gst_inter_video_sink_debug_category
49
50 /* prototypes */
51 static void gst_inter_video_sink_set_property (GObject * object,
52 guint property_id, const GValue * value, GParamSpec * pspec);
53 static void gst_inter_video_sink_get_property (GObject * object,
54 guint property_id, GValue * value, GParamSpec * pspec);
55 static void gst_inter_video_sink_finalize (GObject * object);
56
57 static void gst_inter_video_sink_get_times (GstBaseSink * sink,
58 GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
59 static gboolean gst_inter_video_sink_start (GstBaseSink * sink);
60 static gboolean gst_inter_video_sink_stop (GstBaseSink * sink);
61 static gboolean gst_inter_video_sink_set_caps (GstBaseSink * sink,
62 GstCaps * caps);
63 static GstFlowReturn gst_inter_video_sink_show_frame (GstVideoSink * sink,
64 GstBuffer * buffer);
65
66 enum
67 {
68 PROP_0,
69 PROP_CHANNEL
70 };
71
72 #define DEFAULT_CHANNEL ("default")
73
74 /* pad templates */
75 static GstStaticPadTemplate gst_inter_video_sink_sink_template =
76 GST_STATIC_PAD_TEMPLATE ("sink",
77 GST_PAD_SINK,
78 GST_PAD_ALWAYS,
79 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL))
80 );
81
82
83 /* class initialization */
84 G_DEFINE_TYPE (GstInterVideoSink, gst_inter_video_sink, GST_TYPE_VIDEO_SINK);
85
86 static void
gst_inter_video_sink_class_init(GstInterVideoSinkClass * klass)87 gst_inter_video_sink_class_init (GstInterVideoSinkClass * klass)
88 {
89 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
90 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
91 GstBaseSinkClass *base_sink_class = GST_BASE_SINK_CLASS (klass);
92 GstVideoSinkClass *video_sink_class = GST_VIDEO_SINK_CLASS (klass);
93
94 GST_DEBUG_CATEGORY_INIT (gst_inter_video_sink_debug_category,
95 "intervideosink", 0, "debug category for intervideosink element");
96
97 gst_element_class_add_static_pad_template (element_class,
98 &gst_inter_video_sink_sink_template);
99
100 gst_element_class_set_static_metadata (element_class,
101 "Internal video sink",
102 "Sink/Video",
103 "Virtual video sink for internal process communication",
104 "David Schleef <ds@schleef.org>");
105
106 gobject_class->set_property = gst_inter_video_sink_set_property;
107 gobject_class->get_property = gst_inter_video_sink_get_property;
108 gobject_class->finalize = gst_inter_video_sink_finalize;
109 base_sink_class->get_times =
110 GST_DEBUG_FUNCPTR (gst_inter_video_sink_get_times);
111 base_sink_class->start = GST_DEBUG_FUNCPTR (gst_inter_video_sink_start);
112 base_sink_class->stop = GST_DEBUG_FUNCPTR (gst_inter_video_sink_stop);
113 base_sink_class->set_caps = GST_DEBUG_FUNCPTR (gst_inter_video_sink_set_caps);
114 video_sink_class->show_frame =
115 GST_DEBUG_FUNCPTR (gst_inter_video_sink_show_frame);
116
117 g_object_class_install_property (gobject_class, PROP_CHANNEL,
118 g_param_spec_string ("channel", "Channel",
119 "Channel name to match inter src and sink elements",
120 DEFAULT_CHANNEL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
121 }
122
123 static void
gst_inter_video_sink_init(GstInterVideoSink * intervideosink)124 gst_inter_video_sink_init (GstInterVideoSink * intervideosink)
125 {
126 intervideosink->channel = g_strdup (DEFAULT_CHANNEL);
127 }
128
129 void
gst_inter_video_sink_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)130 gst_inter_video_sink_set_property (GObject * object, guint property_id,
131 const GValue * value, GParamSpec * pspec)
132 {
133 GstInterVideoSink *intervideosink = GST_INTER_VIDEO_SINK (object);
134
135 switch (property_id) {
136 case PROP_CHANNEL:
137 g_free (intervideosink->channel);
138 intervideosink->channel = g_value_dup_string (value);
139 break;
140 default:
141 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
142 break;
143 }
144 }
145
146 void
gst_inter_video_sink_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)147 gst_inter_video_sink_get_property (GObject * object, guint property_id,
148 GValue * value, GParamSpec * pspec)
149 {
150 GstInterVideoSink *intervideosink = GST_INTER_VIDEO_SINK (object);
151
152 switch (property_id) {
153 case PROP_CHANNEL:
154 g_value_set_string (value, intervideosink->channel);
155 break;
156 default:
157 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
158 break;
159 }
160 }
161
162 void
gst_inter_video_sink_finalize(GObject * object)163 gst_inter_video_sink_finalize (GObject * object)
164 {
165 GstInterVideoSink *intervideosink = GST_INTER_VIDEO_SINK (object);
166
167 /* clean up object here */
168 g_free (intervideosink->channel);
169
170 G_OBJECT_CLASS (gst_inter_video_sink_parent_class)->finalize (object);
171 }
172
173
174 static void
gst_inter_video_sink_get_times(GstBaseSink * sink,GstBuffer * buffer,GstClockTime * start,GstClockTime * end)175 gst_inter_video_sink_get_times (GstBaseSink * sink, GstBuffer * buffer,
176 GstClockTime * start, GstClockTime * end)
177 {
178 GstInterVideoSink *intervideosink = GST_INTER_VIDEO_SINK (sink);
179
180 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
181 *start = GST_BUFFER_TIMESTAMP (buffer);
182 if (GST_BUFFER_DURATION_IS_VALID (buffer)) {
183 *end = *start + GST_BUFFER_DURATION (buffer);
184 } else {
185 if (intervideosink->info.fps_n > 0) {
186 *end = *start +
187 gst_util_uint64_scale_int (GST_SECOND, intervideosink->info.fps_d,
188 intervideosink->info.fps_n);
189 }
190 }
191 }
192 }
193
194 static gboolean
gst_inter_video_sink_start(GstBaseSink * sink)195 gst_inter_video_sink_start (GstBaseSink * sink)
196 {
197 GstInterVideoSink *intervideosink = GST_INTER_VIDEO_SINK (sink);
198
199 intervideosink->surface = gst_inter_surface_get (intervideosink->channel);
200 g_mutex_lock (&intervideosink->surface->mutex);
201 memset (&intervideosink->surface->video_info, 0, sizeof (GstVideoInfo));
202 g_mutex_unlock (&intervideosink->surface->mutex);
203
204 return TRUE;
205 }
206
207 static gboolean
gst_inter_video_sink_stop(GstBaseSink * sink)208 gst_inter_video_sink_stop (GstBaseSink * sink)
209 {
210 GstInterVideoSink *intervideosink = GST_INTER_VIDEO_SINK (sink);
211
212 g_mutex_lock (&intervideosink->surface->mutex);
213 if (intervideosink->surface->video_buffer) {
214 gst_buffer_unref (intervideosink->surface->video_buffer);
215 }
216 intervideosink->surface->video_buffer = NULL;
217 memset (&intervideosink->surface->video_info, 0, sizeof (GstVideoInfo));
218 g_mutex_unlock (&intervideosink->surface->mutex);
219
220 gst_inter_surface_unref (intervideosink->surface);
221 intervideosink->surface = NULL;
222
223 return TRUE;
224 }
225
226 static gboolean
gst_inter_video_sink_set_caps(GstBaseSink * sink,GstCaps * caps)227 gst_inter_video_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
228 {
229 GstInterVideoSink *intervideosink = GST_INTER_VIDEO_SINK (sink);
230 GstVideoInfo info;
231
232 if (!gst_video_info_from_caps (&info, caps)) {
233 GST_ERROR_OBJECT (sink, "Failed to parse caps %" GST_PTR_FORMAT, caps);
234 return FALSE;
235 }
236
237 g_mutex_lock (&intervideosink->surface->mutex);
238 intervideosink->surface->video_info = info;
239 intervideosink->info = info;
240 g_mutex_unlock (&intervideosink->surface->mutex);
241
242 return TRUE;
243 }
244
245 static GstFlowReturn
gst_inter_video_sink_show_frame(GstVideoSink * sink,GstBuffer * buffer)246 gst_inter_video_sink_show_frame (GstVideoSink * sink, GstBuffer * buffer)
247 {
248 GstInterVideoSink *intervideosink = GST_INTER_VIDEO_SINK (sink);
249
250 GST_DEBUG_OBJECT (intervideosink, "render ts %" GST_TIME_FORMAT,
251 GST_TIME_ARGS (GST_BUFFER_PTS (buffer)));
252
253 g_mutex_lock (&intervideosink->surface->mutex);
254 if (intervideosink->surface->video_buffer) {
255 gst_buffer_unref (intervideosink->surface->video_buffer);
256 }
257 intervideosink->surface->video_buffer = gst_buffer_ref (buffer);
258 intervideosink->surface->video_buffer_count = 0;
259 g_mutex_unlock (&intervideosink->surface->mutex);
260
261 return GST_FLOW_OK;
262 }
263