v4l2src_calls.c 25.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* G-Streamer Video4linux2 video-capture plugin - system calls
 * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
 *
 * 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
23
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

24
25
26
27
28
29
30
31
32
33
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include "v4l2src_calls.h"
#include <sys/time.h>
34
35
#include <unistd.h>

Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
36
37
38
#include "gstv4l2tuner.h"

GST_DEBUG_CATEGORY_EXTERN (v4l2src_debug);
39
40
41
42
43
#define GST_CAT_DEFAULT v4l2src_debug

/* lalala... */
#define GST_V4L2_SET_ACTIVE(element) (element)->buffer = GINT_TO_POINTER (-1)
#define GST_V4L2_SET_INACTIVE(element) (element)->buffer = NULL
44
45

#define DEBUG(format, args...) \
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
46
47
48
	GST_CAT_DEBUG_OBJECT (\
		v4l2src_debug, v4l2src, \
		"V4L2SRC: " format, ##args)
49
50
51
52
53
54

/* On some systems MAP_FAILED seems to be missing */
#ifndef MAP_FAILED
#define MAP_FAILED ( (caddr_t) -1 )
#endif

55
56
57
58
59
60
61
/******************************************************
 * gst_v4l2src_fill_format_list():
 *   create list of supported capture formats
 * return value: TRUE on success, FALSE on error
 ******************************************************/

gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
62
gst_v4l2src_fill_format_list (GstV4l2Src * v4l2src)
63
{
64
65
66
67
68
69
  gint n;
  struct v4l2_fmtdesc *format;

  GST_DEBUG_OBJECT (v4l2src, "getting src format enumerations");

  /* format enumeration */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
70
  for (n = 0;; n++) {
71
    format = g_new (struct v4l2_fmtdesc, 1);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
72

73
74
    format->index = n;
    format->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
75
    if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_ENUM_FMT,
76
            format) < 0) {
77
      if (errno == EINVAL) {
78
        break;                  /* end of enumeration */
79
      } else {
80
81
        GST_ELEMENT_ERROR (v4l2src, RESOURCE, SETTINGS, (NULL),
            ("failed to get number %d in pixelformat enumeration for %s: %s",
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
82
                n, GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno)));
83
84
        g_free (format);
        return FALSE;
85
86
      }
    }
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
87
    GST_LOG_OBJECT (v4l2src, "got format" GST_FOURCC_FORMAT,
88
        GST_FOURCC_ARGS (format->pixelformat));
89
90
91
92
    v4l2src->formats = g_slist_prepend (v4l2src->formats, format);
  }

  return TRUE;
93
94
95
96
}


/******************************************************
97
 * gst_v4l2src_clear_format_list():
98
99
100
101
102
 *   free list of supported capture formats
 * return value: TRUE on success, FALSE on error
 ******************************************************/

gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
103
gst_v4l2src_clear_format_list (GstV4l2Src * v4l2src)
104
{
105
106
  g_slist_foreach (v4l2src->formats, (GFunc) g_free, NULL);
  g_slist_free (v4l2src->formats);
107
  v4l2src->formats = NULL;
108

109
  return TRUE;
110
111
112
}


113
114
115
116
117
118
/******************************************************
 * gst_v4l2src_queue_frame():
 *   queue a frame for capturing
 * return value: TRUE on success, FALSE on error
 ******************************************************/

119
gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
120
gst_v4l2src_queue_frame (GstV4l2Src * v4l2src, guint i)
121
{
122
  GST_LOG_OBJECT (v4l2src, "queueing frame %u", i);
123

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
124
  if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_QBUF,
125
          &v4l2src->pool->buffers[i].buffer) < 0) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
126
    GST_ELEMENT_ERROR (v4l2src, RESOURCE, WRITE,
127
        (_("Could not write to device \"%s\"."),
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
128
            GST_V4L2ELEMENT (v4l2src)->videodev),
129
        ("Error queueing buffer %u on device %s", i, g_strerror (errno)));
