gstv4l2object.c 72.7 KB
Newer Older
1
2
/* GStreamer
 *
3
 * Copyright (C) 2001-2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
4
5
6
 *               2006 Edgard Lima <edgard.lima@indt.org.br>
 *
 * gstv4l2object.c: base class for V4L2 elements
7
8
9
10
11
12
13
14
15
16
17
 *
 * 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,
Rob Clark's avatar
Rob Clark committed
18
 * USA.
19
20
 */

21
22
23
24
/* FIXME 0.11: suppress warnings for deprecated API such as GValueArray
 * with newer GLib versions (>= 2.31.0) */
#define GLIB_DISABLE_DEPRECATION_WARNINGS

25
26
27
28
29
30
31
32
33
34
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>

35
36
37
38
#ifdef HAVE_GUDEV
#include <gudev/gudev.h>
#endif

39
40
#include "v4l2_calls.h"
#include "gstv4l2tuner.h"
Rob Clark's avatar
Rob Clark committed
41
#ifdef HAVE_XVIDEO
42
#include "gstv4l2videooverlay.h"
43
44
45
#endif
#include "gstv4l2colorbalance.h"

Rob Clark's avatar
Rob Clark committed
46
47
#include "gst/gst-i18n-plugin.h"

48
49
#include <gst/video/video.h>

50
51
52
53
54
55
56
/* videodev2.h is not versioned and we can't easily check for the presence
 * of enum values at compile time, but the V4L2_CAP_VIDEO_OUTPUT_OVERLAY define
 * was added in the same commit as V4L2_FIELD_INTERLACED_{TB,BT} (b2787845) */
#ifndef V4L2_CAP_VIDEO_OUTPUT_OVERLAY
#define V4L2_FIELD_INTERLACED_TB 8
#define V4L2_FIELD_INTERLACED_BT 9
#endif
Rob Clark's avatar
Rob Clark committed
57
58

GST_DEBUG_CATEGORY_EXTERN (v4l2_debug);
59
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
Rob Clark's avatar
Rob Clark committed
60
61
#define GST_CAT_DEFAULT v4l2_debug

62
#define DEFAULT_PROP_DEVICE_NAME 	NULL
63
#define DEFAULT_PROP_DEVICE_FD          -1
64
#define DEFAULT_PROP_FLAGS              0
Rob Clark's avatar
Rob Clark committed
65
#define DEFAULT_PROP_TV_NORM            0
66
#define DEFAULT_PROP_CHANNEL            NULL
67
#define DEFAULT_PROP_FREQUENCY          0
Wim Taymans's avatar
Wim Taymans committed
68
#define DEFAULT_PROP_IO_MODE            GST_V4L2_IO_AUTO
69

Stefan Kost's avatar
Stefan Kost committed
70
71
72
73
74
75
enum
{
  PROP_0,
  V4L2_STD_OBJECT_PROPS,
};

76
#if 0
Wim Taymans's avatar
Wim Taymans committed
77
78
G_LOCK_DEFINE_STATIC (probe_lock);

Stefan Kost's avatar
Stefan Kost committed
79
const GList *
80
81
82
83
84
gst_v4l2_probe_get_properties (GstPropertyProbe * probe)
{
  GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
  static GList *list = NULL;

Wim Taymans's avatar
Wim Taymans committed
85
  G_LOCK (probe_lock);
86
87
88
89
90

  if (!list) {
    list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
  }

Wim Taymans's avatar
Wim Taymans committed
91
  G_UNLOCK (probe_lock);
92
93
94
95

  return list;
}

96
97
98
99
static gboolean init = FALSE;
static GList *devices = NULL;

#ifdef HAVE_GUDEV
100
static gboolean
101
gst_v4l2_class_probe_devices_with_udev (GstElementClass * klass, gboolean check,
102
103
    GList ** klass_devices)
{
104
  GUdevClient *client = NULL;
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
  GList *item;

  if (!check) {
    while (devices) {
      gchar *device = devices->data;
      devices = g_list_remove (devices, device);
      g_free (device);
    }

    GST_INFO ("Enumerating video4linux devices from udev");
    client = g_udev_client_new (NULL);
    if (!client) {
      GST_WARNING ("Failed to initialize gudev client");
      goto finish;
    }

    item = g_udev_client_query_by_subsystem (client, "video4linux");
    while (item) {
      GUdevDevice *device = item->data;
      gchar *devnode = g_strdup (g_udev_device_get_device_file (device));
      gint api = g_udev_device_get_property_as_int (device, "ID_V4L_VERSION");
      GST_INFO ("Found new device: %s, API: %d", devnode, api);
      /* Append v4l2 devices only. If api is 0 probably v4l_id has
         been stripped out of the current udev installation, append
         anyway */
      if (api == 0) {
        GST_WARNING
            ("Couldn't retrieve ID_V4L_VERSION, silly udev installation?");
      }
      if ((api == 2 || api == 0)) {
        devices = g_list_append (devices, devnode);
      } else {
        g_free (devnode);
      }
      g_object_unref (device);
      item = item->next;
    }
    g_list_free (item);
    init = TRUE;
  }

finish:
  if (client) {
    g_object_unref (client);
  }

  *klass_devices = devices;
152

153
154
155
156
157
158
159
160
  return init;
}
#endif /* HAVE_GUDEV */

