~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.0.1/pjmedia/src/pjmedia-videodev/v4l2_dev.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (1.1.11)
  • mto: This revision was merged to the branch mainline in revision 24.
  • Revision ID: package-import@ubuntu.com-20140128182336-3xenud1kbnwmf3mz
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: v4l2_dev.c 3901 2011-12-07 10:43:28Z nanang $ */
2
 
/*
3
 
 * Copyright (C) 2008-2011 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 */
49
 
typedef 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 */
56
 
enum 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 */
65
 
typedef struct vid4lin_buffer
66
 
{
67
 
    void   *start;
68
 
    size_t  length;
69
 
} vid4lin_buffer;
70
 
 
71
 
/* v4l2 device info */
72
 
typedef 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 */
80
 
typedef struct vid4lin_factory
81
 
{
82
 
    pjmedia_vid_dev_factory      base;
83
 
    pj_pool_t                   *pool;
84
 
    pj_pool_t                   *dev_pool;
85
 
    pj_pool_factory             *pf;
86
 
 
87
 
    unsigned                     dev_count;
88
 
    vid4lin_dev_info            *dev_info;
89
 
} vid4lin_factory;
90
 
 
91
 
/* Video stream. */
92
 
typedef struct vid4lin_stream
93
 
{
94
 
    pjmedia_vid_dev_stream       base;          /**< Base stream        */
95
 
    pjmedia_vid_dev_param        param;         /**< Settings           */
96
 
    pj_pool_t                   *pool;          /**< Memory pool.       */
97
 
 
98
 
    int                          fd;            /**< Video fd.          */
99
 
    char                         name[64];      /**< Name for log       */
100
 
    enum vid4lin_io_type         io_type;       /**< I/O method.        */
101
 
    unsigned                     buf_cnt;       /**< MMap buf cnt.      */
102
 
    vid4lin_buffer              *buffers;       /**< MMap buffers.      */
103
 
    pj_time_val                  start_time;    /**< Time when started  */
104
 
 
105
 
    pjmedia_vid_dev_cb           vid_cb;        /**< Stream callback    */
106
 
    void                        *user_data;     /**< Application data   */
107
 
} vid4lin_stream;
108
 
 
109
 
/* Use this to convert between pjmedia_format_id and V4L2 fourcc */
110
 
static vid4lin_fmt_map v4l2_fmt_maps[] =
111
 
{
112
 
    { PJMEDIA_FORMAT_RGB24,     V4L2_PIX_FMT_BGR24 },
113
 
    { PJMEDIA_FORMAT_RGBA,      V4L2_PIX_FMT_BGR32 },
114
 
    { PJMEDIA_FORMAT_RGB32,     V4L2_PIX_FMT_BGR32 },
115
 
    { PJMEDIA_FORMAT_AYUV,      V4L2_PIX_FMT_YUV32 },
116
 
    { PJMEDIA_FORMAT_YUY2,      V4L2_PIX_FMT_YUYV },
117
 
    { PJMEDIA_FORMAT_UYVY,      V4L2_PIX_FMT_UYVY }
118
 
};
119
 
 
120
 
/* Prototypes */
121
 
static pj_status_t vid4lin_factory_init(pjmedia_vid_dev_factory *f);
122
 
static pj_status_t vid4lin_factory_destroy(pjmedia_vid_dev_factory *f);
123
 
static pj_status_t vid4lin_factory_refresh(pjmedia_vid_dev_factory *f);
124
 
static unsigned    vid4lin_factory_get_dev_count(pjmedia_vid_dev_factory *f);
125
 
static pj_status_t vid4lin_factory_get_dev_info(pjmedia_vid_dev_factory *f,
126
 
                                                unsigned index,
127
 
                                                pjmedia_vid_dev_info *info);
128
 
static pj_status_t vid4lin_factory_default_param(pj_pool_t *pool,
129
 
                                                 pjmedia_vid_dev_factory *f,
130
 
                                                 unsigned index,
131
 
                                                 pjmedia_vid_dev_param *param);
132
 
static pj_status_t vid4lin_factory_create_stream(pjmedia_vid_dev_factory *f,
133
 
                                                 pjmedia_vid_dev_param *prm,
134
 
                                                 const pjmedia_vid_dev_cb *cb,
135
 
                                                 void *user_data,
136
 
                                                 pjmedia_vid_dev_stream **p);
137
 
 
138
 
