Quote URI in the example script
[gst-am7xxxsink.git] / src / gstam7xxxsink.c
1 /* GStreamer am7xxx sink plugin
2  * Copyright (C) 2013  Antonio Ospite <ospite@studenti.unina.it>
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 St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "gstam7xxxsink.h"
25
26 /* Debugging category */
27 GST_DEBUG_CATEGORY_STATIC (am7xxxsink_debug);
28 #define GST_CAT_DEFAULT am7xxxsink_debug
29
30 enum
31 {
32   ARG_0,
33   ARG_DEVICE_INDEX
34 };
35
36 #if 0
37 static void gst_am7xxxsink_get_times (GstBaseSink * basesink,
38     GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
39 #endif
40
41 static GstFlowReturn gst_am7xxxsink_show_frame (GstVideoSink * videosink,
42     GstBuffer * buff);
43
44 static gboolean gst_am7xxxsink_start (GstBaseSink * bsink);
45 static gboolean gst_am7xxxsink_stop (GstBaseSink * bsink);
46
47 static GstCaps *gst_am7xxxsink_getcaps (GstBaseSink * bsink, GstCaps * filter);
48 static gboolean gst_am7xxxsink_setcaps (GstBaseSink * bsink, GstCaps * caps);
49
50 static void gst_am7xxxsink_finalize (GObject * object);
51 static void gst_am7xxxsink_set_property (GObject * object,
52     guint prop_id, const GValue * value, GParamSpec * pspec);
53 static void gst_am7xxxsink_get_property (GObject * object,
54     guint prop_id, GValue * value, GParamSpec * pspec);
55 static GstStateChangeReturn gst_am7xxxsink_change_state (GstElement * element,
56     GstStateChange transition);
57
58 #define RAW_VIDEO_CAPS "{ NV12 }"
59
60 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
61     GST_PAD_SINK,
62     GST_PAD_ALWAYS,
63     GST_STATIC_CAPS ("image/jpeg;" GST_VIDEO_CAPS_MAKE (RAW_VIDEO_CAPS))
64     );
65
66 #define parent_class gst_am7xxxsink_parent_class
67 G_DEFINE_TYPE (GstAM7XXXSink, gst_am7xxxsink, GST_TYPE_VIDEO_SINK);
68
69 static void
70 gst_am7xxxsink_init (GstAM7XXXSink * am7xxxsink)
71 {
72   /* nothing to do here yet */
73 }
74
75 #if 0
76 static void
77 gst_am7xxxsink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
78     GstClockTime * start, GstClockTime * end)
79 {
80   GstAM7XXXSink *am7xxxsink;
81
82   am7xxxsink = GST_AM7XXXSINK (basesink);
83
84   if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
85     *start = GST_BUFFER_TIMESTAMP (buffer);
86     if (GST_BUFFER_DURATION_IS_VALID (buffer)) {
87       *end = *start + GST_BUFFER_DURATION (buffer);
88     } else {
89       if (am7xxxsink->fps_n > 0) {
90         *end = *start +
91             gst_util_uint64_scale_int (GST_SECOND, am7xxxsink->fps_d,
92             am7xxxsink->fps_n);
93       }
94     }
95   }
96 }
97 #endif
98
99 static GstCaps *
100 gst_am7xxxsink_getcaps (GstBaseSink * bsink, GstCaps * filter)
101 {
102   GstAM7XXXSink *am7xxxsink;
103   GstCaps *caps;
104
105   am7xxxsink = GST_AM7XXXSINK (bsink);
106
107   caps = gst_static_pad_template_get_caps (&sink_template);
108
109   if (!am7xxxsink->dev)
110     goto done;
111
112   /* display size negotiation */
113   caps = gst_caps_make_writable (caps);
114   gst_caps_set_simple (caps,
115                        "width", GST_TYPE_INT_RANGE, 1, GST_VIDEO_SINK_WIDTH(am7xxxsink),
116                        "height", GST_TYPE_INT_RANGE, 1, GST_VIDEO_SINK_HEIGHT(am7xxxsink),
117                        "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL);
118
119 done:
120
121   if (filter != NULL) {
122     GstCaps *icaps;
123
124     icaps = gst_caps_intersect (caps, filter);
125     gst_caps_unref (caps);
126     caps = icaps;
127   }
128
129   return caps;
130 }
131
132 static gboolean
133 gst_am7xxxsink_setcaps (GstBaseSink * bsink, GstCaps * vscapslist)
134 {
135   GstAM7XXXSink *am7xxxsink;
136   GstStructure *structure;
137   const gchar *mimetype;
138   GstVideoInfo  vinfo;
139   int ret;
140
141   am7xxxsink = GST_AM7XXXSINK (bsink);
142
143   ret = gst_video_info_from_caps (&vinfo, vscapslist);
144   if (!ret)
145     goto unknown_format;
146
147   structure = gst_caps_get_structure (vscapslist, 0);
148   mimetype = gst_structure_get_name (structure);
149
150   if (g_str_equal (mimetype, "image/jpeg")) {
151     am7xxxsink->format = AM7XXX_IMAGE_FORMAT_JPEG;
152   } else if (g_str_equal (mimetype, "video/x-raw")) {
153     if (GST_VIDEO_INFO_FORMAT (&vinfo) == GST_VIDEO_FORMAT_NV12) {
154       am7xxxsink->format = AM7XXX_IMAGE_FORMAT_NV12;
155     } else {
156       goto unknown_format;
157     }
158   } else {
159     goto unknown_format;
160   }
161
162   am7xxxsink->width = GST_VIDEO_INFO_WIDTH(&vinfo);
163   am7xxxsink->height = GST_VIDEO_INFO_HEIGHT(&vinfo);
164
165   return TRUE;
166
167 unknown_format:
168   {
169     GST_WARNING_OBJECT (am7xxxsink, "Could not figure format of input caps");
170     return FALSE;
171   }
172 }
173
174
175 static GstFlowReturn
176 gst_am7xxxsink_show_frame (GstVideoSink * videosink, GstBuffer * buf)
177 {
178
179   GstAM7XXXSink *am7xxxsink;
180   GstMapInfo map;
181   int ret;
182
183   am7xxxsink = GST_AM7XXXSINK (videosink);
184
185   ret = gst_buffer_map (buf, &map, GST_MAP_READ);
186   if (!ret)
187     return GST_FLOW_ERROR;
188
189   ret = am7xxx_send_image_async(am7xxxsink->dev, am7xxxsink->format,
190       am7xxxsink->width, am7xxxsink->height,
191       map.data, map.size);
192   if (ret < 0) {
193     ret = GST_FLOW_ERROR;
194     goto out;
195   }
196
197   ret = GST_FLOW_OK;
198
199 out:
200   gst_buffer_unmap (buf, &map);
201
202   return ret;
203 }
204
205 static gboolean
206 gst_am7xxxsink_start (GstBaseSink * bsink)
207 {
208   GstAM7XXXSink *am7xxxsink;
209   int ret;
210
211   am7xxxsink = GST_AM7XXXSINK (bsink);
212
213   ret = am7xxx_init(&am7xxxsink->ctx);
214   if (ret < 0)
215     return FALSE;
216
217   ret = am7xxx_open_device(am7xxxsink->ctx, &am7xxxsink->dev, am7xxxsink->device_index);
218   if (ret < 0)
219     return FALSE;
220
221   ret = am7xxx_get_device_info(am7xxxsink->dev, &am7xxxsink->device_info);
222   if (ret < 0)
223     return FALSE;
224
225   GST_VIDEO_SINK_WIDTH (am7xxxsink) = (am7xxxsink->device_info).native_width;
226   GST_VIDEO_SINK_HEIGHT (am7xxxsink) = (am7xxxsink->device_info).native_height;
227
228   return TRUE;
229 }
230
231 static gboolean
232 gst_am7xxxsink_stop (GstBaseSink * bsink)
233 {
234   GstAM7XXXSink *am7xxxsink;
235
236   am7xxxsink = GST_AM7XXXSINK (bsink);
237
238   /* open devices will be closed by am7xxx_shutdown() */
239   am7xxx_shutdown(am7xxxsink->ctx);
240
241   return TRUE;
242 }
243
244 static void
245 gst_am7xxxsink_set_property (GObject * object, guint prop_id,
246     const GValue * value, GParamSpec * pspec)
247 {
248   GstAM7XXXSink *am7xxxsink;
249
250   am7xxxsink = GST_AM7XXXSINK (object);
251
252   switch (prop_id) {
253     case ARG_DEVICE_INDEX:{
254       am7xxxsink->device_index = g_value_get_uint (value);
255       break;
256     }
257     default:
258       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
259       break;
260   }
261 }
262
263
264 static void
265 gst_am7xxxsink_get_property (GObject * object, guint prop_id, GValue * value,
266     GParamSpec * pspec)
267 {
268   GstAM7XXXSink *am7xxxsink;
269
270   am7xxxsink = GST_AM7XXXSINK (object);
271
272   switch (prop_id) {
273     case ARG_DEVICE_INDEX:{
274       g_value_set_uint (value, am7xxxsink->device_index);
275       break;
276     }
277     default:
278       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
279       break;
280   }
281 }
282
283 static GstStateChangeReturn
284 gst_am7xxxsink_change_state (GstElement * element, GstStateChange transition)
285 {
286   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
287
288   g_return_val_if_fail (GST_IS_AM7XXXSINK (element), GST_STATE_CHANGE_FAILURE);
289
290   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
291
292   switch (transition) {
293     default:
294       break;
295   }
296   return ret;
297 }
298
299 static gboolean
300 plugin_init (GstPlugin * plugin)
301 {
302   if (!gst_element_register (plugin, "am7xxxsink", GST_RANK_NONE,
303           GST_TYPE_AM7XXXSINK))
304     return FALSE;
305
306   GST_DEBUG_CATEGORY_INIT (am7xxxsink_debug, "am7xxxsink", 0,
307       "am7xxx video sink element");
308
309   return TRUE;
310 }
311
312 static void
313 gst_am7xxxsink_class_init (GstAM7XXXSinkClass * klass)
314 {
315   GObjectClass *gobject_class;
316   GstElementClass *gstelement_class;
317   GstBaseSinkClass *basesink_class;
318   GstVideoSinkClass *videosink_class;
319
320   gobject_class = (GObjectClass *) klass;
321   gstelement_class = (GstElementClass *) klass;
322   basesink_class = (GstBaseSinkClass *) klass;
323   videosink_class = (GstVideoSinkClass *) klass;
324
325   gobject_class->set_property = gst_am7xxxsink_set_property;
326   gobject_class->get_property = gst_am7xxxsink_get_property;
327   gobject_class->finalize = gst_am7xxxsink_finalize;
328
329   gstelement_class->change_state =
330       GST_DEBUG_FUNCPTR (gst_am7xxxsink_change_state);
331
332   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DEVICE_INDEX,
333       g_param_spec_uint ("device-index", "device index",
334           "The am7xxx device index eg: 0", 0, G_MAXUINT, 0, G_PARAM_READWRITE));
335
336   basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_am7xxxsink_setcaps);
337   basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_am7xxxsink_getcaps);
338 #if 0
339   basesink_class->get_times = GST_DEBUG_FUNCPTR (gst_am7xxxsink_get_times);
340 #endif
341   basesink_class->start = GST_DEBUG_FUNCPTR (gst_am7xxxsink_start);
342   basesink_class->stop = GST_DEBUG_FUNCPTR (gst_am7xxxsink_stop);
343
344   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_am7xxxsink_show_frame);
345
346   gst_element_class_set_static_metadata (gstelement_class, "am7xxx video sink",
347       "Sink/Video", "A videosink for projectors supported by libam7xxx",
348       "Antonio Ospite <ospite@studenti.unina.it>");
349
350   gst_element_class_add_pad_template (gstelement_class,
351       gst_static_pad_template_get (&sink_template));
352 }
353
354 static void
355 gst_am7xxxsink_finalize (GObject * object)
356 {
357   G_OBJECT_CLASS (parent_class)->finalize (object);
358 }
359
360 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
361     GST_VERSION_MINOR,
362     am7xxxsink,
363     "libam7xxx video sink",
364     plugin_init, VERSION, "LGPL", "gst-am7xxxsink", "http://ao2.it")