blob: b43dfffbbf93afe11e011eefb2c487ad56179ba8 [file] [log] [blame]
Benny Prijono9f468d12011-07-07 07:46:33 +00001/* $Id$ */
2/*
3 * Copyright (C) 2011-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 <pjsua-lib/pjsua.h>
20#include <pjsua-lib/pjsua_internal.h>
21
22#define THIS_FILE "pjsua_vid.c"
23
24#if PJSUA_HAS_VIDEO
25
Nanang Izzuddinc12a19d2011-10-03 05:23:59 +000026#define ENABLE_EVENT 1
Benny Prijonoaa15fbb2011-09-19 08:26:35 +000027#define VID_TEE_MAX_PORT (PJSUA_MAX_CALLS + 1)
28
29#define PJSUA_SHOW_WINDOW 1
30#define PJSUA_HIDE_WINDOW 0
31
Nanang Izzuddin15ad7f22011-08-26 04:19:04 +000032
Nanang Izzuddin62053a62011-07-12 11:08:32 +000033static void free_vid_win(pjsua_vid_win_id wid);
34
Benny Prijono9f468d12011-07-07 07:46:33 +000035/*****************************************************************************
36 * pjsua video subsystem.
37 */
38pj_status_t pjsua_vid_subsys_init(void)
39{
40 unsigned i;
41 pj_status_t status;
42
Benny Prijonob90fd382011-09-18 14:59:56 +000043 PJ_LOG(4,(THIS_FILE, "Initializing video subsystem.."));
44 pj_log_push_indent();
45
Benny Prijono9f468d12011-07-07 07:46:33 +000046 status = pjmedia_video_format_mgr_create(pjsua_var.pool, 64, 0, NULL);
47 if (status != PJ_SUCCESS) {
48 PJ_PERROR(1,(THIS_FILE, status,
49 "Error creating PJMEDIA video format manager"));
Benny Prijonob90fd382011-09-18 14:59:56 +000050 goto on_error;
Benny Prijono9f468d12011-07-07 07:46:33 +000051 }
52
53 status = pjmedia_converter_mgr_create(pjsua_var.pool, NULL);
54 if (status != PJ_SUCCESS) {
55 PJ_PERROR(1,(THIS_FILE, status,
56 "Error creating PJMEDIA converter manager"));
Benny Prijonob90fd382011-09-18 14:59:56 +000057 goto on_error;
Benny Prijono9f468d12011-07-07 07:46:33 +000058 }
59
60 status = pjmedia_vid_codec_mgr_create(pjsua_var.pool, NULL);
61 if (status != PJ_SUCCESS) {
62 PJ_PERROR(1,(THIS_FILE, status,
63 "Error creating PJMEDIA video codec manager"));
Benny Prijonob90fd382011-09-18 14:59:56 +000064 goto on_error;
Benny Prijono9f468d12011-07-07 07:46:33 +000065 }
66
67 status = pjmedia_vid_dev_subsys_init(&pjsua_var.cp.factory);
68 if (status != PJ_SUCCESS) {
69 PJ_PERROR(1,(THIS_FILE, status,
70 "Error creating PJMEDIA video subsystem"));
Benny Prijonob90fd382011-09-18 14:59:56 +000071 goto on_error;
Benny Prijono9f468d12011-07-07 07:46:33 +000072 }
73
74#if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_FFMPEG_CODEC
75 status = pjmedia_codec_ffmpeg_init(NULL, &pjsua_var.cp.factory);
76 if (status != PJ_SUCCESS) {
77 PJ_PERROR(1,(THIS_FILE, status,
78 "Error initializing ffmpeg library"));
Benny Prijonob90fd382011-09-18 14:59:56 +000079 goto on_error;
Benny Prijono9f468d12011-07-07 07:46:33 +000080 }
81#endif
82
83 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
84 if (pjsua_var.win[i].pool == NULL) {
85 pjsua_var.win[i].pool = pjsua_pool_create("win%p", 512, 512);
Benny Prijonob90fd382011-09-18 14:59:56 +000086 if (pjsua_var.win[i].pool == NULL) {
87 status = PJ_ENOMEM;
88 goto on_error;
89 }
Benny Prijono9f468d12011-07-07 07:46:33 +000090 }
91 }
92
Benny Prijonob90fd382011-09-18 14:59:56 +000093 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +000094 return PJ_SUCCESS;
Benny Prijonob90fd382011-09-18 14:59:56 +000095
96on_error:
97 pj_log_pop_indent();
98 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +000099}
100
101pj_status_t pjsua_vid_subsys_start(void)
102{
103 return PJ_SUCCESS;
104}
105
106pj_status_t pjsua_vid_subsys_destroy(void)
107{
108 unsigned i;
109
Benny Prijonob90fd382011-09-18 14:59:56 +0000110 PJ_LOG(4,(THIS_FILE, "Destroying video subsystem.."));
111 pj_log_push_indent();
112
Benny Prijono9f468d12011-07-07 07:46:33 +0000113 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
114 if (pjsua_var.win[i].pool) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000115 free_vid_win(i);
Benny Prijono9f468d12011-07-07 07:46:33 +0000116 pj_pool_release(pjsua_var.win[i].pool);
117 pjsua_var.win[i].pool = NULL;
118 }
119 }
120
121 pjmedia_vid_dev_subsys_shutdown();
122
123#if PJMEDIA_HAS_FFMPEG_CODEC
124 pjmedia_codec_ffmpeg_deinit();
125#endif
126
Benny Prijonob90fd382011-09-18 14:59:56 +0000127 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +0000128 return PJ_SUCCESS;
129}
130
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000131PJ_DEF(const char*) pjsua_vid_win_type_name(pjsua_vid_win_type wt)
132{
133 const char *win_type_names[] = {
134 "none",
135 "preview",
136 "stream"
137 };
138
139 return (wt < PJ_ARRAY_SIZE(win_type_names)) ? win_type_names[wt] : "??";
140}
141
Benny Prijonoe212bc12011-08-15 09:38:42 +0000142PJ_DEF(void)
143pjsua_call_vid_strm_op_param_default(pjsua_call_vid_strm_op_param *param)
144{
145 pj_bzero(param, sizeof(*param));
146 param->med_idx = -1;
147 param->dir = PJMEDIA_DIR_ENCODING_DECODING;
148 param->cap_dev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
149}
Benny Prijono9f468d12011-07-07 07:46:33 +0000150
Benny Prijono2047bd72011-08-25 11:59:39 +0000151PJ_DEF(void) pjsua_vid_preview_param_default(pjsua_vid_preview_param *p)
152{
153 p->rend_id = PJMEDIA_VID_DEFAULT_RENDER_DEV;
154 p->show = PJ_TRUE;
155}
156
157
Benny Prijono9f468d12011-07-07 07:46:33 +0000158/*****************************************************************************
159 * Devices.
160 */
161
162/*
163 * Get the number of video devices installed in the system.
164 */
165PJ_DEF(unsigned) pjsua_vid_dev_count(void)
166{
167 return pjmedia_vid_dev_count();
168}
169
170/*
171 * Retrieve the video device info for the specified device index.
172 */
173PJ_DEF(pj_status_t) pjsua_vid_dev_get_info(pjmedia_vid_dev_index id,
174 pjmedia_vid_dev_info *vdi)
175{
176 return pjmedia_vid_dev_get_info(id, vdi);
177}
178
179/*
180 * Enum all video devices installed in the system.
181 */
182PJ_DEF(pj_status_t) pjsua_vid_enum_devs(pjmedia_vid_dev_info info[],
183 unsigned *count)
184{
185 unsigned i, dev_count;
186
187 dev_count = pjmedia_vid_dev_count();
188
189 if (dev_count > *count) dev_count = *count;
190
191 for (i=0; i<dev_count; ++i) {
192 pj_status_t status;
193
194 status = pjmedia_vid_dev_get_info(i, &info[i]);
195 if (status != PJ_SUCCESS)
196 return status;
197 }
198
199 *count = dev_count;
200
201 return PJ_SUCCESS;
202}
203
204
205/*****************************************************************************
206 * Codecs.
207 */
208
209/*
210 * Enum all supported video codecs in the system.
211 */
212PJ_DEF(pj_status_t) pjsua_vid_enum_codecs( pjsua_codec_info id[],
213 unsigned *p_count )
214{
215 pjmedia_vid_codec_info info[32];
216 unsigned i, j, count, prio[32];
217 pj_status_t status;
218
219 count = PJ_ARRAY_SIZE(info);
220 status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, info, prio);
221 if (status != PJ_SUCCESS) {
222 *p_count = 0;
223 return status;
224 }
225
226 for (i=0, j=0; i<count && j<*p_count; ++i) {
Benny Prijono84685572011-09-29 08:31:15 +0000227 if (info[i].packings & PJMEDIA_VID_PACKING_PACKETS) {
Benny Prijono9f468d12011-07-07 07:46:33 +0000228 pj_bzero(&id[j], sizeof(pjsua_codec_info));
229
230 pjmedia_vid_codec_info_to_id(&info[i], id[j].buf_, sizeof(id[j].buf_));
231 id[j].codec_id = pj_str(id[j].buf_);
232 id[j].priority = (pj_uint8_t) prio[i];
233
234 if (id[j].codec_id.slen < sizeof(id[j].buf_)) {
235 id[j].desc.ptr = id[j].codec_id.ptr + id[j].codec_id.slen + 1;
236 pj_strncpy(&id[j].desc, &info[i].encoding_desc,
237 sizeof(id[j].buf_) - id[j].codec_id.slen - 1);
238 }
239
240 ++j;
241 }
242 }
243
244 *p_count = j;
245
246 return PJ_SUCCESS;
247}
248
249
250/*
251 * Change video codec priority.
252 */
253PJ_DEF(pj_status_t) pjsua_vid_codec_set_priority( const pj_str_t *codec_id,
254 pj_uint8_t priority )
255{
256 const pj_str_t all = { NULL, 0 };
257
258 if (codec_id->slen==1 && *codec_id->ptr=='*')
259 codec_id = &all;
260
261 return pjmedia_vid_codec_mgr_set_codec_priority(NULL, codec_id,
262 priority);
263}
264
265
266/*
267 * Get video codec parameters.
268 */
269PJ_DEF(pj_status_t) pjsua_vid_codec_get_param(
270 const pj_str_t *codec_id,
271 pjmedia_vid_codec_param *param)
272{
273 const pj_str_t all = { NULL, 0 };
274 const pjmedia_vid_codec_info *info;
275 unsigned count = 1;
276 pj_status_t status;
277
278 if (codec_id->slen==1 && *codec_id->ptr=='*')
279 codec_id = &all;
280
281 status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, codec_id,
282 &count, &info, NULL);
283 if (status != PJ_SUCCESS)
284 return status;
285
286 if (count != 1)
287 return (count > 1? PJ_ETOOMANY : PJ_ENOTFOUND);
288
289 status = pjmedia_vid_codec_mgr_get_default_param(NULL, info, param);
290 return status;
291}
292
293
294/*
295 * Set video codec parameters.
296 */
297PJ_DEF(pj_status_t) pjsua_vid_codec_set_param(
298 const pj_str_t *codec_id,
299 const pjmedia_vid_codec_param *param)
300{
301 const pjmedia_vid_codec_info *info[2];
302 unsigned count = 2;
303 pj_status_t status;
304
305 status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, codec_id,
306 &count, info, NULL);
307 if (status != PJ_SUCCESS)
308 return status;
309
310 /* Codec ID should be specific */
311 if (count > 1) {
312 pj_assert(!"Codec ID is not specific");
313 return PJ_ETOOMANY;
314 }
315
316 status = pjmedia_vid_codec_mgr_set_default_param(NULL, pjsua_var.pool,
317 info[0], param);
318 return status;
319}
320
321
322/*****************************************************************************
323 * Preview
324 */
325
Benny Prijono39203b82011-09-20 10:07:55 +0000326static pjsua_vid_win_id vid_preview_get_win(pjmedia_vid_dev_index id,
327 pj_bool_t running_only)
Benny Prijono9f468d12011-07-07 07:46:33 +0000328{
329 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
330 unsigned i;
331
332 PJSUA_LOCK();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000333
334 /* Get real capture ID, if set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV */
335 if (id == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
336 pjmedia_vid_dev_info info;
337 pjmedia_vid_dev_get_info(id, &info);
338 id = info.id;
339 }
340
Benny Prijono9f468d12011-07-07 07:46:33 +0000341 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
342 pjsua_vid_win *w = &pjsua_var.win[i];
343 if (w->type == PJSUA_WND_TYPE_PREVIEW && w->preview_cap_id == id) {
344 wid = i;
345 break;
346 }
347 }
Benny Prijono39203b82011-09-20 10:07:55 +0000348
349 if (wid != PJSUA_INVALID_ID && running_only) {
350 pjsua_vid_win *w = &pjsua_var.win[wid];
351 wid = w->preview_running ? wid : PJSUA_INVALID_ID;
352 }
353
Benny Prijono9f468d12011-07-07 07:46:33 +0000354 PJSUA_UNLOCK();
355
356 return wid;
357}
358
Benny Prijono39203b82011-09-20 10:07:55 +0000359/*
360 * NOTE: internal function don't use this!!! Use vid_preview_get_win()
361 * instead. This is because this function will only return window ID
362 * if preview is currently running.
363 */
364PJ_DEF(pjsua_vid_win_id) pjsua_vid_preview_get_win(pjmedia_vid_dev_index id)
365{
366 return vid_preview_get_win(id, PJ_TRUE);
367}
368
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000369PJ_DEF(void) pjsua_vid_win_reset(pjsua_vid_win_id wid)
370{
371 pjsua_vid_win *w = &pjsua_var.win[wid];
372 pj_pool_t *pool = w->pool;
373
374 pj_bzero(w, sizeof(*w));
375 if (pool) pj_pool_reset(pool);
376 w->ref_cnt = 0;
377 w->pool = pool;
378 w->preview_cap_id = PJMEDIA_VID_INVALID_DEV;
379}
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000380
381/* Allocate and initialize pjsua video window:
382 * - If the type is preview, video capture, tee, and render
383 * will be instantiated.
384 * - If the type is stream, only renderer will be created.
385 */
386static pj_status_t create_vid_win(pjsua_vid_win_type type,
387 const pjmedia_format *fmt,
388 pjmedia_vid_dev_index rend_id,
389 pjmedia_vid_dev_index cap_id,
390 pj_bool_t show,
391 pjsua_vid_win_id *id)
Benny Prijono9f468d12011-07-07 07:46:33 +0000392{
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000393 pj_bool_t enable_native_preview;
Benny Prijono9f468d12011-07-07 07:46:33 +0000394 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000395 pjsua_vid_win *w = NULL;
396 pjmedia_vid_port_param vp_param;
397 pjmedia_format fmt_;
398 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000399 unsigned i;
400
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000401 enable_native_preview = pjsua_var.media_cfg.vid_preview_enable_native;
402
403 PJ_LOG(4,(THIS_FILE,
404 "Creating video window: type=%s, cap_id=%d, rend_id=%d",
405 pjsua_vid_win_type_name(type), cap_id, rend_id));
Benny Prijonob90fd382011-09-18 14:59:56 +0000406 pj_log_push_indent();
407
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000408 /* If type is preview, check if it exists already */
409 if (type == PJSUA_WND_TYPE_PREVIEW) {
Benny Prijono39203b82011-09-20 10:07:55 +0000410 wid = vid_preview_get_win(cap_id, PJ_FALSE);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000411 if (wid != PJSUA_INVALID_ID) {
412 /* Yes, it exists */
Benny Prijono2047bd72011-08-25 11:59:39 +0000413 /* Show/hide window */
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000414 pjmedia_vid_dev_stream *strm;
Benny Prijono2047bd72011-08-25 11:59:39 +0000415 pj_bool_t hide = !show;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000416
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000417 w = &pjsua_var.win[wid];
418
419 PJ_LOG(4,(THIS_FILE,
420 "Window already exists for cap_dev=%d, returning wid=%d",
421 cap_id, wid));
422
423
424 if (w->is_native) {
425 strm = pjmedia_vid_port_get_stream(w->vp_cap);
426 } else {
427 strm = pjmedia_vid_port_get_stream(w->vp_rend);
428 }
429
430 pj_assert(strm);
Benny Prijono2047bd72011-08-25 11:59:39 +0000431 status = pjmedia_vid_dev_stream_set_cap(
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000432 strm, PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE,
Benny Prijono2047bd72011-08-25 11:59:39 +0000433 &hide);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000434
435 /* Done */
436 *id = wid;
Benny Prijonob90fd382011-09-18 14:59:56 +0000437 pj_log_pop_indent();
Benny Prijono39203b82011-09-20 10:07:55 +0000438
439 return status;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000440 }
441 }
442
443 /* Allocate window */
Benny Prijono9f468d12011-07-07 07:46:33 +0000444 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000445 w = &pjsua_var.win[i];
Benny Prijono9f468d12011-07-07 07:46:33 +0000446 if (w->type == PJSUA_WND_TYPE_NONE) {
447 wid = i;
448 w->type = type;
449 break;
450 }
451 }
Benny Prijonob90fd382011-09-18 14:59:56 +0000452 if (i == PJSUA_MAX_VID_WINS) {
453 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000454 return PJ_ETOOMANY;
Benny Prijonob90fd382011-09-18 14:59:56 +0000455 }
Benny Prijono9f468d12011-07-07 07:46:33 +0000456
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000457 /* Initialize window */
458 pjmedia_vid_port_param_default(&vp_param);
459
460 if (w->type == PJSUA_WND_TYPE_PREVIEW) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000461 pjmedia_vid_dev_info vdi;
462
463 /*
464 * Determine if the device supports native preview.
465 */
466 status = pjmedia_vid_dev_get_info(cap_id, &vdi);
467 if (status != PJ_SUCCESS)
468 goto on_error;
469
470 if (enable_native_preview &&
471 (vdi.caps & PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW))
472 {
473 /* Device supports native preview! */
474 w->is_native = PJ_TRUE;
475 }
476
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000477 status = pjmedia_vid_dev_default_param(w->pool, cap_id,
478 &vp_param.vidparam);
479 if (status != PJ_SUCCESS)
480 goto on_error;
481
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000482 if (w->is_native) {
483 vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
484 vp_param.vidparam.window_hide = !show;
485 }
486
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000487 /* Normalize capture ID, in case it was set to
488 * PJMEDIA_VID_DEFAULT_CAPTURE_DEV
489 */
490 cap_id = vp_param.vidparam.cap_id;
491
492 /* Assign preview capture device ID */
493 w->preview_cap_id = cap_id;
494
495 /* Create capture video port */
496 vp_param.active = PJ_TRUE;
497 vp_param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
498 if (fmt)
499 vp_param.vidparam.fmt = *fmt;
500
501 status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_cap);
502 if (status != PJ_SUCCESS)
503 goto on_error;
504
505 /* Update format info */
506 fmt_ = vp_param.vidparam.fmt;
507 fmt = &fmt_;
508
509 /* Create video tee */
Nanang Izzuddin15ad7f22011-08-26 04:19:04 +0000510 status = pjmedia_vid_tee_create(w->pool, fmt, VID_TEE_MAX_PORT,
511 &w->tee);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000512 if (status != PJ_SUCCESS)
513 goto on_error;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000514
515 /* If device supports native preview, enable it */
516 if (w->is_native) {
517 pjmedia_vid_dev_stream *cap_dev;
518 pj_bool_t enabled = PJ_TRUE;
519
520 cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
521 status = pjmedia_vid_dev_stream_set_cap(
522 cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
523 &enabled);
524 if (status != PJ_SUCCESS) {
525 PJ_PERROR(1,(THIS_FILE, status,
526 "Error activating native preview, falling back "
527 "to software preview.."));
528 w->is_native = PJ_FALSE;
529 }
530 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000531 }
532
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000533 /* Create renderer video port, only if it's not a native preview */
534 if (!w->is_native) {
535 status = pjmedia_vid_dev_default_param(w->pool, rend_id,
536 &vp_param.vidparam);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000537 if (status != PJ_SUCCESS)
538 goto on_error;
539
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000540 vp_param.active = (w->type == PJSUA_WND_TYPE_STREAM);
541 vp_param.vidparam.dir = PJMEDIA_DIR_RENDER;
542 vp_param.vidparam.fmt = *fmt;
543 vp_param.vidparam.disp_size = fmt->det.vid.size;
544 vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
545 vp_param.vidparam.window_hide = !show;
546
547 status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_rend);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000548 if (status != PJ_SUCCESS)
549 goto on_error;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000550
551 /* For preview window, connect capturer & renderer (via tee) */
552 if (w->type == PJSUA_WND_TYPE_PREVIEW) {
553 pjmedia_port *rend_port;
554
555 status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
556 if (status != PJ_SUCCESS)
557 goto on_error;
558
559 rend_port = pjmedia_vid_port_get_passive_port(w->vp_rend);
560 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, rend_port);
561 if (status != PJ_SUCCESS)
562 goto on_error;
563 }
564
565 PJ_LOG(4,(THIS_FILE,
566 "%s window id %d created for cap_dev=%d rend_dev=%d",
567 pjsua_vid_win_type_name(type), wid, cap_id, rend_id));
568 } else {
569 PJ_LOG(4,(THIS_FILE,
570 "Preview window id %d created for cap_dev %d, "
571 "using built-in preview!",
572 wid, cap_id));
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000573 }
574
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000575
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000576 /* Done */
577 *id = wid;
578
Benny Prijonob90fd382011-09-18 14:59:56 +0000579 PJ_LOG(4,(THIS_FILE, "Window %d created", wid));
580 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000581 return PJ_SUCCESS;
582
583on_error:
584 free_vid_win(wid);
Benny Prijonob90fd382011-09-18 14:59:56 +0000585 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000586 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000587}
588
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000589
Benny Prijono9f468d12011-07-07 07:46:33 +0000590static void free_vid_win(pjsua_vid_win_id wid)
591{
592 pjsua_vid_win *w = &pjsua_var.win[wid];
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000593
Benny Prijonob90fd382011-09-18 14:59:56 +0000594 PJ_LOG(4,(THIS_FILE, "Window %d: destroying..", wid));
595 pj_log_push_indent();
596
Benny Prijono9f468d12011-07-07 07:46:33 +0000597 if (w->vp_cap) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000598 pjmedia_vid_port_stop(w->vp_cap);
599 pjmedia_vid_port_disconnect(w->vp_cap);
Benny Prijono9f468d12011-07-07 07:46:33 +0000600 pjmedia_vid_port_destroy(w->vp_cap);
601 }
602 if (w->vp_rend) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000603 pjmedia_vid_port_stop(w->vp_rend);
Benny Prijono9f468d12011-07-07 07:46:33 +0000604 pjmedia_vid_port_destroy(w->vp_rend);
605 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000606 if (w->tee) {
607 pjmedia_port_destroy(w->tee);
608 }
Benny Prijono9f468d12011-07-07 07:46:33 +0000609 pjsua_vid_win_reset(wid);
Benny Prijonob90fd382011-09-18 14:59:56 +0000610
611 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +0000612}
613
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000614
615static void inc_vid_win(pjsua_vid_win_id wid)
616{
617 pjsua_vid_win *w;
618
619 pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
620
621 w = &pjsua_var.win[wid];
622 pj_assert(w->type != PJSUA_WND_TYPE_NONE);
623 ++w->ref_cnt;
624}
625
626static void dec_vid_win(pjsua_vid_win_id wid)
627{
628 pjsua_vid_win *w;
629
630 pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
631
632 w = &pjsua_var.win[wid];
633 pj_assert(w->type != PJSUA_WND_TYPE_NONE);
634 if (--w->ref_cnt == 0)
635 free_vid_win(wid);
636}
637
638
639/* Internal function: update video channel after SDP negotiation */
640pj_status_t video_channel_update(pjsua_call_media *call_med,
641 pj_pool_t *tmp_pool,
642 const pjmedia_sdp_session *local_sdp,
643 const pjmedia_sdp_session *remote_sdp)
644{
645 pjsua_call *call = call_med->call;
646 pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
647 pjmedia_vid_stream_info the_si, *si = &the_si;
648 pjmedia_port *media_port;
649 unsigned strm_idx = call_med->idx;
650 pj_status_t status;
651
Benny Prijonob90fd382011-09-18 14:59:56 +0000652 PJ_LOG(4,(THIS_FILE, "Video channel update.."));
653 pj_log_push_indent();
654
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000655 status = pjmedia_vid_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt,
656 local_sdp, remote_sdp, strm_idx);
657 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000658 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000659
660 /* Check if no media is active */
661 if (si->dir == PJMEDIA_DIR_NONE) {
662 /* Call media state */
663 call_med->state = PJSUA_CALL_MEDIA_NONE;
664
665 /* Call media direction */
666 call_med->dir = PJMEDIA_DIR_NONE;
667
668 } else {
669 pjmedia_transport_info tp_info;
670
671 /* Start/restart media transport */
672 status = pjmedia_transport_media_start(call_med->tp,
673 tmp_pool, local_sdp,
674 remote_sdp, strm_idx);
675 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000676 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000677
Sauw Ming73ecfe82011-09-21 10:20:01 +0000678 set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000679
680 /* Get remote SRTP usage policy */
681 pjmedia_transport_info_init(&tp_info);
682 pjmedia_transport_get_info(call_med->tp, &tp_info);
683 if (tp_info.specific_info_cnt > 0) {
684 unsigned i;
685 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
686 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
687 {
688 pjmedia_srtp_info *srtp_info =
689 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
690
691 call_med->rem_srtp_use = srtp_info->peer_use;
692 break;
693 }
694 }
695 }
696
697 /* Optionally, application may modify other stream settings here
698 * (such as jitter buffer parameters, codec ptime, etc.)
699 */
700 si->jb_init = pjsua_var.media_cfg.jb_init;
701 si->jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
702 si->jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
703 si->jb_max = pjsua_var.media_cfg.jb_max;
704
705 /* Set SSRC */
706 si->ssrc = call_med->ssrc;
707
708 /* Set RTP timestamp & sequence, normally these value are intialized
709 * automatically when stream session created, but for some cases (e.g:
710 * call reinvite, call update) timestamp and sequence need to be kept
711 * contigue.
712 */
713 si->rtp_ts = call_med->rtp_tx_ts;
714 si->rtp_seq = call_med->rtp_tx_seq;
715 si->rtp_seq_ts_set = call_med->rtp_tx_seq_ts_set;
716
717#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
718 /* Enable/disable stream keep-alive and NAT hole punch. */
719 si->use_ka = pjsua_var.acc[call->acc_id].cfg.use_stream_ka;
720#endif
721
722 /* Try to get shared format ID between the capture device and
723 * the encoder to avoid format conversion in the capture device.
724 */
725 if (si->dir & PJMEDIA_DIR_ENCODING) {
726 pjmedia_vid_dev_info dev_info;
727 pjmedia_vid_codec_info *codec_info = &si->codec_info;
728 unsigned i, j;
729
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +0000730 status = pjmedia_vid_dev_get_info(call_med->strm.v.cap_dev,
731 &dev_info);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000732 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000733 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000734
735 /* Find matched format ID */
736 for (i = 0; i < codec_info->dec_fmt_id_cnt; ++i) {
737 for (j = 0; j < dev_info.fmt_cnt; ++j) {
738 if (codec_info->dec_fmt_id[i] ==
739 (pjmedia_format_id)dev_info.fmt[j].id)
740 {
741 /* Apply the matched format ID to the codec */
742 si->codec_param->dec_fmt.id =
743 codec_info->dec_fmt_id[i];
744
745 /* Force outer loop to break */
746 i = codec_info->dec_fmt_id_cnt;
747 break;
748 }
749 }
750 }
751 }
752
753 /* Create session based on session info. */
754 status = pjmedia_vid_stream_create(pjsua_var.med_endpt, NULL, si,
755 call_med->tp, NULL,
756 &call_med->strm.v.stream);
757 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000758 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000759
760 /* Start stream */
761 status = pjmedia_vid_stream_start(call_med->strm.v.stream);
762 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000763 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000764
765 /* Setup decoding direction */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000766 if (si->dir & PJMEDIA_DIR_DECODING)
767 {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000768 pjsua_vid_win_id wid;
769 pjsua_vid_win *w;
770
Benny Prijonob90fd382011-09-18 14:59:56 +0000771 PJ_LOG(4,(THIS_FILE, "Setting up RX.."));
772 pj_log_push_indent();
773
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000774 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
775 PJMEDIA_DIR_DECODING,
776 &media_port);
Benny Prijonob90fd382011-09-18 14:59:56 +0000777 if (status != PJ_SUCCESS) {
778 pj_log_pop_indent();
779 goto on_error;
780 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000781
782 /* Create stream video window */
783 status = create_vid_win(PJSUA_WND_TYPE_STREAM,
784 &media_port->info.fmt,
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000785 call_med->strm.v.rdr_dev,
786 //acc->cfg.vid_rend_dev,
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000787 PJSUA_INVALID_ID,
788 acc->cfg.vid_in_auto_show,
789 &wid);
Benny Prijonob90fd382011-09-18 14:59:56 +0000790 if (status != PJ_SUCCESS) {
791 pj_log_pop_indent();
792 goto on_error;
793 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000794
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000795 w = &pjsua_var.win[wid];
796
Benny Prijono6565b582011-08-29 09:54:02 +0000797#if ENABLE_EVENT
Benny Prijonoee0ba182011-07-15 06:18:29 +0000798 /* Register to video events */
799 pjmedia_event_subscribe(
800 pjmedia_vid_port_get_event_publisher(w->vp_rend),
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000801 &call_med->esub_rend);
Benny Prijono6565b582011-08-29 09:54:02 +0000802#endif
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000803
804 /* Connect renderer to stream */
805 status = pjmedia_vid_port_connect(w->vp_rend, media_port,
806 PJ_FALSE);
Benny Prijonob90fd382011-09-18 14:59:56 +0000807 if (status != PJ_SUCCESS) {
808 pj_log_pop_indent();
809 goto on_error;
810 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000811
812 /* Start renderer */
813 status = pjmedia_vid_port_start(w->vp_rend);
Benny Prijonob90fd382011-09-18 14:59:56 +0000814 if (status != PJ_SUCCESS) {
815 pj_log_pop_indent();
816 goto on_error;
817 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000818
819 /* Done */
820 inc_vid_win(wid);
821 call_med->strm.v.rdr_win_id = wid;
Benny Prijonob90fd382011-09-18 14:59:56 +0000822 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000823 }
824
825 /* Setup encoding direction */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000826 if (si->dir & PJMEDIA_DIR_ENCODING && !call->local_hold)
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000827 {
828 pjsua_vid_win *w;
829 pjsua_vid_win_id wid;
830
Benny Prijonob90fd382011-09-18 14:59:56 +0000831 PJ_LOG(4,(THIS_FILE, "Setting up TX.."));
832 pj_log_push_indent();
833
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000834 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
835 PJMEDIA_DIR_ENCODING,
836 &media_port);
Benny Prijonob90fd382011-09-18 14:59:56 +0000837 if (status != PJ_SUCCESS) {
838 pj_log_pop_indent();
839 goto on_error;
840 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000841
Benny Prijono39203b82011-09-20 10:07:55 +0000842 /* Note: calling pjsua_vid_preview_get_win() even though
843 * create_vid_win() will automatically create the window
844 * if it doesn't exist, because create_vid_win() will modify
845 * existing window SHOW/HIDE value.
846 */
847 wid = vid_preview_get_win(call_med->strm.v.cap_dev, PJ_FALSE);
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000848 if (wid == PJSUA_INVALID_ID) {
849 /* Create preview video window */
850 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
851 &media_port->info.fmt,
852 call_med->strm.v.rdr_dev,
853 call_med->strm.v.cap_dev,
854 //acc->cfg.vid_rend_dev,
855 //acc->cfg.vid_cap_dev,
856 PJSUA_HIDE_WINDOW,
857 &wid);
858 if (status != PJ_SUCCESS) {
Benny Prijonob90fd382011-09-18 14:59:56 +0000859 pj_log_pop_indent();
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000860 return status;
861 }
Benny Prijonob90fd382011-09-18 14:59:56 +0000862 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000863
864 w = &pjsua_var.win[wid];
Benny Prijono6565b582011-08-29 09:54:02 +0000865#if ENABLE_EVENT
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000866 pjmedia_event_subscribe(
867 pjmedia_vid_port_get_event_publisher(w->vp_cap),
868 &call_med->esub_cap);
Benny Prijono6565b582011-08-29 09:54:02 +0000869#endif
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000870
871 /* Connect stream to capturer (via video window tee) */
872 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
Benny Prijonob90fd382011-09-18 14:59:56 +0000873 if (status != PJ_SUCCESS) {
874 pj_log_pop_indent();
875 goto on_error;
876 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000877
878 /* Start renderer */
879 status = pjmedia_vid_port_start(w->vp_rend);
Benny Prijonob90fd382011-09-18 14:59:56 +0000880 if (status != PJ_SUCCESS) {
881 pj_log_pop_indent();
882 goto on_error;
883 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000884
885 /* Start capturer */
886 status = pjmedia_vid_port_start(w->vp_cap);
Benny Prijonob90fd382011-09-18 14:59:56 +0000887 if (status != PJ_SUCCESS) {
888 pj_log_pop_indent();
889 goto on_error;
890 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000891
892 /* Done */
893 inc_vid_win(wid);
894 call_med->strm.v.cap_win_id = wid;
Benny Prijonob90fd382011-09-18 14:59:56 +0000895 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000896 }
897
898 /* Call media direction */
899 call_med->dir = si->dir;
900
901 /* Call media state */
902 if (call->local_hold)
903 call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
904 else if (call_med->dir == PJMEDIA_DIR_DECODING)
905 call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
906 else
907 call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
908 }
909
910 /* Print info. */
911 {
912 char info[80];
913 int info_len = 0;
914 int len;
915 const char *dir;
916
917 switch (si->dir) {
918 case PJMEDIA_DIR_NONE:
919 dir = "inactive";
920 break;
921 case PJMEDIA_DIR_ENCODING:
922 dir = "sendonly";
923 break;
924 case PJMEDIA_DIR_DECODING:
925 dir = "recvonly";
926 break;
927 case PJMEDIA_DIR_ENCODING_DECODING:
928 dir = "sendrecv";
929 break;
930 default:
931 dir = "unknown";
932 break;
933 }
934 len = pj_ansi_sprintf( info+info_len,
935 ", stream #%d: %.*s (%s)", strm_idx,
936 (int)si->codec_info.encoding_name.slen,
937 si->codec_info.encoding_name.ptr,
938 dir);
939 if (len > 0)
940 info_len += len;
Benny Prijonob90fd382011-09-18 14:59:56 +0000941 PJ_LOG(4,(THIS_FILE,"Video updated%s", info));
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000942 }
943
Sauw Mingc7bc3aa2011-07-15 07:22:49 +0000944 if (!acc->cfg.vid_out_auto_transmit && call_med->strm.v.stream) {
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000945 status = pjmedia_vid_stream_pause(call_med->strm.v.stream,
946 PJMEDIA_DIR_ENCODING);
947 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000948 goto on_error;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000949 }
950
Benny Prijonob90fd382011-09-18 14:59:56 +0000951 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000952 return PJ_SUCCESS;
Benny Prijonob90fd382011-09-18 14:59:56 +0000953
954on_error:
955 pj_log_pop_indent();
956 return status;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000957}
958
959
960/* Internal function to stop video stream */
961void stop_video_stream(pjsua_call_media *call_med)
962{
963 pjmedia_vid_stream *strm = call_med->strm.v.stream;
964 pjmedia_rtcp_stat stat;
965
966 pj_assert(call_med->type == PJMEDIA_TYPE_VIDEO);
967
968 if (!strm)
969 return;
970
Benny Prijonob90fd382011-09-18 14:59:56 +0000971 PJ_LOG(4,(THIS_FILE, "Stopping video stream.."));
972 pj_log_push_indent();
973
Benny Prijonoa0dbe052011-08-29 04:16:14 +0000974 if (call_med->strm.v.cap_win_id != PJSUA_INVALID_ID) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000975 pjmedia_port *media_port;
Nanang Izzuddinc12a19d2011-10-03 05:23:59 +0000976 pjsua_vid_win *w = &pjsua_var.win[call_med->strm.v.cap_win_id];
Nanang Izzuddinca356182011-09-23 04:00:27 +0000977 pj_status_t status;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000978
Nanang Izzuddinc12a19d2011-10-03 05:23:59 +0000979 /* Stop the capture before detaching stream and unsubscribing event */
980 pjmedia_vid_port_stop(w->vp_cap);
981
Nanang Izzuddinca356182011-09-23 04:00:27 +0000982 /* Disconnect video stream from capture device */
983 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
984 PJMEDIA_DIR_ENCODING,
985 &media_port);
986 if (status == PJ_SUCCESS) {
Nanang Izzuddinca356182011-09-23 04:00:27 +0000987 pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
Nanang Izzuddinca356182011-09-23 04:00:27 +0000988 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000989
Nanang Izzuddinc12a19d2011-10-03 05:23:59 +0000990 /* Unsubscribe event */
991 pjmedia_event_unsubscribe(&call_med->esub_cap);
992
993 /* Re-start capture again, if it is used by other stream */
994 if (w->ref_cnt > 1)
995 pjmedia_vid_port_start(w->vp_cap);
996
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000997 dec_vid_win(call_med->strm.v.cap_win_id);
Nanang Izzuddinca356182011-09-23 04:00:27 +0000998 call_med->strm.v.cap_win_id = PJSUA_INVALID_ID;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000999 }
1000
Benny Prijonoa0dbe052011-08-29 04:16:14 +00001001 if (call_med->strm.v.rdr_win_id != PJSUA_INVALID_ID) {
Nanang Izzuddinc12a19d2011-10-03 05:23:59 +00001002 pjsua_vid_win *w = &pjsua_var.win[call_med->strm.v.rdr_win_id];
1003
1004 /* Stop the render before unsubscribing event */
1005 pjmedia_vid_port_stop(w->vp_rend);
1006 pjmedia_event_unsubscribe(&call_med->esub_rend);
1007
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001008 dec_vid_win(call_med->strm.v.rdr_win_id);
Nanang Izzuddinca356182011-09-23 04:00:27 +00001009 call_med->strm.v.rdr_win_id = PJSUA_INVALID_ID;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001010 }
1011
1012 if ((call_med->dir & PJMEDIA_DIR_ENCODING) &&
1013 (pjmedia_vid_stream_get_stat(strm, &stat) == PJ_SUCCESS))
1014 {
1015 /* Save RTP timestamp & sequence, so when media session is
1016 * restarted, those values will be restored as the initial
1017 * RTP timestamp & sequence of the new media session. So in
1018 * the same call session, RTP timestamp and sequence are
1019 * guaranteed to be contigue.
1020 */
1021 call_med->rtp_tx_seq_ts_set = 1 | (1 << 1);
1022 call_med->rtp_tx_seq = stat.rtp_tx_last_seq;
1023 call_med->rtp_tx_ts = stat.rtp_tx_last_ts;
1024 }
1025
1026 pjmedia_vid_stream_destroy(strm);
1027 call_med->strm.v.stream = NULL;
Benny Prijonob90fd382011-09-18 14:59:56 +00001028
1029 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001030}
1031
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001032/*
1033 * Does it have built-in preview support.
1034 */
1035PJ_DEF(pj_bool_t) pjsua_vid_preview_has_native(pjmedia_vid_dev_index id)
1036{
1037 pjmedia_vid_dev_info vdi;
1038
1039 return (pjmedia_vid_dev_get_info(id, &vdi)==PJ_SUCCESS) ?
1040 ((vdi.caps & PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW)!=0) : PJ_FALSE;
1041}
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001042
Benny Prijono9f468d12011-07-07 07:46:33 +00001043/*
1044 * Start video preview window for the specified capture device.
1045 */
1046PJ_DEF(pj_status_t) pjsua_vid_preview_start(pjmedia_vid_dev_index id,
Benny Prijono2047bd72011-08-25 11:59:39 +00001047 const pjsua_vid_preview_param *prm)
Benny Prijono9f468d12011-07-07 07:46:33 +00001048{
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001049 pjsua_vid_win_id wid;
1050 pjsua_vid_win *w;
1051 pjmedia_vid_dev_index rend_id;
Benny Prijono2047bd72011-08-25 11:59:39 +00001052 pjsua_vid_preview_param default_param;
Benny Prijono9f468d12011-07-07 07:46:33 +00001053 pj_status_t status;
1054
Benny Prijono2047bd72011-08-25 11:59:39 +00001055 if (!prm) {
1056 pjsua_vid_preview_param_default(&default_param);
1057 prm = &default_param;
Benny Prijono9f468d12011-07-07 07:46:33 +00001058 }
1059
Benny Prijonob90fd382011-09-18 14:59:56 +00001060 PJ_LOG(4,(THIS_FILE, "Starting preview for cap_dev=%d, show=%d",
1061 id, prm->show));
1062 pj_log_push_indent();
1063
1064 PJSUA_LOCK();
1065
Benny Prijono2047bd72011-08-25 11:59:39 +00001066 rend_id = prm->rend_id;
1067
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001068 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW, NULL, rend_id, id,
Benny Prijono2047bd72011-08-25 11:59:39 +00001069 prm->show, &wid);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001070 if (status != PJ_SUCCESS) {
Benny Prijono9f468d12011-07-07 07:46:33 +00001071 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001072 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001073 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001074 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001075
Benny Prijono9f468d12011-07-07 07:46:33 +00001076 w = &pjsua_var.win[wid];
Benny Prijono39203b82011-09-20 10:07:55 +00001077 if (w->preview_running) {
1078 PJSUA_UNLOCK();
1079 pj_log_pop_indent();
1080 return PJ_SUCCESS;
1081 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001082
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001083 /* Start renderer, unless it's native preview */
Benny Prijono39203b82011-09-20 10:07:55 +00001084 if (w->is_native && !pjmedia_vid_port_is_running(w->vp_cap)) {
1085 pjmedia_vid_dev_stream *cap_dev;
1086 pj_bool_t enabled = PJ_TRUE;
1087
1088 cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
1089 status = pjmedia_vid_dev_stream_set_cap(
1090 cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
1091 &enabled);
1092 if (status != PJ_SUCCESS) {
1093 PJ_PERROR(1,(THIS_FILE, status,
1094 "Error activating native preview, falling back "
1095 "to software preview.."));
1096 w->is_native = PJ_FALSE;
1097 }
1098 }
1099
1100 if (!w->is_native && !pjmedia_vid_port_is_running(w->vp_rend)) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001101 status = pjmedia_vid_port_start(w->vp_rend);
1102 if (status != PJ_SUCCESS) {
1103 PJSUA_UNLOCK();
1104 pj_log_pop_indent();
1105 return status;
1106 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001107 }
1108
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001109 /* Start capturer */
Benny Prijono39203b82011-09-20 10:07:55 +00001110 if (!pjmedia_vid_port_is_running(w->vp_cap)) {
1111 status = pjmedia_vid_port_start(w->vp_cap);
1112 if (status != PJ_SUCCESS) {
1113 PJSUA_UNLOCK();
1114 pj_log_pop_indent();
1115 return status;
1116 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001117 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001118
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001119 inc_vid_win(wid);
Benny Prijono39203b82011-09-20 10:07:55 +00001120 w->preview_running = PJ_TRUE;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001121
Benny Prijono9f468d12011-07-07 07:46:33 +00001122 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001123 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +00001124 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +00001125}
1126
1127/*
1128 * Stop video preview.
1129 */
1130PJ_DEF(pj_status_t) pjsua_vid_preview_stop(pjmedia_vid_dev_index id)
1131{
1132 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
Benny Prijono39203b82011-09-20 10:07:55 +00001133 pjsua_vid_win *w;
1134 pj_status_t status;
Benny Prijonob90fd382011-09-18 14:59:56 +00001135
Benny Prijono9f468d12011-07-07 07:46:33 +00001136 PJSUA_LOCK();
1137 wid = pjsua_vid_preview_get_win(id);
1138 if (wid == PJSUA_INVALID_ID) {
1139 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001140 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +00001141 return PJ_ENOTFOUND;
1142 }
1143
Benny Prijono39203b82011-09-20 10:07:55 +00001144 PJ_LOG(4,(THIS_FILE, "Stopping preview for cap_dev=%d", id));
1145 pj_log_push_indent();
1146
1147 w = &pjsua_var.win[wid];
1148 if (w->preview_running) {
1149 if (w->is_native) {
1150 pjmedia_vid_dev_stream *cap_dev;
1151 pj_bool_t enabled = PJ_FALSE;
1152
1153 cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
1154 status = pjmedia_vid_dev_stream_set_cap(
1155 cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
1156 &enabled);
1157 } else {
1158 status = pjmedia_vid_port_stop(w->vp_rend);
1159 }
1160
1161 if (status != PJ_SUCCESS) {
1162 PJ_PERROR(1,(THIS_FILE, status, "Error stopping %spreview",
1163 (w->is_native ? "native " : "")));
1164 PJSUA_UNLOCK();
1165 pj_log_pop_indent();
1166 return status;
1167 }
1168
1169 dec_vid_win(wid);
1170 w->preview_running = PJ_FALSE;
1171 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001172
1173 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001174 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +00001175
1176 return PJ_SUCCESS;
1177}
1178
1179
1180/*****************************************************************************
1181 * Window
1182 */
1183
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001184
1185/*
1186 * Enumerates all video windows.
1187 */
1188PJ_DEF(pj_status_t) pjsua_vid_enum_wins( pjsua_vid_win_id wids[],
1189 unsigned *count)
1190{
1191 unsigned i, cnt;
1192
1193 cnt = 0;
1194
1195 for (i=0; i<PJSUA_MAX_VID_WINS && cnt <*count; ++i) {
1196 pjsua_vid_win *w = &pjsua_var.win[i];
1197 if (w->type != PJSUA_WND_TYPE_NONE)
1198 wids[cnt++] = i;
1199 }
1200
1201 *count = cnt;
1202
1203 return PJ_SUCCESS;
1204}
1205
1206
Benny Prijono9f468d12011-07-07 07:46:33 +00001207/*
1208 * Get window info.
1209 */
1210PJ_DEF(pj_status_t) pjsua_vid_win_get_info( pjsua_vid_win_id wid,
1211 pjsua_vid_win_info *wi)
1212{
1213 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001214 pjmedia_vid_dev_stream *s;
Sauw Ming5291a2d2011-07-15 07:52:44 +00001215 pjmedia_vid_dev_param vparam;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001216 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001217
1218 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && wi, PJ_EINVAL);
1219
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001220 pj_bzero(wi, sizeof(*wi));
1221
Benny Prijono9f468d12011-07-07 07:46:33 +00001222 PJSUA_LOCK();
1223 w = &pjsua_var.win[wid];
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001224
1225 wi->is_native = w->is_native;
1226
1227 if (w->is_native) {
1228 pjmedia_vid_dev_stream *cap_strm;
1229 pjmedia_vid_dev_cap cap = PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
1230
1231 cap_strm = pjmedia_vid_port_get_stream(w->vp_cap);
1232 if (!cap_strm) {
1233 status = PJ_EINVAL;
1234 } else {
1235 status = pjmedia_vid_dev_stream_get_cap(cap_strm, cap, &wi->hwnd);
1236 }
1237
1238 PJSUA_UNLOCK();
1239 return status;
1240 }
1241
Benny Prijono9f468d12011-07-07 07:46:33 +00001242 if (w->vp_rend == NULL) {
1243 PJSUA_UNLOCK();
1244 return PJ_EINVAL;
1245 }
1246
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001247 s = pjmedia_vid_port_get_stream(w->vp_rend);
1248 if (s == NULL) {
1249 PJSUA_UNLOCK();
1250 return PJ_EINVAL;
1251 }
1252
1253 status = pjmedia_vid_dev_stream_get_param(s, &vparam);
1254 if (status != PJ_SUCCESS) {
1255 PJSUA_UNLOCK();
1256 return status;
1257 }
1258
Nanang Izzuddindb9b0022011-07-26 08:17:25 +00001259 wi->rdr_dev = vparam.rend_id;
Nanang Izzuddin6e2fcc32011-07-22 04:49:36 +00001260 wi->hwnd = vparam.window;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001261 wi->show = !vparam.window_hide;
1262 wi->pos = vparam.window_pos;
1263 wi->size = vparam.disp_size;
1264
Benny Prijono9f468d12011-07-07 07:46:33 +00001265 PJSUA_UNLOCK();
1266
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001267 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +00001268}
1269
1270/*
1271 * Show or hide window.
1272 */
1273PJ_DEF(pj_status_t) pjsua_vid_win_set_show( pjsua_vid_win_id wid,
1274 pj_bool_t show)
1275{
1276 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001277 pjmedia_vid_dev_stream *s;
Nanang Izzuddinc16590a2011-07-14 02:14:25 +00001278 pj_bool_t hide;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001279 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001280
1281 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
1282
1283 PJSUA_LOCK();
1284 w = &pjsua_var.win[wid];
1285 if (w->vp_rend == NULL) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001286 /* Native window */
Benny Prijono9f468d12011-07-07 07:46:33 +00001287 PJSUA_UNLOCK();
1288 return PJ_EINVAL;
1289 }
1290
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001291 s = pjmedia_vid_port_get_stream(w->vp_rend);
1292 if (s == NULL) {
1293 PJSUA_UNLOCK();
1294 return PJ_EINVAL;
1295 }
1296
Nanang Izzuddinc16590a2011-07-14 02:14:25 +00001297 hide = !show;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001298 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinc16590a2011-07-14 02:14:25 +00001299 PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE, &hide);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001300
Benny Prijono9f468d12011-07-07 07:46:33 +00001301 PJSUA_UNLOCK();
1302
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001303 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001304}
1305
1306/*
1307 * Set video window position.
1308 */
1309PJ_DEF(pj_status_t) pjsua_vid_win_set_pos( pjsua_vid_win_id wid,
1310 const pjmedia_coord *pos)
1311{
1312 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001313 pjmedia_vid_dev_stream *s;
1314 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001315
1316 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && pos, PJ_EINVAL);
1317
1318 PJSUA_LOCK();
1319 w = &pjsua_var.win[wid];
1320 if (w->vp_rend == NULL) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001321 /* Native window */
Benny Prijono9f468d12011-07-07 07:46:33 +00001322 PJSUA_UNLOCK();
1323 return PJ_EINVAL;
1324 }
1325
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001326 s = pjmedia_vid_port_get_stream(w->vp_rend);
1327 if (s == NULL) {
1328 PJSUA_UNLOCK();
1329 return PJ_EINVAL;
1330 }
1331
1332 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001333 PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION, pos);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001334
Benny Prijono9f468d12011-07-07 07:46:33 +00001335 PJSUA_UNLOCK();
1336
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001337 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001338}
1339
1340/*
1341 * Resize window.
1342 */
1343PJ_DEF(pj_status_t) pjsua_vid_win_set_size( pjsua_vid_win_id wid,
1344 const pjmedia_rect_size *size)
1345{
1346 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001347 pjmedia_vid_dev_stream *s;
1348 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001349
1350 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && size, PJ_EINVAL);
1351
1352 PJSUA_LOCK();
1353 w = &pjsua_var.win[wid];
1354 if (w->vp_rend == NULL) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001355 /* Native window */
Benny Prijono9f468d12011-07-07 07:46:33 +00001356 PJSUA_UNLOCK();
1357 return PJ_EINVAL;
1358 }
1359
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001360 s = pjmedia_vid_port_get_stream(w->vp_rend);
1361 if (s == NULL) {
1362 PJSUA_UNLOCK();
1363 return PJ_EINVAL;
1364 }
1365
1366 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001367 PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE, size);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001368
Benny Prijono9f468d12011-07-07 07:46:33 +00001369 PJSUA_UNLOCK();
1370
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001371 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001372}
1373
Nanang Izzuddine9b279b2011-09-27 05:24:06 +00001374/*
1375 * Set video orientation.
1376 */
1377PJ_DEF(pj_status_t) pjsua_vid_win_rotate( pjsua_vid_win_id wid,
1378 int angle)
1379{
1380 pjsua_vid_win *w;
1381 pjmedia_vid_dev_stream *s;
1382 pjmedia_orient orient;
1383 pj_status_t status;
1384
1385 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
1386 PJ_ASSERT_RETURN((angle % 90) == 0, PJ_EINVAL);
1387
1388 /* Normalize angle, so it must be 0, 90, 180, or 270. */
1389 angle %= 360;
1390 if (angle < 0)
1391 angle += 360;
1392
1393 /* Convert angle to pjmedia_orient */
1394 switch(angle) {
1395 case 0:
1396 /* No rotation */
1397 return PJ_SUCCESS;
1398 case 90:
1399 orient = PJMEDIA_ORIENT_ROTATE_90DEG;
1400 break;
1401 case 180:
1402 orient = PJMEDIA_ORIENT_ROTATE_180DEG;
1403 break;
1404 case 270:
1405 orient = PJMEDIA_ORIENT_ROTATE_270DEG;
1406 break;
1407 default:
1408 pj_assert(!"Angle must have been validated");
1409 return PJ_EBUG;
1410 }
1411
1412 PJSUA_LOCK();
1413 w = &pjsua_var.win[wid];
1414 if (w->vp_rend == NULL) {
1415 /* Native window */
1416 PJSUA_UNLOCK();
1417 return PJ_EINVAL;
1418 }
1419
1420 s = pjmedia_vid_port_get_stream(w->vp_rend);
1421 if (s == NULL) {
1422 PJSUA_UNLOCK();
1423 return PJ_EINVAL;
1424 }
1425
1426 status = pjmedia_vid_dev_stream_set_cap(s,
1427 PJMEDIA_VID_DEV_CAP_ORIENTATION, &orient);
1428
1429 PJSUA_UNLOCK();
1430
1431 return status;
1432}
1433
Benny Prijono9f468d12011-07-07 07:46:33 +00001434
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001435static void call_get_vid_strm_info(pjsua_call *call,
1436 int *first_active,
1437 int *first_inactive,
1438 unsigned *active_cnt,
1439 unsigned *cnt)
1440{
1441 unsigned i, var_cnt = 0;
1442
1443 if (first_active && ++var_cnt)
1444 *first_active = -1;
1445 if (first_inactive && ++var_cnt)
1446 *first_inactive = -1;
1447 if (active_cnt && ++var_cnt)
1448 *active_cnt = 0;
1449 if (cnt && ++var_cnt)
1450 *cnt = 0;
1451
1452 for (i = 0; i < call->med_cnt && var_cnt; ++i) {
1453 if (call->media[i].type == PJMEDIA_TYPE_VIDEO) {
1454 if (call->media[i].dir != PJMEDIA_DIR_NONE)
1455 {
1456 if (first_active && *first_active == -1) {
1457 *first_active = i;
1458 --var_cnt;
1459 }
1460 if (active_cnt)
1461 ++(*active_cnt);
1462 } else if (first_inactive && *first_inactive == -1) {
1463 *first_inactive = i;
1464 --var_cnt;
1465 }
1466 if (cnt)
1467 ++(*cnt);
1468 }
1469 }
1470}
1471
1472
1473/* Send SDP reoffer. */
1474static pj_status_t call_reoffer_sdp(pjsua_call_id call_id,
1475 const pjmedia_sdp_session *sdp)
1476{
1477 pjsua_call *call;
1478 pjsip_tx_data *tdata;
1479 pjsip_dialog *dlg;
1480 pj_status_t status;
1481
1482 status = acquire_call("call_reoffer_sdp()", call_id, &call, &dlg);
1483 if (status != PJ_SUCCESS)
1484 return status;
1485
1486 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1487 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
1488 pjsip_dlg_dec_lock(dlg);
1489 return PJSIP_ESESSIONSTATE;
1490 }
1491
1492 /* Create re-INVITE with new offer */
1493 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1494 if (status != PJ_SUCCESS) {
1495 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
1496 pjsip_dlg_dec_lock(dlg);
1497 return status;
1498 }
1499
1500 /* Send the request */
1501 status = pjsip_inv_send_msg( call->inv, tdata);
1502 if (status != PJ_SUCCESS) {
1503 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
1504 pjsip_dlg_dec_lock(dlg);
1505 return status;
1506 }
1507
1508 pjsip_dlg_dec_lock(dlg);
1509
1510 return PJ_SUCCESS;
1511}
1512
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001513/* Add a new video stream into a call */
1514static pj_status_t call_add_video(pjsua_call *call,
Nanang Izzuddin98085612011-07-15 07:41:02 +00001515 pjmedia_vid_dev_index cap_dev,
1516 pjmedia_dir dir)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001517{
1518 pj_pool_t *pool = call->inv->pool_prov;
1519 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1520 pjsua_call_media *call_med;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001521 const pjmedia_sdp_session *current_sdp;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001522 pjmedia_sdp_session *sdp;
1523 pjmedia_sdp_media *sdp_m;
1524 pjmedia_transport_info tpinfo;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001525 unsigned active_cnt;
1526 pj_status_t status;
1527
1528 /* Verify media slot availability */
1529 if (call->med_cnt == PJSUA_MAX_CALL_MEDIA)
1530 return PJ_ETOOMANY;
1531
1532 call_get_vid_strm_info(call, NULL, NULL, &active_cnt, NULL);
1533 if (active_cnt == acc_cfg->max_video_cnt)
1534 return PJ_ETOOMANY;
1535
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001536 /* Get active local SDP and clone it */
1537 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &current_sdp);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001538 if (status != PJ_SUCCESS)
1539 return status;
1540
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001541 sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, current_sdp);
1542
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001543 /* Initialize call media */
1544 call_med = &call->media[call->med_cnt++];
1545
1546 status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1547 &acc_cfg->rtp_cfg, call->secure_level,
Sauw Ming73ecfe82011-09-21 10:20:01 +00001548 NULL, PJ_FALSE, NULL);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001549 if (status != PJ_SUCCESS)
1550 goto on_error;
1551
1552 /* Override default capture device setting */
1553 call_med->strm.v.cap_dev = cap_dev;
1554
1555 /* Init transport media */
1556 status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1557 NULL, call_med->idx);
1558 if (status != PJ_SUCCESS)
1559 goto on_error;
1560
Sauw Ming73ecfe82011-09-21 10:20:01 +00001561 set_media_tp_state(call_med, PJSUA_MED_TP_INIT);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001562
1563 /* Get transport address info */
1564 pjmedia_transport_info_init(&tpinfo);
1565 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1566
1567 /* Create SDP media line */
1568 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1569 &tpinfo.sock_info, 0, &sdp_m);
1570 if (status != PJ_SUCCESS)
1571 goto on_error;
1572
1573 sdp->media[sdp->media_count++] = sdp_m;
1574
Nanang Izzuddin98085612011-07-15 07:41:02 +00001575 /* Update media direction, if it is not 'sendrecv' */
1576 if (dir != PJMEDIA_DIR_ENCODING_DECODING) {
1577 pjmedia_sdp_attr *a;
1578
1579 /* Remove sendrecv direction attribute, if any */
1580 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
1581
1582 if (dir == PJMEDIA_DIR_ENCODING)
1583 a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
1584 else if (dir == PJMEDIA_DIR_DECODING)
1585 a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
1586 else
1587 a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
1588
1589 pjmedia_sdp_media_add_attr(sdp_m, a);
1590 }
1591
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001592 /* Update SDP media line by media transport */
1593 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1594 sdp, NULL, call_med->idx);
1595 if (status != PJ_SUCCESS)
1596 goto on_error;
1597
1598 status = call_reoffer_sdp(call->index, sdp);
1599 if (status != PJ_SUCCESS)
1600 goto on_error;
1601
1602 return PJ_SUCCESS;
1603
1604on_error:
1605 if (call_med->tp) {
1606 pjmedia_transport_close(call_med->tp);
1607 call_med->tp = call_med->tp_orig = NULL;
1608 }
1609
1610 return status;
1611}
1612
1613
Nanang Izzuddin98085612011-07-15 07:41:02 +00001614/* Modify a video stream from a call, i.e: update direction,
1615 * remove/disable.
1616 */
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001617static pj_status_t call_modify_video(pjsua_call *call,
1618 int med_idx,
Nanang Izzuddin98085612011-07-15 07:41:02 +00001619 pjmedia_dir dir,
1620 pj_bool_t remove)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001621{
1622 pjsua_call_media *call_med;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001623 const pjmedia_sdp_session *current_sdp;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001624 pjmedia_sdp_session *sdp;
1625 pj_status_t status;
1626
1627 /* Verify and normalize media index */
1628 if (med_idx == -1) {
1629 int first_active;
1630
1631 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1632 if (first_active == -1)
1633 return PJ_ENOTFOUND;
1634
1635 med_idx = first_active;
1636 }
1637
1638 call_med = &call->media[med_idx];
1639
1640 /* Verify if the stream media type is video */
1641 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1642 return PJ_EINVAL;
1643
Nanang Izzuddin98085612011-07-15 07:41:02 +00001644 /* Verify if the stream dir is not changed */
1645 if ((!remove && call_med->dir == dir) ||
1646 ( remove && (call_med->tp_st == PJSUA_MED_TP_DISABLED ||
1647 call_med->tp == NULL)))
1648 {
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001649 return PJ_SUCCESS;
Nanang Izzuddin98085612011-07-15 07:41:02 +00001650 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001651
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001652 /* Get active local SDP and clone it */
1653 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &current_sdp);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001654 if (status != PJ_SUCCESS)
1655 return status;
1656
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001657 sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, current_sdp);
1658
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001659 pj_assert(med_idx < (int)sdp->media_count);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001660
Nanang Izzuddin98085612011-07-15 07:41:02 +00001661 if (!remove) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001662 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1663 pj_pool_t *pool = call->inv->pool_prov;
1664 pjmedia_sdp_media *sdp_m;
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001665
1666 status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1667 &acc_cfg->rtp_cfg, call->secure_level,
Sauw Ming73ecfe82011-09-21 10:20:01 +00001668 NULL, PJ_FALSE, NULL);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001669 if (status != PJ_SUCCESS)
1670 goto on_error;
1671
1672 /* Init transport media */
Nanang Izzuddin98085612011-07-15 07:41:02 +00001673 if (call_med->tp && call_med->tp_st == PJSUA_MED_TP_IDLE) {
1674 status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1675 NULL, call_med->idx);
1676 if (status != PJ_SUCCESS)
1677 goto on_error;
1678 }
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001679
Nanang Izzuddin98085612011-07-15 07:41:02 +00001680 sdp_m = sdp->media[med_idx];
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001681
Nanang Izzuddin98085612011-07-15 07:41:02 +00001682 /* Create new SDP media line if the stream is disabled */
1683 if (sdp->media[med_idx]->desc.port == 0) {
1684 pjmedia_transport_info tpinfo;
1685
1686 /* Get transport address info */
1687 pjmedia_transport_info_init(&tpinfo);
1688 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1689
1690 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1691 &tpinfo.sock_info, 0, &sdp_m);
1692 if (status != PJ_SUCCESS)
1693 goto on_error;
1694 }
1695
1696 {
1697 pjmedia_sdp_attr *a;
1698
1699 /* Remove any direction attributes */
1700 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
1701 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendonly");
1702 pjmedia_sdp_media_remove_all_attr(sdp_m, "recvonly");
1703 pjmedia_sdp_media_remove_all_attr(sdp_m, "inactive");
1704
1705 /* Update media direction */
1706 if (dir == PJMEDIA_DIR_ENCODING_DECODING)
1707 a = pjmedia_sdp_attr_create(pool, "sendrecv", NULL);
1708 else if (dir == PJMEDIA_DIR_ENCODING)
1709 a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
1710 else if (dir == PJMEDIA_DIR_DECODING)
1711 a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
1712 else
1713 a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
1714
1715 pjmedia_sdp_media_add_attr(sdp_m, a);
1716 }
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001717
1718 sdp->media[med_idx] = sdp_m;
1719
1720 /* Update SDP media line by media transport */
1721 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1722 sdp, NULL, call_med->idx);
1723 if (status != PJ_SUCCESS)
1724 goto on_error;
1725
1726on_error:
1727 if (status != PJ_SUCCESS) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001728 return status;
1729 }
Nanang Izzuddin98085612011-07-15 07:41:02 +00001730
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001731 } else {
Nanang Izzuddin98085612011-07-15 07:41:02 +00001732
1733 pj_pool_t *pool = call->inv->pool_prov;
1734
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001735 /* Mark media transport to disabled */
1736 // Don't close this here, as SDP negotiation has not been
1737 // done and stream may be still active.
Sauw Ming73ecfe82011-09-21 10:20:01 +00001738 set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001739
Nanang Izzuddin98085612011-07-15 07:41:02 +00001740 /* Deactivate the stream */
1741 pjmedia_sdp_media_deactivate(pool, sdp->media[med_idx]);
1742
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001743 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001744
1745 status = call_reoffer_sdp(call->index, sdp);
1746 if (status != PJ_SUCCESS)
1747 return status;
1748
1749 return PJ_SUCCESS;
1750}
1751
1752
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001753/* Change capture device of a video stream in a call */
1754static pj_status_t call_change_cap_dev(pjsua_call *call,
1755 int med_idx,
1756 pjmedia_vid_dev_index cap_dev)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001757{
1758 pjsua_call_media *call_med;
Benny Prijono10ff56e2011-10-04 13:35:41 +00001759 pjmedia_vid_dev_stream *old_dev;
1760 pjmedia_vid_dev_switch_param switch_prm;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001761 pjmedia_vid_dev_info info;
1762 pjsua_vid_win *w, *new_w = NULL;
1763 pjsua_vid_win_id wid, new_wid;
1764 pjmedia_port *media_port;
1765 pj_status_t status;
1766
1767 /* Verify and normalize media index */
1768 if (med_idx == -1) {
1769 int first_active;
1770
1771 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1772 if (first_active == -1)
1773 return PJ_ENOTFOUND;
1774
1775 med_idx = first_active;
1776 }
1777
1778 call_med = &call->media[med_idx];
1779
1780 /* Verify if the stream media type is video */
1781 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1782 return PJ_EINVAL;
1783
1784 /* Verify the capture device */
1785 status = pjmedia_vid_dev_get_info(cap_dev, &info);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001786 if (status != PJ_SUCCESS || info.dir != PJMEDIA_DIR_CAPTURE)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001787 return PJ_EINVAL;
1788
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001789 /* The specified capture device is being used already */
1790 if (call_med->strm.v.cap_dev == cap_dev)
1791 return PJ_SUCCESS;
1792
1793 /* == Apply the new capture device == */
1794
1795 wid = call_med->strm.v.cap_win_id;
1796 w = &pjsua_var.win[wid];
1797 pj_assert(w->type == PJSUA_WND_TYPE_PREVIEW && w->vp_cap);
1798
Benny Prijono10ff56e2011-10-04 13:35:41 +00001799 /* If the old device supports fast switching, then that's excellent! */
1800 old_dev = pjmedia_vid_port_get_stream(w->vp_cap);
1801 pjmedia_vid_dev_switch_param_default(&switch_prm);
1802 switch_prm.target_id = cap_dev;
1803 status = pjmedia_vid_dev_stream_set_cap(old_dev,
1804 PJMEDIA_VID_DEV_CAP_SWITCH,
1805 &switch_prm);
1806 if (status == PJ_SUCCESS) {
1807 w->preview_cap_id = cap_dev;
Benny Prijono86e91622011-10-04 13:49:40 +00001808 call_med->strm.v.cap_dev = cap_dev;
Benny Prijono10ff56e2011-10-04 13:35:41 +00001809 return PJ_SUCCESS;
1810 }
1811
1812 /* No it doesn't support fast switching. Do slow switching then.. */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001813 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
1814 PJMEDIA_DIR_ENCODING, &media_port);
1815 if (status != PJ_SUCCESS)
1816 return status;
Benny Prijono1fe04ee2011-07-15 07:27:05 +00001817
1818 pjmedia_event_unsubscribe(&call_med->esub_cap);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001819
1820 /* = Detach stream port from the old capture device = */
1821 status = pjmedia_vid_port_disconnect(w->vp_cap);
1822 if (status != PJ_SUCCESS)
1823 return status;
1824
1825 status = pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
1826 if (status != PJ_SUCCESS) {
1827 /* Connect back the old capturer */
1828 pjmedia_vid_port_connect(w->vp_cap, media_port, PJ_FALSE);
1829 return status;
1830 }
1831
1832 /* = Attach stream port to the new capture device = */
1833
Benny Prijono39203b82011-09-20 10:07:55 +00001834 /* Note: calling pjsua_vid_preview_get_win() even though
1835 * create_vid_win() will automatically create the window
1836 * if it doesn't exist, because create_vid_win() will modify
1837 * existing window SHOW/HIDE value.
1838 */
1839 new_wid = vid_preview_get_win(cap_dev, PJ_FALSE);
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001840 if (new_wid == PJSUA_INVALID_ID) {
1841 /* Create preview video window */
1842 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
1843 &media_port->info.fmt,
1844 call_med->strm.v.rdr_dev,
1845 cap_dev,
1846 PJSUA_HIDE_WINDOW,
1847 &new_wid);
1848 if (status != PJ_SUCCESS)
1849 goto on_error;
1850 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001851
1852 inc_vid_win(new_wid);
1853 new_w = &pjsua_var.win[new_wid];
1854
1855 /* Connect stream to capturer (via video window tee) */
1856 status = pjmedia_vid_tee_add_dst_port2(new_w->tee, 0, media_port);
1857 if (status != PJ_SUCCESS)
1858 goto on_error;
1859
1860 /* Connect capturer to tee */
1861 status = pjmedia_vid_port_connect(new_w->vp_cap, new_w->tee, PJ_FALSE);
1862 if (status != PJ_SUCCESS)
1863 return status;
1864
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001865 if (w->vp_rend) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001866 /* Start renderer */
1867 status = pjmedia_vid_port_start(new_w->vp_rend);
1868 if (status != PJ_SUCCESS)
1869 goto on_error;
1870 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001871
Nanang Izzuddinc12a19d2011-10-03 05:23:59 +00001872#if ENABLE_EVENT
1873 pjmedia_event_subscribe(
1874 pjmedia_vid_port_get_event_publisher(new_w->vp_cap),
1875 &call_med->esub_cap);
1876#endif
1877
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001878 /* Start capturer */
1879 status = pjmedia_vid_port_start(new_w->vp_cap);
1880 if (status != PJ_SUCCESS)
1881 goto on_error;
1882
1883 /* Finally */
1884 call_med->strm.v.cap_dev = cap_dev;
1885 call_med->strm.v.cap_win_id = new_wid;
1886 dec_vid_win(wid);
1887
1888 return PJ_SUCCESS;
1889
1890on_error:
1891 if (new_w) {
1892 /* Disconnect media port from the new capturer */
1893 pjmedia_vid_tee_remove_dst_port(new_w->tee, media_port);
1894 /* Release the new capturer */
1895 dec_vid_win(new_wid);
1896 }
1897
1898 /* Revert back to the old capturer */
1899 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
1900 if (status != PJ_SUCCESS)
1901 return status;
1902
1903 status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
1904 if (status != PJ_SUCCESS)
1905 return status;
1906
1907 return status;
1908}
1909
1910
Nanang Izzuddin98085612011-07-15 07:41:02 +00001911/* Start/stop transmitting video stream in a call */
1912static pj_status_t call_set_tx_video(pjsua_call *call,
1913 int med_idx,
1914 pj_bool_t enable)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001915{
1916 pjsua_call_media *call_med;
1917 pj_status_t status;
1918
1919 /* Verify and normalize media index */
1920 if (med_idx == -1) {
1921 int first_active;
1922
1923 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1924 if (first_active == -1)
1925 return PJ_ENOTFOUND;
1926
1927 med_idx = first_active;
1928 }
1929
1930 call_med = &call->media[med_idx];
1931
1932 /* Verify if the stream is transmitting video */
1933 if (call_med->type != PJMEDIA_TYPE_VIDEO ||
Nanang Izzuddin98085612011-07-15 07:41:02 +00001934 (enable && (call_med->dir & PJMEDIA_DIR_ENCODING) == 0))
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001935 {
1936 return PJ_EINVAL;
1937 }
1938
Nanang Izzuddin98085612011-07-15 07:41:02 +00001939 if (enable) {
1940 /* Start stream in encoding direction */
1941 status = pjmedia_vid_stream_resume(call_med->strm.v.stream,
1942 PJMEDIA_DIR_ENCODING);
1943 } else {
1944 /* Pause stream in encoding direction */
1945 status = pjmedia_vid_stream_pause( call_med->strm.v.stream,
1946 PJMEDIA_DIR_ENCODING);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001947 }
1948
Nanang Izzuddin98085612011-07-15 07:41:02 +00001949 return status;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001950}
1951
1952
1953/*
1954 * Start, stop, and/or manipulate video transmission for the specified call.
1955 */
1956PJ_DEF(pj_status_t) pjsua_call_set_vid_strm (
1957 pjsua_call_id call_id,
1958 pjsua_call_vid_strm_op op,
1959 const pjsua_call_vid_strm_op_param *param)
1960{
1961 pjsua_call *call;
1962 pjsua_call_vid_strm_op_param param_;
1963 pj_status_t status;
1964
1965 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1966 PJ_EINVAL);
Benny Prijonoe212bc12011-08-15 09:38:42 +00001967 PJ_ASSERT_RETURN(op != PJSUA_CALL_VID_STRM_NO_OP, PJ_EINVAL);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001968
Benny Prijonob90fd382011-09-18 14:59:56 +00001969 PJ_LOG(4,(THIS_FILE, "Call %d: set video stream, op=%d",
1970 call_id, op));
1971 pj_log_push_indent();
1972
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001973 PJSUA_LOCK();
1974
1975 call = &pjsua_var.calls[call_id];
1976
1977 if (param) {
1978 param_ = *param;
1979 } else {
Benny Prijonoe212bc12011-08-15 09:38:42 +00001980 pjsua_call_vid_strm_op_param_default(&param_);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001981 }
1982
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001983 /* If set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
1984 * account default video capture device.
1985 */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001986 if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001987 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1988 param_.cap_dev = acc_cfg->vid_cap_dev;
1989
1990 /* If the account default video capture device is
1991 * PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
1992 * global default video capture device.
1993 */
1994 if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
1995 pjmedia_vid_dev_info info;
1996 pjmedia_vid_dev_get_info(param_.cap_dev, &info);
1997 pj_assert(info.dir == PJMEDIA_DIR_CAPTURE);
1998 param_.cap_dev = info.id;
1999 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002000 }
2001
2002 switch (op) {
2003 case PJSUA_CALL_VID_STRM_ADD:
Nanang Izzuddin98085612011-07-15 07:41:02 +00002004 status = call_add_video(call, param_.cap_dev, param_.dir);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002005 break;
Nanang Izzuddin98085612011-07-15 07:41:02 +00002006 case PJSUA_CALL_VID_STRM_REMOVE:
2007 status = call_modify_video(call, param_.med_idx, PJMEDIA_DIR_NONE,
2008 PJ_TRUE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002009 break;
Nanang Izzuddin98085612011-07-15 07:41:02 +00002010 case PJSUA_CALL_VID_STRM_CHANGE_DIR:
2011 status = call_modify_video(call, param_.med_idx, param_.dir, PJ_FALSE);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00002012 break;
2013 case PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV:
2014 status = call_change_cap_dev(call, param_.med_idx, param_.cap_dev);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002015 break;
2016 case PJSUA_CALL_VID_STRM_START_TRANSMIT:
Nanang Izzuddin98085612011-07-15 07:41:02 +00002017 status = call_set_tx_video(call, param_.med_idx, PJ_TRUE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002018 break;
2019 case PJSUA_CALL_VID_STRM_STOP_TRANSMIT:
Nanang Izzuddin98085612011-07-15 07:41:02 +00002020 status = call_set_tx_video(call, param_.med_idx, PJ_FALSE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002021 break;
2022 default:
2023 status = PJ_EINVALIDOP;
2024 break;
2025 }
2026
2027 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00002028 pj_log_pop_indent();
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002029
2030 return status;
2031}
2032
Benny Prijono9f468d12011-07-07 07:46:33 +00002033
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00002034/*
2035 * Get the media stream index of the default video stream in the call.
2036 */
2037PJ_DEF(int) pjsua_call_get_vid_stream_idx(pjsua_call_id call_id)
2038{
2039 pjsua_call *call;
2040 int first_active, first_inactive;
2041
2042 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2043 PJ_EINVAL);
2044
2045 PJSUA_LOCK();
2046 call = &pjsua_var.calls[call_id];
2047 call_get_vid_strm_info(call, &first_active, &first_inactive, NULL, NULL);
2048 PJSUA_UNLOCK();
2049
2050 if (first_active == -1)
2051 return first_inactive;
2052
2053 return first_active;
2054}
2055
2056
Benny Prijono83a9eaf2011-10-04 08:23:07 +00002057/*
2058 * Determine if video stream for the specified call is currently running
2059 * for the specified direction.
2060 */
2061PJ_DEF(pj_bool_t) pjsua_call_vid_stream_is_running( pjsua_call_id call_id,
2062 int med_idx,
2063 pjmedia_dir dir)
2064{
2065 pjsua_call *call;
2066 pjsua_call_media *call_med;
2067
2068 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2069 PJ_EINVAL);
2070
2071 /* Verify and normalize media index */
2072 if (med_idx == -1) {
2073 med_idx = pjsua_call_get_vid_stream_idx(call_id);
2074 }
2075
2076 call = &pjsua_var.calls[call_id];
2077 PJ_ASSERT_RETURN(med_idx >= 0 && med_idx < call->med_cnt, PJ_EINVAL);
2078
2079 call_med = &call->media[med_idx];
2080
2081 /* Verify if the stream is transmitting video */
2082 if (call_med->type != PJMEDIA_TYPE_VIDEO || (call_med->dir & dir) == 0 ||
2083 !call_med->strm.v.stream)
2084 {
2085 return PJ_FALSE;
2086 }
2087
2088 return pjmedia_vid_stream_is_running(call_med->strm.v.stream, dir);
2089}
2090
Benny Prijono9f468d12011-07-07 07:46:33 +00002091#endif /* PJSUA_HAS_VIDEO */
2092