static pj_status_t vid4lin_stream_get_param(pjmedia_vid_dev_stream *strm,
139
 
                                            pjmedia_vid_dev_param *param);
140
 
static pj_status_t vid4lin_stream_get_cap(pjmedia_vid_dev_stream *strm,
141
 
                                          pjmedia_vid_dev_cap cap,
142
 
                                          void *value);
143
 
static pj_status_t vid4lin_stream_set_cap(pjmedia_vid_dev_stream *strm,
144
 
                                          pjmedia_vid_dev_cap cap,
145
 
                                          const void *value);
146
 
static pj_status_t vid4lin_stream_get_frame(pjmedia_vid_dev_stream *strm,
147
 
                                            pjmedia_frame *frame);
148
 
static pj_status_t vid4lin_stream_start(pjmedia_vid_dev_stream *strm);
149
 
static pj_status_t vid4lin_stream_stop(pjmedia_vid_dev_stream *strm);
150
 
static pj_status_t vid4lin_stream_destroy(pjmedia_vid_dev_stream *strm);
151
 
 
152
 
/* Operations */
153
 
static pjmedia_vid_dev_factory_op factory_op =
154
 
{
155
 
    &vid4lin_factory_init,
156
 
    &vid4lin_factory_destroy,
157
 
    &vid4lin_factory_get_dev_count,
158
 
    &vid4lin_factory_get_dev_info,
159
 
    &vid4lin_factory_default_param,
160
 
    &vid4lin_factory_create_stream,
161
 
    &vid4lin_factory_refresh
162
 
};
163
 
 
164
 
static pjmedia_vid_dev_stream_op stream_op =
165
 
{
166
 
    &vid4lin_stream_get_param,
167
 
    &vid4lin_stream_get_cap,
168
 
    &vid4lin_stream_set_cap,
169
 
    &vid4lin_stream_start,
170
 
    &vid4lin_stream_get_frame,
171
 
    NULL,
172
 
    &vid4lin_stream_stop,
173
 
    &vid4lin_stream_destroy
174
 
};
175
 
 
176
 
 
177
 
/****************************************************************************
178
 
 * Factory operations
179
 
 */
180
 
/*
181
 
 * Factory creation function.
182
 
 */
183
 
pjmedia_vid_dev_factory* pjmedia_v4l2_factory(pj_pool_factory *pf)
184
 
{
185
 
    vid4lin_factory *f;
186
 
    pj_pool_t *pool;
187
 
 
188
 
    pool = pj_pool_create(pf, DRIVER_NAME, 512, 512, NULL);
189
 
    f = PJ_POOL_ZALLOC_T(pool, vid4lin_factory);
190
 
    f->pf = pf;
191
 
    f->pool = pool;
192
 
    f->base.op = &factory_op;
193
 
 
194
 
    return &f->base;
195
 
}
196
 
 
197
 
/* util: ioctl that tries harder. */
198
 
static pj_status_t xioctl(int fh, int request, void *arg)
199
 
{
200
 
    enum { RETRY = MAX_IOCTL_RETRY };
201
 
    int r, c=0;
202
 
 
203
 
    do {
204
 
        r = v4l2_ioctl(fh, request, arg);
205
 
    } while (r==-1 && c++<RETRY && ((errno==EINTR) || (errno==EAGAIN)));
206
 
 
207
 
    return (r == -1) ? pj_get_os_error() : PJ_SUCCESS;
208
 
}
209
 
 
210
 
/* Scan V4L2 devices */
211
 
static pj_status_t v4l2_scan_devs(vid4lin_factory *f)
212
 