static gboolean
gst_v4l2_class_probe_devices (GstElementClass * klass, gboolean check,
    GList ** klass_devices)
{
161
  if (!check) {
Stefan Kost's avatar
Stefan Kost committed
162
    const gchar *dev_base[] = { "/dev/video", "/dev/v4l2/video", NULL };
163
164
165
    gint base, n, fd;

    while (devices) {
166
      gchar *device = devices->data;
167
      devices = g_list_remove (devices, device);
168
169
170
171
      g_free (device);
    }

    /*
Rob Clark's avatar
Rob Clark committed
172
     * detect /dev entries
173
174
175
176
177
178
179
180
181
     */
    for (n = 0; n < 64; n++) {
      for (base = 0; dev_base[base] != NULL; base++) {
        struct stat s;
        gchar *device = g_strdup_printf ("%s%d",
            dev_base[base],
            n);

        /*
Rob Clark's avatar
Rob Clark committed
182
         * does the /dev/ entry exist at all?
183
184
185
         */
        if (stat (device, &s) == 0) {
          /*
Rob Clark's avatar
Rob Clark committed
186
           * yes: is a device attached?
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
           */
          if (S_ISCHR (s.st_mode)) {

            if ((fd = open (device, O_RDWR | O_NONBLOCK)) > 0 || errno == EBUSY) {
              if (fd > 0)
                close (fd);

              devices = g_list_append (devices, device);
              break;
            }
          }
        }
        g_free (device);
      }
    }
    init = TRUE;
  }

  *klass_devices = devices;

  return init;
}

void
gst_v4l2_probe_probe_property (GstPropertyProbe * probe,
    guint prop_id, const GParamSpec * pspec, GList ** klass_devices)
{
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (probe);

  switch (prop_id) {
    case PROP_DEVICE:
218
219
220
221
#ifdef HAVE_GUDEV
      if (!gst_v4l2_class_probe_devices_with_udev (klass, FALSE, klass_devices))
        gst_v4l2_class_probe_devices (klass, FALSE, klass_devices);
#else /* !HAVE_GUDEV */
222
      gst_v4l2_class_probe_devices (klass, FALSE, klass_devices);
223
#endif /* HAVE_GUDEV */
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
      break;
  }
}

gboolean
gst_v4l2_probe_needs_probe (GstPropertyProbe * probe,
    guint prop_id, const GParamSpec * pspec, GList ** klass_devices)
{
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (probe);
  gboolean ret = FALSE;

  switch (prop_id) {
    case PROP_DEVICE:
240
241
242
243
#ifdef HAVE_GUDEV
      ret =
          !gst_v4l2_class_probe_devices_with_udev (klass, FALSE, klass_devices);
#else /* !HAVE_GUDEV */
244
      ret = !gst_v4l2_class_probe_devices (klass, TRUE, klass_devices);
245
#endif /* HAVE_GUDEV */
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
      break;
  }
  return ret;
}

static GValueArray *
gst_v4l2_class_list_devices (GstElementClass * klass, GList ** klass_devices)
{
  GValueArray *array;
  GValue value = { 0 };
  GList *item;

  if (!*klass_devices)
    return NULL;

  array = g_value_array_new (g_list_length (*klass_devices));
  item = *klass_devices;
  g_value_init (&value, G_TYPE_STRING);
  while (item) {
    gchar *device = item->data;

    g_value_set_string (&value, device);
    g_value_array_append (array, &value);

    item = item->next;
  }
  g_value_unset (&value);

  return array;
}

GValueArray *
gst_v4l2_probe_get_values (GstPropertyProbe * probe,
    guint prop_id, const GParamSpec * pspec, GList ** klass_devices)
{
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (probe);
  GValueArray *array = NULL;

  switch (prop_id) {
    case PROP_DEVICE:
      array = gst_v4l2_class_list_devices (klass, klass_devices);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
      break;
  }

  return array;
}
298
#endif
299
300

#define GST_TYPE_V4L2_DEVICE_FLAGS (gst_v4l2_device_get_type ())
301
static GType
302
303
304
305
306
307
gst_v4l2_device_get_type (void)
{
  static GType v4l2_device_type = 0;

  if (v4l2_device_type == 0) {
    static const GFlagsValue values[] = {
308
309
310
311
312
313
314
315
316
      {V4L2_CAP_VIDEO_CAPTURE, "Device supports video capture", "capture"},
      {V4L2_CAP_VIDEO_OUTPUT, "Device supports video playback", "output"},
      {V4L2_CAP_VIDEO_OVERLAY, "Device supports video overlay", "overlay"},

      {V4L2_CAP_VBI_CAPTURE, "Device supports the VBI capture", "vbi-capture"},
      {V4L2_CAP_VBI_OUTPUT, "Device supports the VBI output", "vbi-output"},

      {V4L2_CAP_TUNER, "Device has a tuner or modulator", "tuner"},
      {V4L2_CAP_AUDIO, "Device has audio inputs or outputs", "audio"},
317
318
319
320
321
322
323
324
325
326
327

      {0, NULL, NULL}
    };

    v4l2_device_type =
        g_flags_register_static ("GstV4l2DeviceTypeFlags", values);
  }

  return v4l2_device_type;
}