130
131
    return FALSE;
  }
132

133
  return TRUE;
134
135
136
137
}


/******************************************************
138
139
 * gst_v4l2src_grab_frame ():
 *   grab a frame for capturing
140
141
142
 * return value: TRUE on success, FALSE on error
 ******************************************************/

143
gint
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
144
gst_v4l2src_grab_frame (GstV4l2Src * v4l2src)
145
{
146
  struct v4l2_buffer buffer;
147
  gint32 trials = 100;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
148

149
  memset (&buffer, 0x00, sizeof (buffer));
150
  buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
151
  buffer.memory = v4l2src->breq.memory;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
152
  while (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_DQBUF, &buffer) < 0) {
153
    /* if the sync() got interrupted, we can retry */
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
    switch (errno) {
      case EAGAIN:
        GST_ELEMENT_ERROR (v4l2src, RESOURCE, SYNC, (NULL),
            ("Non-blocking I/O has been selected using O_NONBLOCK and"
                " no buffer was in the outgoing queue. device %s: %s",
                GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno)));
        break;
      case EINVAL:
        GST_ELEMENT_ERROR (v4l2src, RESOURCE, SYNC, (NULL),
            ("The buffer type is not supported, or the index is out of bounds,"
                " or no buffers have been allocated yet, or the userptr"
                " or length are invalid. device %s: %s",
                GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno)));
        break;
      case ENOMEM:
        GST_ELEMENT_ERROR (v4l2src, RESOURCE, SYNC, (NULL),
            ("isufficient memory to enqueue a user pointer buffer. device %s: %s",
                GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno)));
        break;
      case EIO:
174
175
176
177
178
179
180
        GST_WARNING_OBJECT (v4l2src,
            "VIDIOC_DQBUF failed due to an internal error."
            " Can also indicate temporary problems like signal loss."
            " Note the driver might dequeue an (empty) buffer despite"
            " returning an error, or even stop capturing."
            " device %s: %s",
            GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno));
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
181
182
183
184
185
186
187
188
189
        break;
      case EINTR:
        GST_ELEMENT_ERROR (v4l2src, RESOURCE, SYNC, (NULL),
            ("could not sync on a buffer on device %s: %s",
                GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno)));
        break;
      default:
        GST_DEBUG_OBJECT (v4l2src, "grab got interrupted");
        break;
190
    }
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
191

192
193
194
195
196
197
198
199
    if (--trials == -1) {
      return -1;
    } else {
      ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_QBUF, &buffer);
      memset (&buffer, 0x00, sizeof (buffer));
      buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
      buffer.memory = v4l2src->breq.memory;
    }
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
200

201
202
203
204
205
  }

  GST_LOG_OBJECT (v4l2src, "grabbed frame %d", buffer.index);

  return buffer.index;
206
207
208
209
210
211
212
213
214
215
}


/******************************************************
 * gst_v4l2src_get_capture():
 *   get capture parameters
 * return value: TRUE on success, FALSE on error
 ******************************************************/

gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
216
gst_v4l2src_get_capture (GstV4l2Src * v4l2src)
217
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
218
  DEBUG ("Getting capture format");
219

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
220
  GST_V4L2_CHECK_OPEN (GST_V4L2ELEMENT (v4l2src));
221

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
222
223
  v4l2src->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_G_FMT,
224
          &v4l2src->format) < 0) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
225
    GST_ELEMENT_ERROR (v4l2src, RESOURCE, SETTINGS, (NULL),
226
        ("failed to get pixelformat for device %s: %s",
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
227
            GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno)));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
228
229
    return FALSE;
  }
230

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
231
  return TRUE;
232
233
234
235
236
237
238
239
240
241
}


/******************************************************
 * gst_v4l2src_set_capture():
 *   set capture parameters
 * return value: TRUE on success, FALSE on error
 ******************************************************/

gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
242
243
gst_v4l2src_set_capture (GstV4l2Src * v4l2src,
    struct v4l2_fmtdesc * fmt, gint width, gint height)
244
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
245
246
247
248
249
250
251
252
253
254
255
256
257
258
  DEBUG ("Setting capture format to %dx%d, format %s",
      width, height, fmt->description);

  GST_V4L2_CHECK_OPEN (GST_V4L2ELEMENT (v4l2src));
  GST_V4L2_CHECK_NOT_ACTIVE (GST_V4L2ELEMENT (v4l2src));

  memset (&v4l2src->format, 0, sizeof (struct v4l2_format));
  v4l2src->format.fmt.pix.width = width;
  v4l2src->format.fmt.pix.height = height;
  v4l2src->format.fmt.pix.pixelformat = fmt->pixelformat;
  v4l2src->format.fmt.pix.field = V4L2_FIELD_INTERLACED;
  v4l2src->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

  if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_S_FMT,
259
          &v4l2src->format) < 0) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
260
    GST_ELEMENT_ERROR (v4l2src, RESOURCE, SETTINGS, (NULL),
261
        ("failed to set pixelformat to %s @ %dx%d for device %s: %s",
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
262
263
            fmt->description, width, height,
            GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno)));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
264
265
266
267
268
    return FALSE;
  }

  /* update internal info */
  return gst_v4l2src_get_capture (v4l2src);
269
270
271
272
273
274
275
276
277
278
}


/******************************************************
 * gst_v4l2src_capture_init():
 *   initialize the capture system
 * return value: TRUE on success, FALSE on error
 ******************************************************/

gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
279
gst_v4l2src_capture_init (GstV4l2Src * v4l2src)
280
{
281
282
283
284
285
  gint n;
  guint buffers;

  GST_DEBUG_OBJECT (v4l2src, "initting the capture system");

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
286
287
  GST_V4L2_CHECK_OPEN (GST_V4L2ELEMENT (v4l2src));
  GST_V4L2_CHECK_NOT_ACTIVE (GST_V4L2ELEMENT (v4l2src));
288
289
290
291
292
293
294
295
296
297

  /* request buffer info */
  buffers = v4l2src->breq.count;
  if (v4l2src->breq.count > GST_V4L2_MAX_BUFFERS) {
    v4l2src->breq.count = GST_V4L2_MAX_BUFFERS;
  }
  if (v4l2src->breq.count < GST_V4L2_MIN_BUFFERS) {
    v4l2src->breq.count = GST_V4L2_MIN_BUFFERS;
  }
  v4l2src->breq.type = v4l2src->format.type;
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
298
299
300
301
302
303
304
  if (GST_V4L2ELEMENT (v4l2src)->vcap.capabilities & V4L2_CAP_STREAMING) {
    v4l2src->breq.memory = V4L2_MEMORY_MMAP;
    if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_REQBUFS,
            &v4l2src->breq) < 0) {
      GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ,
          (_("Could not get buffers from device \"%s\"."),
              GST_V4L2ELEMENT (v4l2src)->videodev),
305
306
          ("error requesting %d buffers: %s",
              v4l2src->breq.count, g_strerror (errno)));
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
307
308
309
310
311
312
313
      return FALSE;
    }
    GST_LOG_OBJECT (v4l2src, "using default mmap method");
  } else if (GST_V4L2ELEMENT (v4l2src)->vcap.capabilities & V4L2_CAP_READWRITE) {
    v4l2src->breq.memory = 0;
    GST_INFO_OBJECT (v4l2src, "using fallback read method");
  } else {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
314
    GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ,
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
315
316
317
318
        (_("the driver of device \"%s\" is broken."),
            GST_V4L2ELEMENT (v4l2src)->videodev),
        ("no supported read capability from %s",
            GST_V4L2ELEMENT (v4l2src)->videodev));
319
320
321
    return FALSE;
  }

Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
322
323
324
325
326
  if (v4l2src->breq.memory > 0) {
    if (v4l2src->breq.count < GST_V4L2_MIN_BUFFERS) {
      GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ,
          (_("Could not get enough buffers from device \"%s\"."),
              GST_V4L2ELEMENT (v4l2src)->videodev),
327
328
          ("we received %d, we want at least %d",
              v4l2src->breq.count, GST_V4L2_MIN_BUFFERS));
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
329
      v4l2src->breq.count = buffers;
330
331
      return FALSE;
    }
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
332
333
334
335
336
337
338
339
340
341
342
343
344
    if (v4l2src->breq.count != buffers)
      g_object_notify (G_OBJECT (v4l2src), "num_buffers");

    GST_INFO_OBJECT (v4l2src,
        "Got %d buffers (" GST_FOURCC_FORMAT ") of size %d KB\n",
        v4l2src->breq.count,
        GST_FOURCC_ARGS (v4l2src->format.fmt.pix.pixelformat),
        v4l2src->format.fmt.pix.sizeimage / 1024);

    /* Map the buffers */
    GST_LOG_OBJECT (v4l2src, "initiating buffer pool");

    v4l2src->pool = g_new (GstV4l2BufferPool, 1);
345
    gst_atomic_int_set (&v4l2src->pool->refcount, 1);
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
346
347
348
349
350
351
352
    v4l2src->pool->video_fd = GST_V4L2ELEMENT (v4l2src)->video_fd;
    v4l2src->pool->buffer_count = v4l2src->breq.count;
    v4l2src->pool->buffers = g_new0 (GstV4l2Buffer, v4l2src->breq.count);

    for (n = 0; n < v4l2src->breq.count; n++) {
      GstV4l2Buffer *buffer = &v4l2src->pool->buffers[n];

353
      gst_atomic_int_set (&buffer->refcount, 1);
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
354
      buffer->pool = v4l2src->pool;
355
      memset (&buffer->buffer, 0x00, sizeof (buffer->buffer));
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
356
      buffer->buffer.index = n;
357
358
359
      buffer->buffer.type = v4l2src->breq.type;
      buffer->buffer.memory = v4l2src->breq.memory;

Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
360
361
362
      if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_QUERYBUF,
              &buffer->buffer) < 0) {
        GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, (NULL),
363
364
            ("Could not get buffer properties of buffer %d: %s",
                n, g_strerror (errno)));
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
        gst_v4l2src_capture_deinit (v4l2src);
        return FALSE;
      }
      buffer->start =
          mmap (0, buffer->buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED,
          GST_V4L2ELEMENT (v4l2src)->video_fd, buffer->buffer.m.offset);
      if (buffer->start == MAP_FAILED) {
        GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, (NULL),
            ("Could not mmap video buffer %d: %s", n, g_strerror (errno)));
        buffer->start = 0;
        gst_v4l2src_capture_deinit (v4l2src);
        return FALSE;
      }
      buffer->length = buffer->buffer.length;
      if (!gst_v4l2src_queue_frame (v4l2src, n)) {
        gst_v4l2src_capture_deinit (v4l2src);
        return FALSE;
      }
383
    }
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
384
385
386
  } else {
    GST_LOG_OBJECT (v4l2src, "no buffer pool used");
    v4l2src->pool = NULL;
387
388
  }

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
389
  GST_V4L2_SET_ACTIVE (GST_V4L2ELEMENT (v4l2src));
390

391
  return TRUE;
392
393
394
395
396
397
398
399
400
401
}


/******************************************************
 * gst_v4l2src_capture_start():
 *   start streaming capture
 * return value: TRUE on success, FALSE on error
 ******************************************************/

gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
402
gst_v4l2src_capture_start (GstV4l2Src * v4l2src)
403
{
404
  gint type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
405

406
  GST_DEBUG_OBJECT (v4l2src, "starting the capturing");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
407
408
409

  GST_V4L2_CHECK_OPEN (GST_V4L2ELEMENT (v4l2src));
  if (!GST_V4L2_IS_ACTIVE (GST_V4L2ELEMENT (v4l2src))) {
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
410
    /* gst_pad_renegotiate (v4l2src->srcpad); FIX: is it still required in 0.10 */
411
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
412
  GST_V4L2_CHECK_ACTIVE (GST_V4L2ELEMENT (v4l2src));
413

414
  v4l2src->quit = FALSE;
415

Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
416
417
418
419
420
421
422
  if (v4l2src->breq.memory != 0) {
    if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_STREAMON, &type) < 0) {
      GST_ELEMENT_ERROR (v4l2src, RESOURCE, OPEN_READ, (NULL),
          ("Error starting streaming capture from device %s: %s",
              GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno)));
      return FALSE;
    }
423
  }
424

425
426
  v4l2src->is_capturing = TRUE;

427
  return TRUE;
428
429
430
431
432
433
434
435
436
437
}


/******************************************************
 * gst_v4l2src_capture_stop():
 *   stop streaming capture
 * return value: TRUE on success, FALSE on error
 ******************************************************/

gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
438
gst_v4l2src_capture_stop (GstV4l2Src * v4l2src)
439
{
440
441
442
  gint type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

  GST_DEBUG_OBJECT (v4l2src, "stopping capturing");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
443
444
  GST_V4L2_CHECK_OPEN (GST_V4L2ELEMENT (v4l2src));
  GST_V4L2_CHECK_ACTIVE (GST_V4L2ELEMENT (v4l2src));
445

Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
446
447
448
449
450
451
452
453
454
455
  if (v4l2src->breq.memory != 0) {
    /* we actually need to sync on all queued buffers but not
     * on the non-queued ones */
    if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_STREAMOFF,
            &type) < 0) {
      GST_ELEMENT_ERROR (v4l2src, RESOURCE, CLOSE, (NULL),
          ("Error stopping streaming capture from device %s: %s",
              GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno)));
      return FALSE;
    }
456
457
458
459
  }

  /* make an optional pending wait stop */
  v4l2src->quit = TRUE;
460
  v4l2src->is_capturing = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
461

462
463
  return TRUE;
}
464

465
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
466
gst_v4l2src_buffer_pool_free (GstV4l2BufferPool * pool, gboolean do_close)
467
468
469
470
{
  guint i;

  for (i = 0; i < pool->buffer_count; i++) {
471
    gst_atomic_int_set (&pool->buffers[i].refcount, 0);
472
473
474
    munmap (pool->buffers[i].start, pool->buffers[i].length);
  }
  g_free (pool->buffers);
475
  gst_atomic_int_set (&pool->refcount, 0);
476
477
478
  if (do_close)
    close (pool->video_fd);
  g_free (pool);
479
480
}

Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
481
482
#if 0

483
void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
484
gst_v4l2src_free_buffer (GstBuffer * buffer)
485
486
{
  GstV4l2Buffer *buf = (GstV4l2Buffer *) GST_BUFFER_PRIVATE (buffer);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
487

488
  GST_LOG ("freeing buffer %p (nr. %d)", buffer, buf->buffer.index);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
489

Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
490
  if (!g_atomic_int_dec_and_test (&buf->refcount)) {
491
492
    /* we're still in use, add to queue again 
       note: this might fail because the device is already stopped (race) */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
493
    if (ioctl (buf->pool->video_fd, VIDIOC_QBUF, &buf->buffer) < 0)
494
495
      GST_INFO ("readding to queue failed, assuming video device is stopped");
  }
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
496
  if (g_atomic_int_dec_and_test (&buf->pool->refcount)) {
497
498
499
500
    /* we're last thing that used all this */
    gst_v4l2src_buffer_pool_free (buf->pool, TRUE);
  }
}
501

Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
502
503
504

#endif

505
506
507
508
509
510
511
/******************************************************
 * gst_v4l2src_capture_deinit():
 *   deinitialize the capture system
 * return value: TRUE on success, FALSE on error
 ******************************************************/

gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
512
gst_v4l2src_capture_deinit (GstV4l2Src * v4l2src)
513
{
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
514
515
  gint i;
  gboolean try_reinit = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
516

517
  GST_DEBUG_OBJECT (v4l2src, "deinitting capture system");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
518
519
520

  GST_V4L2_CHECK_OPEN (GST_V4L2ELEMENT (v4l2src));
  GST_V4L2_CHECK_ACTIVE (GST_V4L2ELEMENT (v4l2src));
521

Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
  if (v4l2src->pool) {
    /* free the buffers */
    for (i = 0; i < v4l2src->breq.count; i++) {
      if (g_atomic_int_dec_and_test (&v4l2src->pool->buffers[i].refcount)) {
        if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_DQBUF,
                &v4l2src->pool->buffers[i].buffer) < 0)
          GST_WARNING_OBJECT (v4l2src,
              "Could not dequeue buffer on uninitialization: %s - will try reinit instead",
              g_strerror (errno));
        try_reinit = TRUE;
      }
    }
    if (g_atomic_int_dec_and_test (&v4l2src->pool->refcount)) {
      /* we're last thing that used all this */
      gst_v4l2src_buffer_pool_free (v4l2src->pool, FALSE);
    }
    v4l2src->pool = NULL;
    /* This is our second try to get the buffers dequeued.
     * Since buffers are normally dequeued automatically when capturing is
     * stopped, but may be enqueued before capturing has started, you get
     * a problem when you abort before capturing started but have enqueued
     * the buffers. We avoid that by starting/stopping capturing once so
     * they get auto-dequeued.
     */
    if (try_reinit) {
      if (!gst_v4l2src_capture_start (v4l2src) ||
          !gst_v4l2src_capture_stop (v4l2src))
        return FALSE;
    }
551
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
552

553
554
  GST_V4L2_SET_INACTIVE (GST_V4L2ELEMENT (v4l2src));
  return TRUE;
555
}
556
557
558
559
560
561
562


/*

 */

gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
563
564
565
gst_v4l2src_get_size_limits (GstV4l2Src * v4l2src,
    struct v4l2_fmtdesc * format,
    gint * min_w, gint * max_w, gint * min_h, gint * max_h)
566
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
567
568
  struct v4l2_format fmt;

Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
569
  GST_LOG_OBJECT (v4l2src, "getting size limits with format " GST_FOURCC_FORMAT,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
      GST_FOURCC_ARGS (format->pixelformat));

  /* get size delimiters */
  memset (&fmt, 0, sizeof (fmt));
  fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  fmt.fmt.pix.width = 0;
  fmt.fmt.pix.height = 0;
  fmt.fmt.pix.pixelformat = format->pixelformat;
  fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
  if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_TRY_FMT, &fmt) < 0) {
    return FALSE;
  }

  if (min_w)
    *min_w = fmt.fmt.pix.width;
  if (min_h)
    *min_h = fmt.fmt.pix.height;
  GST_LOG_OBJECT (v4l2src, "got min size %dx%d", fmt.fmt.pix.width,
      fmt.fmt.pix.height);

  fmt.fmt.pix.width = G_MAXINT;
  fmt.fmt.pix.height = 576;
  if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_TRY_FMT, &fmt) < 0) {
    return FALSE;
  }

  if (max_w)
    *max_w = fmt.fmt.pix.width;
  if (max_h)
    *max_h = fmt.fmt.pix.height;
  GST_LOG_OBJECT (v4l2src, "got max size %dx%d", fmt.fmt.pix.width,
      fmt.fmt.pix.height);

  return TRUE;
604
}
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786