{
213
 
    vid4lin_dev_info vdi[V4L2_MAX_DEVS];
214
 
    char dev_name[32];
215
 
    unsigned i, old_count;
216
 
    pj_status_t status;
217
 
 
218
 
    if (f->dev_pool) {
219
 
        pj_pool_release(f->dev_pool);
220
 
        f->dev_pool = NULL;
221
 
    }
222
 
 
223
 
    pj_bzero(vdi, sizeof(vdi));
224
 
    old_count = f->dev_count;
225
 
    f->dev_count = 0;
226
 
    f->dev_pool = pj_pool_create(f->pf, DRIVER_NAME, 500, 500, NULL);
227
 
 
228
 
    for (i=0; i<V4L2_MAX_DEVS && f->dev_count < V4L2_MAX_DEVS; ++i) {
229
 
        int fd;
230
 
        vid4lin_dev_info *pdi;
231
 
        pj_uint32_t fmt_cap[8];
232
 
        int j, fmt_cnt=0;
233
 
 
234
 
        pdi = &vdi[f->dev_count];
235
 
 
236
 
        snprintf(dev_name, sizeof(dev_name), "/dev/video%d", i);
237
 
        if (!pj_file_exists(dev_name))
238
 
            continue;
239
 
 
240
 
        fd = v4l2_open(dev_name, O_RDWR, 0);
241
 
        if (fd == -1)
242
 
            continue;
243
 
 
244
 
        status = xioctl(fd, VIDIOC_QUERYCAP, &pdi->v4l2_cap);
245
 
        if (status != PJ_SUCCESS) {
246
 
            PJ_PERROR(4,(THIS_FILE, status, "Error querying %s", dev_name));
247
 
            v4l2_close(fd);
248
 
            continue;
249
 
        }
250
 
 
251
 
        if ((pdi->v4l2_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {
252
 
            v4l2_close(fd);
253
 
            continue;
254
 
        }
255
 
 
256
 
        PJ_LOG(5,(THIS_FILE, "Found capture device %s", pdi->v4l2_cap.card));
257
 
        PJ_LOG(5,(THIS_FILE, "  Enumerating formats:"));
258
 
        for (j=0; fmt_cnt<PJ_ARRAY_SIZE(fmt_cap); ++j) {
259
 
            struct v4l2_fmtdesc fdesc;
260
 
            unsigned k;
261
 
 
262
 
            pj_bzero(&fdesc, sizeof(fdesc));
263
 
            fdesc.index = j;
264
 
            fdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
265
 
 
266
 
            status = xioctl(fd, VIDIOC_ENUM_FMT, &fdesc);
267
 
            if (status != PJ_SUCCESS)
268
 
                break;
269
 
 
270
 
            for (k=0; k<PJ_ARRAY_SIZE(v4l2_fmt_maps); ++k) {
271
 
                if (v4l2_fmt_maps[k].v4l2_fmt_id == fdesc.pixelformat) {
272
 
                    fmt_cap[fmt_cnt++] = v4l2_fmt_maps[k].pjmedia_fmt_id;
273
 
                    PJ_LOG(5,(THIS_FILE, "   Supported: %s",
274
 
                              fdesc.description));
275
 
                    break;
276
 
                }
277
 
            }
278
 
            if (k==PJ_ARRAY_SIZE(v4l2_fmt_maps)) {
279
 
                PJ_LOG(5,(THIS_FILE, "   Unsupported: %s", fdesc.description));
280
 
            }
281
 
        }
282
 
 
283
 
        v4l2_close(fd);
284
 
 
285
 
        if (fmt_cnt==0) {
286
 
            PJ_LOG(5,(THIS_FILE, "    Found no common format"));
287
 
            continue;
288
 
        }
289
 
 
290
 
        strncpy(pdi->dev_name, dev_name, sizeof(pdi->dev_name));
291
 
        pdi->dev_name[sizeof(pdi->dev_name)-1] = '\0';
292
 
        strncpy(pdi->info.name, (char*)pdi->v4l2_cap.card,
293
 
                sizeof(pdi->info.name));
294
 
        pdi->info.name[sizeof(pdi->info.name)-1] = '\0';
295
 
        strncpy(pdi->info.driver, DRIVER_NAME, sizeof(pdi->info.driver));
296
 
        pdi->info.driver[sizeof(pdi->info.driver)-1] = '\0';
297
 
        pdi->info.dir = PJMEDIA_DIR_CAPTURE;
298
 
        pdi->info.has_callback = PJ_FALSE;
299
 
        pdi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
300
 
 
301
 
        pdi->info.fmt_cnt = fmt_cnt;
302
 
        for (j=0; j<fmt_cnt; ++j) {
303
 
            pjmedia_format_init_video(&pdi->info.fmt[j],
304
 
                                      fmt_cap[j],
305
 
                                      DEFAULT_WIDTH,
306
 
                                      DEFAULT_HEIGHT,
307
 
                                      DEFAULT_FPS, 1);
308
 
        }
309
 
        if (j < fmt_cnt)
310
 
            continue;
311
 
 
312
 
        f->dev_count++;
313
 
    }
314
 
 
315
 
    if (f->dev_count == 0)
316
 
        return PJ_SUCCESS;
317
 
 
318
 
    if (f->dev_count > old_count || f->dev_info == NULL) {
319
 
        f->dev_info = (vid4lin_dev_info*)
320
 
                      pj_pool_calloc(f->dev_pool,
321
 
                                     f->dev_count,
322
 
                                     sizeof(vid4lin_dev_info));
323
 
    }
324
 
    pj_memcpy(f->dev_info, vdi, f->dev_count * sizeof(vid4lin_dev_info));
325
 
 
326
 
    return PJ_SUCCESS;
327
 
}
328
 
 
329
 
 
330
 
/* API: init factory */
331
 
static pj_status_t vid4lin_factory_init(pjmedia_vid_dev_factory *f)
332
 
{
333
 
    return vid4lin_factory_refresh(f);
334
 
}
335
 
 
336
 
/* API: destroy factory */
337
 
static pj_status_t vid4lin_factory_destroy(pjmedia_vid_dev_factory *f)
338
 
{
339
 
    vid4lin_factory *cf = (vid4lin_factory*)f;
340
 
    pj_pool_t *pool = cf->pool;
341
 
 
342
 
    if (cf->dev_pool)
343
 
        pj_pool_release(cf->dev_pool);
344
 
    if (cf->pool) {
345
 
        cf->pool = NULL;
346
 
        pj_pool_release(pool);
347
 
    }
348
 
 
349
 
    return PJ_SUCCESS;
350
 
}
351
 
 
352
 
/* API: refresh the list of devices */
353
 
static pj_status_t vid4lin_factory_refresh(pjmedia_vid_dev_factory *f)
354
 
{
355
 
    vid4lin_factory *cf = (vid4lin_factory*)f;
356
 
    pj_status_t status;
357
 
 
358
 
    status = v4l2_scan_devs(cf);
359
 
    if (status != PJ_SUCCESS)
360
 
        return status;
361
 
 
362
 
    PJ_LOG(4, (THIS_FILE, "Video4Linux2 has %d devices",
363
 
               cf->dev_count));
364
 
 
365
 
    return PJ_SUCCESS;
366
 
}
367
 
 
368
 
/* API: get number of devices */
369
 
static unsigned vid4lin_factory_get_dev_count(pjmedia_vid_dev_factory *f)
370
 
{
371
 
    vid4lin_factory *cf = (vid4lin_factory*)f;
372
 
    return cf->dev_count;
373
 
}
374
 
 
375
 
/* API: get device info */
376
 
static pj_status_t vid4lin_factory_get_dev_info(pjmedia_vid_dev_factory *f,
377
 
                                             unsigned index,
378
 
                                             pjmedia_vid_dev_info *info)
379
 
{
380
 
    vid4lin_factory *cf = (vid4lin_factory*)f;
381
 
 
382
 
    PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV);
383
 
 
384
 
    pj_memcpy(info, &cf->dev_info[index].info, sizeof(*info));
385
 
 
386
 
    return PJ_SUCCESS;
387
 
}
388
 
 
389
 
/* API: create default device parameter */
390
 
static pj_status_t vid4lin_factory_default_param(pj_pool_t *pool,
391
 
                                                 pjmedia_vid_dev_factory *f,
392
 
                                                 unsigned index,
393
 
                                                 pjmedia_vid_dev_param *param)
394
 
{
395
 
    vid4lin_factory *cf = (vid4lin_factory*)f;
396
 
 
397
 
    PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV);
398
 
 
399
 
    pj_bzero(param, sizeof(*param));
400
 
    param->dir = PJMEDIA_DIR_CAPTURE;
401
 
    param->cap_id = index;
402
 
    param->rend_id = PJMEDIA_VID_INVALID_DEV;
403
 
    param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
404
 
    param->clock_rate = DEFAULT_CLOCK_RATE;
405
 
    pjmedia_format_copy(&param->fmt, &cf->dev_info[index].info.fmt[0]);
406
 
 
407
 
    return PJ_SUCCESS;
408
 
}
409
 
 
410
 
