v4l2src_calls.c 12.4 KB
Newer Older
1
2
/* GStreamer
 *
3
 * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
4
5
6
 *               2006 Edgard Lima <edgard.lima@indt.org.br>
 *
 * v4l2src.c - system calls
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 * 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.
 */

24
25
26
27
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

28
29
30
31
32
33
34
35
36
37
#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>
38
#include <unistd.h>
39
40
41
42
43
#ifdef __sun
/* Needed on older Solaris Nevada builds (72 at least) */
#include <stropts.h>
#include <sys/ioccom.h>
#endif
44

Edgard Lima's avatar
Edgard Lima committed
45
#include "gstv4l2tuner.h"
Rob Clark's avatar
Rob Clark committed
46
47
48
#include "gstv4l2bufferpool.h"

#include "gst/gst-i18n-plugin.h"
Edgard Lima's avatar
Edgard Lima committed
49

50
#define GST_CAT_DEFAULT v4l2src_debug
51
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
52
53
54
55

/* lalala... */
#define GST_V4L2_SET_ACTIVE(element) (element)->buffer = GINT_TO_POINTER (-1)
#define GST_V4L2_SET_INACTIVE(element) (element)->buffer = NULL
56
57
58

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

62

63
/* Local functions */
64
65

static gboolean
Rob Clark's avatar
Rob Clark committed
66
67
gst_v4l2src_buffer_pool_activate (GstV4l2BufferPool * pool,
    GstV4l2Src * v4l2src)
68
{
Rob Clark's avatar
Rob Clark committed
69
  GstV4l2Buffer *buf;
70

71
  while ((buf = gst_v4l2_buffer_pool_get (pool)) != NULL)
Rob Clark's avatar
Rob Clark committed
72
    if (!gst_v4l2_buffer_pool_qbuf (pool, buf))
73
74
75
76
      goto queue_failed;

  return TRUE;

77
  /* ERRORS */
78
79
80
81
82
83
queue_failed:
  {
    GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ,
        (_("Could not enqueue buffers in device '%s'."),
            v4l2src->v4l2object->videodev),
        ("enqueing buffer %d/%d failed: %s",
Rob Clark's avatar
Rob Clark committed
84
            buf->vbuffer.index, v4l2src->num_buffers, g_strerror (errno)));
85
86
87
88
    return FALSE;
  }
}

89
/******************************************************
90
91
 * gst_v4l2src_grab_frame ():
 *   grab a frame for capturing
92
 * return value: GST_FLOW_OK, GST_FLOW_WRONG_STATE or GST_FLOW_ERROR
93
 ******************************************************/
94
95
GstFlowReturn
gst_v4l2src_grab_frame (GstV4l2Src * v4l2src, GstBuffer ** buf)
96
{
97
#define NUM_TRIALS 50
Rob Clark's avatar
Rob Clark committed
98
99
  GstV4l2Object *v4l2object;
  GstV4l2BufferPool *pool;
100
  gint32 trials = NUM_TRIALS;
101
102
  GstBuffer *pool_buffer;
  gboolean need_copy;
103
  gint ret;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
104

Rob Clark's avatar
Rob Clark committed
105
106
  v4l2object = v4l2src->v4l2object;
  pool = v4l2src->pool;
Stefan Kost's avatar
Stefan Kost committed
107
108
  if (!pool)
    goto no_buffer_pool;
Rob Clark's avatar
Rob Clark committed
109
110

  GST_DEBUG_OBJECT (v4l2src, "grab frame");
111

112
  for (;;) {
Rob Clark's avatar
Rob Clark committed
113
114
    if (v4l2object->can_poll_device) {
      ret = gst_poll_wait (v4l2object->poll, GST_CLOCK_TIME_NONE);
115
116
117
118
119
120
      if (G_UNLIKELY (ret < 0)) {
        if (errno == EBUSY)
          goto stopped;
        if (errno == ENXIO) {
          GST_DEBUG_OBJECT (v4l2src,
              "v4l2 device doesn't support polling. Disabling");
Rob Clark's avatar
Rob Clark committed
121
          v4l2object->can_poll_device = FALSE;
122
123
124
125
126
        } else {
          if (errno != EAGAIN && errno != EINTR)
            goto select_error;
        }
      }
127
128
    }

Rob Clark's avatar
Rob Clark committed
129
130
    pool_buffer = GST_BUFFER (gst_v4l2_buffer_pool_dqbuf (pool));
    if (pool_buffer)
131
      break;
132

Rob Clark's avatar
Rob Clark committed
133
    GST_WARNING_OBJECT (pool->v4l2elem, "trials=%d", trials);
134

135
    /* if the sync() got interrupted, we can retry */
Edgard Lima's avatar
Edgard Lima committed
136
137
138
    switch (errno) {
      case EINVAL:
      case ENOMEM:
Rob Clark's avatar
Rob Clark committed
139
140
141
142
        /* fatal */
        return GST_FLOW_ERROR;

      case EAGAIN:
Edgard Lima's avatar
Edgard Lima committed
143
144
145
      case EIO:
      case EINTR:
      default:
Rob Clark's avatar
Rob Clark committed
146
        /* try again, until too many trials */
Edgard Lima's avatar
Edgard Lima committed
147
        break;
148
    }
Edgard Lima's avatar
Edgard Lima committed
149

150
    /* check nr. of attempts to capture */
151
    if (--trials == -1) {
152
      goto too_many_trials;
153
    }
154
155
  }

156
157
  /* if we are handing out the last buffer in the pool, we need to make a
   * copy and bring the buffer back in the pool. */
158
  need_copy = v4l2src->always_copy
Rob Clark's avatar
Rob Clark committed
159
      || !gst_v4l2_buffer_pool_available_buffers (pool);
160

161
  if (G_UNLIKELY (need_copy)) {
162
163
164
165
    if (!v4l2src->always_copy) {
      GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, v4l2src,
          "running out of buffers, making a copy to reuse current one");
    }
166
    *buf = gst_buffer_copy (pool_buffer);
167
    GST_BUFFER_FLAG_UNSET (*buf, GST_BUFFER_FLAG_READONLY);
168
169
170
171
    /* this will requeue */
    gst_buffer_unref (pool_buffer);
  } else {
    *buf = pool_buffer;
172
  }