Rob Clark's avatar
Rob Clark committed
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
#define GST_TYPE_V4L2_TV_NORM (gst_v4l2_tv_norm_get_type ())
static GType
gst_v4l2_tv_norm_get_type (void)
{
  static GType v4l2_tv_norm = 0;

  if (!v4l2_tv_norm) {
    static const GEnumValue tv_norms[] = {
      {0, "none", "none"},

      {V4L2_STD_NTSC, "NTSC", "NTSC"},
      {V4L2_STD_NTSC_M, "NTSC-M", "NTSC-M"},
      {V4L2_STD_NTSC_M_JP, "NTSC-M-JP", "NTSC-M-JP"},
      {V4L2_STD_NTSC_M_KR, "NTSC-M-KR", "NTSC-M-KR"},
      {V4L2_STD_NTSC_443, "NTSC-443", "NTSC-443"},

      {V4L2_STD_PAL, "PAL", "PAL"},
      {V4L2_STD_PAL_BG, "PAL-BG", "PAL-BG"},
      {V4L2_STD_PAL_B, "PAL-B", "PAL-B"},
      {V4L2_STD_PAL_B1, "PAL-B1", "PAL-B1"},
      {V4L2_STD_PAL_G, "PAL-G", "PAL-G"},
      {V4L2_STD_PAL_H, "PAL-H", "PAL-H"},
      {V4L2_STD_PAL_I, "PAL-I", "PAL-I"},
      {V4L2_STD_PAL_DK, "PAL-DK", "PAL-DK"},
      {V4L2_STD_PAL_D, "PAL-D", "PAL-D"},
      {V4L2_STD_PAL_D1, "PAL-D1", "PAL-D1"},
      {V4L2_STD_PAL_K, "PAL-K", "PAL-K"},
      {V4L2_STD_PAL_M, "PAL-M", "PAL-M"},
      {V4L2_STD_PAL_N, "PAL-N", "PAL-N"},
      {V4L2_STD_PAL_Nc, "PAL-Nc", "PAL-Nc"},
      {V4L2_STD_PAL_60, "PAL-60", "PAL-60"},

      {V4L2_STD_SECAM, "SECAM", "SECAM"},
      {V4L2_STD_SECAM_B, "SECAM-B", "SECAM-B"},
      {V4L2_STD_SECAM_G, "SECAM-G", "SECAM-G"},
      {V4L2_STD_SECAM_H, "SECAM-H", "SECAM-H"},
      {V4L2_STD_SECAM_DK, "SECAM-DK", "SECAM-DK"},
      {V4L2_STD_SECAM_D, "SECAM-D", "SECAM-D"},
      {V4L2_STD_SECAM_K, "SECAM-K", "SECAM-K"},
      {V4L2_STD_SECAM_K1, "SECAM-K1", "SECAM-K1"},
      {V4L2_STD_SECAM_L, "SECAM-L", "SECAM-L"},
      {V4L2_STD_SECAM_LC, "SECAM-Lc", "SECAM-Lc"},

      {0, NULL, NULL}
    };

    v4l2_tv_norm = g_enum_register_static ("V4L2_TV_norms", tv_norms);
  }

  return v4l2_tv_norm;
}

Wim Taymans's avatar
Wim Taymans committed
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
#define GST_TYPE_V4L2_IO_MODE (gst_v4l2_io_mode_get_type ())
static GType
gst_v4l2_io_mode_get_type (void)
{
  static GType v4l2_io_mode = 0;

  if (!v4l2_io_mode) {
    static const GEnumValue io_modes[] = {
      {GST_V4L2_IO_AUTO, "GST_V4L2_IO_AUTO", "auto"},
      {GST_V4L2_IO_RW, "GST_V4L2_IO_RW", "rw"},
      {GST_V4L2_IO_MMAP, "GST_V4L2_IO_MMAP", "mmap"},
      {GST_V4L2_IO_USERPTR, "GST_V4L2_IO_USERPTR", "userptr"},

      {0, NULL, NULL}
    };
    v4l2_io_mode = g_enum_register_static ("GstV4l2IOMode", io_modes);
  }
  return v4l2_io_mode;
}

400
void
Rob Clark's avatar
Rob Clark committed
401
402
gst_v4l2_object_install_properties_helper (GObjectClass * gobject_class,
    const char *default_device)