static vid4lin_fmt_map* get_v4l2_format_info(pjmedia_format_id id)
411
 
{
412
 
    unsigned i;
413
 
 
414
 
    for (i = 0; i < PJ_ARRAY_SIZE(v4l2_fmt_maps); i++) {
415
 
        if (v4l2_fmt_maps[i].pjmedia_fmt_id == id)
416
 
            return &v4l2_fmt_maps[i];
417
 
    }
418
 
 
419
 
    return NULL;
420
 
}
421
 
 
422
 
/* util: setup format */
423
 
static pj_status_t vid4lin_stream_init_fmt(vid4lin_stream *stream,
424
 
                                        const pjmedia_vid_dev_param *param,
425
 
                                        pj_uint32_t pix_fmt)
426
 
{
427
 
    pjmedia_video_format_detail *vfd;
428
 
    struct v4l2_format v4l2_fmt;
429
 
    pj_status_t status;
430
 
 
431
 
    vfd = pjmedia_format_get_video_format_detail(&param->fmt, PJ_TRUE);
432
 
    if (vfd == NULL)
433
 
        return PJMEDIA_EVID_BADFORMAT;
434
 
 
435
 
    pj_bzero(&v4l2_fmt, sizeof(v4l2_fmt));
436
 
    v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
437
 
    v4l2_fmt.fmt.pix.width       = vfd->size.w;
438
 
    v4l2_fmt.fmt.pix.height      = vfd->size.h;
439
 
    v4l2_fmt.fmt.pix.pixelformat = pix_fmt;
440
 
    v4l2_fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;
441
 
    status = xioctl(stream->fd, VIDIOC_S_FMT, &v4l2_fmt);
442
 
    if (status != PJ_SUCCESS)
443
 
        return status;
444
 
 
445
 
    if (v4l2_fmt.fmt.pix.pixelformat != pix_fmt) {
446
 
        status = PJMEDIA_EVID_BADFORMAT;
447
 
        return status;
448
 
    }
449
 
 
450
 
    if ((v4l2_fmt.fmt.pix.width != vfd->size.w) ||
451
 
        (v4l2_fmt.fmt.pix.height != vfd->size.h))
452
 
    {
453
 
        /* Size has changed */
454
 
        vfd->size.w = v4l2_fmt.fmt.pix.width;
455
 
        vfd->size.h = v4l2_fmt.fmt.pix.height;
456
 
    }
457
 
 
458
 
    return PJ_SUCCESS;
459
 
}
460
 
 
461
 
/* Util: initiate v4l2 streaming via mmap */
462
 
static pj_status_t vid4lin_stream_init_streaming(vid4lin_stream *stream)
463
 
{
464
 
    struct v4l2_requestbuffers req;
465
 
    unsigned i;
466
 
    pj_status_t status;
467
 
 
468
 
    pj_bzero(&req, sizeof(req));
469
 
    req.count = BUFFER_CNT;
470
 
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
471
 
    req.memory = V4L2_MEMORY_MMAP;
472
 
    status = xioctl(stream->fd, VIDIOC_REQBUFS, &req);
473
 
    if (status != PJ_SUCCESS)
474
 
        return status;
475
 
 
476
 
    stream->buffers = pj_pool_calloc(stream->pool, req.count,
477
 
                                     sizeof(*stream->buffers));
478
 
    stream->buf_cnt = 0;
479
 
 
480
 
    for (i = 0; i < req.count; ++i) {
481
 
        struct v4l2_buffer buf;
482
 
 
483
 
        pj_bzero(&buf, sizeof(buf));
484
 
 
485
 
        buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
486
 
        buf.memory      = V4L2_MEMORY_MMAP;
487
 
        buf.index       = i;
488
 
 
489
 
        status = xioctl(stream->fd, VIDIOC_QUERYBUF, &buf);
490
 
        if (status != PJ_SUCCESS)
491
 
            goto on_error;
492
 
 
493
 
        stream->buffers[i].length = buf.length;
494
 
        stream->buffers[i].start = v4l2_mmap(NULL, buf.length,
495
 
                                             PROT_READ | PROT_WRITE,
496
 
                                             MAP_SHARED, stream->fd,
497
 
                                             buf.m.offset);
498
 
 
499
 
        if (MAP_FAILED == stream->buffers[i].start) {
500
 
            status = pj_get_os_error();
501
 
            goto on_error;
502
 
        }
503
 
 
504
 
        stream->buf_cnt++;
505
 
    }
506
 
 
507
 
    PJ_LOG(5,(THIS_FILE, "  mmap streaming initialized"));
508
 
 
509
 
    stream->io_type = IO_TYPE_MMAP;
510
 
    return PJ_SUCCESS;
511
 
 
512
 
on_error:
513
 
    return status;
514
 
}
515
 
 
516
 
/* init streaming with user pointer */
517
 
static pj_status_t vid4lin_stream_init_streaming_user(vid4lin_stream *stream)
518
 
{
519
 
    return PJ_ENOTSUP;
520
 
}
521
 
 
522
 
/* init streaming with read() */
523
 
static pj_status_t vid4lin_stream_init_read_write(vid4lin_stream *stream)
524
 
{
525
 
    return PJ_ENOTSUP;
526
 
}
527
 
 
528
 
/* API: create stream */
529
 
static pj_status_t vid4lin_factory_create_stream(pjmedia_vid_dev_factory *f,
530
 
                                      pjmedia_vid_dev_param *param,
531
 
                                      const pjmedia_vid_dev_cb *cb,
532
 
                                      void *user_data,
533
 
                                      pjmedia_vid_dev_stream **p_vid_strm)
534
 
{
535
 
    vid4lin_factory *cf = (vid4lin_factory*)f;
536
 
    pj_pool_t *pool;
537
 
    vid4lin_stream *stream;
538
 
    vid4lin_dev_info *vdi;
539
 
    const vid4lin_fmt_map *fmt_map;
540
 
    const pjmedia_video_format_info *fmt_info;
541
 
    pjmedia_video_format_detail *vfd;
542
 
    pj_status_t status = PJ_SUCCESS;
543
 
 
544
 
 
545
 
    PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL);
546
 
    PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO &&
547
 
                     param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO &&
548
 
                     param->dir == PJMEDIA_DIR_CAPTURE,
549
 
                     PJ_EINVAL);
550
 
    PJ_ASSERT_RETURN(param->cap_id >= 0 && param->cap_id < cf->dev_count,
551
 
                     PJMEDIA_EVID_INVDEV);
552
 
 
553
 
    fmt_info = pjmedia_get_video_format_info(NULL, param->fmt.id);
554
 
    if (!fmt_info || (fmt_map=get_v4l2_format_info(param->fmt.id))==NULL)
555
 
        return PJMEDIA_EVID_BADFORMAT;
556
 
 
557
 
    vdi = &cf->dev_info[param->cap_id];
558
 
    vfd = pjmedia_format_get_video_format_detail(&param->fmt, PJ_TRUE);
559
 
 
560
 
    /* Create and Initialize stream descriptor */
561
 
    pool = pj_pool_create(cf->pf, vdi->info.name, 512, 512, NULL);
562
 
    PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
563
 
 
564
 
    stream = PJ_POOL_ZALLOC_T(pool, vid4lin_stream);
565
 
    pj_memcpy(&stream->param, param, sizeof(*param));
566
 
    stream->pool = pool;
567
 
    pj_memcpy(&stream->vid_cb, cb, sizeof(*cb));
568
 
    strncpy(stream->name, vdi->info.name, sizeof(stream->name));
569
 
    stream->name[sizeof(stream->name)-1] = '\0';
570
 
    stream->user_data = user_data;
571
 
    stream->fd = INVALID_FD;
572
 
 
573
 
    stream->fd = v4l2_open(vdi->dev_name, O_RDWR, 0);
574
 
    if (stream->fd < 0)
575
 
        goto on_error;
576
 
 
577
 
    status = vid4lin_stream_init_fmt(stream, param, fmt_map->v4l2_fmt_id);
578
 
    if (status != PJ_SUCCESS)
579
 
        goto on_error;
580
 
 
581
 
    if (vdi->v4l2_cap.capabilities & V4L2_CAP_STREAMING)
582
 
        status = vid4lin_stream_init_streaming(stream);
583
 
 
584
 
    if (status!=PJ_SUCCESS && vdi->v4l2_cap.capabilities & V4L2_CAP_STREAMING)
585
 
        status = vid4lin_stream_init_streaming_user(stream);
586
 
 
587
 
    if (status!=PJ_SUCCESS && vdi->v4l2_cap.capabilities & V4L2_CAP_READWRITE)
588
 
        status = vid4lin_stream_init_read_write(stream);
589
 
 
590
 
    if (status != PJ_SUCCESS) {
591
 
        PJ_LOG(1,(THIS_FILE, "Error: unable to initiate I/O on %s",
592
 
                  stream->name));
593
 
        goto on_error;
594
 
    }
595
 
 
596
 
    /* Done */
597
 
    stream->base.op = &stream_op;
598
 
    *p_vid_strm = &stream->base;
599
 
 
600
 
    return PJ_SUCCESS;
601
 
 
602
 
on_error:
603
 
    if (status == PJ_SUCCESS)
604
 
        status = PJ_RETURN_OS_ERROR(errno);
605
 
 
606
 
    vid4lin_stream_destroy(&stream->base);
607
 
    return status;
608
 
}
609
 
 
610
 
/* API: Get stream info. */
611
 
static pj_status_t vid4lin_stream_get_param(pjmedia_vid_dev_stream *s,
612
 
                                            pjmedia_vid_dev_param *pi)
613
 
{
614
 
    vid4lin_stream *strm = (vid4lin_stream*)s;
615
 
 
616
 
    PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
617
 
 
618
 
    pj_memcpy(pi, &strm->param, sizeof(*pi));
619
 
 
620
 
    return PJ_SUCCESS;
621
 
}
622
 
 
623
 
/* API: get capability */
624
 
static pj_status_t vid4lin_stream_get_cap(pjmedia_vid_dev_stream *s,
625
 
                                          pjmedia_vid_dev_cap cap,
626
 
                                          void *pval)
627
 
{
628
 
    vid4lin_stream *strm = (vid4lin_stream*)s;
629
 
 
630
 
    PJ_UNUSED_ARG(strm);
631
 
 
632
 
    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
633
 
 
634
 
    if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
635
 
    {
636
 
        return PJMEDIA_EVID_INVCAP;
637
 
//      return PJ_SUCCESS;
638
 
    } else {
639
 
        return PJMEDIA_EVID_INVCAP;
640
 
    }
641
 
}
642
 
 
643
 
/* API: set capability */
644
 
static pj_status_t vid4lin_stream_set_cap(pjmedia_vid_dev_stream *s,
645
 
                                          pjmedia_vid_dev_cap cap,
646
 
                                          const void *pval)
647
 
{
648
 
    vid4lin_stream *strm = (vid4lin_stream*)s;
649
 
 
650
 
 
651
 
    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
652
 
 
653
 
    /*
654
 
    if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
655
 
    {
656
 
        return PJ_SUCCESS;
657
 
    }
658
 
    */
659
 
    PJ_UNUSED_ARG(strm);
660
 
    PJ_UNUSED_ARG(cap);
661
 
    PJ_UNUSED_ARG(pval);
662
 
 
663
 
    return PJMEDIA_EVID_INVCAP;
664
 
}
665
 
 
666
 
/* get frame from mmap */
667
 
static pj_status_t vid4lin_stream_get_frame_mmap(vid4lin_stream *stream,
668
 
                                                 pjmedia_frame *frame)
669
 
{
670
 
    struct v4l2_buffer buf;
671
 
    pj_time_val time;
672
 
    pj_status_t status = PJ_SUCCESS;
673
 
 
674
 
    pj_bzero(&buf, sizeof(buf));
675
 
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
676
 
    buf.memory = V4L2_MEMORY_MMAP;
677
 
    status = xioctl(stream->fd, VIDIOC_DQBUF, &buf);
678
 
    if (status != PJ_SUCCESS)
679
 
        return status;
680
 
 
681
 
    if (frame->size < buf.bytesused) {
682
 
        /* supplied buffer is too small */
683
 
        pj_assert(!"frame buffer is too small for v4l2");
684
 
        status = PJ_ETOOSMALL;
685
 
        goto on_return;
686
 
    }
687
 
 
688
 
    time.sec = buf.timestamp.tv_sec;
689
 
    time.msec = buf.timestamp.tv_usec / 1000;
690
 
    PJ_TIME_VAL_SUB(time, stream->start_time);
691
 
 
692
 
    frame->type = PJMEDIA_FRAME_TYPE_VIDEO;
693
 
    frame->bit_info = 0;
694
 
    frame->size = buf.bytesused;
695
 
    frame->timestamp.u64 = PJ_UINT64(1) * PJ_TIME_VAL_MSEC(time) *
696
 
                           stream->param.clock_rate / PJ_UINT64(1000);
697
 
    pj_memcpy(frame->buf, stream->buffers[buf.index].start, buf.bytesused);
698
 
 
699
 
on_return:
700
 
    pj_bzero(&buf, sizeof(buf));
701
 
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
702
 
    buf.memory = V4L2_MEMORY_MMAP;
703
 
    xioctl(stream->fd, VIDIOC_QBUF, &buf);
704
 
 
705
 
    return status;
706
 
}
707
 
 
708
 
/* API: Get frame from stream */
709
 
static pj_status_t vid4lin_stream_get_frame(pjmedia_vid_dev_stream *strm,
710
 
                                            pjmedia_frame *frame)
711
 