173
  /* we set the buffer metadata in gst_v4l2src_create() */
174
175

  return GST_FLOW_OK;
176
177

  /* ERRORS */
Stefan Kost's avatar
Stefan Kost committed
178
179
180
181
182
no_buffer_pool:
  {
    GST_DEBUG ("no buffer pool");
    return GST_FLOW_WRONG_STATE;
  }
183
184
select_error:
  {
Rob Clark's avatar
Rob Clark committed
185
    GST_ELEMENT_ERROR (pool->v4l2elem, RESOURCE, READ, (NULL),
186
187
188
189
190
191
192
193
        ("select error %d: %s (%d)", ret, g_strerror (errno), errno));
    return GST_FLOW_ERROR;
  }
stopped:
  {
    GST_DEBUG ("stop called");
    return GST_FLOW_WRONG_STATE;
  }
194
195
too_many_trials:
  {
Rob Clark's avatar
Rob Clark committed
196
    GST_ELEMENT_ERROR (pool->v4l2elem, RESOURCE, FAILED,
197
        (_("Failed trying to get video frames from device '%s'."),
Rob Clark's avatar
Rob Clark committed
198
            v4l2object->videodev),
199
        (_("Failed after %d tries. device %s. system error: %s"),
Rob Clark's avatar
Rob Clark committed
200
            NUM_TRIALS, v4l2object->videodev, g_strerror (errno)));
201
    return GST_FLOW_ERROR;
202
  }
203
204
}

205
206
207
208
/* Note about fraction simplification
 * n1/d1 == n2/d2  is also written as  n1 == ( n2 * d1 ) / d2
 */
#define fractions_are_equal(n1,d1,n2,d2) ((n1) == gst_util_uint64_scale_int((n2), (d1), (d2)))
209
210

/******************************************************
211
212
 * gst_v4l2src_set_capture():
 *   set capture parameters
213
214
215
 * return value: TRUE on success, FALSE on error
 ******************************************************/
gboolean
216
217
gst_v4l2src_set_capture (GstV4l2Src * v4l2src, guint32 pixelformat,
    guint32 width, guint32 height, guint fps_n, guint fps_d)