403
{
404
405
  g_object_class_install_property (gobject_class, PROP_DEVICE,
      g_param_spec_string ("device", "Device", "Device location",
406
          default_device, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
407
408
  g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
      g_param_spec_string ("device-name", "Device name",
409
410
          "Name of the device", DEFAULT_PROP_DEVICE_NAME,
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
411
412
413
  g_object_class_install_property (gobject_class, PROP_DEVICE_FD,
      g_param_spec_int ("device-fd", "File descriptor",
          "File descriptor of the device", -1, G_MAXINT, DEFAULT_PROP_DEVICE_FD,
414
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
415
416
  g_object_class_install_property (gobject_class, PROP_FLAGS,
      g_param_spec_flags ("flags", "Flags", "Device type flags",
417
418
          GST_TYPE_V4L2_DEVICE_FLAGS, DEFAULT_PROP_FLAGS,
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
419

420
421
422
423
424
425
426
  /**
   * GstV4l2Src:brightness
   *
   * Picture brightness, or more precisely, the black level
   *
   * Since: 0.10.26
   */
427
428
429
430
431
  g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
      g_param_spec_int ("brightness", "Brightness",
          "Picture brightness, or more precisely, the black level", G_MININT,
          G_MAXINT, 0,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
432
433
434
435
436
437
438
  /**
   * GstV4l2Src:contrast
   *
   * Picture contrast or luma gain
   *
   * Since: 0.10.26
   */
439
440
441
442
443
  g_object_class_install_property (gobject_class, PROP_CONTRAST,
      g_param_spec_int ("contrast", "Contrast",
          "Picture contrast or luma gain", G_MININT,
          G_MAXINT, 0,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
444
445
446
447
448
449
450
  /**
   * GstV4l2Src:saturation
   *
   * Picture color saturation or chroma gain
   *
   * Since: 0.10.26
   */
451
452
453
454
455
  g_object_class_install_property (gobject_class, PROP_SATURATION,
      g_param_spec_int ("saturation", "Saturation",
          "Picture color saturation or chroma gain", G_MININT,
          G_MAXINT, 0,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
456
457
458
459
460
461
462
  /**
   * GstV4l2Src:hue
   *
   * Hue or color balance
   *
   * Since: 0.10.26
   */
463
464
465
466
467
  g_object_class_install_property (gobject_class, PROP_HUE,
      g_param_spec_int ("hue", "Hue",
          "Hue or color balance", G_MININT,
          G_MAXINT, 0,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
Rob Clark's avatar
Rob Clark committed
468
469
470
471
472
473

  /**
   * GstV4l2Src:norm
   *
   * TV norm
   *
474
   * Since: 0.10.31
Rob Clark's avatar
Rob Clark committed
475
476
477
478
479
480
   */
  g_object_class_install_property (gobject_class, PROP_TV_NORM,
      g_param_spec_enum ("norm", "TV norm",
          "video standard",
          GST_TYPE_V4L2_TV_NORM, DEFAULT_PROP_TV_NORM,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
481
482
483
484
485
486
487
488
489
490
491

  /**
   * GstV4l2Src:io-mode
   *
   * IO Mode
   */
  g_object_class_install_property (gobject_class, PROP_IO_MODE,
      g_param_spec_enum ("io-mode", "IO mode",
          "I/O mode",
          GST_TYPE_V4L2_IO_MODE, DEFAULT_PROP_IO_MODE,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
492
493
494
}

GstV4l2Object *
495
gst_v4l2_object_new (GstElement * element,
Rob Clark's avatar
Rob Clark committed
496
    enum v4l2_buf_type type,
497
    const char *default_device,
498
    GstV4l2GetInOutFunction get_in_out_func,
499
500
    GstV4l2SetInOutFunction set_in_out_func,
    GstV4l2UpdateFpsFunction update_fps_func)
501
502
503
504
{
  GstV4l2Object *v4l2object;

  /*
Rob Clark's avatar
Rob Clark committed
505
   * some default values
506
507
508
   */
  v4l2object = g_new0 (GstV4l2Object, 1);

Rob Clark's avatar
Rob Clark committed
509
510
511
  v4l2object->type = type;
  v4l2object->formats = NULL;

512
513
514
  v4l2object->element = element;
  v4l2object->get_in_out_func = get_in_out_func;
  v4l2object->set_in_out_func = set_in_out_func;
515
  v4l2object->update_fps_func = update_fps_func;
516
517

  v4l2object->video_fd = -1;
518
  v4l2object->poll = gst_poll_new (TRUE);
519
  v4l2object->active = FALSE;
Rob Clark's avatar
Rob Clark committed
520
  v4l2object->videodev = g_strdup (default_device);
521

522
523
  v4l2object->norms = NULL;
  v4l2object->channels = NULL;
524
525
526
527
528
529
530
  v4l2object->colors = NULL;

  v4l2object->xwindow_id = 0;

  return v4l2object;
}

Rob Clark's avatar
Rob Clark committed
531
532
533
static gboolean gst_v4l2_object_clear_format_list (GstV4l2Object * v4l2object);


534
void
535
gst_v4l2_object_destroy (GstV4l2Object * v4l2object)
536
{
537
  g_return_if_fail (v4l2object != NULL);
538

539
540
541
  if (v4l2object->videodev)
    g_free (v4l2object->videodev);

542
543
544
  if (v4l2object->poll)
    gst_poll_free (v4l2object->poll);

545
546
547
  if (v4l2object->channel)
    g_free (v4l2object->channel);

Rob Clark's avatar
Rob Clark committed
548
549
550
551
  if (v4l2object->formats) {
    gst_v4l2_object_clear_format_list (v4l2object);
  }

552
  g_free (v4l2object);
553
554
}

Rob Clark's avatar
Rob Clark committed
555
556
557
558
559
560
561
562
563
564
565

static gboolean
gst_v4l2_object_clear_format_list (GstV4l2Object * v4l2object)
{
  g_slist_foreach (v4l2object->formats, (GFunc) g_free, NULL);
  g_slist_free (v4l2object->formats);
  v4l2object->formats = NULL;

  return TRUE;
}

566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
static gint
gst_v4l2_object_prop_to_cid (guint prop_id)
{
  gint cid = -1;

  switch (prop_id) {
    case PROP_BRIGHTNESS:
      cid = V4L2_CID_BRIGHTNESS;
      break;
    case PROP_CONTRAST:
      cid = V4L2_CID_CONTRAST;
      break;
    case PROP_SATURATION:
      cid = V4L2_CID_SATURATION;
      break;
    case PROP_HUE:
      cid = V4L2_CID_HUE;
      break;
    default:
      GST_WARNING ("unmapped property id: %d", prop_id);
  }
  return cid;
}

Rob Clark's avatar
Rob Clark committed
590

591
gboolean
592
gst_v4l2_object_set_property_helper (GstV4l2Object * v4l2object,
593
594
595
596
    guint prop_id, const GValue * value, GParamSpec * pspec)
{
  switch (prop_id) {
    case PROP_DEVICE:
Wim Taymans's avatar
Wim Taymans committed
597
      g_free (v4l2object->videodev);
598
      v4l2object->videodev = g_value_dup_string (value);
599
      break;
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
    case PROP_BRIGHTNESS:
    case PROP_CONTRAST:
    case PROP_SATURATION:
    case PROP_HUE:
    {
      gint cid = gst_v4l2_object_prop_to_cid (prop_id);

      if (cid != -1) {
        if (GST_V4L2_IS_OPEN (v4l2object)) {
          gst_v4l2_set_attribute (v4l2object, cid, g_value_get_int (value));
        }
      }
      return TRUE;
    }
      break;
Rob Clark's avatar
Rob Clark committed
615
616
    case PROP_TV_NORM:
      v4l2object->tv_norm = g_value_get_enum (value);
617
      break;
Rob Clark's avatar
Rob Clark committed
618
#if 0
619
    case PROP_CHANNEL:
620
621
622
      if (GST_V4L2_IS_OPEN (v4l2object)) {
        GstTuner *tuner = GST_TUNER (v4l2object->element);
        GstTunerChannel *channel = gst_tuner_find_channel_by_name (tuner,
623
            (gchar *) g_value_get_string (value));
624
625
626
627
628
629
630

        if (channel) {
          /* like gst_tuner_set_channel (tuner, channel)
             without g_object_notify */
          gst_v4l2_tuner_set_channel (v4l2object, channel);
        }
      } else {
631
632
        g_free (v4l2object->channel);
        v4l2object->channel = g_value_dup_string (value);
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
      }
      break;
    case PROP_FREQUENCY:
      if (GST_V4L2_IS_OPEN (v4l2object)) {
        GstTuner *tuner = GST_TUNER (v4l2object->element);
        GstTunerChannel *channel = gst_tuner_get_channel (tuner);

        if (channel &&
            GST_TUNER_CHANNEL_HAS_FLAG (channel, GST_TUNER_CHANNEL_FREQUENCY)) {
          /* like
             gst_tuner_set_frequency (tuner, channel, g_value_get_ulong (value))
             without g_object_notify */
          gst_v4l2_tuner_set_frequency (v4l2object, channel,
              g_value_get_ulong (value));
        }
      } else {
        v4l2object->frequency = g_value_get_ulong (value);
      }
      break;
652
#endif
Wim Taymans's avatar
Wim Taymans committed
653
654
655
    case PROP_IO_MODE:
      v4l2object->req_mode = g_value_get_enum (value);
      break;
656
657
658
659
660
661
662
663
664
    default:
      return FALSE;
      break;
  }
  return TRUE;
}


gboolean
665
gst_v4l2_object_get_property_helper (GstV4l2Object * v4l2object,
666
667
668
669
670
671
672
673
    guint prop_id, GValue * value, GParamSpec * pspec)
{
  switch (prop_id) {
    case PROP_DEVICE:
      g_value_set_string (value, v4l2object->videodev);
      break;
    case PROP_DEVICE_NAME:
    {
674
      const guchar *new = NULL;
675

676
      if (GST_V4L2_IS_OPEN (v4l2object)) {
677
        new = v4l2object->vcap.card;
678
      } else if (gst_v4l2_open (v4l2object)) {
679
        new = v4l2object->vcap.card;
680
681
        gst_v4l2_close (v4l2object);
      }
682
      g_value_set_string (value, (gchar *) new);
683
684
      break;
    }
685
686
687
688
689
690
691
692
    case PROP_DEVICE_FD:
    {
      if (GST_V4L2_IS_OPEN (v4l2object))
        g_value_set_int (value, v4l2object->video_fd);
      else
        g_value_set_int (value, DEFAULT_PROP_DEVICE_FD);
      break;
    }
693
694
695
696
697
698
699
700
    case PROP_FLAGS:
    {
      guint flags = 0;

      if (GST_V4L2_IS_OPEN (v4l2object)) {
        flags |= v4l2object->vcap.capabilities &
            (V4L2_CAP_VIDEO_CAPTURE |
            V4L2_CAP_VIDEO_OUTPUT |
Edgard Lima's avatar
Edgard Lima committed
701
702
703
            V4L2_CAP_VIDEO_OVERLAY |
            V4L2_CAP_VBI_CAPTURE |
            V4L2_CAP_VBI_OUTPUT | V4L2_CAP_TUNER | V4L2_CAP_AUDIO);
704
705
706
707
      }
      g_value_set_flags (value, flags);
      break;
    }
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
    case PROP_BRIGHTNESS:
    case PROP_CONTRAST:
    case PROP_SATURATION:
    case PROP_HUE:
    {
      gint cid = gst_v4l2_object_prop_to_cid (prop_id);

      if (cid != -1) {
        if (GST_V4L2_IS_OPEN (v4l2object)) {
          gint v;
          if (gst_v4l2_get_attribute (v4l2object, cid, &v)) {
            g_value_set_int (value, v);
          }
        }
      }
      return TRUE;
    }
      break;
Rob Clark's avatar
Rob Clark committed
726
727
728
    case PROP_TV_NORM:
      g_value_set_enum (value, v4l2object->tv_norm);
      break;
Wim Taymans's avatar
Wim Taymans committed
729
730
731
    case PROP_IO_MODE:
      g_value_set_enum (value, v4l2object->req_mode);
      break;
732
733
734
735
736
737
738
739
740
741
742
743
    default:
      return FALSE;
      break;
  }
  return TRUE;
}

static void
gst_v4l2_set_defaults (GstV4l2Object * v4l2object)
{
  GstTunerNorm *norm = NULL;
  GstTunerChannel *channel = NULL;
Rob Clark's avatar
Rob Clark committed
744
745
746
747
748
749
  GstTuner *tuner;

  if (!GST_IS_TUNER (v4l2object->element))
    return;

  tuner = GST_TUNER (v4l2object->element);
750

Rob Clark's avatar
Rob Clark committed
751
752
  if (v4l2object->tv_norm)
    norm = gst_v4l2_tuner_get_norm_by_std_id (v4l2object, v4l2object->tv_norm);
753
754
  GST_DEBUG_OBJECT (v4l2object->element, "tv_norm=0x%" G_GINT64_MODIFIER "x, "
      "norm=%p", (guint64) v4l2object->tv_norm, norm);
755
756
757
758
759
760
  if (norm) {
    gst_tuner_set_norm (tuner, norm);
  } else {
    norm =
        GST_TUNER_NORM (gst_tuner_get_norm (GST_TUNER (v4l2object->element)));
    if (norm) {
Rob Clark's avatar
Rob Clark committed
761
762
      v4l2object->tv_norm =
          gst_v4l2_tuner_get_std_id_by_norm (v4l2object, norm);
763
764
765
766
      gst_tuner_norm_changed (tuner, norm);
    }
  }

767
768
  if (v4l2object->channel)
    channel = gst_tuner_find_channel_by_name (tuner, v4l2object->channel);
769
770
771
772
  if (channel) {
    gst_tuner_set_channel (tuner, channel);
  } else {
    channel =
773
774
        GST_TUNER_CHANNEL (gst_tuner_get_channel (GST_TUNER
            (v4l2object->element)));
775
776
777
778
779
    if (channel) {
      g_free (v4l2object->channel);
      v4l2object->channel = g_strdup (channel->label);
      gst_tuner_channel_changed (tuner, channel);
    }
780
781
  }

782
783
  if (channel
      && GST_TUNER_CHANNEL_HAS_FLAG (channel, GST_TUNER_CHANNEL_FREQUENCY)) {
784
785
786
787
788
789
790
791
792
793
794
795
796
797
    if (v4l2object->frequency != 0) {
      gst_tuner_set_frequency (tuner, channel, v4l2object->frequency);
    } else {
      v4l2object->frequency = gst_tuner_get_frequency (tuner, channel);
      if (v4l2object->frequency == 0) {
        /* guess */
        gst_tuner_set_frequency (tuner, channel, 1000);
      } else {
      }
    }
  }
}

gboolean
798
gst_v4l2_object_open (GstV4l2Object * v4l2object)
799
800
801
802
803
804
{
  if (gst_v4l2_open (v4l2object))
    gst_v4l2_set_defaults (v4l2object);
  else
    return FALSE;

Rob Clark's avatar
Rob Clark committed
805
#ifdef HAVE_XVIDEO
806
  gst_v4l2_video_overlay_start (v4l2object);
807
808
809
810
811
812
#endif

  return TRUE;
}

gboolean
813
gst_v4l2_object_close (GstV4l2Object * v4l2object)
814
{
Rob Clark's avatar
Rob Clark committed
815
#ifdef HAVE_XVIDEO
816
  gst_v4l2_video_overlay_stop (v4l2object);
817
818
819
820
821
#endif

  if (!gst_v4l2_close (v4l2object))
    return FALSE;

822
823
824
825
  if (v4l2object->formats) {
    gst_v4l2_object_clear_format_list (v4l2object);
  }

826
827
  return TRUE;
}
Rob Clark's avatar
Rob Clark committed
828
829
830
831
832


/*
 * common format / caps utilities:
 */
833
834
835
836
837
typedef struct
{
  guint32 format;
  gboolean dimensions;
} GstV4L2FormatDesc;
Rob Clark's avatar
Rob Clark committed
838

839
static const GstV4L2FormatDesc gst_v4l2_formats[] = {
Rob Clark's avatar
Rob Clark committed
840
  /* from Linux 2.6.15 videodev2.h */
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
  {V4L2_PIX_FMT_RGB332, TRUE},
  {V4L2_PIX_FMT_RGB555, TRUE},
  {V4L2_PIX_FMT_RGB565, TRUE},
  {V4L2_PIX_FMT_RGB555X, TRUE},
  {V4L2_PIX_FMT_RGB565X, TRUE},
  {V4L2_PIX_FMT_BGR24, TRUE},
  {V4L2_PIX_FMT_RGB24, TRUE},
  {V4L2_PIX_FMT_BGR32, TRUE},
  {V4L2_PIX_FMT_RGB32, TRUE},
  {V4L2_PIX_FMT_GREY, TRUE},
  {V4L2_PIX_FMT_YVU410, TRUE},
  {V4L2_PIX_FMT_YVU420, TRUE},
  {V4L2_PIX_FMT_YUYV, TRUE},
  {V4L2_PIX_FMT_UYVY, TRUE},
  {V4L2_PIX_FMT_YUV422P, TRUE},
  {V4L2_PIX_FMT_YUV411P, TRUE},
  {V4L2_PIX_FMT_Y41P, TRUE},
Rob Clark's avatar
Rob Clark committed
858
859

  /* two planes -- one Y, one Cr + Cb interleaved  */
860
861
  {V4L2_PIX_FMT_NV12, TRUE},
  {V4L2_PIX_FMT_NV21, TRUE},
Rob Clark's avatar
Rob Clark committed
862
863

  /*  The following formats are not defined in the V4L2 specification */
864
865
866
867
  {V4L2_PIX_FMT_YUV410, TRUE},
  {V4L2_PIX_FMT_YUV420, TRUE},
  {V4L2_PIX_FMT_YYUV, TRUE},
  {V4L2_PIX_FMT_HI240, TRUE},
Rob Clark's avatar
Rob Clark committed
868
869
870

  /* see http://www.siliconimaging.com/RGB%20Bayer.htm */
#ifdef V4L2_PIX_FMT_SBGGR8
871
  {V4L2_PIX_FMT_SBGGR8, TRUE},
Rob Clark's avatar
Rob Clark committed
872
873
874
#endif

  /* compressed formats */
875
876
  {V4L2_PIX_FMT_MJPEG, TRUE},
  {V4L2_PIX_FMT_JPEG, TRUE},
877
#ifdef V4L2_PIX_FMT_PJPG
Thiago Santos's avatar
Thiago Santos committed
878
  {V4L2_PIX_FMT_PJPG, TRUE},
879
#endif
880
881
  {V4L2_PIX_FMT_DV, TRUE},
  {V4L2_PIX_FMT_MPEG, FALSE},
Rob Clark's avatar
Rob Clark committed
882

883
#ifdef V4L2_PIX_FMT_H264
884
  {V4L2_PIX_FMT_H264, TRUE},
885
#endif
886

Rob Clark's avatar
Rob Clark committed
887
  /*  Vendor-specific formats   */
888
  {V4L2_PIX_FMT_WNVA, TRUE},
Rob Clark's avatar
Rob Clark committed
889
890

#ifdef V4L2_PIX_FMT_SN9C10X
891
  {V4L2_PIX_FMT_SN9C10X, TRUE},
Rob Clark's avatar
Rob Clark committed
892
893
#endif
#ifdef V4L2_PIX_FMT_PWC1
894
  {V4L2_PIX_FMT_PWC1, TRUE},
Rob Clark's avatar
Rob Clark committed
895
896
#endif
#ifdef V4L2_PIX_FMT_PWC2
897
  {V4L2_PIX_FMT_PWC2, TRUE},
Rob Clark's avatar
Rob Clark committed
898
899
#endif
#ifdef V4L2_PIX_FMT_YVYU
900
  {V4L2_PIX_FMT_YVYU, TRUE},
Rob Clark's avatar
Rob Clark committed
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
#endif
};

#define GST_V4L2_FORMAT_COUNT (G_N_ELEMENTS (gst_v4l2_formats))


static struct v4l2_fmtdesc *
gst_v4l2_object_get_format_from_fourcc (GstV4l2Object * v4l2object,
    guint32 fourcc)
{
  struct v4l2_fmtdesc *fmt;
  GSList *walk;

  if (fourcc == 0)
    return NULL;

  walk = gst_v4l2_object_get_format_list (v4l2object);
  while (walk) {
    fmt = (struct v4l2_fmtdesc *) walk->data;
    if (fmt->pixelformat == fourcc)
      return fmt;
    /* special case for jpeg */
Thiago Santos's avatar
Thiago Santos committed
923
    if (fmt->pixelformat == V4L2_PIX_FMT_MJPEG ||
924
925
926
927
928
929
930
931
932
933
        fmt->pixelformat == V4L2_PIX_FMT_JPEG
#ifdef V4L2_PIX_FMT_PJPG
        || fmt->pixelformat == V4L2_PIX_FMT_PJPG
#endif
        ) {
      if (fourcc == V4L2_PIX_FMT_JPEG || fourcc == V4L2_PIX_FMT_MJPEG
#ifdef V4L2_PIX_FMT_PJPG
          || fourcc == V4L2_PIX_FMT_PJPG
#endif
          ) {
Thiago Santos's avatar
Thiago Santos committed
934
935
        return fmt;
      }
Rob Clark's avatar
Rob Clark committed
936
937
938
939
940
941
942
943
944
945
    }
    walk = g_slist_next (walk);
  }

  return NULL;
}



/* complete made up ranking, the values themselves are meaningless */
946
947
/* These ranks MUST be X such that X<<15 fits on a signed int - see
   the comment at the end of gst_v4l2_object_format_get_rank. */
Rob Clark's avatar
Rob Clark committed
948
949
950
951
952
953
954
955
956
957
958
#define YUV_BASE_RANK     1000
#define JPEG_BASE_RANK     500
#define DV_BASE_RANK       200
#define RGB_BASE_RANK      100
#define YUV_ODD_BASE_RANK   50
#define RGB_ODD_BASE_RANK   25
#define BAYER_BASE_RANK     15
#define S910_BASE_RANK      10
#define GREY_BASE_RANK       5
#define PWC_BASE_RANK        1

959
960
961
962
963
964
965
/* This flag is already used by libv4l2 although
 * it was added to the Linux kernel in 2.6.32
 */
#ifndef V4L2_FMT_FLAG_EMULATED
#define V4L2_FMT_FLAG_EMULATED 0x0002
#endif

Rob Clark's avatar
Rob Clark committed
966
static gint
967
gst_v4l2_object_format_get_rank (const struct v4l2_fmtdesc *fmt)
Rob Clark's avatar
Rob Clark committed
968
{
969
  guint32 fourcc = fmt->pixelformat;
970
  gboolean emulated = ((fmt->flags & V4L2_FMT_FLAG_EMULATED) != 0);
971
972
  gint rank = 0;

Rob Clark's avatar
Rob Clark committed
973
974
  switch (fourcc) {
    case V4L2_PIX_FMT_MJPEG:
975
#ifdef V4L2_PIX_FMT_PJPG
Thiago Santos's avatar
Thiago Santos committed
976
    case V4L2_PIX_FMT_PJPG:
977
978
      rank = JPEG_BASE_RANK;
      break;
979
#endif
Rob Clark's avatar
Rob Clark committed
980
    case V4L2_PIX_FMT_JPEG:
981
982
      rank = JPEG_BASE_RANK + 1;
      break;
983
984
985
    case V4L2_PIX_FMT_MPEG:    /* MPEG          */
      rank = JPEG_BASE_RANK + 2;
      break;
Rob Clark's avatar
Rob Clark committed
986
987
988
989
990
991

    case V4L2_PIX_FMT_RGB332:
    case V4L2_PIX_FMT_RGB555:
    case V4L2_PIX_FMT_RGB555X:
    case V4L2_PIX_FMT_RGB565:
    case V4L2_PIX_FMT_RGB565X:
992
993
      rank = RGB_ODD_BASE_RANK;
      break;
Rob Clark's avatar
Rob Clark committed
994
995
996

    case V4L2_PIX_FMT_RGB24:
    case V4L2_PIX_FMT_BGR24:
997
998
      rank = RGB_BASE_RANK - 1;
      break;
Rob Clark's avatar
Rob Clark committed
999
1000

    case V4L2_PIX_FMT_RGB32: