gstgoom.c 12.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* gstgoom.c: implementation of goom drawing element
 * Copyright (C) <2001> Richard Boulton <richard@tartarus.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

20
21
22
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
23

24
#include <string.h>
25
#include <gst/gst.h>
26
#include <gst/video/video.h>
27
#include <gst/base/gstadapter.h>
28
29
#include "goom_core.h"

30
31
32
33
34
GST_DEBUG_CATEGORY_STATIC (goom_debug);
#define GST_CAT_DEFAULT goom_debug

#define GOOM_SAMPLES 512

35
36
37
38
39
40
41
42
43
#define GST_TYPE_GOOM (gst_goom_get_type())
#define GST_GOOM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GOOM,GstGOOM))
#define GST_GOOM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GOOM,GstGOOM))
#define GST_IS_GOOM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GOOM))
#define GST_IS_GOOM_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GOOM))

typedef struct _GstGOOM GstGOOM;
typedef struct _GstGOOMClass GstGOOMClass;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
44
45
struct _GstGOOM
{
46
47
48
  GstElement element;

  /* pads */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
49
  GstPad *sinkpad, *srcpad;
50
  GstAdapter *adapter;
51

52
53
54
55
  /* input tracking */
  gint sample_rate;

  gint16 datain[2][GOOM_SAMPLES];
56
  /* the timestamp of the next frame */
57
58
  GstClockTime audio_basetime;
  guint64 samples_consumed;
59

60
  /* video state */
David Schleef's avatar
David Schleef committed
61
  gdouble fps;
62
63
  gint width;
  gint height;
Wim Taymans's avatar
Wim Taymans committed
64
  gint channels;
65
66

  gboolean disposed;
67
68
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
69
70
struct _GstGOOMClass
{
71
72
73
  GstElementClass parent_class;
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
74
GType gst_goom_get_type (void);
75
76
77
78
79


/* elementfactory information */
static GstElementDetails gst_goom_details = {
  "GOOM: what a GOOM!",
80
  "Visualization",
81
  "Takes frames of data and outputs video frames using the GOOM filter",
Ronald S. Bultje's avatar
Ronald S. Bultje committed
82
  "Wim Taymans <wim.taymans@chello.be>"
83
84
85
};

/* signals and args */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
86
87
enum
{
88
89
90
91
  /* FILL ME */
  LAST_SIGNAL
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
92
93
enum
{
94
95
  ARG_0
      /* FILL ME */
96
97
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
98
99
100
101
102
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB_HOST_ENDIAN)
    );
103

104
105
106
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",    /* the name of the pads */
    GST_PAD_SINK,               /* type of the pad */
    GST_PAD_ALWAYS,             /* ALWAYS/SOMETIMES */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
107
    GST_STATIC_CAPS ("audio/x-raw-int, "
108
109
110
111
112
        "endianness = (int) BYTE_ORDER, "
        "signed = (boolean) TRUE, "
        "width = (int) 16, "
        "depth = (int) 16, "
        "rate = (int) [ 8000, 96000 ], " "channels = (int) [ 1, 2 ]")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
113
114
115
116
117
118
119
    );


static void gst_goom_class_init (GstGOOMClass * klass);
static void gst_goom_base_init (GstGOOMClass * klass);
static void gst_goom_init (GstGOOM * goom);
static void gst_goom_dispose (GObject * object);
120

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
121
static GstElementStateReturn gst_goom_change_state (GstElement * element);
122

Wim Taymans's avatar
Wim Taymans committed
123
static GstFlowReturn gst_goom_chain (GstPad * pad, GstBuffer * buffer);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
124

Wim Taymans's avatar
Wim Taymans committed
125
126
static GstPadLinkReturn gst_goom_sink_setcaps (GstPad * pad, GstCaps * caps);
static GstPadLinkReturn gst_goom_src_setcaps (GstPad * pad, GstCaps * caps);
127
128
129
130
131
132
133
134
135
136

static GstElementClass *parent_class = NULL;

GType
gst_goom_get_type (void)
{
  static GType type = 0;

  if (!type) {
    static const GTypeInfo info = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
137
138
139
      sizeof (GstGOOMClass),
      (GBaseInitFunc) gst_goom_base_init,
      NULL,
140
141
142
143
144
145
146
      (GClassInitFunc) gst_goom_class_init,
      NULL,
      NULL,
      sizeof (GstGOOM),
      0,
      (GInstanceInitFunc) gst_goom_init,
    };
147

148
149
150
151
152
    type = g_type_register_static (GST_TYPE_ELEMENT, "GstGOOM", &info, 0);
  }
  return type;
}