218
{
219
220
221
  gint fd = v4l2src->v4l2object->video_fd;
  struct v4l2_streamparm stream;

222
223
224
  if (pixelformat == GST_MAKE_FOURCC ('M', 'P', 'E', 'G'))
    return TRUE;

Rob Clark's avatar
Rob Clark committed
225
226
227
228
  if (!gst_v4l2_object_set_format (v4l2src->v4l2object, pixelformat, width,
          height)) {
    /* error already reported */
    return FALSE;
229
  }
230

231
232
233
  /* Is there a reason we require the caller to always specify a framerate? */
  GST_LOG_OBJECT (v4l2src, "Desired framerate: %u/%u", fps_n, fps_d);

234
235
  memset (&stream, 0x00, sizeof (struct v4l2_streamparm));
  stream.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
236
  if (v4l2_ioctl (fd, VIDIOC_G_PARM, &stream) < 0) {
237
238
239
    GST_ELEMENT_WARNING (v4l2src, RESOURCE, SETTINGS,
        (_("Could not get parameters on device '%s'"),
            v4l2src->v4l2object->videodev), GST_ERROR_SYSTEM);
240
241
242
243
244
245
    goto done;
  }

  /* Note: V4L2 provides the frame interval, we have the frame rate */
  if (fractions_are_equal (stream.parm.capture.timeperframe.numerator,
          stream.parm.capture.timeperframe.denominator, fps_d, fps_n)) {
246
247
248
    GST_LOG_OBJECT (v4l2src, "Desired framerate already set");
    v4l2src->fps_n = fps_n;
    v4l2src->fps_d = fps_d;
249
250
    goto done;
  }
251

252
253
254
255
256
  /* We want to change the frame rate, so check whether we can. Some cheap USB
   * cameras don't have the capability */
  if ((stream.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) == 0) {
    GST_DEBUG_OBJECT (v4l2src, "Not setting framerate (not supported)");
    goto done;
257
  }
Edgard Lima's avatar
Edgard Lima committed
258

259
260
261
262
263
264
265
  GST_LOG_OBJECT (v4l2src, "Setting framerate to %u/%u", fps_n, fps_d);

  /* Note: V4L2 wants the frame interval, we have the frame rate */
  stream.parm.capture.timeperframe.numerator = fps_d;
  stream.parm.capture.timeperframe.denominator = fps_n;

  /* some cheap USB cam's won't accept any change */
266
  if (v4l2_ioctl (fd, VIDIOC_S_PARM, &stream) < 0) {
267
268
269
270
271
272
273
274
    GST_ELEMENT_WARNING (v4l2src, RESOURCE, SETTINGS,
        (_("Video input device did not accept new frame rate setting.")),
        GST_ERROR_SYSTEM);
    goto done;
  }

  v4l2src->fps_n = fps_n;
  v4l2src->fps_d = fps_d;
Stefan Kost's avatar
Stefan Kost committed
275
276
277
278
279
280
281
282
283
284
285
286

  /* if we have a framerate pre-calculate duration */
  if (fps_n > 0 && fps_d > 0) {
    v4l2src->duration = gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
  } else {
    v4l2src->duration = GST_CLOCK_TIME_NONE;
  }

  GST_INFO_OBJECT (v4l2src,
      "Set framerate to %u/%u and duration to %" GST_TIME_FORMAT, fps_n, fps_d,
      GST_TIME_ARGS (v4l2src->duration));

287
288
289

done:

290
  return TRUE;
291
292
293
294
295
296
297
298
}

/******************************************************
 * gst_v4l2src_capture_init():
 *   initialize the capture system
 * return value: TRUE on success, FALSE on error
 ******************************************************/
gboolean
299
gst_v4l2src_capture_init (GstV4l2Src * v4l2src, GstCaps * caps)
300
{
301
  GST_DEBUG_OBJECT (v4l2src, "initializing the capture system");
302

303
304
  GST_V4L2_CHECK_OPEN (v4l2src->v4l2object);
  GST_V4L2_CHECK_NOT_ACTIVE (v4l2src->v4l2object);
305

306
  if (v4l2src->v4l2object->vcap.capabilities & V4L2_CAP_STREAMING) {
307

Edgard Lima's avatar
Edgard Lima committed
308
309
310
    /* Map the buffers */
    GST_LOG_OBJECT (v4l2src, "initiating buffer pool");

Rob Clark's avatar
Rob Clark committed
311
312
313
    if (!(v4l2src->pool = gst_v4l2_buffer_pool_new (GST_ELEMENT (v4l2src),
                v4l2src->v4l2object->video_fd,
                v4l2src->num_buffers, caps, TRUE, V4L2_BUF_TYPE_VIDEO_CAPTURE)))
314
      goto buffer_pool_new_failed;
315
316
317

    GST_INFO_OBJECT (v4l2src, "capturing buffers via mmap()");
    v4l2src->use_mmap = TRUE;
Rob Clark's avatar
Rob Clark committed
318
319
320
321
322
323

    if (v4l2src->num_buffers != v4l2src->pool->buffer_count) {
      v4l2src->num_buffers = v4l2src->pool->buffer_count;
      g_object_notify (G_OBJECT (v4l2src), "queue-size");
    }

324
325
326
  } else if (v4l2src->v4l2object->vcap.capabilities & V4L2_CAP_READWRITE) {
    GST_INFO_OBJECT (v4l2src, "capturing buffers via read()");
    v4l2src->use_mmap = FALSE;
Edgard Lima's avatar
Edgard Lima committed
327
    v4l2src->pool = NULL;
328
329
  } else {
    goto no_supported_capture_method;
330
331
  }

332
  GST_V4L2_SET_ACTIVE (v4l2src->v4l2object);
333

334
  return TRUE;
335
336

  /* ERRORS */
337
buffer_pool_new_failed:
338
339
  {
    GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ,
340
        (_("Could not map buffers from device '%s'"),
341
            v4l2src->v4l2object->videodev),
342
        ("Failed to create buffer pool: %s", g_strerror (errno)));
343
344
    return FALSE;
  }