gboolean
gst_v4l2src_get_fps (GstV4l2Src * v4l2src, gint * fps_n, gint * fps_d)
{
  v4l2_std_id std;
  const GList *item;

  if (!GST_V4L2_IS_OPEN (GST_V4L2ELEMENT (v4l2src)))
    return FALSE;

  if (!gst_v4l2_get_norm (GST_V4L2ELEMENT (v4l2src), &std))
    return FALSE;
  for (item = GST_V4L2ELEMENT (v4l2src)->stds; item != NULL; item = item->next) {
    GstV4l2TunerNorm *v4l2norm = item->data;

    if (v4l2norm->index == std) {
      *fps_n =
          gst_value_get_fraction_numerator (&GST_TUNER_NORM (v4l2norm)->
          framerate);
      *fps_d =
          gst_value_get_fraction_denominator (&GST_TUNER_NORM (v4l2norm)->
          framerate);
      return TRUE;
    }
  }
  return FALSE;

}

#if 0

/* get a list of possible framerates
 * this is only done for webcams;
 * other devices return NULL here.
 * this function takes a LONG time to execute.
 */
GValue *
gst_v4l2src_get_fps_list (GstV4l2Src * v4l2src)
{
  gint fps_index;
  struct video_window *vwin = &GST_V4L2ELEMENT (v4l2src)->vwin;
  GstV4l2Element *v4l2element = GST_V4L2ELEMENT (v4l2src);

  /* check if we have vwin window properties giving a framerate,
   * as is done for webcams
   * See http://www.smcc.demon.nl/webcam/api.html
   * which is used for the Philips and qce-ga drivers */
  fps_index = (vwin->flags >> 16) & 0x3F;       /* 6 bit index for framerate */

  /* webcams have a non-zero fps_index */
  if (fps_index == 0) {
    GST_DEBUG_OBJECT (v4l2src, "fps_index is 0, no webcam");
    return NULL;
  }
  GST_DEBUG_OBJECT (v4l2src, "fps_index is %d, so webcam", fps_index);

  {
    int i;
    GValue *list = NULL;
    GValue value = { 0 };

    /* webcam detected, so try all framerates and return a list */

    list = g_new0 (GValue, 1);
    g_value_init (list, GST_TYPE_LIST);

    /* index of 16 corresponds to 15 fps */
    GST_DEBUG_OBJECT (v4l2src, "device reports fps of %d/%d (%.4f)",
        fps_index * 15, 16, fps_index * 15.0 / 16);
    for (i = 0; i < 63; ++i) {
      /* set bits 16 to 21 to 0 */
      vwin->flags &= (0x3F00 - 1);
      /* set bits 16 to 21 to the index */
      vwin->flags |= i << 16;
      if (gst_v4l2_set_window_properties (v4l2element)) {
        /* setting it succeeded.  FIXME: get it and check. */
        g_value_init (&value, GST_TYPE_FRACTION);
        gst_value_set_fraction (&value, i * 15, 16);
        gst_value_list_append_value (list, &value);
        g_value_unset (&value);
      }
    }
    /* FIXME: set back the original fps_index */
    vwin->flags &= (0x3F00 - 1);
    vwin->flags |= fps_index << 16;
    gst_v4l2_set_window_properties (v4l2element);
    return list;
  }
  return NULL;
}

#endif

#define GST_TYPE_V4L2SRC_BUFFER (gst_v4l2src_buffer_get_type())
#define GST_IS_V4L2SRC_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_V4L2SRC_BUFFER))
#define GST_V4L2SRC_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_V4L2SRC_BUFFER, GstV4l2SrcBuffer))

typedef struct _GstV4l2SrcBuffer
{
  GstBuffer buffer;

  GstV4l2Buffer *buf;
} GstV4l2SrcBuffer;

static void gst_v4l2src_buffer_class_init (gpointer g_class,
    gpointer class_data);
static void gst_v4l2src_buffer_init (GTypeInstance * instance,
    gpointer g_class);
