blob: 658d091acab7c47412695925ccd091fc6139e4d0 [file] [log] [blame]
Alexandre Lision67916dd2014-01-24 13:33:04 -05001/* $Id$ */
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 <pj/assert.h>
21#include <pj/errno.h>
22#include <pj/log.h>
23#include <pj/pool.h>
24#include <pj/string.h>
25
26
27#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
28
29
30#define THIS_FILE "videodev.c"
31
32#define DEFINE_CAP(name, info) {name, info}
33
34/* Capability names */
35static struct cap_info
36{
37 const char *name;
38 const char *info;
39} cap_infos[] =
40{
41 DEFINE_CAP("format", "Video format"),
42 DEFINE_CAP("scale", "Input dimension"),
43 DEFINE_CAP("window", "Window handle"),
44 DEFINE_CAP("resize", "Renderer resize"),
45 DEFINE_CAP("position", "Renderer position"),
46 DEFINE_CAP("hide", "Renderer hide"),
47 DEFINE_CAP("preview", "Input preview"),
48 DEFINE_CAP("orientation", "Video orientation"),
49 DEFINE_CAP("switch", "Switch device"),
50 DEFINE_CAP("wndflags", "Window flags")
51};
52
53
54/*
55 * The device index seen by application and driver is different.
56 *
57 * At application level, device index is index to global list of device.
58 * At driver level, device index is index to device list on that particular
59 * factory only.
60 */
61#define MAKE_DEV_ID(f_id, index) (((f_id & 0xFFFF) << 16) | (index & 0xFFFF))
62#define GET_INDEX(dev_id) ((dev_id) & 0xFFFF)
63#define GET_FID(dev_id) ((dev_id) >> 16)
64#define DEFAULT_DEV_ID 0
65
66
67/* extern functions to create factories */
68#if PJMEDIA_VIDEO_DEV_HAS_NULL_VIDEO
69pjmedia_vid_dev_factory* pjmedia_null_video_factory(pj_pool_factory *pf);
70#endif
71
72#if PJMEDIA_VIDEO_DEV_HAS_DSHOW
73pjmedia_vid_dev_factory* pjmedia_dshow_factory(pj_pool_factory *pf);
74#endif
75
76#if PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC
77pjmedia_vid_dev_factory* pjmedia_cbar_factory(pj_pool_factory *pf);
78#endif
79
80#if PJMEDIA_VIDEO_DEV_HAS_SDL
81pjmedia_vid_dev_factory* pjmedia_sdl_factory(pj_pool_factory *pf);
82#endif
83
84#if PJMEDIA_VIDEO_DEV_HAS_FFMPEG
85pjmedia_vid_dev_factory* pjmedia_ffmpeg_factory(pj_pool_factory *pf);
86#endif
87
88#if PJMEDIA_VIDEO_DEV_HAS_V4L2
89pjmedia_vid_dev_factory* pjmedia_v4l2_factory(pj_pool_factory *pf);
90#endif
91
92#if PJMEDIA_VIDEO_DEV_HAS_QT
93pjmedia_vid_dev_factory* pjmedia_qt_factory(pj_pool_factory *pf);
94#endif
95
96#if PJMEDIA_VIDEO_DEV_HAS_IOS
97pjmedia_vid_dev_factory* pjmedia_ios_factory(pj_pool_factory *pf);
98#endif
99
100#define MAX_DRIVERS 16
101#define MAX_DEVS 64
102
103
104/* driver structure */
105struct driver
106{
107 /* Creation function */
108 pjmedia_vid_dev_factory_create_func_ptr create;
109 /* Factory instance */
110 pjmedia_vid_dev_factory *f;
111 char name[32]; /* Driver name */
112 unsigned dev_cnt; /* Number of devices */
113 unsigned start_idx; /* Start index in global list */
114 int cap_dev_idx; /* Default capture device. */
115 int rend_dev_idx; /* Default render device */
116};
117
118/* The video device subsystem */
119static struct vid_subsys
120{
121 unsigned init_count; /* How many times init() is called */
122 pj_pool_factory *pf; /* The pool factory. */
123
124 unsigned drv_cnt; /* Number of drivers. */
125 struct driver drv[MAX_DRIVERS]; /* Array of drivers. */
126
127 unsigned dev_cnt; /* Total number of devices. */
128 pj_uint32_t dev_list[MAX_DEVS];/* Array of device IDs. */
129
130} vid_subsys;
131
132/* API: get capability name/info */
133PJ_DEF(const char*) pjmedia_vid_dev_cap_name(pjmedia_vid_dev_cap cap,
134 const char **p_desc)
135{
136 const char *desc;
137 unsigned i;
138
139 if (p_desc==NULL) p_desc = &desc;
140
141 for (i=0; i<PJ_ARRAY_SIZE(cap_infos); ++i) {
142 if ((1 << i)==cap)
143 break;
144 }
145
146 if (i==PJ_ARRAY_SIZE(cap_infos)) {
147 *p_desc = "??";
148 return "??";
149 }
150
151 *p_desc = cap_infos[i].info;
152 return cap_infos[i].name;
153}
154
155static pj_status_t get_cap_pointer(const pjmedia_vid_dev_param *param,
156 pjmedia_vid_dev_cap cap,
157 void **ptr,
158 unsigned *size)
159{
160#define FIELD_INFO(name) *ptr = (void*)&param->name; \
161 *size = sizeof(param->name)
162
163 switch (cap) {
164 case PJMEDIA_VID_DEV_CAP_FORMAT:
165 FIELD_INFO(fmt);
166 break;
167 case PJMEDIA_VID_DEV_CAP_INPUT_SCALE:
168 FIELD_INFO(disp_size);
169 break;
170 case PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW:
171 FIELD_INFO(window);
172 break;
173 case PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE:
174 FIELD_INFO(disp_size);
175 break;
176 case PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION:
177 FIELD_INFO(window_pos);
178 break;
179 case PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE:
180 FIELD_INFO(window_hide);
181 break;
182 case PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW:
183 FIELD_INFO(native_preview);
184 break;
185 case PJMEDIA_VID_DEV_CAP_ORIENTATION:
186 FIELD_INFO(orient);
187 break;
188 /* The PJMEDIA_VID_DEV_CAP_SWITCH does not have an entry in the
189 * param (it doesn't make sense to open a stream and tell it
190 * to switch immediately).
191 */
192 case PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS:
193 FIELD_INFO(window_flags);
194 break;
195 default:
196 return PJMEDIA_EVID_INVCAP;
197 }
198
199#undef FIELD_INFO
200
201 return PJ_SUCCESS;
202}
203
204/* API: set cap value to param */
205PJ_DEF(pj_status_t)
206pjmedia_vid_dev_param_set_cap( pjmedia_vid_dev_param *param,
207 pjmedia_vid_dev_cap cap,
208 const void *pval)
209{
210 void *cap_ptr;
211 unsigned cap_size;
212 pj_status_t status;
213
214 status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
215 if (status != PJ_SUCCESS)
216 return status;
217
218 pj_memcpy(cap_ptr, pval, cap_size);
219 param->flags |= cap;
220
221 return PJ_SUCCESS;
222}
223
224/* API: get cap value from param */
225PJ_DEF(pj_status_t)
226pjmedia_vid_dev_param_get_cap( const pjmedia_vid_dev_param *param,
227 pjmedia_vid_dev_cap cap,
228 void *pval)
229{
230 void *cap_ptr;
231 unsigned cap_size;
232 pj_status_t status;
233
234 status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
235 if (status != PJ_SUCCESS)
236 return status;
237
238 if ((param->flags & cap) == 0) {
239 pj_bzero(cap_ptr, cap_size);
240 return PJMEDIA_EVID_INVCAP;
241 }
242
243 pj_memcpy(pval, cap_ptr, cap_size);
244 return PJ_SUCCESS;
245}
246
247/* Internal: init driver */
248static pj_status_t init_driver(unsigned drv_idx, pj_bool_t refresh)
249{
250 struct driver *drv = &vid_subsys.drv[drv_idx];
251 pjmedia_vid_dev_factory *f;
252 unsigned i, dev_cnt;
253 pj_status_t status;
254
255 if (!refresh) {
256 /* Create the factory */
257 f = (*drv->create)(vid_subsys.pf);
258 if (!f)
259 return PJ_EUNKNOWN;
260
261 /* Call factory->init() */
262 status = f->op->init(f);
263 if (status != PJ_SUCCESS) {
264 f->op->destroy(f);
265 return status;
266 }
267 } else {
268 f = drv->f;
269 }
270
271 /* Get number of devices */
272 dev_cnt = f->op->get_dev_count(f);
273 if (dev_cnt + vid_subsys.dev_cnt > MAX_DEVS) {
274 PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because"
275 " there are too many devices",
276 vid_subsys.dev_cnt + dev_cnt - MAX_DEVS));
277 dev_cnt = MAX_DEVS - vid_subsys.dev_cnt;
278 }
279
280 /* enabling this will cause pjsua-lib initialization to fail when there
281 * is no video device installed in the system, even when pjsua has been
282 * run with --null-video
283 *
284 if (dev_cnt == 0) {
285 f->op->destroy(f);
286 return PJMEDIA_EVID_NODEV;
287 }
288 */
289
290 /* Fill in default devices */
291 drv->rend_dev_idx = drv->cap_dev_idx = -1;
292 for (i=0; i<dev_cnt; ++i) {
293 pjmedia_vid_dev_info info;
294
295 status = f->op->get_dev_info(f, i, &info);
296 if (status != PJ_SUCCESS) {
297 f->op->destroy(f);
298 return status;
299 }
300
301 if (drv->name[0]=='\0') {
302 /* Set driver name */
303 pj_ansi_strncpy(drv->name, info.driver, sizeof(drv->name));
304 drv->name[sizeof(drv->name)-1] = '\0';
305 }
306
307 if (drv->rend_dev_idx < 0 && (info.dir & PJMEDIA_DIR_RENDER)) {
308 /* Set default render device */
309 drv->rend_dev_idx = i;
310 }
311 if (drv->cap_dev_idx < 0 && (info.dir & PJMEDIA_DIR_CAPTURE)) {
312 /* Set default capture device */
313 drv->cap_dev_idx = i;
314 }
315
316 if (drv->rend_dev_idx >= 0 && drv->cap_dev_idx >= 0) {
317 /* Done. */
318 break;
319 }
320 }
321
322 /* Register the factory */
323 drv->f = f;
324 drv->f->sys.drv_idx = drv_idx;
325 drv->start_idx = vid_subsys.dev_cnt;
326 drv->dev_cnt = dev_cnt;
327
328 /* Register devices to global list */
329 for (i=0; i<dev_cnt; ++i) {
330 vid_subsys.dev_list[vid_subsys.dev_cnt++] = MAKE_DEV_ID(drv_idx, i);
331 }
332
333 return PJ_SUCCESS;
334}
335
336/* Internal: deinit driver */
337static void deinit_driver(unsigned drv_idx)
338{
339 struct driver *drv = &vid_subsys.drv[drv_idx];
340
341 if (drv->f) {
342 drv->f->op->destroy(drv->f);
343 drv->f = NULL;
344 }
345
346 drv->dev_cnt = 0;
347 drv->rend_dev_idx = drv->cap_dev_idx = -1;
348}
349
350/* API: Initialize the video device subsystem. */
351PJ_DEF(pj_status_t) pjmedia_vid_dev_subsys_init(pj_pool_factory *pf)
352{
353 unsigned i;
354 pj_status_t status = PJ_SUCCESS;
355
356 /* Allow init() to be called multiple times as long as there is matching
357 * number of shutdown().
358 */
359 if (vid_subsys.init_count++ != 0) {
360 return PJ_SUCCESS;
361 }
362
363 /* Register error subsystem */
364 pj_register_strerror(PJMEDIA_VIDEODEV_ERRNO_START,
365 PJ_ERRNO_SPACE_SIZE,
366 &pjmedia_videodev_strerror);
367
368 /* Init */
369 vid_subsys.pf = pf;
370 vid_subsys.drv_cnt = 0;
371 vid_subsys.dev_cnt = 0;
372
373 /* Register creation functions */
374#if PJMEDIA_VIDEO_DEV_HAS_V4L2
375 vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_v4l2_factory;
376#endif
377#if PJMEDIA_VIDEO_DEV_HAS_QT
378 vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_qt_factory;
379#endif
380#if PJMEDIA_VIDEO_DEV_HAS_IOS
381 vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_ios_factory;
382#endif
383#if PJMEDIA_VIDEO_DEV_HAS_DSHOW
384 vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_dshow_factory;
385#endif
386#if PJMEDIA_VIDEO_DEV_HAS_FFMPEG
387 vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_ffmpeg_factory;
388#endif
389#if PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC
390 vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_cbar_factory;
391#endif
392#if PJMEDIA_VIDEO_DEV_HAS_SDL
393 vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_sdl_factory;
394#endif
395
396 /* Initialize each factory and build the device ID list */
397 for (i=0; i<vid_subsys.drv_cnt; ++i) {
398 status = init_driver(i, PJ_FALSE);
399 if (status != PJ_SUCCESS) {
400 deinit_driver(i);
401 continue;
402 }
403 }
404
405 return vid_subsys.dev_cnt ? PJ_SUCCESS : status;
406}
407
408/* API: register a video device factory to the video device subsystem. */
409PJ_DEF(pj_status_t)
410pjmedia_vid_register_factory(pjmedia_vid_dev_factory_create_func_ptr adf,
411 pjmedia_vid_dev_factory *factory)
412{
413 pj_bool_t refresh = PJ_FALSE;
414 pj_status_t status;
415
416 if (vid_subsys.init_count == 0)
417 return PJMEDIA_EVID_INIT;
418
419 vid_subsys.drv[vid_subsys.drv_cnt].create = adf;
420 vid_subsys.drv[vid_subsys.drv_cnt].f = factory;
421
422 if (factory) {
423 /* Call factory->init() */
424 status = factory->op->init(factory);
425 if (status != PJ_SUCCESS) {
426 factory->op->destroy(factory);
427 return status;
428 }
429 refresh = PJ_TRUE;
430 }
431
432 status = init_driver(vid_subsys.drv_cnt, refresh);
433 if (status == PJ_SUCCESS) {
434 vid_subsys.drv_cnt++;
435 } else {
436 deinit_driver(vid_subsys.drv_cnt);
437 }
438
439 return status;
440}
441
442/* API: unregister a video device factory from the video device subsystem. */
443PJ_DEF(pj_status_t)
444pjmedia_vid_unregister_factory(pjmedia_vid_dev_factory_create_func_ptr adf,
445 pjmedia_vid_dev_factory *factory)
446{
447 unsigned i, j;
448
449 if (vid_subsys.init_count == 0)
450 return PJMEDIA_EVID_INIT;
451
452 for (i=0; i<vid_subsys.drv_cnt; ++i) {
453 struct driver *drv = &vid_subsys.drv[i];
454
455 if ((factory && drv->f==factory) || (adf && drv->create == adf)) {
456 for (j = drv->start_idx; j < drv->start_idx + drv->dev_cnt; j++)
457 {
458 vid_subsys.dev_list[j] = (pj_uint32_t)PJMEDIA_VID_INVALID_DEV;
459 }
460
461 deinit_driver(i);
462 pj_bzero(drv, sizeof(*drv));
463 return PJ_SUCCESS;
464 }
465 }
466
467 return PJMEDIA_EVID_ERR;
468}
469
470/* API: get the pool factory registered to the video device subsystem. */
471PJ_DEF(pj_pool_factory*) pjmedia_vid_dev_subsys_get_pool_factory(void)
472{
473 return vid_subsys.pf;
474}
475
476/* API: Shutdown the video device subsystem. */
477PJ_DEF(pj_status_t) pjmedia_vid_dev_subsys_shutdown(void)
478{
479 unsigned i;
480
481 /* Allow shutdown() to be called multiple times as long as there is
482 * matching number of init().
483 */
484 if (vid_subsys.init_count == 0) {
485 return PJ_SUCCESS;
486 }
487 --vid_subsys.init_count;
488
489 if (vid_subsys.init_count == 0) {
490 for (i=0; i<vid_subsys.drv_cnt; ++i) {
491 deinit_driver(i);
492 }
493
494 vid_subsys.pf = NULL;
495 }
496 return PJ_SUCCESS;
497}
498
499/* API: Refresh the list of video devices installed in the system. */
500PJ_DEF(pj_status_t) pjmedia_vid_dev_refresh(void)
501{
502 unsigned i;
503
504 vid_subsys.dev_cnt = 0;
505 for (i=0; i<vid_subsys.drv_cnt; ++i) {
506 struct driver *drv = &vid_subsys.drv[i];
507
508 if (drv->f && drv->f->op->refresh) {
509 pj_status_t status = drv->f->op->refresh(drv->f);
510 if (status != PJ_SUCCESS) {
511 PJ_PERROR(4, (THIS_FILE, status, "Unable to refresh device "
512 "list for %s", drv->name));
513 }
514 }
515 init_driver(i, PJ_TRUE);
516 }
517 return PJ_SUCCESS;
518}
519
520/* API: Get the number of video devices installed in the system. */
521PJ_DEF(unsigned) pjmedia_vid_dev_count(void)
522{
523 return vid_subsys.dev_cnt;
524}
525
526/* Internal: convert local index to global device index */
527static pj_status_t make_global_index(unsigned drv_idx,
528 pjmedia_vid_dev_index *id)
529{
530 if (*id < 0) {
531 return PJ_SUCCESS;
532 }
533
534 /* Check that factory still exists */
535 PJ_ASSERT_RETURN(vid_subsys.drv[drv_idx].f, PJ_EBUG);
536
537 /* Check that device index is valid */
538 PJ_ASSERT_RETURN(*id>=0 && *id<(int)vid_subsys.drv[drv_idx].dev_cnt,
539 PJ_EBUG);
540
541 *id += vid_subsys.drv[drv_idx].start_idx;
542 return PJ_SUCCESS;
543}
544
545/* Internal: lookup device id */
546static pj_status_t lookup_dev(pjmedia_vid_dev_index id,
547 pjmedia_vid_dev_factory **p_f,
548 unsigned *p_local_index)
549{
550 int f_id, index;
551
552 if (id < 0) {
553 unsigned i;
554
555 if (id <= PJMEDIA_VID_INVALID_DEV)
556 return PJMEDIA_EVID_INVDEV;
557
558 for (i=0; i<vid_subsys.drv_cnt; ++i) {
559 struct driver *drv = &vid_subsys.drv[i];
560 if (id==PJMEDIA_VID_DEFAULT_CAPTURE_DEV &&
561 drv->cap_dev_idx >= 0)
562 {
563 id = drv->cap_dev_idx;
564 make_global_index(i, &id);
565 break;
566 } else if (id==PJMEDIA_VID_DEFAULT_RENDER_DEV &&
567 drv->rend_dev_idx >= 0)
568 {
569 id = drv->rend_dev_idx;
570 make_global_index(i, &id);
571 break;
572 }
573 }
574
575 if (id < 0) {
576 return PJMEDIA_EVID_NODEFDEV;
577 }
578 }
579
580 f_id = GET_FID(vid_subsys.dev_list[id]);
581 index = GET_INDEX(vid_subsys.dev_list[id]);
582
583 if (f_id < 0 || f_id >= (int)vid_subsys.drv_cnt)
584 return PJMEDIA_EVID_INVDEV;
585
586 if (index < 0 || index >= (int)vid_subsys.drv[f_id].dev_cnt)
587 return PJMEDIA_EVID_INVDEV;
588
589 *p_f = vid_subsys.drv[f_id].f;
590 *p_local_index = (unsigned)index;
591
592 return PJ_SUCCESS;
593
594}
595
596/* API: lookup device id */
597PJ_DEF(pj_status_t)
598pjmedia_vid_dev_get_local_index(pjmedia_vid_dev_index id,
599 pjmedia_vid_dev_factory **p_f,
600 unsigned *p_local_index)
601{
602 return lookup_dev(id, p_f, p_local_index);
603}
604
605/* API: from factory and local index, get global index */
606PJ_DEF(pj_status_t)
607pjmedia_vid_dev_get_global_index(const pjmedia_vid_dev_factory *f,
608 unsigned local_idx,
609 pjmedia_vid_dev_index *pid)
610{
611 PJ_ASSERT_RETURN(f->sys.drv_idx >= 0 && f->sys.drv_idx < MAX_DRIVERS,
612 PJ_EINVALIDOP);
613 *pid = local_idx;
614 return make_global_index(f->sys.drv_idx, pid);
615}
616
617/* API: Get device information. */
618PJ_DEF(pj_status_t) pjmedia_vid_dev_get_info(pjmedia_vid_dev_index id,
619 pjmedia_vid_dev_info *info)
620{
621 pjmedia_vid_dev_factory *f;
622 unsigned index;
623 pj_status_t status;
624
625 PJ_ASSERT_RETURN(info, PJ_EINVAL);
626 PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
627
628 if (id <= PJMEDIA_VID_INVALID_DEV)
629 return PJMEDIA_EVID_INVDEV;
630
631 status = lookup_dev(id, &f, &index);
632 if (status != PJ_SUCCESS)
633 return status;
634
635 status = f->op->get_dev_info(f, index, info);
636
637 /* Make sure device ID is the real ID (not PJMEDIA_VID_DEFAULT_*_DEV) */
638 info->id = index;
639 make_global_index(f->sys.drv_idx, &info->id);
640
641 return status;
642}
643
644/* API: find device */
645PJ_DEF(pj_status_t) pjmedia_vid_dev_lookup( const char *drv_name,
646 const char *dev_name,
647 pjmedia_vid_dev_index *id)
648{
649 pjmedia_vid_dev_factory *f = NULL;
650 unsigned drv_idx, dev_idx;
651
652 PJ_ASSERT_RETURN(drv_name && dev_name && id, PJ_EINVAL);
653 PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
654
655 for (drv_idx=0; drv_idx<vid_subsys.drv_cnt; ++drv_idx) {
656 if (!pj_ansi_stricmp(drv_name, vid_subsys.drv[drv_idx].name))
657 {
658 f = vid_subsys.drv[drv_idx].f;
659 break;
660 }
661 }
662
663 if (!f)
664 return PJ_ENOTFOUND;
665
666 for (dev_idx=0; dev_idx<vid_subsys.drv[drv_idx].dev_cnt; ++dev_idx)
667 {
668 pjmedia_vid_dev_info info;
669 pj_status_t status;
670
671 status = f->op->get_dev_info(f, dev_idx, &info);
672 if (status != PJ_SUCCESS)
673 return status;
674
675 if (!pj_ansi_stricmp(dev_name, info.name))
676 break;
677 }
678
679 if (dev_idx==vid_subsys.drv[drv_idx].dev_cnt)
680 return PJ_ENOTFOUND;
681
682 *id = dev_idx;
683 make_global_index(drv_idx, id);
684
685 return PJ_SUCCESS;
686}
687
688/* API: Initialize the video device parameters with default values for the
689 * specified device.
690 */
691PJ_DEF(pj_status_t) pjmedia_vid_dev_default_param(pj_pool_t *pool,
692 pjmedia_vid_dev_index id,
693 pjmedia_vid_dev_param *param)
694{
695 pjmedia_vid_dev_factory *f;
696 unsigned index;
697 pj_status_t status;
698
699 PJ_ASSERT_RETURN(param, PJ_EINVAL);
700 PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
701
702 if (id <= PJMEDIA_VID_INVALID_DEV)
703 return PJMEDIA_EVID_INVDEV;
704
705 status = lookup_dev(id, &f, &index);
706 if (status != PJ_SUCCESS)
707 return status;
708
709 status = f->op->default_param(pool, f, index, param);
710 if (status != PJ_SUCCESS)
711 return status;
712
713 /* Normalize device IDs */
714 make_global_index(f->sys.drv_idx, &param->cap_id);
715 make_global_index(f->sys.drv_idx, &param->rend_id);
716
717 return PJ_SUCCESS;
718}
719
720/* API: Open video stream object using the specified parameters. */
721PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_create(
722 pjmedia_vid_dev_param *prm,
723 const pjmedia_vid_dev_cb *cb,
724 void *user_data,
725 pjmedia_vid_dev_stream **p_vid_strm)
726{
727 pjmedia_vid_dev_factory *cap_f=NULL, *rend_f=NULL, *f=NULL;
728 pj_status_t status;
729
730 PJ_ASSERT_RETURN(prm && prm->dir && p_vid_strm, PJ_EINVAL);
731 PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
732 PJ_ASSERT_RETURN(prm->dir==PJMEDIA_DIR_CAPTURE ||
733 prm->dir==PJMEDIA_DIR_RENDER ||
734 prm->dir==PJMEDIA_DIR_CAPTURE_RENDER,
735 PJ_EINVAL);
736
737 /* Normalize cap_id */
738 if (prm->dir & PJMEDIA_DIR_CAPTURE) {
739 unsigned index;
740
741 if (prm->cap_id < 0)
742 prm->cap_id = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
743
744 status = lookup_dev(prm->cap_id, &cap_f, &index);
745 if (status != PJ_SUCCESS)
746 return status;
747
748 prm->cap_id = index;
749 f = cap_f;
750 }
751
752 /* Normalize rend_id */
753 if (prm->dir & PJMEDIA_DIR_RENDER) {
754 unsigned index;
755
756 if (prm->rend_id < 0)
757 prm->rend_id = PJMEDIA_VID_DEFAULT_RENDER_DEV;
758
759 status = lookup_dev(prm->rend_id, &rend_f, &index);
760 if (status != PJ_SUCCESS)
761 return status;
762
763 prm->rend_id = index;
764 f = rend_f;
765 }
766
767 PJ_ASSERT_RETURN(f != NULL, PJ_EBUG);
768
769 /* For now, cap_id and rend_id must belong to the same factory */
770 PJ_ASSERT_RETURN((prm->dir != PJMEDIA_DIR_CAPTURE_RENDER) ||
771 (cap_f == rend_f),
772 PJMEDIA_EVID_INVDEV);
773
774 /* Create the stream */
775 status = f->op->create_stream(f, prm, cb,
776 user_data, p_vid_strm);
777 if (status != PJ_SUCCESS)
778 return status;
779
780 /* Assign factory id to the stream */
781 (*p_vid_strm)->sys.drv_idx = f->sys.drv_idx;
782 return PJ_SUCCESS;
783}
784
785/* API: Get the running parameters for the specified video stream. */
786PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_get_param(
787 pjmedia_vid_dev_stream *strm,
788 pjmedia_vid_dev_param *param)
789{
790 pj_status_t status;
791
792 PJ_ASSERT_RETURN(strm && param, PJ_EINVAL);
793 PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
794
795 status = strm->op->get_param(strm, param);
796 if (status != PJ_SUCCESS)
797 return status;
798
799 /* Normalize device id's */
800 make_global_index(strm->sys.drv_idx, &param->cap_id);
801 make_global_index(strm->sys.drv_idx, &param->rend_id);
802
803 return PJ_SUCCESS;
804}
805
806/* API: Get the value of a specific capability of the video stream. */
807PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_get_cap(
808 pjmedia_vid_dev_stream *strm,
809 pjmedia_vid_dev_cap cap,
810 void *value)
811{
812 return strm->op->get_cap(strm, cap, value);
813}
814
815/* API: Set the value of a specific capability of the video stream. */
816PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_set_cap(
817 pjmedia_vid_dev_stream *strm,
818 pjmedia_vid_dev_cap cap,
819 const void *value)
820{
821 return strm->op->set_cap(strm, cap, value);
822}
823
824/* API: Start the stream. */
825PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_start(pjmedia_vid_dev_stream *strm)
826{
827 pj_status_t status;
828
829 if (pjmedia_vid_dev_stream_is_running(strm))
830 return PJ_SUCCESS;
831
832 status = strm->op->start(strm);
833 if (status == PJ_SUCCESS)
834 strm->sys.is_running = PJ_TRUE;
835 return status;
836}
837
838/* API: has it been started? */
839PJ_DEF(pj_bool_t)
840pjmedia_vid_dev_stream_is_running(pjmedia_vid_dev_stream *strm)
841{
842 return strm->sys.is_running;
843}
844
845PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_get_frame(
846 pjmedia_vid_dev_stream *strm,
847 pjmedia_frame *frame)
848{
849 pj_assert(strm->op->get_frame);
850 return strm->op->get_frame(strm, frame);
851}
852
853PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_put_frame(
854 pjmedia_vid_dev_stream *strm,
855 const pjmedia_frame *frame)
856{
857 pj_assert(strm->op->put_frame);
858 return strm->op->put_frame(strm, frame);
859}
860
861/* API: Stop the stream. */
862PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_stop(pjmedia_vid_dev_stream *strm)
863{
864 strm->sys.is_running = PJ_FALSE;
865 return strm->op->stop(strm);
866}
867
868/* API: Destroy the stream. */
869PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_destroy(
870 pjmedia_vid_dev_stream *strm)
871{
872 strm->sys.is_running = PJ_FALSE;
873 return strm->op->destroy(strm);
874}
875
876
877#endif /* PJMEDIA_HAS_VIDEO */