Ronald S. Bultje's avatar
Ronald S. Bultje committed
153
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
154
gst_goom_base_init (GstGOOMClass * klass)
Ronald S. Bultje's avatar
Ronald S. Bultje committed
155
156
157
158
159
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);

  gst_element_class_set_details (element_class, &gst_goom_details);
  gst_element_class_add_pad_template (element_class,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
160
      gst_static_pad_template_get (&sink_template));
Ronald S. Bultje's avatar
Ronald S. Bultje committed
161
  gst_element_class_add_pad_template (element_class,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
162
      gst_static_pad_template_get (&src_template));
Ronald S. Bultje's avatar
Ronald S. Bultje committed
163
164
}

165
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
166
gst_goom_class_init (GstGOOMClass * klass)
167
168
169
170
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
171
172
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
173
174
175

  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
176
  gobject_class->dispose = gst_goom_dispose;
177
178

  gstelement_class->change_state = gst_goom_change_state;
179
180

  GST_DEBUG_CATEGORY_INIT (goom_debug, "goom", 0, "goom visualisation element");
181
182
183
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
184
gst_goom_init (GstGOOM * goom)
185
186
{
  /* create the sink and src pads */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
187
188
189
190
191
192
  goom->sinkpad =
      gst_pad_new_from_template (gst_static_pad_template_get (&sink_template),
      "sink");
  goom->srcpad =
      gst_pad_new_from_template (gst_static_pad_template_get (&src_template),
      "src");
193
194
195
196
  gst_element_add_pad (GST_ELEMENT (goom), goom->sinkpad);
  gst_element_add_pad (GST_ELEMENT (goom), goom->srcpad);

  gst_pad_set_chain_function (goom->sinkpad, gst_goom_chain);
Wim Taymans's avatar
Wim Taymans committed
197
198
  gst_pad_set_setcaps_function (goom->sinkpad, gst_goom_sink_setcaps);
  gst_pad_set_setcaps_function (goom->srcpad, gst_goom_src_setcaps);
Wim Taymans's avatar
Wim Taymans committed
199

200
201
  goom->adapter = gst_adapter_new ();

202
203
  goom->width = 320;
  goom->height = 200;
204
  goom->fps = 25.;              /* desired frame rate */
Wim Taymans's avatar
Wim Taymans committed
205
  goom->channels = 0;
206
207
208
209
210
  goom->sample_rate = 0;
  goom->audio_basetime = GST_CLOCK_TIME_NONE;
  goom->samples_consumed = 0;
  goom->disposed = FALSE;

Wim Taymans's avatar
Wim Taymans committed
211
  goom_init (goom->width, goom->height);
Wim Taymans's avatar
Wim Taymans committed
212
213
214
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
215
gst_goom_dispose (GObject * object)
Wim Taymans's avatar
Wim Taymans committed
216
{
217
218
219
220
221
222
223
224
225
  GstGOOM *goom = GST_GOOM (object);

  if (!goom->disposed) {
    goom_close ();
    goom->disposed = TRUE;

    g_object_unref (goom->adapter);
    goom->adapter = NULL;
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
226

Wim Taymans's avatar
Wim Taymans committed
227
  G_OBJECT_CLASS (parent_class)->dispose (object);
228
229
}

Wim Taymans's avatar
Wim Taymans committed
230
231
static gboolean
gst_goom_sink_setcaps (GstPad * pad, GstCaps * caps)
232
233
{
  GstGOOM *goom;
David Schleef's avatar
David Schleef committed
234
235
  GstStructure *structure;

Wim Taymans's avatar
Wim Taymans committed
236
  goom = GST_GOOM (GST_PAD_PARENT (pad));
237

David Schleef's avatar
David Schleef committed
238
  structure = gst_caps_get_structure (caps, 0);
239

David Schleef's avatar
David Schleef committed
240
  gst_structure_get_int (structure, "channels", &goom->channels);
241
  gst_structure_get_int (structure, "rate", &goom->sample_rate);
Wim Taymans's avatar
Wim Taymans committed
242
243

  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
244
245
}

Wim Taymans's avatar
Wim Taymans committed
246
247
static gboolean
gst_goom_src_setcaps (GstPad * pad, GstCaps * caps)
Wim Taymans's avatar
Wim Taymans committed
248
249
{
  GstGOOM *goom;
David Schleef's avatar
David Schleef committed
250
251
  GstStructure *structure;

Wim Taymans's avatar
Wim Taymans committed
252
  goom = GST_GOOM (GST_PAD_PARENT (pad));
Wim Taymans's avatar
Wim Taymans committed
253

David Schleef's avatar
David Schleef committed
254
  structure = gst_caps_get_structure (caps, 0);
Wim Taymans's avatar
Wim Taymans committed
255

David Schleef's avatar
David Schleef committed
256
257
258
  gst_structure_get_int (structure, "width", &goom->width);
  gst_structure_get_int (structure, "height", &goom->height);
  gst_structure_get_double (structure, "framerate", &goom->fps);
Wim Taymans's avatar
Wim Taymans committed
259
260
261

  goom_set_resolution (goom->width, goom->height);

Wim Taymans's avatar
Wim Taymans committed
262
  return TRUE;
263
264
}

Wim Taymans's avatar
Wim Taymans committed
265
266
static gboolean
gst_goom_src_negotiate (GstGOOM * goom)
267
{
Wim Taymans's avatar
Wim Taymans committed
268
  GstCaps *othercaps, *target, *intersect;
269
  GstStructure *structure;
Wim Taymans's avatar
Wim Taymans committed
270
  const GstCaps *templ;
271

Wim Taymans's avatar
Wim Taymans committed
272
  templ = gst_pad_get_pad_template_caps (goom->srcpad);
273

Wim Taymans's avatar
Wim Taymans committed
274
275
276
277
278
  /* see what the peer can do */
  othercaps = gst_pad_peer_get_caps (goom->srcpad);
  if (othercaps) {
    intersect = gst_caps_intersect (othercaps, templ);
    gst_caps_unref (othercaps);
279

Wim Taymans's avatar
Wim Taymans committed
280
281
282
283
284
285
286
    if (gst_caps_is_empty (intersect))
      goto no_format;

    target = gst_caps_copy_nth (intersect, 0);
    gst_caps_unref (intersect);
  } else {
    target = gst_caps_ref ((GstCaps *) templ);
287
288
  }

Wim Taymans's avatar
Wim Taymans committed
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
  structure = gst_caps_get_structure (target, 0);
  gst_caps_structure_fixate_field_nearest_int (structure, "width", 320);
  gst_caps_structure_fixate_field_nearest_int (structure, "height", 240);
  gst_caps_structure_fixate_field_nearest_double (structure, "framerate", 30.0);

  gst_pad_set_caps (goom->srcpad, target);
  gst_caps_unref (target);

  return TRUE;

no_format:
  {
    gst_caps_unref (intersect);
    gst_caps_unref (othercaps);
    return FALSE;
  }
305
306
}

Wim Taymans's avatar
Wim Taymans committed
307
308
static gboolean
gst_goom_event (GstPad * pad, GstEvent * event)
309
{
Wim Taymans's avatar
Wim Taymans committed
310
  gboolean res;
311
312
  GstGOOM *goom;

Wim Taymans's avatar
Wim Taymans committed
313
  goom = GST_GOOM (GST_PAD_PARENT (pad));
Wim Taymans's avatar
Wim Taymans committed
314

Wim Taymans's avatar
Wim Taymans committed
315
316
317
318
  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_DISCONTINUOUS:
    {
      gint64 start = 0, stop = 0;
Wim Taymans's avatar
Wim Taymans committed
319

Wim Taymans's avatar
Wim Taymans committed
320
321
322
323
324
      gst_event_discont_get_value (event, GST_FORMAT_TIME, &start, &stop);
      gst_adapter_clear (goom->adapter);
      goom->audio_basetime = start;
      goom->samples_consumed = 0;
      GST_DEBUG ("Got discont. Adjusting time to=%" G_GUINT64_FORMAT, start);
Wim Taymans's avatar
Wim Taymans committed
325
    }
Wim Taymans's avatar
Wim Taymans committed
326
327
328
    default:
      res = gst_pad_event_default (pad, event);
      break;
Wim Taymans's avatar
Wim Taymans committed
329
  }
Wim Taymans's avatar
Wim Taymans committed
330
331
  return res;
}
Wim Taymans's avatar
Wim Taymans committed
332

Wim Taymans's avatar
Wim Taymans committed
333
334
335
336
337
338
339
340
static GstFlowReturn
gst_goom_chain (GstPad * pad, GstBuffer * bufin)
{
  GstGOOM *goom;
  guint32 bytesperread;
  gint16 *data;
  gint samples_per_frame;
  GstFlowReturn ret;
Wim Taymans's avatar
Wim Taymans committed
341

Wim Taymans's avatar
Wim Taymans committed
342
343
344
345
346
347
  goom = GST_GOOM (GST_PAD_PARENT (pad));

  GST_STREAM_LOCK (pad);

  if (goom->channels == 0)
    goto not_negotiated;
348

349
350
  if (goom->audio_basetime == GST_CLOCK_TIME_NONE)
    goom->audio_basetime = GST_BUFFER_TIMESTAMP (bufin);
351

352
353
  if (goom->audio_basetime == GST_CLOCK_TIME_NONE)
    goom->audio_basetime = 0;
Wim Taymans's avatar
Wim Taymans committed
354

355
356
  bytesperread = GOOM_SAMPLES * goom->channels * sizeof (gint16);
  samples_per_frame = goom->sample_rate / goom->fps;
Wim Taymans's avatar
Wim Taymans committed
357
  data = (gint16 *) GST_BUFFER_DATA (bufin);
358

359
360
361
362
363
364
  gst_adapter_push (goom->adapter, bufin);

  GST_DEBUG ("Input buffer has %d samples, time=%" G_GUINT64_FORMAT,
      GST_BUFFER_SIZE (bufin) * sizeof (gint16) * goom->channels,
      GST_BUFFER_TIMESTAMP (bufin));

Wim Taymans's avatar
Wim Taymans committed
365
366
367
368
369
370
371
  ret = GST_FLOW_OK;

  if (GST_RPAD_CAPS (goom->srcpad) == NULL) {
    if (!gst_goom_src_negotiate (goom))
      goto no_format;
  }

372
373
374
  /* Collect samples until we have enough for an output frame */
  while (gst_adapter_available (goom->adapter) > MAX (bytesperread,
          samples_per_frame * goom->channels * sizeof (gint16))) {
Wim Taymans's avatar
Wim Taymans committed
375
    const guint16 *data;
376
377
    GstBuffer *bufout;
    guchar *out_frame;
Wim Taymans's avatar
Wim Taymans committed
378
    GstClockTimeDiff frame_duration;
379
380
    gint i;

Wim Taymans's avatar
Wim Taymans committed
381
382
383
    frame_duration = GST_SECOND / goom->fps;
    data = (const guint16 *) gst_adapter_peek (goom->adapter, bytesperread);

384
385
386
387
388
389
390
391
392
393
394
    if (goom->channels == 2) {
      for (i = 0; i < GOOM_SAMPLES; i++) {
        goom->datain[0][i] = *data++;
        goom->datain[1][i] = *data++;
      }
    } else {
      for (i = 0; i < GOOM_SAMPLES; i++) {
        goom->datain[0][i] = *data;
        goom->datain[1][i] = *data++;
      }
    }
395

Wim Taymans's avatar
Wim Taymans committed
396
397
    bufout = gst_pad_alloc_buffer (goom->srcpad, GST_BUFFER_OFFSET_NONE,
        goom->width * goom->height * 4, GST_RPAD_CAPS (goom->srcpad));
398
399
400
401
402
    GST_BUFFER_TIMESTAMP (bufout) =
        goom->audio_basetime +
        (GST_SECOND * goom->samples_consumed / goom->sample_rate);
    GST_BUFFER_DURATION (bufout) = frame_duration;
    GST_BUFFER_SIZE (bufout) = goom->width * goom->height * 4;
Wim Taymans's avatar
Wim Taymans committed
403

404
405
    out_frame = (guchar *) goom_update (goom->datain);
    memcpy (GST_BUFFER_DATA (bufout), out_frame, GST_BUFFER_SIZE (bufout));
406

407
408
409
    GST_DEBUG ("Pushing frame with time=%" G_GUINT64_FORMAT ", duration=%"
        G_GUINT64_FORMAT, GST_BUFFER_TIMESTAMP (bufout),
        GST_BUFFER_DURATION (bufout));
Wim Taymans's avatar
Wim Taymans committed
410
    ret = gst_pad_push (goom->srcpad, bufout);
411

412
413
414
    goom->samples_consumed += samples_per_frame;
    gst_adapter_flush (goom->adapter,
        samples_per_frame * goom->channels * sizeof (gint16));
Wim Taymans's avatar
Wim Taymans committed
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438

    if (ret != GST_FLOW_OK)
      break;
  }
  GST_STREAM_UNLOCK (pad);

  return ret;

  /* ERRORS */
not_negotiated:
  {
    GST_ELEMENT_ERROR (goom, CORE, NEGOTIATION, (NULL),
        ("Format wasn't negotiated before chain function"));
    gst_buffer_unref (bufin);
    GST_STREAM_UNLOCK (pad);
    return GST_FLOW_NOT_NEGOTIATED;
  }
no_format:
  {
    GST_ELEMENT_ERROR (goom, CORE, NEGOTIATION, (NULL),
        ("Could not negotiate format"));
    gst_buffer_unref (bufin);
    GST_STREAM_UNLOCK (pad);
    return GST_FLOW_ERROR;
439
  }
440
441
}

442
static GstElementStateReturn
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
443
444
gst_goom_change_state (GstElement * element)
{
445
  GstGOOM *goom = GST_GOOM (element);
Wim Taymans's avatar
Wim Taymans committed
446
447
  gint transition;
  GstElementStateReturn ret;
448

Wim Taymans's avatar
Wim Taymans committed
449
450
451
  transition = GST_STATE_TRANSITION (element);

  switch (transition) {
452
453
454
    case GST_STATE_NULL_TO_READY:
      break;
    case GST_STATE_READY_TO_PAUSED:
455
456
      goom->audio_basetime = GST_CLOCK_TIME_NONE;
      gst_adapter_clear (goom->adapter);
457
      goom->channels = 0;
458
459
460
461
462
      break;
    default:
      break;
  }

Wim Taymans's avatar
Wim Taymans committed
463
464
465
466
467
468
469
470
471
472
  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);

  switch (transition) {
    case GST_STATE_PAUSED_TO_READY:
      break;
    case GST_STATE_READY_TO_NULL:
      break;
    default:
      break;
  }
473

Wim Taymans's avatar
Wim Taymans committed
474
  return ret;
475
476
}

477
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
478
plugin_init (GstPlugin * plugin)
479
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
480
  return gst_element_register (plugin, "goom", GST_RANK_NONE, GST_TYPE_GOOM);
481
482
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
483
484
485
486
487
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    "goom",
    "GOOM visualization filter",
    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)