{
712
 
    vid4lin_stream *stream = (vid4lin_stream*)strm;
713
 
 
714
 
    if (stream->io_type == IO_TYPE_MMAP)
715
 
        return vid4lin_stream_get_frame_mmap(stream, frame);
716
 
    else {
717
 
        pj_assert(!"Unsupported i/o type");
718
 
        return PJ_EINVALIDOP;
719
 
    }
720
 
}
721
 
 
722
 
/* API: Start stream. */
723
 
static pj_status_t vid4lin_stream_start(pjmedia_vid_dev_stream *strm)
724
 
{
725
 
    vid4lin_stream *stream = (vid4lin_stream*)strm;
726
 
    struct v4l2_buffer buf;
727
 
    enum v4l2_buf_type type;
728
 
    unsigned i;
729
 
    pj_status_t status;
730
 
 
731
 
    PJ_ASSERT_RETURN(stream->fd != -1, PJ_EINVALIDOP);
732
 
 
733
 
    PJ_LOG(4, (THIS_FILE, "Starting v4l2 video stream %s", stream->name));
734
 
 
735
 
    pj_gettimeofday(&stream->start_time);
736
 
 
737
 
    for (i = 0; i < stream->buf_cnt; ++i) {
738
 
        pj_bzero(&buf, sizeof(buf));
739
 
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
740
 
        buf.memory = V4L2_MEMORY_MMAP;
741
 
        buf.index = i;
742
 
        status = xioctl(stream->fd, VIDIOC_QBUF, &buf);
743
 
        if (status != PJ_SUCCESS)
744
 
            goto on_error;
745
 
    }
746
 
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
747
 
 
748
 
    status = xioctl(stream->fd, VIDIOC_STREAMON, &type);
749
 
    if (status != PJ_SUCCESS)
750
 
        goto on_error;
751
 
 
752
 
    return PJ_SUCCESS;
753
 
 
754
 
on_error:
755
 
    if (i > 0) {
756
 
        /* Dequeue already enqueued buffers. Can we do this while streaming
757
 
         * is not started?
758
 
         */
759
 
        unsigned n = i;
760
 
        for (i=0; i<n; ++i) {
761
 
            pj_bzero(&buf, sizeof(buf));
762
 
            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
763
 
            buf.memory = V4L2_MEMORY_MMAP;
764
 
            xioctl(stream->fd, VIDIOC_DQBUF, &buf);
765
 
        }
766
 
    }
767
 
    return status;
768
 
}
769
 
 
770
 
/* API: Stop stream. */
771
 
static pj_status_t vid4lin_stream_stop(pjmedia_vid_dev_stream *strm)
772
 
{
773
 
    vid4lin_stream *stream = (vid4lin_stream*)strm;
774
 
    enum v4l2_buf_type type;
775
 
    pj_status_t status;
776
 
 
777
 
    if (stream->fd < 0)
778
 
        return PJ_SUCCESS;
779
 
 
780
 
    PJ_LOG(4, (THIS_FILE, "Stopping v4l2 video stream %s", stream->name));
781
 
 
782
 
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
783
 
    status = xioctl(stream->fd, VIDIOC_STREAMOFF, &type);
784
 
    if (status != PJ_SUCCESS)
785
 
        return status;
786
 
 
787
 
    return PJ_SUCCESS;
788
 
}
789
 
 
790
 
 
791
 
/* API: Destroy stream. */
792
 
static pj_status_t vid4lin_stream_destroy(pjmedia_vid_dev_stream *strm)
793
 
{
794
 
    vid4lin_stream *stream = (vid4lin_stream*)strm;
795
 
    unsigned i;
796
 
 
797
 
    PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
798
 
 
799
 
    vid4lin_stream_stop(strm);
800
 
 
801
 
    PJ_LOG(4, (THIS_FILE, "Destroying v4l2 video stream %s", stream->name));
802
 
 
803
 
    for (i=0; i<stream->buf_cnt; ++i) {
804
 
        if (stream->buffers[i].start != MAP_FAILED) {
805
 
            v4l2_munmap(stream->buffers[i].start, stream->buffers[i].length);
806
 
            stream->buffers[i].start = MAP_FAILED;
807
 
        }
808
 
    }
809
 
 
810
 
    if (stream->fd >= 0) {
811
 
        v4l2_close(stream->fd);
812
 
        stream->fd = -1;
813
 
    }
814
 
    pj_pool_release(stream->pool);
815
 
 
816
 
    return PJ_SUCCESS;
817
 
}
818
 
 
819
 
#endif  /* PJMEDIA_VIDEO_DEV_HAS_V4L2 */