ges-smart-video-mixer.c 12.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-  */
/*
 * gst-editing-services
 *
 * Copyright (C) 2013 Mathieu Duponchelle <mduponchelle1@gmail.com>
 * gst-editing-services is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * gst-editing-services 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.";
 */
19
20
21
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
22

23
#include "gstframepositioner.h"
24
25
26
27
#include "ges-types.h"
#include "ges-internal.h"
#include "ges-smart-video-mixer.h"

28
29
30
31
32
33
34
35
36
37
#define GES_TYPE_SMART_MIXER_PAD             (ges_smart_mixer_pad_get_type ())
#define GES_SMART_MIXER_PAD(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_SMART_MIXER_PAD, GESSmartMixerPad))
#define GES_SMART_MIXER_PAD_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_SMART_MIXER_PAD, GESSmartMixerPadClass))
#define GES_IS_SMART_MIXER_PAD(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_SMART_MIXER_PAD))
#define GES_IS_SMART_MIXER_PAD_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_SMART_MIXER_PAD))
#define GES_SMART_MIXER_PAD_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_SMART_MIXER_PAD, GESSmartMixerPadClass))

typedef struct _GESSmartMixerPad GESSmartMixerPad;
typedef struct _GESSmartMixerPadClass GESSmartMixerPadClass;

38
39
40
41
42
43
44
45
struct _GESSmartMixerPad
{
  GstGhostPad parent;

  gdouble alpha;
  GstSegment segment;
};

46
47
48
49
50
struct _GESSmartMixerPadClass
{
  GstGhostPadClass parent_class;
};

51
52
53
54
55
56
enum
{
  PROP_PAD_0,
  PROP_PAD_ALPHA,
};

57
58
59
60
static GType ges_smart_mixer_pad_get_type (void);