345
346
347
348
349
350
351
no_supported_capture_method:
  {
    GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ,
        (_("The driver of device '%s' does not support any known capture "
                "method."), v4l2src->v4l2object->videodev), (NULL));
    return FALSE;
  }
352
353
354
355
356
357
358
359
360
}


/******************************************************
 * 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
361
gst_v4l2src_capture_start (GstV4l2Src * v4l2src)
362
{
363
  GST_DEBUG_OBJECT (v4l2src, "starting the capturing");
364
  //GST_V4L2_CHECK_OPEN (v4l2src->v4l2object);
365
  GST_V4L2_CHECK_ACTIVE (v4l2src->v4l2object);
366

367
  v4l2src->quit = FALSE;
368

369
  if (v4l2src->use_mmap) {
Rob Clark's avatar
Rob Clark committed
370
371
372
    if (!gst_v4l2src_buffer_pool_activate (v4l2src->pool, v4l2src)) {
      return FALSE;
    }
373

Rob Clark's avatar
Rob Clark committed
374
375
376
    if (!gst_v4l2_object_start_streaming (v4l2src->v4l2object)) {
      return FALSE;
    }
377
  }
378

379
380
  v4l2src->is_capturing = TRUE;

381
  return TRUE;
382
}
383
384
385
386
387
388
389

/******************************************************
 * 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
390
gst_v4l2src_capture_stop (GstV4l2Src * v4l2src)
391
{
392
  GST_DEBUG_OBJECT (v4l2src, "stopping capturing");
393

Edgard Lima's avatar
Edgard Lima committed
394
395
396
397
398
399
  if (!GST_V4L2_IS_OPEN (v4l2src->v4l2object)) {
    goto done;
  }
  if (!GST_V4L2_IS_ACTIVE (v4l2src->v4l2object)) {
    goto done;
  }
400

401
  if (v4l2src->use_mmap) {
Edgard Lima's avatar
Edgard Lima committed
402
403
    /* we actually need to sync on all queued buffers but not
     * on the non-queued ones */
Rob Clark's avatar
Rob Clark committed
404
405
406
    if (!gst_v4l2_object_stop_streaming (v4l2src->v4l2object)) {
      return FALSE;
    }
407
408
  }

Edgard Lima's avatar
Edgard Lima committed
409
410
done:

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

415
416
  return TRUE;
}
417

418
419
420
421
422
423
/******************************************************
 * 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
424
gst_v4l2src_capture_deinit (GstV4l2Src * v4l2src)
425
{
426
  GST_DEBUG_OBJECT (v4l2src, "deinitting capture system");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
427

Edgard Lima's avatar
Edgard Lima committed
428
429
430
431
432
433
  if (!GST_V4L2_IS_OPEN (v4l2src->v4l2object)) {
    return TRUE;
  }
  if (!GST_V4L2_IS_ACTIVE (v4l2src->v4l2object)) {
    return TRUE;
  }
434

Edgard Lima's avatar
Edgard Lima committed
435
  if (v4l2src->pool) {
436
    gst_v4l2_buffer_pool_destroy (v4l2src->pool);
Edgard Lima's avatar
Edgard Lima committed
437
    v4l2src->pool = NULL;
438
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
439

440
  GST_V4L2_SET_INACTIVE (v4l2src->v4l2object);
441

442
  return TRUE;
443
}