blob: 4ae77116387fe24ff5b9cfb996611ff684a9d642 [file] [log] [blame]
Benny Prijonoc45d9512010-12-10 11:04:30 +00001/* $Id$ */
2/*
3 * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjmedia-videodev/videodev_imp.h>
20#include <pjmedia/errno.h>
21#include <pj/assert.h>
22#include <pj/errno.h>
23#include <pj/file_access.h>
24#include <pj/log.h>
25#include <pj/os.h>
26#include <pj/rand.h>
27
28#if PJMEDIA_VIDEO_DEV_HAS_V4L2
29
30#include <linux/videodev2.h>
31#include <libv4l2.h>
32#include <fcntl.h>
33#include <errno.h>
34#include <sys/mman.h>
35
36#define THIS_FILE "v4l2_dev.c"
37#define DRIVER_NAME "v4l2"
38#define V4L2_MAX_DEVS 4
39#define DEFAULT_WIDTH 640
40#define DEFAULT_HEIGHT 480
41#define DEFAULT_FPS 25
42#define DEFAULT_CLOCK_RATE 90000
43#define INVALID_FD -1
44#define BUFFER_CNT 2
45#define MAX_IOCTL_RETRY 20
46
47
48/* mapping between pjmedia_fmt_id and v4l2 pixel format */
49typedef struct vid4lin_fmt_map
50{
51 pj_uint32_t pjmedia_fmt_id;
52 pj_uint32_t v4l2_fmt_id;
53} vid4lin_fmt_map;
54
55/* I/O type being used */
56enum vid4lin_io_type
57{
58 IO_TYPE_NONE,
59 IO_TYPE_READ,
60 IO_TYPE_MMAP,
61 IO_TYPE_MMAP_USER
62};
63
64/* descriptor for each mmap-ed buffer */
65typedef struct vid4lin_buffer
66{
67 void *start;
68 size_t length;
69} vid4lin_buffer;
70
71/* v4l2 device info */
72typedef struct vid4lin_dev_info
73{
74 pjmedia_vid_dev_info info;
75 char dev_name[32];
76 struct v4l2_capability v4l2_cap;
77} vid4lin_dev_info;
78
79/* v4l2 factory */
80typedef struct vid4lin_factory
81{
82 pjmedia_vid_dev_factory base;
83 pj_pool_t *pool;
84 pj_pool_factory *pf;
85
86 unsigned dev_count;
87 vid4lin_dev_info *dev_info;
88} vid4lin_factory;
89
90/* Video stream. */
91typedef struct vid4lin_stream
92{
Benny Prijono6c7e95e2011-03-15 11:22:04 +000093 pjmedia_vid_dev_stream base; /**< Base stream */
Benny Prijonoc45d9512010-12-10 11:04:30 +000094 pjmedia_vid_param param; /**< Settings */
95 pj_pool_t *pool; /**< Memory pool. */
96
97 int fd; /**< Video fd. */
98 char name[64]; /**< Name for log */
99 enum vid4lin_io_type io_type; /**< I/O method. */
100 unsigned buf_cnt; /**< MMap buf cnt. */
101 vid4lin_buffer *buffers; /**< MMap buffers. */
102 pj_time_val start_time; /**< Time when started */
103
104 pjmedia_vid_cb vid_cb; /**< Stream callback */
105 void *user_data; /**< Application data */
106} vid4lin_stream;
107
108/* Use this to convert between pjmedia_format_id and V4L2 fourcc */
109static vid4lin_fmt_map v4l2_fmt_maps[] =
110{
111 { PJMEDIA_FORMAT_RGB24, V4L2_PIX_FMT_BGR24 },
112 { PJMEDIA_FORMAT_RGBA, V4L2_PIX_FMT_BGR32 },
113 { PJMEDIA_FORMAT_RGB32, V4L2_PIX_FMT_BGR32 },
114 { PJMEDIA_FORMAT_AYUV, V4L2_PIX_FMT_YUV32 },
115 { PJMEDIA_FORMAT_YUY2, V4L2_PIX_FMT_YUYV },
116 { PJMEDIA_FORMAT_UYVY, V4L2_PIX_FMT_UYVY }
117};
118
119/* Prototypes */
120static pj_status_t vid4lin_factory_init(pjmedia_vid_dev_factory *f);
121static pj_status_t vid4lin_factory_destroy(pjmedia_vid_dev_factory *f);
122static unsigned vid4lin_factory_get_dev_count(pjmedia_vid_dev_factory *f);
123static pj_status_t vid4lin_factory_get_dev_info(pjmedia_vid_dev_factory *f,
124 unsigned index,
125 pjmedia_vid_dev_info *info);
126static pj_status_t vid4lin_factory_default_param(pj_pool_t *pool,
127 pjmedia_vid_dev_factory *f,
128 unsigned index,
129 pjmedia_vid_param *param);
130static pj_status_t vid4lin_factory_create_stream(pjmedia_vid_dev_factory *f,
Benny Prijonoe9f70d82011-03-25 08:38:26 +0000131 pjmedia_vid_param *prm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000132 const pjmedia_vid_cb *cb,
133 void *user_data,
Benny Prijono6c7e95e2011-03-15 11:22:04 +0000134 pjmedia_vid_dev_stream **p);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000135
Benny Prijono6c7e95e2011-03-15 11:22:04 +0000136static pj_status_t vid4lin_stream_get_param(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000137 pjmedia_vid_param *param);
Benny Prijono6c7e95e2011-03-15 11:22:04 +0000138static pj_status_t vid4lin_stream_get_cap(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000139 pjmedia_vid_dev_cap cap,
140 void *value);
Benny Prijono6c7e95e2011-03-15 11:22:04 +0000141static pj_status_t vid4lin_stream_set_cap(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000142 pjmedia_vid_dev_cap cap,
143 const void *value);
Benny Prijono6c7e95e2011-03-15 11:22:04 +0000144static pj_status_t vid4lin_stream_get_frame(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000145 pjmedia_frame *frame);
Benny Prijono6c7e95e2011-03-15 11:22:04 +0000146static pj_status_t vid4lin_stream_start(pjmedia_vid_dev_stream *strm);
147static pj_status_t vid4lin_stream_stop(pjmedia_vid_dev_stream *strm);
148static pj_status_t vid4lin_stream_destroy(pjmedia_vid_dev_stream *strm);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000149
150/* Operations */
151static pjmedia_vid_dev_factory_op factory_op =
152{
153 &vid4lin_factory_init,
154 &vid4lin_factory_destroy,
155 &vid4lin_factory_get_dev_count,
156 &vid4lin_factory_get_dev_info,
157 &vid4lin_factory_default_param,
158 &vid4lin_factory_create_stream
159};
160
Benny Prijono6c7e95e2011-03-15 11:22:04 +0000161static pjmedia_vid_dev_stream_op stream_op =
Benny Prijonoc45d9512010-12-10 11:04:30 +0000162{
163 &vid4lin_stream_get_param,
164 &vid4lin_stream_get_cap,
165 &vid4lin_stream_set_cap,
166 &vid4lin_stream_start,
167 &vid4lin_stream_get_frame,
168 NULL,
169 &vid4lin_stream_stop,
170 &vid4lin_stream_destroy
171};
172
173
174/****************************************************************************
175 * Factory operations
176 */
177/*
178 * Factory creation function.
179 */
180pjmedia_vid_dev_factory* pjmedia_v4l2_factory(pj_pool_factory *pf)
181{
182 vid4lin_factory *f;
183 pj_pool_t *pool;
184
185 pool = pj_pool_create(pf, DRIVER_NAME, 512, 512, NULL);
186 f = PJ_POOL_ZALLOC_T(pool, vid4lin_factory);
187 f->pf = pf;
188 f->pool = pool;
189 f->base.op = &factory_op;
190
191 return &f->base;
192}
193
194/* util: ioctl that tries harder. */
195static pj_status_t xioctl(int fh, int request, void *arg)
196{
197 enum { RETRY = MAX_IOCTL_RETRY };
198 int r, c=0;
199
200 do {
201 r = v4l2_ioctl(fh, request, arg);
202 } while (r==-1 && c++<RETRY && ((errno==EINTR) || (errno==EAGAIN)));
203
204 return (r == -1) ? pj_get_os_error() : PJ_SUCCESS;
205}
206
207/* Scan V4L2 devices */
208static pj_status_t v4l2_scan_devs(vid4lin_factory *f)
209{
210 vid4lin_dev_info vdi[V4L2_MAX_DEVS];
211 char dev_name[32];
212 unsigned i, old_count;
213 pj_status_t status;
214
215 pj_bzero(vdi, sizeof(vdi));
216 old_count = f->dev_count;
217 f->dev_count = 0;
218
219 for (i=0; i<V4L2_MAX_DEVS && f->dev_count < V4L2_MAX_DEVS; ++i) {
220 int fd;
221 vid4lin_dev_info *pdi;
222 pj_pool_t *pool = f->pool;
223 pj_uint32_t fmt_cap[8];
224 int j, fmt_cnt=0;
225
226 pdi = &vdi[f->dev_count];
227
228 snprintf(dev_name, sizeof(dev_name), "/dev/video%d", i);
229 if (!pj_file_exists(dev_name))
230 continue;
231
232 fd = v4l2_open(dev_name, O_RDWR, 0);
233 if (fd == -1)
234 continue;
235
236 status = xioctl(fd, VIDIOC_QUERYCAP, &pdi->v4l2_cap);
237 if (status != PJ_SUCCESS) {
238 PJ_PERROR(4,(THIS_FILE, status, "Error querying %s", dev_name));
239 v4l2_close(fd);
240 continue;
241 }
242
243 if ((pdi->v4l2_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {
244 v4l2_close(fd);
245 continue;
246 }
247
248 PJ_LOG(5,(THIS_FILE, "Found capture device %s", pdi->v4l2_cap.card));
249 PJ_LOG(5,(THIS_FILE, " Enumerating formats:"));
250 for (j=0; fmt_cnt<PJ_ARRAY_SIZE(fmt_cap); ++j) {
251 struct v4l2_fmtdesc fdesc;
252 unsigned k;
253
254 pj_bzero(&fdesc, sizeof(fdesc));
255 fdesc.index = j;
256 fdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
257
258 status = xioctl(fd, VIDIOC_ENUM_FMT, &fdesc);
259 if (status != PJ_SUCCESS)
260 break;
261
262 for (k=0; k<PJ_ARRAY_SIZE(v4l2_fmt_maps); ++k) {
263 if (v4l2_fmt_maps[k].v4l2_fmt_id == fdesc.pixelformat) {
264 fmt_cap[fmt_cnt++] = v4l2_fmt_maps[k].pjmedia_fmt_id;
265 PJ_LOG(5,(THIS_FILE, " Supported: %s",
266 fdesc.description));
267 break;
268 }
269 }
270 if (k==PJ_ARRAY_SIZE(v4l2_fmt_maps)) {
271 PJ_LOG(5,(THIS_FILE, " Unsupported: %s", fdesc.description));
272 }
273 }
274
275 v4l2_close(fd);
276
277 if (fmt_cnt==0) {
278 PJ_LOG(5,(THIS_FILE, " Found no common format"));
279 continue;
280 }
281
282 strncpy(pdi->dev_name, dev_name, sizeof(pdi->dev_name));
283 pdi->dev_name[sizeof(pdi->dev_name)-1] = '\0';
284 strncpy(pdi->info.name, (char*)pdi->v4l2_cap.card,
285 sizeof(pdi->info.name));
286 pdi->info.name[sizeof(pdi->info.name)-1] = '\0';
287 strncpy(pdi->info.driver, DRIVER_NAME, sizeof(pdi->info.driver));
288 pdi->info.driver[sizeof(pdi->info.driver)-1] = '\0';
289 pdi->info.dir = PJMEDIA_DIR_CAPTURE;
290 pdi->info.has_callback = PJ_FALSE;
291 pdi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
292
293 pdi->info.fmt_cnt = fmt_cnt;
294 pdi->info.fmt = (pjmedia_format*)
295 pj_pool_calloc(pool, sizeof(pjmedia_format), fmt_cnt);
296
297 for (j=0; j<fmt_cnt; ++j) {
298 pjmedia_format_init_video(&pdi->info.fmt[j],
299 fmt_cap[j],
300 DEFAULT_WIDTH,
301 DEFAULT_HEIGHT,
302 DEFAULT_FPS, 1);
303 }
304 if (j < fmt_cnt)
305 continue;
306
307 f->dev_count++;
308 }
309
310 if (f->dev_count == 0)
311 return PJ_SUCCESS;
312
313 if (f->dev_count > old_count || f->dev_info == NULL) {
314 f->dev_info = (vid4lin_dev_info*)
315 pj_pool_calloc(f->pool,
316 f->dev_count,
317 sizeof(vid4lin_dev_info));
318 }
319 pj_memcpy(f->dev_info, vdi, f->dev_count * sizeof(vid4lin_dev_info));
320
321 return PJ_SUCCESS;
322}
323
324
325/* API: init factory */
326static pj_status_t vid4lin_factory_init(pjmedia_vid_dev_factory *f)
327{
328 vid4lin_factory *cf = (vid4lin_factory*)f;
329 pj_status_t status;
330
331 status = v4l2_scan_devs(cf);
332 if (status != PJ_SUCCESS)
333 return status;
334
335 PJ_LOG(4, (THIS_FILE, "Video4Linux2 initialized with %d devices",
336 cf->dev_count));
337
338 return PJ_SUCCESS;
339}
340
341/* API: destroy factory */
342static pj_status_t vid4lin_factory_destroy(pjmedia_vid_dev_factory *f)
343{
344 vid4lin_factory *cf = (vid4lin_factory*)f;
345 pj_pool_t *pool = cf->pool;
346
347 if (cf->pool) {
348 cf->pool = NULL;
349 pj_pool_release(pool);
350 }
351
352 return PJ_SUCCESS;
353}
354
355/* API: get number of devices */
356static unsigned vid4lin_factory_get_dev_count(pjmedia_vid_dev_factory *f)
357{
358 vid4lin_factory *cf = (vid4lin_factory*)f;
359 return cf->dev_count;
360}
361
362/* API: get device info */
363static pj_status_t vid4lin_factory_get_dev_info(pjmedia_vid_dev_factory *f,
364 unsigned index,
365 pjmedia_vid_dev_info *info)
366{
367 vid4lin_factory *cf = (vid4lin_factory*)f;
368
369 PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV);
370
371 pj_memcpy(info, &cf->dev_info[index].info, sizeof(*info));
372
373 return PJ_SUCCESS;
374}
375
376/* API: create default device parameter */
377static pj_status_t vid4lin_factory_default_param(pj_pool_t *pool,
378 pjmedia_vid_dev_factory *f,
379 unsigned index,
380 pjmedia_vid_param *param)
381{
382 vid4lin_factory *cf = (vid4lin_factory*)f;
383
384 PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV);
385
386 pj_bzero(param, sizeof(*param));
387 param->dir = PJMEDIA_DIR_CAPTURE;
388 param->cap_id = index;
389 param->rend_id = PJMEDIA_VID_INVALID_DEV;
390 param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
391 param->clock_rate = DEFAULT_CLOCK_RATE;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000392 pjmedia_format_copy(&param->fmt, &cf->dev_info[index].info.fmt[0]);
393
394 return PJ_SUCCESS;
395}
396
397static vid4lin_fmt_map* get_v4l2_format_info(pjmedia_format_id id)
398{
399 unsigned i;
400
401 for (i = 0; i < PJ_ARRAY_SIZE(v4l2_fmt_maps); i++) {
402 if (v4l2_fmt_maps[i].pjmedia_fmt_id == id)
403 return &v4l2_fmt_maps[i];
404 }
405
406 return NULL;
407}
408
409/* util: setup format */
410static pj_status_t vid4lin_stream_init_fmt(vid4lin_stream *stream,
411 const pjmedia_vid_param *param,
412 pj_uint32_t pix_fmt)
413{
Benny Prijonoe9f70d82011-03-25 08:38:26 +0000414 pjmedia_video_format_detail *vfd;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000415 struct v4l2_format v4l2_fmt;
416 pj_status_t status;
417
418 vfd = pjmedia_format_get_video_format_detail(&param->fmt, PJ_TRUE);
419 if (vfd == NULL)
420 return PJMEDIA_EVID_BADFORMAT;
421
422 pj_bzero(&v4l2_fmt, sizeof(v4l2_fmt));
423 v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
424 v4l2_fmt.fmt.pix.width = vfd->size.w;
425 v4l2_fmt.fmt.pix.height = vfd->size.h;
426 v4l2_fmt.fmt.pix.pixelformat = pix_fmt;
427 v4l2_fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
428 status = xioctl(stream->fd, VIDIOC_S_FMT, &v4l2_fmt);
429 if (status != PJ_SUCCESS)
430 return status;
431
432 if (v4l2_fmt.fmt.pix.pixelformat != pix_fmt) {
433 status = PJMEDIA_EVID_BADFORMAT;
434 return status;
435 }
436
437 if ((v4l2_fmt.fmt.pix.width != vfd->size.w) ||
438 (v4l2_fmt.fmt.pix.height != vfd->size.h))
439 {
Benny Prijonoe9f70d82011-03-25 08:38:26 +0000440 /* Size has changed */
441 vfd->size.w = v4l2_fmt.fmt.pix.width;
442 vfd->size.h = v4l2_fmt.fmt.pix.height;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000443 }
444
445 return PJ_SUCCESS;
446}
447
448/* Util: initiate v4l2 streaming via mmap */
449static pj_status_t vid4lin_stream_init_streaming(vid4lin_stream *stream)
450{
451 struct v4l2_requestbuffers req;
452 unsigned i;
453 pj_status_t status;
454
455 pj_bzero(&req, sizeof(req));
456 req.count = BUFFER_CNT;
457 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
458 req.memory = V4L2_MEMORY_MMAP;
459 status = xioctl(stream->fd, VIDIOC_REQBUFS, &req);
460 if (status != PJ_SUCCESS)
461 return status;
462
463 stream->buffers = pj_pool_calloc(stream->pool, req.count,
464 sizeof(*stream->buffers));
465 stream->buf_cnt = 0;
466
467 for (i = 0; i < req.count; ++i) {
468 struct v4l2_buffer buf;
469
470 pj_bzero(&buf, sizeof(buf));
471
472 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
473 buf.memory = V4L2_MEMORY_MMAP;
474 buf.index = i;
475
476 status = xioctl(stream->fd, VIDIOC_QUERYBUF, &buf);
477 if (status != PJ_SUCCESS)
478 goto on_error;
479
480 stream->buffers[i].length = buf.length;
481 stream->buffers[i].start = v4l2_mmap(NULL, buf.length,
482 PROT_READ | PROT_WRITE,
483 MAP_SHARED, stream->fd,
484 buf.m.offset);
485
486 if (MAP_FAILED == stream->buffers[i].start) {
487 status = pj_get_os_error();
488 goto on_error;
489 }
490
491 stream->buf_cnt++;
492 }
493
494 PJ_LOG(5,(THIS_FILE, " mmap streaming initialized"));
495
496 stream->io_type = IO_TYPE_MMAP;
497 return PJ_SUCCESS;
498
499on_error:
500 return status;
501}
502
503/* init streaming with user pointer */
504static pj_status_t vid4lin_stream_init_streaming_user(vid4lin_stream *stream)
505{
506 return PJ_ENOTSUP;
507}
508
509/* init streaming with read() */
510static pj_status_t vid4lin_stream_init_read_write(vid4lin_stream *stream)
511{
512 return PJ_ENOTSUP;
513}
514
515/* API: create stream */
516static pj_status_t vid4lin_factory_create_stream(pjmedia_vid_dev_factory *f,
Benny Prijonoe9f70d82011-03-25 08:38:26 +0000517 pjmedia_vid_param *param,
Benny Prijono6c7e95e2011-03-15 11:22:04 +0000518 const pjmedia_vid_cb *cb,
519 void *user_data,
520 pjmedia_vid_dev_stream **p_vid_strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000521{
522 vid4lin_factory *cf = (vid4lin_factory*)f;
523 pj_pool_t *pool;
524 vid4lin_stream *stream;
525 vid4lin_dev_info *vdi;
526 const vid4lin_fmt_map *fmt_map;
527 const pjmedia_video_format_info *fmt_info;
Benny Prijonoe9f70d82011-03-25 08:38:26 +0000528 pjmedia_video_format_detail *vfd;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000529 pj_status_t status = PJ_SUCCESS;
530
Benny Prijonoe9f70d82011-03-25 08:38:26 +0000531
Benny Prijonoc45d9512010-12-10 11:04:30 +0000532 PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL);
533 PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO &&
534 param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO,
535 PJ_EINVAL);
536 PJ_ASSERT_RETURN(param->cap_id >= 0 && param->cap_id < cf->dev_count,
537 PJMEDIA_EVID_INVDEV);
538
539 fmt_info = pjmedia_get_video_format_info(NULL, param->fmt.id);
540 if (!fmt_info || (fmt_map=get_v4l2_format_info(param->fmt.id))==NULL)
541 return PJMEDIA_EVID_BADFORMAT;
542
543 vdi = &cf->dev_info[param->cap_id];
Benny Prijonoe9f70d82011-03-25 08:38:26 +0000544 vfd = pjmedia_format_get_video_format_detail(&param->fmt, PJ_TRUE);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000545
546 /* Create and Initialize stream descriptor */
547 pool = pj_pool_create(cf->pf, vdi->info.name, 512, 512, NULL);
548 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
549
550 stream = PJ_POOL_ZALLOC_T(pool, vid4lin_stream);
551 pj_memcpy(&stream->param, param, sizeof(*param));
552 stream->pool = pool;
553 pj_memcpy(&stream->vid_cb, cb, sizeof(*cb));
554 strncpy(stream->name, vdi->info.name, sizeof(stream->name));
555 stream->name[sizeof(stream->name)-1] = '\0';
556 stream->user_data = user_data;
557 stream->fd = INVALID_FD;
558
Benny Prijonoc45d9512010-12-10 11:04:30 +0000559 stream->fd = v4l2_open(vdi->dev_name, O_RDWR | O_NONBLOCK, 0);
560 if (stream->fd < 0)
561 goto on_error;
562
563 status = vid4lin_stream_init_fmt(stream, param, fmt_map->v4l2_fmt_id);
564 if (status != PJ_SUCCESS)
565 goto on_error;
566
567 if (vdi->v4l2_cap.capabilities & V4L2_CAP_STREAMING)
568 status = vid4lin_stream_init_streaming(stream);
569
570 if (status!=PJ_SUCCESS && vdi->v4l2_cap.capabilities & V4L2_CAP_STREAMING)
571 status = vid4lin_stream_init_streaming_user(stream);
572
573 if (status!=PJ_SUCCESS && vdi->v4l2_cap.capabilities & V4L2_CAP_READWRITE)
574 status = vid4lin_stream_init_read_write(stream);
575
576 if (status != PJ_SUCCESS) {
577 PJ_LOG(1,(THIS_FILE, "Error: unable to initiate I/O on %s",
578 stream->name));
579 goto on_error;
580 }
581
582 /* Done */
583 stream->base.op = &stream_op;
584 *p_vid_strm = &stream->base;
585
586 return PJ_SUCCESS;
587
588on_error:
589 if (status == PJ_SUCCESS)
590 status = PJ_RETURN_OS_ERROR(errno);
591
592 vid4lin_stream_destroy(&stream->base);
593 return status;
594}
595
596/* API: Get stream info. */
Benny Prijono6c7e95e2011-03-15 11:22:04 +0000597static pj_status_t vid4lin_stream_get_param(pjmedia_vid_dev_stream *s,
598 pjmedia_vid_param *pi)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000599{
600 vid4lin_stream *strm = (vid4lin_stream*)s;
601
602 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
603
604 pj_memcpy(pi, &strm->param, sizeof(*pi));
605
606 return PJ_SUCCESS;
607}
608
609/* API: get capability */
Benny Prijono6c7e95e2011-03-15 11:22:04 +0000610static pj_status_t vid4lin_stream_get_cap(pjmedia_vid_dev_stream *s,
611 pjmedia_vid_dev_cap cap,
612 void *pval)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000613{
614 vid4lin_stream *strm = (vid4lin_stream*)s;
615
616 PJ_UNUSED_ARG(strm);
617
618 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
619
620 if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
621 {
622 return PJMEDIA_EVID_INVCAP;
623// return PJ_SUCCESS;
624 } else {
625 return PJMEDIA_EVID_INVCAP;
626 }
627}
628
629/* API: set capability */
Benny Prijono6c7e95e2011-03-15 11:22:04 +0000630static pj_status_t vid4lin_stream_set_cap(pjmedia_vid_dev_stream *s,
631 pjmedia_vid_dev_cap cap,
632 const void *pval)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000633{
634 vid4lin_stream *strm = (vid4lin_stream*)s;
635
636
637 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
638
639 /*
640 if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
641 {
642 return PJ_SUCCESS;
643 }
644 */
645 PJ_UNUSED_ARG(strm);
646 PJ_UNUSED_ARG(cap);
647 PJ_UNUSED_ARG(pval);
648
649 return PJMEDIA_EVID_INVCAP;
650}
651
652/* get frame from mmap */
653static pj_status_t vid4lin_stream_get_frame_mmap(vid4lin_stream *stream,
654 pjmedia_frame *frame)
655{
656 struct v4l2_buffer buf;
657 pj_time_val time;
658 pj_status_t status = PJ_SUCCESS;
659
660 pj_bzero(&buf, sizeof(buf));
661 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
662 buf.memory = V4L2_MEMORY_MMAP;
663 status = xioctl(stream->fd, VIDIOC_DQBUF, &buf);
664 if (status != PJ_SUCCESS)
665 return status;
666
667 if (frame->size < buf.bytesused) {
668 /* supplied buffer is too small */
669 pj_assert(!"frame buffer is too small for v4l2");
670 status = PJ_ETOOSMALL;
671 goto on_return;
672 }
673
674 time.sec = buf.timestamp.tv_sec;
675 time.msec = buf.timestamp.tv_usec / 1000;
676 PJ_TIME_VAL_SUB(time, stream->start_time);
677
678 frame->type = PJMEDIA_FRAME_TYPE_VIDEO;
679 frame->size = buf.bytesused;
Benny Prijono349037b2011-03-17 11:25:19 +0000680 frame->timestamp.u64 = PJ_UINT64(1) * PJ_TIME_VAL_MSEC(time) *
681 stream->param.clock_rate / PJ_UINT64(1000);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000682 pj_memcpy(frame->buf, stream->buffers[buf.index].start, buf.bytesused);
683
684on_return:
685 pj_bzero(&buf, sizeof(buf));
686 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
687 buf.memory = V4L2_MEMORY_MMAP;
688 xioctl(stream->fd, VIDIOC_QBUF, &buf);
689
690 return status;
691}
692
693/* API: Get frame from stream */
Benny Prijono6c7e95e2011-03-15 11:22:04 +0000694static pj_status_t vid4lin_stream_get_frame(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000695 pjmedia_frame *frame)
696{
697 vid4lin_stream *stream = (vid4lin_stream*)strm;
698
699 if (stream->io_type == IO_TYPE_MMAP)
700 return vid4lin_stream_get_frame_mmap(stream, frame);
701 else {
702 pj_assert(!"Unsupported i/o type");
703 return PJ_EINVALIDOP;
704 }
705}
706
707/* API: Start stream. */
Benny Prijono6c7e95e2011-03-15 11:22:04 +0000708static pj_status_t vid4lin_stream_start(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000709{
710 vid4lin_stream *stream = (vid4lin_stream*)strm;
711 struct v4l2_buffer buf;
712 enum v4l2_buf_type type;
713 unsigned i;
714 pj_status_t status;
715
716 PJ_ASSERT_RETURN(stream->fd != -1, PJ_EINVALIDOP);
717
718 PJ_LOG(4, (THIS_FILE, "Starting v4l2 video stream %s", stream->name));
719
720 pj_gettimeofday(&stream->start_time);
721
722 for (i = 0; i < stream->buf_cnt; ++i) {
723 pj_bzero(&buf, sizeof(buf));
724 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
725 buf.memory = V4L2_MEMORY_MMAP;
726 buf.index = i;
727 status = xioctl(stream->fd, VIDIOC_QBUF, &buf);
728 if (status != PJ_SUCCESS)
729 goto on_error;
730 }
731 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
732
733 status = xioctl(stream->fd, VIDIOC_STREAMON, &type);
734 if (status != PJ_SUCCESS)
735 goto on_error;
736
737 return PJ_SUCCESS;
738
739on_error:
740 if (i > 0) {
741 /* Dequeue already enqueued buffers. Can we do this while streaming
742 * is not started?
743 */
744 unsigned n = i;
745 for (i=0; i<n; ++i) {
746 pj_bzero(&buf, sizeof(buf));
747 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
748 buf.memory = V4L2_MEMORY_MMAP;
749 xioctl(stream->fd, VIDIOC_DQBUF, &buf);
750 }
751 }
752 return status;
753}
754
755/* API: Stop stream. */
Benny Prijono6c7e95e2011-03-15 11:22:04 +0000756static pj_status_t vid4lin_stream_stop(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000757{
758 vid4lin_stream *stream = (vid4lin_stream*)strm;
759 enum v4l2_buf_type type;
760 pj_status_t status;
761
762 if (stream->fd < 0)
763 return PJ_SUCCESS;
764
765 PJ_LOG(4, (THIS_FILE, "Stopping v4l2 video stream %s", stream->name));
766
767 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
768 status = xioctl(stream->fd, VIDIOC_STREAMOFF, &type);
769 if (status != PJ_SUCCESS)
770 return status;
771
772 return PJ_SUCCESS;
773}
774
775
776/* API: Destroy stream. */
Benny Prijono6c7e95e2011-03-15 11:22:04 +0000777static pj_status_t vid4lin_stream_destroy(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000778{
779 vid4lin_stream *stream = (vid4lin_stream*)strm;
780 unsigned i;
781
782 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
783
784 vid4lin_stream_stop(strm);
785
786 PJ_LOG(4, (THIS_FILE, "Destroying v4l2 video stream %s", stream->name));
787
788 for (i=0; i<stream->buf_cnt; ++i) {
789 if (stream->buffers[i].start != MAP_FAILED) {
790 v4l2_munmap(stream->buffers[i].start, stream->buffers[i].length);
791 stream->buffers[i].start = MAP_FAILED;
792 }
793 }
794
795 if (stream->fd >= 0) {
796 v4l2_close(stream->fd);
797 stream->fd = -1;
798 }
799 pj_pool_release(stream->pool);
800
801 return PJ_SUCCESS;
802}
803
804#endif /* PJMEDIA_VIDEO_DEV_HAS_V4L2 */