G_DEFINE_TYPE (GESSmartMixerPad, ges_smart_mixer_pad, GST_TYPE_GHOST_PAD);

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
static void
ges_smart_mixer_pad_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  GESSmartMixerPad *pad = GES_SMART_MIXER_PAD (object);

  switch (prop_id) {
    case PROP_PAD_ALPHA:
      g_value_set_double (value, pad->alpha);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
ges_smart_mixer_pad_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GESSmartMixerPad *pad = GES_SMART_MIXER_PAD (object);

  switch (prop_id) {
    case PROP_PAD_ALPHA:
      pad->alpha = g_value_get_double (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
ges_smart_mixer_pad_init (GESSmartMixerPad * self)
{
  gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
}

static void
ges_smart_mixer_pad_class_init (GESSmartMixerPadClass * klass)
{
  GObjectClass *gobject_class = (GObjectClass *) klass;

  gobject_class->get_property = ges_smart_mixer_pad_get_property;
  gobject_class->set_property = ges_smart_mixer_pad_set_property;

  g_object_class_install_property (gobject_class, PROP_PAD_ALPHA,
      g_param_spec_double ("alpha", "Alpha", "Alpha of the picture", 0.0, 1.0,
          1.0,
          G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
}

113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
G_DEFINE_TYPE (GESSmartMixer, ges_smart_mixer, GST_TYPE_BIN);

#define GET_LOCK(obj) (&((GESSmartMixer*)(obj))->lock)
#define LOCK(obj) (g_mutex_lock (GET_LOCK(obj)))
#define UNLOCK(obj) (g_mutex_unlock (GET_LOCK(obj)))

static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-raw")
    );

static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
    GST_PAD_SINK,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS ("video/x-raw")
    );

typedef struct _PadInfos
{
  GESSmartMixer *self;
  GstPad *mixer_pad;
  GstElement *bin;
136
  gulong probe_id;
137
138
139
140
141
} PadInfos;

static void
destroy_pad (PadInfos * infos)
{
142
143
  gst_pad_remove_probe (infos->mixer_pad, infos->probe_id);

144
145
146
147
148
149
  if (G_LIKELY (infos->bin)) {
    gst_element_set_state (infos->bin, GST_STATE_NULL);
    gst_element_unlink (infos->bin, infos->self->mixer);
    gst_bin_remove (GST_BIN (infos->self), infos->bin);
  }

150
  if (infos->mixer_pad) {
151
    gst_element_release_request_pad (infos->self->mixer, infos->mixer_pad);
152
153
    gst_object_unref (infos->mixer_pad);
  }
154

155
156
157
  g_slice_free (PadInfos, infos);
}

158
159
160
161
162
163
164
165
166
167
168
169
GstPad *
ges_smart_mixer_get_mixer_pad (GESSmartMixer * self, GstPad ** mixerpad)
{
  PadInfos *info;
  GstPad *sinkpad;

  sinkpad = gst_element_get_request_pad (GST_ELEMENT (self), "sink_%u");

  if (sinkpad == NULL)
    return NULL;

  info = g_hash_table_lookup (self->pads_infos, sinkpad);
170
  *mixerpad = gst_object_ref (info->mixer_pad);
171
172
173
174

  return sinkpad;
}

175
/* These metadata will get set by the upstream framepositioner element,
176
177
   added in the video sources' bin */
static GstPadProbeReturn
178
parse_metadata (GstPad * mixer_pad, GstPadProbeInfo * info,
179
    GESSmartMixerPad * ghost)
180
{
181
  GstFramePositionerMeta *meta;
182
  GESSmartMixer *self = GES_SMART_MIXER (GST_OBJECT_PARENT (ghost));
183
184

  meta =
185
186
      (GstFramePositionerMeta *) gst_buffer_get_meta ((GstBuffer *) info->data,
      gst_frame_positioner_meta_api_get_type ());
187
188

  if (!meta) {
189
    GST_WARNING ("The current source should use a framepositioner");
190
191
192
    return GST_PAD_PROBE_OK;
  }

193
194
195
  if (!self->disable_zorder_alpha) {
    g_object_set (mixer_pad, "alpha", meta->alpha,
        "zorder", meta->zorder, NULL);
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
  } else {
    gint64 stream_time;
    gdouble transalpha;

    GST_OBJECT_LOCK (ghost);
    if (ghost->segment.format == GST_FORMAT_UNDEFINED) {
      const GstSegment *seg;
      GstEvent *segev;

      GST_OBJECT_UNLOCK (ghost);
      segev = gst_pad_get_sticky_event (GST_PAD (ghost), GST_EVENT_SEGMENT, 0);
      gst_event_parse_segment (segev, &seg);
      gst_event_unref (segev);
      GST_OBJECT_LOCK (ghost);

      ghost->segment = *seg;

    }

    stream_time = gst_segment_to_stream_time (&ghost->segment, GST_FORMAT_TIME,
        GST_BUFFER_PTS (info->data));
    GST_OBJECT_UNLOCK (ghost);

    if (GST_CLOCK_TIME_IS_VALID (stream_time))
      gst_object_sync_values (GST_OBJECT (ghost), stream_time);

    g_object_get (ghost, "alpha", &transalpha, NULL);
    g_object_set (mixer_pad, "alpha", meta->alpha * transalpha, NULL);
224
225
226
  }

  g_object_set (mixer_pad, "xpos", meta->posx, "ypos",
227
      meta->posy, "width", meta->width, "height", meta->height, NULL);
228
229
230
231

  return GST_PAD_PROBE_OK;
}

232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
/****************************************************
 *              GstElement vmetods                  *
 ****************************************************/
static GstPad *
_request_new_pad (GstElement * element, GstPadTemplate * templ,
    const gchar * name, const GstCaps * caps)
{
  GstPad *videoconvert_srcpad, *videoconvert_sinkpad, *tmpghost;
  PadInfos *infos = g_slice_new0 (PadInfos);
  GESSmartMixer *self = GES_SMART_MIXER (element);
  GstPad *ghost;
  GstElement *videoconvert;

  infos->mixer_pad = gst_element_request_pad (self->mixer,
      gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (self->mixer),
          "sink_%u"), NULL, NULL);

  if (infos->mixer_pad == NULL) {
    GST_WARNING_OBJECT (element, "Could not get any pad from GstMixer");
251
    g_slice_free (PadInfos, infos);
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269

    return NULL;
  }

  infos->self = self;

  infos->bin = gst_bin_new (NULL);
  videoconvert = gst_element_factory_make ("videoconvert", NULL);

  gst_bin_add (GST_BIN (infos->bin), videoconvert);

  videoconvert_sinkpad = gst_element_get_static_pad (videoconvert, "sink");
  tmpghost = GST_PAD (gst_ghost_pad_new (NULL, videoconvert_sinkpad));
  gst_object_unref (videoconvert_sinkpad);
  gst_pad_set_active (tmpghost, TRUE);
  gst_element_add_pad (GST_ELEMENT (infos->bin), tmpghost);

  gst_bin_add (GST_BIN (self), infos->bin);
270
271
272
273
  ghost = g_object_new (ges_smart_mixer_pad_get_type (), "name", name,
      "direction", GST_PAD_DIRECTION (tmpghost), NULL);
  gst_ghost_pad_construct (GST_GHOST_PAD (ghost));
  gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (ghost), tmpghost);
274
275
276
277
278
279
280
281
282
283
284
  gst_pad_set_active (ghost, TRUE);
  if (!gst_element_add_pad (GST_ELEMENT (self), ghost))
    goto could_not_add;

  videoconvert_srcpad = gst_element_get_static_pad (videoconvert, "src");
  tmpghost = GST_PAD (gst_ghost_pad_new (NULL, videoconvert_srcpad));
  gst_object_unref (videoconvert_srcpad);
  gst_pad_set_active (tmpghost, TRUE);
  gst_element_add_pad (GST_ELEMENT (infos->bin), tmpghost);
  gst_pad_link (tmpghost, infos->mixer_pad);

285
286
  infos->probe_id =
      gst_pad_add_probe (infos->mixer_pad, GST_PAD_PROBE_TYPE_BUFFER,
287
      (GstPadProbeCallback) parse_metadata, ghost, NULL);
288

289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
  LOCK (self);
  g_hash_table_insert (self->pads_infos, ghost, infos);
  UNLOCK (self);

  GST_DEBUG_OBJECT (self, "Returning new pad %" GST_PTR_FORMAT, ghost);
  return ghost;

could_not_add:
  {
    GST_ERROR_OBJECT (self, "could not add pad");
    destroy_pad (infos);
    return NULL;
  }
}

static void
_release_pad (GstElement * element, GstPad * pad)
{
307
  GstPad *peer;
308
309
310
311
  GST_DEBUG_OBJECT (element, "Releasing pad %" GST_PTR_FORMAT, pad);

  LOCK (element);
  g_hash_table_remove (GES_SMART_MIXER (element)->pads_infos, pad);
312
313
314
315
316
317
318
319
  peer = gst_pad_get_peer (pad);
  if (peer) {
    gst_pad_unlink (peer, pad);

    gst_object_unref (peer);
  }
  gst_pad_set_active (pad, FALSE);
  gst_element_remove_pad (element, pad);
320
321
322
323
324
325
  UNLOCK (element);
}

/****************************************************
 *              GObject vmethods                    *
 ****************************************************/
326
327
328
329
330
331
332
333
334
335
336
337
338
static void
ges_smart_mixer_dispose (GObject * object)
{
  GESSmartMixer *self = GES_SMART_MIXER (object);

  if (self->pads_infos != NULL) {
    g_hash_table_unref (self->pads_infos);
    self->pads_infos = NULL;
  }

  G_OBJECT_CLASS (ges_smart_mixer_parent_class)->dispose (object);
}

339
340
341
342
343
344
345
346
347
348
static void
ges_smart_mixer_finalize (GObject * object)
{
  GESSmartMixer *self = GES_SMART_MIXER (object);

  g_mutex_clear (&self->lock);

  G_OBJECT_CLASS (ges_smart_mixer_parent_class)->finalize (object);
}

349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
static void
ges_smart_mixer_constructed (GObject * obj)
{
  GstPad *pad;

  GESSmartMixer *self = GES_SMART_MIXER (obj);
  gchar *cname = g_strdup_printf ("%s-compositor", GST_OBJECT_NAME (self));

  self->mixer =
      gst_element_factory_create (ges_get_compositor_factory (), cname);
  g_free (cname);
  g_object_set (self->mixer, "background", 1, NULL);
  gst_bin_add (GST_BIN (self), self->mixer);

  pad = gst_element_get_static_pad (self->mixer, "src");
  self->srcpad = gst_ghost_pad_new ("src", pad);
  gst_pad_set_active (self->srcpad, TRUE);
  gst_object_unref (pad);
  gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
}


371
372
373
374
375
376
377
378
379
static void
ges_smart_mixer_class_init (GESSmartMixerClass * klass)
{
/*   GstBinClass *parent_class = GST_BIN_CLASS (klass);
 */
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);

  /* FIXME Make sure the MixerClass doesn get destroy before ourself */
380
381
  gst_element_class_add_static_pad_template (element_class, &src_template);
  gst_element_class_add_static_pad_template (element_class, &sink_template);
382
383
384
385
386
387
388
389
  gst_element_class_set_static_metadata (element_class, "GES Smart mixer",
      "Generic/Audio",
      "Use mixer making use of GES informations",
      "Thibault Saunier <thibault.saunier@collabora.com>");

  element_class->request_new_pad = GST_DEBUG_FUNCPTR (_request_new_pad);
  element_class->release_pad = GST_DEBUG_FUNCPTR (_release_pad);

390
  object_class->dispose = ges_smart_mixer_dispose;
391
  object_class->finalize = ges_smart_mixer_finalize;
392
  object_class->constructed = ges_smart_mixer_constructed;
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
}

static void
ges_smart_mixer_init (GESSmartMixer * self)
{
  g_mutex_init (&self->lock);
  self->pads_infos = g_hash_table_new_full (g_direct_hash, g_direct_equal,
      NULL, (GDestroyNotify) destroy_pad);
}

GstElement *
ges_smart_mixer_new (GESTrack * track)
{
  GESSmartMixer *self = g_object_new (GES_TYPE_SMART_MIXER, NULL);

  /* FIXME Make mixer smart and let it properly negotiate caps! */
  return GST_ELEMENT (self);
}