change NULL to (NULL) for GST_ELEMENT_ERROR
[platform/upstream/gst-plugins-base.git] / sys / v4l / gstv4lmjpegsink.c
1 /* G-Streamer hardware MJPEG video sink plugin
2  * Copyright (C) 2001-2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <string.h>
25 #include "v4lmjpegsink_calls.h"
26
27 /* elementfactory information */
28 static GstElementDetails gst_v4lmjpegsink_details = {
29   "Video (video4linux/MJPEG) sink",
30   "Sink/Video",
31   "Writes MJPEG-encoded frames to a zoran MJPEG/video4linux device",
32   "Ronald Bultje <rbultje@ronald.bitfreak.net>"
33 };
34
35 /* v4lmjpegsink signals and args */
36 enum {
37   SIGNAL_FRAME_DISPLAYED,
38   LAST_SIGNAL
39 };
40
41 enum {
42   ARG_0,
43   ARG_NUMBUFS,
44   ARG_BUFSIZE,
45   ARG_X_OFFSET,
46   ARG_Y_OFFSET,
47   ARG_FRAMES_DISPLAYED,
48   ARG_FRAME_TIME,
49 };
50
51
52 /* init functions */
53 static void                  gst_v4lmjpegsink_base_init   (gpointer g_class);
54 static void                  gst_v4lmjpegsink_class_init   (GstV4lMjpegSinkClass *klass);
55 static void                  gst_v4lmjpegsink_init         (GstV4lMjpegSink      *v4lmjpegsink);
56
57 /* the chain of buffers */
58 static GstPadLinkReturn   gst_v4lmjpegsink_sinkconnect  (GstPad               *pad,
59                                                             const GstCaps              *vscapslist);
60 static void                  gst_v4lmjpegsink_chain        (GstPad               *pad,
61                                                             GstData              *_data);
62
63 /* get/set gst object functions */
64 static void                  gst_v4lmjpegsink_set_property (GObject              *object,
65                                                             guint                prop_id,
66                                                             const GValue         *value,
67                                                             GParamSpec           *pspec);
68 static void                  gst_v4lmjpegsink_get_property (GObject              *object,
69                                                             guint                prop_id,
70                                                             GValue               *value,
71                                                             GParamSpec           *pspec);
72 static GstElementStateReturn gst_v4lmjpegsink_change_state (GstElement           *element);
73 static void                  gst_v4lmjpegsink_set_clock    (GstElement *element, GstClock *clock);
74
75
76 static GstElementClass *parent_class = NULL;
77 static guint gst_v4lmjpegsink_signals[LAST_SIGNAL] = { 0 };
78
79
80 GType
81 gst_v4lmjpegsink_get_type (void)
82 {
83   static GType v4lmjpegsink_type = 0;
84
85   if (!v4lmjpegsink_type) {
86     static const GTypeInfo v4lmjpegsink_info = {
87       sizeof(GstV4lMjpegSinkClass),
88       gst_v4lmjpegsink_base_init,
89       NULL,
90       (GClassInitFunc)gst_v4lmjpegsink_class_init,
91       NULL,
92       NULL,
93       sizeof(GstV4lMjpegSink),
94       0,
95       (GInstanceInitFunc)gst_v4lmjpegsink_init,
96     };
97     v4lmjpegsink_type = g_type_register_static(GST_TYPE_V4LELEMENT, "GstV4lMjpegSink", &v4lmjpegsink_info, 0);
98   }
99   return v4lmjpegsink_type;
100 }
101
102 static void
103 gst_v4lmjpegsink_base_init (gpointer g_class)
104 {
105   static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE (
106       "sink",
107       GST_PAD_SINK,
108       GST_PAD_ALWAYS,
109       GST_STATIC_CAPS ("video/x-jpeg, "
110         "width = (int) [ 1, MAX ], "
111         "height = (int) [ 1, MAX ], "
112         "framerate = (double) [ 0, MAX ]")
113   );
114   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
115
116   gst_element_class_set_details (gstelement_class, &gst_v4lmjpegsink_details);
117
118   gst_element_class_add_pad_template (gstelement_class,
119       gst_static_pad_template_get(&sink_template));
120 }
121 static void
122 gst_v4lmjpegsink_class_init (GstV4lMjpegSinkClass *klass)
123 {
124   GObjectClass *gobject_class;
125   GstElementClass *gstelement_class;
126
127   gobject_class = (GObjectClass*)klass;
128   gstelement_class = (GstElementClass*)klass;
129
130   parent_class = g_type_class_ref (GST_TYPE_V4LELEMENT);
131
132   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_NUMBUFS,
133     g_param_spec_int("num_buffers","num_buffers","num_buffers",
134     G_MININT,G_MAXINT,0,G_PARAM_READWRITE));
135   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BUFSIZE,
136     g_param_spec_int("buffer_size","buffer_size","buffer_size",
137     G_MININT,G_MAXINT,0,G_PARAM_READWRITE));
138
139   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_X_OFFSET,
140     g_param_spec_int("x_offset","x_offset","x_offset",
141     G_MININT,G_MAXINT,0,G_PARAM_WRITABLE));
142   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_Y_OFFSET,
143     g_param_spec_int("y_offset","y_offset","y_offset",
144     G_MININT,G_MAXINT,0,G_PARAM_WRITABLE));
145
146   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FRAMES_DISPLAYED,
147     g_param_spec_int("frames_displayed","frames_displayed","frames_displayed",
148     G_MININT,G_MAXINT,0,G_PARAM_READABLE));
149   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FRAME_TIME,
150     g_param_spec_int("frame_time","frame_time","frame_time",
151     G_MININT,G_MAXINT,0,G_PARAM_READABLE));
152
153   gobject_class->set_property = gst_v4lmjpegsink_set_property;
154   gobject_class->get_property = gst_v4lmjpegsink_get_property;
155
156   gst_v4lmjpegsink_signals[SIGNAL_FRAME_DISPLAYED] =
157     g_signal_new ("frame_displayed", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
158                    G_STRUCT_OFFSET (GstV4lMjpegSinkClass, frame_displayed), NULL, NULL,
159                    g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
160
161   gstelement_class->change_state = gst_v4lmjpegsink_change_state;
162   gstelement_class->set_clock    = gst_v4lmjpegsink_set_clock;
163 }
164
165
166 static void
167 gst_v4lmjpegsink_init (GstV4lMjpegSink *v4lmjpegsink)
168 {
169   GstElementClass *klass = GST_ELEMENT_GET_CLASS (v4lmjpegsink);
170
171   v4lmjpegsink->sinkpad = gst_pad_new_from_template (
172         gst_element_class_get_pad_template (klass, "sink"), "sink");
173   gst_element_add_pad (GST_ELEMENT (v4lmjpegsink), v4lmjpegsink->sinkpad);
174
175   gst_pad_set_chain_function (v4lmjpegsink->sinkpad, gst_v4lmjpegsink_chain);
176   gst_pad_set_link_function (v4lmjpegsink->sinkpad, gst_v4lmjpegsink_sinkconnect);
177
178   v4lmjpegsink->clock = NULL;
179
180   v4lmjpegsink->width = -1;
181   v4lmjpegsink->height = -1;
182
183   v4lmjpegsink->x_offset = -1;
184   v4lmjpegsink->y_offset = -1;
185
186   v4lmjpegsink->numbufs = 64;
187   v4lmjpegsink->bufsize = 256;
188
189   GST_FLAG_SET(v4lmjpegsink, GST_ELEMENT_THREAD_SUGGESTED);
190 }
191
192
193 static GstPadLinkReturn
194 gst_v4lmjpegsink_sinkconnect (GstPad  *pad,
195                               const GstCaps *vscapslist)
196 {
197   GstV4lMjpegSink *v4lmjpegsink;
198   GstStructure *structure;
199
200   v4lmjpegsink = GST_V4LMJPEGSINK (gst_pad_get_parent (pad));
201
202   /* in case the buffers are active (which means that we already
203    * did capsnego before and didn't clean up), clean up anyways */
204   if (GST_V4L_IS_ACTIVE(GST_V4LELEMENT(v4lmjpegsink)))
205     if (!gst_v4lmjpegsink_playback_deinit(v4lmjpegsink))
206       return GST_PAD_LINK_REFUSED;
207
208   structure = gst_caps_get_structure (vscapslist, 0);
209
210   gst_structure_get_int  (structure, "width", &v4lmjpegsink->width);
211   gst_structure_get_int  (structure, "height", &v4lmjpegsink->height);
212
213   if (!gst_v4lmjpegsink_set_playback(v4lmjpegsink,
214        v4lmjpegsink->width, v4lmjpegsink->height,
215        v4lmjpegsink->x_offset, v4lmjpegsink->y_offset,
216        GST_V4LELEMENT(v4lmjpegsink)->vchan.norm, 0)) /* TODO: interlacing */
217     return GST_PAD_LINK_REFUSED;
218
219   /* set buffer info */
220   if (!gst_v4lmjpegsink_set_buffer(v4lmjpegsink,
221        v4lmjpegsink->numbufs, v4lmjpegsink->bufsize))
222     return GST_PAD_LINK_REFUSED;
223   if (!gst_v4lmjpegsink_playback_init(v4lmjpegsink))
224     return GST_PAD_LINK_REFUSED;
225
226   return GST_PAD_LINK_OK;
227
228 }
229
230
231 static void
232 gst_v4lmjpegsink_set_clock (GstElement *element, GstClock *clock)
233 {
234   GstV4lMjpegSink *v4mjpegsink = GST_V4LMJPEGSINK (element);
235
236   v4mjpegsink->clock = clock;
237 }
238
239
240 static void
241 gst_v4lmjpegsink_chain (GstPad    *pad,
242                         GstData *_data)
243 {
244   GstBuffer *buf = GST_BUFFER (_data);
245   GstV4lMjpegSink *v4lmjpegsink;
246   gint num;
247
248   g_return_if_fail (pad != NULL);
249   g_return_if_fail (GST_IS_PAD (pad));
250   g_return_if_fail (buf != NULL);
251
252   v4lmjpegsink = GST_V4LMJPEGSINK (gst_pad_get_parent (pad));
253
254   if (v4lmjpegsink->clock) {
255     GST_DEBUG ("videosink: clock wait: %" G_GUINT64_FORMAT, GST_BUFFER_TIMESTAMP(buf));
256
257     gst_element_wait (GST_ELEMENT(v4lmjpegsink), GST_BUFFER_TIMESTAMP(buf));
258   }
259
260 #if 0
261   if (GST_BUFFER_POOL(buf) == v4lmjpegsink->bufferpool)
262   {
263     num = GPOINTER_TO_INT(GST_BUFFER_POOL_PRIVATE(buf));
264     gst_v4lmjpegsink_play_frame(v4lmjpegsink, num);
265   }
266   else
267   {
268 #endif
269     /* check size */
270     if (GST_BUFFER_SIZE(buf) > v4lmjpegsink->breq.size)
271     {
272       GST_ELEMENT_ERROR (v4lmjpegsink, RESOURCE, WRITE, (NULL),
273         ("Buffer too big (%d KB), max. buffersize is %ld KB",
274         GST_BUFFER_SIZE(buf)/1024, v4lmjpegsink->breq.size/1024));
275       return;
276     }
277
278     /* put JPEG data to the device */
279     gst_v4lmjpegsink_wait_frame(v4lmjpegsink, &num);
280     memcpy(gst_v4lmjpegsink_get_buffer(v4lmjpegsink, num),
281       GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf));
282     gst_v4lmjpegsink_play_frame(v4lmjpegsink, num);
283 #if 0
284   }
285 #endif
286
287   g_signal_emit(G_OBJECT(v4lmjpegsink),gst_v4lmjpegsink_signals[SIGNAL_FRAME_DISPLAYED],0);
288
289   gst_buffer_unref(buf);
290 }
291
292
293 #if 0
294 static GstBuffer *
295 gst_v4lmjpegsink_buffer_new (GstBufferPool *pool,
296                              guint64        offset,
297                              guint          size,
298                              gpointer       user_data)
299 {
300   GstV4lMjpegSink *v4lmjpegsink = GST_V4LMJPEGSINK(user_data);
301   GstBuffer *buffer = NULL;
302   guint8 *data;
303   gint num;
304
305   if (!GST_V4L_IS_ACTIVE(GST_V4LELEMENT(v4lmjpegsink)))
306     return NULL;
307   if (v4lmjpegsink->breq.size < size) {
308     GST_DEBUG ("Requested buffer size is too large (%d > %ld)",
309       size, v4lmjpegsink->breq.size);
310     return NULL;
311   }
312   if (!gst_v4lmjpegsink_wait_frame(v4lmjpegsink, &num))
313     return NULL;
314   data = gst_v4lmjpegsink_get_buffer(v4lmjpegsink, num);
315   if (!data)
316     return NULL;
317   buffer = gst_buffer_new();
318   GST_BUFFER_DATA(buffer) = data;
319   GST_BUFFER_MAXSIZE(buffer) = v4lmjpegsink->breq.size;
320   GST_BUFFER_SIZE(buffer) = size;
321   GST_BUFFER_POOL(buffer) = pool;
322   GST_BUFFER_POOL_PRIVATE(buffer) = GINT_TO_POINTER(num);
323
324   /* with this flag set, we don't need our own buffer_free() function */
325   GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_DONTFREE);
326
327   return buffer;
328 }
329 #endif
330
331
332 static void
333 gst_v4lmjpegsink_set_property (GObject      *object,
334                                guint        prop_id,
335                                const GValue *value,
336                                GParamSpec   *pspec)
337 {
338   GstV4lMjpegSink *v4lmjpegsink;
339
340   /* it's not null if we got it, but it might not be ours */
341   g_return_if_fail (GST_IS_V4LMJPEGSINK (object));
342
343   v4lmjpegsink = GST_V4LMJPEGSINK (object);
344
345   switch (prop_id)
346   {
347     case ARG_NUMBUFS:
348       v4lmjpegsink->numbufs = g_value_get_int(value);
349       break;
350     case ARG_BUFSIZE:
351       v4lmjpegsink->bufsize = g_value_get_int(value);
352       break;
353     case ARG_X_OFFSET:
354       v4lmjpegsink->x_offset = g_value_get_int(value);
355       break;
356     case ARG_Y_OFFSET:
357       v4lmjpegsink->y_offset = g_value_get_int(value);
358       break;
359     default:
360       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
361       break;
362   }
363 }
364
365
366 static void
367 gst_v4lmjpegsink_get_property (GObject    *object,
368                                guint      prop_id,
369                                GValue     *value,
370                                GParamSpec *pspec)
371 {
372   GstV4lMjpegSink *v4lmjpegsink;
373
374   /* it's not null if we got it, but it might not be ours */
375   v4lmjpegsink = GST_V4LMJPEGSINK(object);
376
377   switch (prop_id) {
378     case ARG_FRAMES_DISPLAYED:
379       g_value_set_int (value, v4lmjpegsink->frames_displayed);
380       break;
381     case ARG_FRAME_TIME:
382       g_value_set_int (value, v4lmjpegsink->frame_time/1000000);
383       break;
384     case ARG_NUMBUFS:
385       g_value_set_int (value, v4lmjpegsink->numbufs);
386       break;
387     case ARG_BUFSIZE:
388       g_value_set_int (value, v4lmjpegsink->bufsize);
389       break;
390     default:
391       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
392       break;
393   }
394 }
395
396
397 static GstElementStateReturn
398 gst_v4lmjpegsink_change_state (GstElement *element)
399 {
400   GstV4lMjpegSink *v4lmjpegsink;
401   GstElementStateReturn parent_value;
402
403   g_return_val_if_fail (GST_IS_V4LMJPEGSINK (element), GST_STATE_FAILURE);
404   v4lmjpegsink = GST_V4LMJPEGSINK(element);
405
406   /* set up change state */
407   switch (GST_STATE_TRANSITION(element)) {
408     case GST_STATE_READY_TO_PAUSED:
409       /* we used to do buffer setup here, but that's now done
410        * right after capsnego */
411       break;
412     case GST_STATE_PAUSED_TO_PLAYING:
413       /* start */
414       if (!gst_v4lmjpegsink_playback_start(v4lmjpegsink))
415         return GST_STATE_FAILURE;
416       break;
417     case GST_STATE_PLAYING_TO_PAUSED:
418       /* de-queue all queued buffers */
419       if (!gst_v4lmjpegsink_playback_stop(v4lmjpegsink))
420         return GST_STATE_FAILURE;
421       break;
422     case GST_STATE_PAUSED_TO_READY:
423       /* stop playback, unmap all buffers */
424       if (!gst_v4lmjpegsink_playback_deinit(v4lmjpegsink))
425         return GST_STATE_FAILURE;
426       break;
427   }
428
429   if (GST_ELEMENT_CLASS (parent_class)->change_state) {
430     parent_value = GST_ELEMENT_CLASS (parent_class)->change_state (element);
431   } else {
432     parent_value = GST_STATE_FAILURE;
433   }
434
435   if (GST_ELEMENT_CLASS (parent_class)->change_state)
436     return parent_value;
437
438   return GST_STATE_SUCCESS;
439 }