static void gst_v4l2src_buffer_finalize (GstV4l2SrcBuffer * v4l2src_buffer);

GType
gst_v4l2src_buffer_get_type (void)
{
  static GType _gst_v4l2src_buffer_type;

  if (G_UNLIKELY (_gst_v4l2src_buffer_type == 0)) {
    static const GTypeInfo v4l2src_buffer_info = {
      sizeof (GstBufferClass),
      NULL,
      NULL,
      gst_v4l2src_buffer_class_init,
      NULL,
      NULL,
      sizeof (GstV4l2SrcBuffer),
      0,
      gst_v4l2src_buffer_init,
      NULL
    };
    _gst_v4l2src_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
        "GstV4l2SrcBuffer", &v4l2src_buffer_info, 0);
  }
  return _gst_v4l2src_buffer_type;
}

static void
gst_v4l2src_buffer_class_init (gpointer g_class, gpointer class_data)
{
  GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);

  mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
      gst_v4l2src_buffer_finalize;
}

static void
gst_v4l2src_buffer_init (GTypeInstance * instance, gpointer g_class)
{

}

static void
gst_v4l2src_buffer_finalize (GstV4l2SrcBuffer * v4l2src_buffer)
{
  GstV4l2Buffer *buf = v4l2src_buffer->buf;

  if (buf) {

    GST_LOG ("freeing buffer %p (nr. %d)", buf, buf->buffer.index);

    if (!g_atomic_int_dec_and_test (&buf->refcount)) {
      /* we're still in use, add to queue again 
         note: this might fail because the device is already stopped (race) */
      if (ioctl (buf->pool->video_fd, VIDIOC_QBUF, &buf->buffer) < 0)
        GST_INFO ("readding to queue failed, assuming video device is stopped");
    }
    if (g_atomic_int_dec_and_test (&buf->pool->refcount)) {
      /* we're last thing that used all this */
      gst_v4l2src_buffer_pool_free (buf->pool, TRUE);
    }

  }
}

/* Create a V4l2Src buffer from our mmap'd data area */
GstBuffer *
gst_v4l2src_buffer_new (GstV4l2Src * v4l2src, guint size, guint8 * data,
    GstV4l2Buffer * srcbuf)
{
  GstBuffer *buf;
  gint fps_n, fps_d;

  GST_DEBUG_OBJECT (v4l2src, "creating buffer %d");

787
788
789
790
  if (!gst_v4l2src_get_fps (v4l2src, &fps_n, &fps_d)) {
    fps_n = 0;
    fps_d = 1;
  }
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
791
792

  if (data == NULL) {
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
793
    buf = gst_buffer_new_and_alloc (size);
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
794
  } else {
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
795
    buf = (GstBuffer *) gst_mini_object_new (GST_TYPE_V4L2SRC_BUFFER);
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
796
    GST_BUFFER_DATA (buf) = data;
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
797
    GST_V4L2SRC_BUFFER (buf)->buf = srcbuf;
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
798
  }
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
799
800


Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
801
802
803
804
805
806
807
  GST_BUFFER_SIZE (buf) = size;

  GST_BUFFER_TIMESTAMP (buf) =
      gst_clock_get_time (GST_ELEMENT (v4l2src)->clock);
  GST_BUFFER_TIMESTAMP (buf) -= GST_ELEMENT (v4l2src)->base_time;

  GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_READONLY);
808
809
810
811
812
813
  if (fps_n > 0) {
    GST_BUFFER_DURATION (buf) = gst_util_uint64_scale_int (GST_SECOND,
        fps_n, fps_d);
  } else {
    GST_BUFFER_DURATION (buf) = GST_CLOCK_TIME_NONE;
  }
Edgard Gusmão Lima's avatar
Edgard Gusmão Lima committed
814
815
816
817
818
819

  /* the negotiate() method already set caps on the source pad */
  gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (v4l2src)));

  return buf;
}