1 /* GStreamer
2 * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
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 <string.h>
25
26 #include "gstfrei0r.h"
27 #include "gstfrei0rsrc.h"
28
29 GST_DEBUG_CATEGORY_EXTERN (frei0r_debug);
30 #define GST_CAT_DEFAULT frei0r_debug
31
32 typedef struct
33 {
34 f0r_plugin_info_t info;
35 GstFrei0rFuncTable ftable;
36 } GstFrei0rSrcClassData;
37
38 static gboolean
gst_frei0r_src_set_caps(GstBaseSrc * src,GstCaps * caps)39 gst_frei0r_src_set_caps (GstBaseSrc * src, GstCaps * caps)
40 {
41 GstFrei0rSrc *self = GST_FREI0R_SRC (src);
42
43 gst_video_info_init (&self->info);
44 if (!gst_video_info_from_caps (&self->info, caps))
45 return FALSE;
46
47 gst_base_src_set_blocksize (src, self->info.size);
48
49 return TRUE;
50 }
51
52 static GstFlowReturn
gst_frei0r_src_fill(GstPushSrc * src,GstBuffer * buf)53 gst_frei0r_src_fill (GstPushSrc * src, GstBuffer * buf)
54 {
55 GstFrei0rSrc *self = GST_FREI0R_SRC (src);
56 GstFrei0rSrcClass *klass = GST_FREI0R_SRC_GET_CLASS (src);
57 GstClockTime timestamp;
58 gdouble time;
59 GstMapInfo map;
60
61 if (G_UNLIKELY (!self->f0r_instance)) {
62 self->f0r_instance =
63 gst_frei0r_instance_construct (klass->ftable, klass->properties,
64 klass->n_properties, self->property_cache, self->info.width,
65 self->info.height);
66
67 if (G_UNLIKELY (!self->f0r_instance))
68 return GST_FLOW_ERROR;
69 }
70
71 timestamp =
72 gst_util_uint64_scale (self->n_frames, GST_SECOND * self->info.fps_d,
73 self->info.fps_n);
74 GST_BUFFER_PTS (buf) = GST_BUFFER_DTS (buf) = timestamp;
75 GST_BUFFER_OFFSET (buf) = self->n_frames;
76 self->n_frames++;
77 GST_BUFFER_OFFSET_END (buf) = self->n_frames;
78 GST_BUFFER_DURATION (buf) =
79 gst_util_uint64_scale (self->n_frames, GST_SECOND * self->info.fps_d,
80 self->info.fps_n) - GST_BUFFER_TIMESTAMP (buf);
81
82 timestamp =
83 gst_segment_to_stream_time (&GST_BASE_SRC_CAST (self)->segment,
84 GST_FORMAT_TIME, timestamp);
85
86 GST_DEBUG_OBJECT (self, "sync to %" GST_TIME_FORMAT,
87 GST_TIME_ARGS (timestamp));
88
89 if (GST_CLOCK_TIME_IS_VALID (timestamp))
90 gst_object_sync_values (GST_OBJECT (self), timestamp);
91
92 time = ((gdouble) GST_BUFFER_TIMESTAMP (buf)) / GST_SECOND;
93
94 GST_OBJECT_LOCK (self);
95
96 if (!gst_buffer_map (buf, &map, GST_MAP_WRITE))
97 goto map_error;
98
99 if (klass->ftable->update2)
100 klass->ftable->update2 (self->f0r_instance, time, NULL, NULL, NULL,
101 (guint32 *) map.data);
102 else
103 klass->ftable->update (self->f0r_instance, time, NULL,
104 (guint32 *) map.data);
105
106 gst_buffer_unmap (buf, &map);
107
108 GST_OBJECT_UNLOCK (self);
109
110 return GST_FLOW_OK;
111
112 map_error:
113 GST_OBJECT_UNLOCK (self);
114 GST_ELEMENT_ERROR (GST_ELEMENT (src), RESOURCE, WRITE, (NULL),
115 ("Could not map buffer for writing"));
116 return GST_FLOW_ERROR;
117 }
118
119 static gboolean
gst_frei0r_src_start(GstBaseSrc * basesrc)120 gst_frei0r_src_start (GstBaseSrc * basesrc)
121 {
122 GstFrei0rSrc *self = GST_FREI0R_SRC (basesrc);
123
124 self->n_frames = 0;
125
126 return TRUE;
127 }
128
129 static gboolean
gst_frei0r_src_stop(GstBaseSrc * basesrc)130 gst_frei0r_src_stop (GstBaseSrc * basesrc)
131 {
132 GstFrei0rSrc *self = GST_FREI0R_SRC (basesrc);
133 GstFrei0rSrcClass *klass = GST_FREI0R_SRC_GET_CLASS (basesrc);
134
135 if (self->f0r_instance) {
136 klass->ftable->destruct (self->f0r_instance);
137 self->f0r_instance = NULL;
138 }
139
140 gst_video_info_init (&self->info);
141 self->n_frames = 0;
142
143 return TRUE;
144 }
145
146 static gboolean
gst_frei0r_src_is_seekable(GstBaseSrc * psrc)147 gst_frei0r_src_is_seekable (GstBaseSrc * psrc)
148 {
149 return TRUE;
150 }
151
152 static gboolean
gst_frei0r_src_do_seek(GstBaseSrc * bsrc,GstSegment * segment)153 gst_frei0r_src_do_seek (GstBaseSrc * bsrc, GstSegment * segment)
154 {
155 GstClockTime time;
156 GstFrei0rSrc *self = GST_FREI0R_SRC (bsrc);
157
158 segment->time = segment->start;
159 time = segment->position;
160
161 /* now move to the time indicated */
162 if (self->info.fps_n) {
163 self->n_frames = gst_util_uint64_scale (time,
164 self->info.fps_n, self->info.fps_d * GST_SECOND);
165 } else {
166 self->n_frames = 0;
167 }
168
169 return TRUE;
170 }
171
172 static gboolean
gst_frei0r_src_query(GstBaseSrc * bsrc,GstQuery * query)173 gst_frei0r_src_query (GstBaseSrc * bsrc, GstQuery * query)
174 {
175 gboolean res;
176 GstFrei0rSrc *self = GST_FREI0R_SRC (bsrc);
177 GstFrei0rSrcClass *klass = GST_FREI0R_SRC_GET_CLASS (self);
178
179 switch (GST_QUERY_TYPE (query)) {
180 case GST_QUERY_CONVERT:
181 {
182 GstFormat src_fmt, dest_fmt;
183 gint64 src_val, dest_val;
184
185 gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
186 if (src_fmt == dest_fmt) {
187 dest_val = src_val;
188 goto done;
189 }
190
191 switch (src_fmt) {
192 case GST_FORMAT_DEFAULT:
193 switch (dest_fmt) {
194 case GST_FORMAT_TIME:
195 /* frames to time */
196 if (self->info.fps_n) {
197 dest_val = gst_util_uint64_scale (src_val,
198 self->info.fps_d * GST_SECOND, self->info.fps_n);
199 } else {
200 dest_val = 0;
201 }
202 break;
203 default:
204 goto error;
205 }
206 break;
207 case GST_FORMAT_TIME:
208 switch (dest_fmt) {
209 case GST_FORMAT_DEFAULT:
210 /* time to frames */
211 if (self->info.fps_n) {
212 dest_val = gst_util_uint64_scale (src_val,
213 self->info.fps_n, self->info.fps_d * GST_SECOND);
214 } else {
215 dest_val = 0;
216 }
217 break;
218 default:
219 goto error;
220 }
221 break;
222 default:
223 goto error;
224 }
225 done:
226 gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
227 res = TRUE;
228 break;
229 }
230 default:
231 res =
232 GST_BASE_SRC_CLASS (g_type_class_peek_parent (klass))->query (bsrc,
233 query);
234 }
235 return res;
236
237 /* ERROR */
238 error:
239 {
240 GST_DEBUG_OBJECT (self, "query failed");
241 return FALSE;
242 }
243 }
244
245 static GstCaps *
gst_frei0r_src_fixate(GstBaseSrc * src,GstCaps * caps)246 gst_frei0r_src_fixate (GstBaseSrc * src, GstCaps * caps)
247 {
248 GstStructure *structure;
249
250 caps = gst_caps_make_writable (caps);
251
252 structure = gst_caps_get_structure (caps, 0);
253
254 gst_structure_fixate_field_nearest_int (structure, "width", 320);
255 gst_structure_fixate_field_nearest_int (structure, "height", 240);
256 gst_structure_fixate_field_nearest_fraction (structure, "framerate", 30, 1);
257
258 return caps;
259 }
260
261 static void
gst_frei0r_src_finalize(GObject * object)262 gst_frei0r_src_finalize (GObject * object)
263 {
264 GstFrei0rSrc *self = GST_FREI0R_SRC (object);
265 GstFrei0rSrcClass *klass = GST_FREI0R_SRC_GET_CLASS (object);
266
267 if (self->f0r_instance) {
268 klass->ftable->destruct (self->f0r_instance);
269 self->f0r_instance = NULL;
270 }
271
272 if (self->property_cache)
273 gst_frei0r_property_cache_free (klass->properties, self->property_cache,
274 klass->n_properties);
275 self->property_cache = NULL;
276
277 G_OBJECT_CLASS (g_type_class_peek_parent (klass))->finalize (object);
278 }
279
280 static void
gst_frei0r_src_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)281 gst_frei0r_src_get_property (GObject * object, guint prop_id, GValue * value,
282 GParamSpec * pspec)
283 {
284 GstFrei0rSrc *self = GST_FREI0R_SRC (object);
285 GstFrei0rSrcClass *klass = GST_FREI0R_SRC_GET_CLASS (object);
286
287 GST_OBJECT_LOCK (self);
288 if (!gst_frei0r_get_property (self->f0r_instance, klass->ftable,
289 klass->properties, klass->n_properties, self->property_cache, prop_id,
290 value))
291 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
292 GST_OBJECT_UNLOCK (self);
293 }
294
295 static void
gst_frei0r_src_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)296 gst_frei0r_src_set_property (GObject * object, guint prop_id,
297 const GValue * value, GParamSpec * pspec)
298 {
299 GstFrei0rSrc *self = GST_FREI0R_SRC (object);
300 GstFrei0rSrcClass *klass = GST_FREI0R_SRC_GET_CLASS (object);
301
302 GST_OBJECT_LOCK (self);
303 if (!gst_frei0r_set_property (self->f0r_instance, klass->ftable,
304 klass->properties, klass->n_properties, self->property_cache, prop_id,
305 value))
306 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
307 GST_OBJECT_UNLOCK (self);
308 }
309
310 static void
gst_frei0r_src_class_init(GstFrei0rSrcClass * klass,GstFrei0rSrcClassData * class_data)311 gst_frei0r_src_class_init (GstFrei0rSrcClass * klass,
312 GstFrei0rSrcClassData * class_data)
313 {
314 GObjectClass *gobject_class = (GObjectClass *) klass;
315 GstElementClass *gstelement_class = (GstElementClass *) klass;
316 GstPushSrcClass *gstpushsrc_class = (GstPushSrcClass *) klass;
317 GstBaseSrcClass *gstbasesrc_class = (GstBaseSrcClass *) klass;
318 GstPadTemplate *templ;
319 const gchar *desc;
320 GstCaps *caps;
321 gchar *author;
322
323 klass->ftable = &class_data->ftable;
324 klass->info = &class_data->info;
325
326 gobject_class->finalize = gst_frei0r_src_finalize;
327 gobject_class->set_property = gst_frei0r_src_set_property;
328 gobject_class->get_property = gst_frei0r_src_get_property;
329
330 klass->n_properties = klass->info->num_params;
331 klass->properties = g_new0 (GstFrei0rProperty, klass->n_properties);
332
333 gst_frei0r_klass_install_properties (gobject_class, klass->ftable,
334 klass->properties, klass->n_properties);
335
336 author =
337 g_strdup_printf
338 ("Sebastian Dröge <sebastian.droege@collabora.co.uk>, %s",
339 class_data->info.author);
340 desc = class_data->info.explanation;
341 if (desc == NULL || *desc == '\0')
342 desc = "No details";
343 gst_element_class_set_metadata (gstelement_class, class_data->info.name,
344 "Src/Video", desc, author);
345 g_free (author);
346
347 caps = gst_frei0r_caps_from_color_model (class_data->info.color_model);
348
349 templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps);
350 gst_element_class_add_pad_template (gstelement_class, templ);
351 gst_caps_unref (caps);
352
353 gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_frei0r_src_set_caps);
354 gstbasesrc_class->is_seekable =
355 GST_DEBUG_FUNCPTR (gst_frei0r_src_is_seekable);
356 gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_frei0r_src_do_seek);
357 gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_frei0r_src_query);
358 gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_frei0r_src_start);
359 gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_frei0r_src_stop);
360 gstbasesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_frei0r_src_fixate);
361
362 gstpushsrc_class->fill = GST_DEBUG_FUNCPTR (gst_frei0r_src_fill);
363 }
364
365 static void
gst_frei0r_src_init(GstFrei0rSrc * self,GstFrei0rSrcClass * klass)366 gst_frei0r_src_init (GstFrei0rSrc * self, GstFrei0rSrcClass * klass)
367 {
368 self->property_cache =
369 gst_frei0r_property_cache_init (klass->properties, klass->n_properties);
370 gst_video_info_init (&self->info);
371 gst_base_src_set_format (GST_BASE_SRC_CAST (self), GST_FORMAT_TIME);
372 }
373
374 GstFrei0rPluginRegisterReturn
gst_frei0r_src_register(GstPlugin * plugin,const gchar * vendor,const f0r_plugin_info_t * info,const GstFrei0rFuncTable * ftable)375 gst_frei0r_src_register (GstPlugin * plugin, const gchar * vendor,
376 const f0r_plugin_info_t * info, const GstFrei0rFuncTable * ftable)
377 {
378 GTypeInfo typeinfo = {
379 sizeof (GstFrei0rSrcClass),
380 NULL,
381 NULL,
382 (GClassInitFunc) gst_frei0r_src_class_init,
383 NULL,
384 NULL,
385 sizeof (GstFrei0rSrc),
386 0,
387 (GInstanceInitFunc) gst_frei0r_src_init
388 };
389 GType type;
390 gchar *type_name, *tmp;
391 GstFrei0rSrcClassData *class_data;
392 GstFrei0rPluginRegisterReturn ret = GST_FREI0R_PLUGIN_REGISTER_RETURN_FAILED;
393
394 if (vendor)
395 tmp = g_strdup_printf ("frei0r-src-%s-%s", vendor, info->name);
396 else
397 tmp = g_strdup_printf ("frei0r-src-%s", info->name);
398 type_name = g_ascii_strdown (tmp, -1);
399 g_free (tmp);
400 g_strcanon (type_name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-+", '-');
401
402 if (g_type_from_name (type_name)) {
403 GST_DEBUG ("Type '%s' already exists", type_name);
404 return GST_FREI0R_PLUGIN_REGISTER_RETURN_ALREADY_REGISTERED;
405 }
406
407 class_data = g_new0 (GstFrei0rSrcClassData, 1);
408 memcpy (&class_data->info, info, sizeof (f0r_plugin_info_t));
409 memcpy (&class_data->ftable, ftable, sizeof (GstFrei0rFuncTable));
410 typeinfo.class_data = class_data;
411
412 type = g_type_register_static (GST_TYPE_PUSH_SRC, type_name, &typeinfo, 0);
413 if (gst_element_register (plugin, type_name, GST_RANK_NONE, type))
414 ret = GST_FREI0R_PLUGIN_REGISTER_RETURN_OK;
415
416 g_free (type_name);
417 return ret;
418 }
419