blob: 8af4bfbbee45c83b15e5c75df7b55a1acff8f077 [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
Benny Prijono6565b582011-08-29 09:54:02 +000026#define ENABLE_EVENT 0
Nanang Izzuddin15ad7f22011-08-26 04:19:04 +000027#define VID_TEE_MAX_PORT (PJSUA_MAX_CALLS + 1)
28
Nanang Izzuddin62053a62011-07-12 11:08:32 +000029static void free_vid_win(pjsua_vid_win_id wid);
30
Benny Prijono9f468d12011-07-07 07:46:33 +000031/*****************************************************************************
32 * pjsua video subsystem.
33 */
34pj_status_t pjsua_vid_subsys_init(void)
35{
36 unsigned i;
37 pj_status_t status;
38
Benny Prijonob90fd382011-09-18 14:59:56 +000039 PJ_LOG(4,(THIS_FILE, "Initializing video subsystem.."));
40 pj_log_push_indent();
41
Benny Prijono9f468d12011-07-07 07:46:33 +000042 status = pjmedia_video_format_mgr_create(pjsua_var.pool, 64, 0, NULL);
43 if (status != PJ_SUCCESS) {
44 PJ_PERROR(1,(THIS_FILE, status,
45 "Error creating PJMEDIA video format manager"));
Benny Prijonob90fd382011-09-18 14:59:56 +000046 goto on_error;
Benny Prijono9f468d12011-07-07 07:46:33 +000047 }
48
49 status = pjmedia_converter_mgr_create(pjsua_var.pool, NULL);
50 if (status != PJ_SUCCESS) {
51 PJ_PERROR(1,(THIS_FILE, status,
52 "Error creating PJMEDIA converter manager"));
Benny Prijonob90fd382011-09-18 14:59:56 +000053 goto on_error;
Benny Prijono9f468d12011-07-07 07:46:33 +000054 }
55
56 status = pjmedia_vid_codec_mgr_create(pjsua_var.pool, NULL);
57 if (status != PJ_SUCCESS) {
58 PJ_PERROR(1,(THIS_FILE, status,
59 "Error creating PJMEDIA video codec manager"));
Benny Prijonob90fd382011-09-18 14:59:56 +000060 goto on_error;
Benny Prijono9f468d12011-07-07 07:46:33 +000061 }
62
63 status = pjmedia_vid_dev_subsys_init(&pjsua_var.cp.factory);
64 if (status != PJ_SUCCESS) {
65 PJ_PERROR(1,(THIS_FILE, status,
66 "Error creating PJMEDIA video subsystem"));
Benny Prijonob90fd382011-09-18 14:59:56 +000067 goto on_error;
Benny Prijono9f468d12011-07-07 07:46:33 +000068 }
69
70#if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_FFMPEG_CODEC
71 status = pjmedia_codec_ffmpeg_init(NULL, &pjsua_var.cp.factory);
72 if (status != PJ_SUCCESS) {
73 PJ_PERROR(1,(THIS_FILE, status,
74 "Error initializing ffmpeg library"));
Benny Prijonob90fd382011-09-18 14:59:56 +000075 goto on_error;
Benny Prijono9f468d12011-07-07 07:46:33 +000076 }
77#endif
78
79 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
80 if (pjsua_var.win[i].pool == NULL) {
81 pjsua_var.win[i].pool = pjsua_pool_create("win%p", 512, 512);
Benny Prijonob90fd382011-09-18 14:59:56 +000082 if (pjsua_var.win[i].pool == NULL) {
83 status = PJ_ENOMEM;
84 goto on_error;
85 }
Benny Prijono9f468d12011-07-07 07:46:33 +000086 }
87 }
88
Benny Prijonob90fd382011-09-18 14:59:56 +000089 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +000090 return PJ_SUCCESS;
Benny Prijonob90fd382011-09-18 14:59:56 +000091
92on_error:
93 pj_log_pop_indent();
94 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +000095}
96
97pj_status_t pjsua_vid_subsys_start(void)
98{
99 return PJ_SUCCESS;
100}
101
102pj_status_t pjsua_vid_subsys_destroy(void)
103{
104 unsigned i;
105
Benny Prijonob90fd382011-09-18 14:59:56 +0000106 PJ_LOG(4,(THIS_FILE, "Destroying video subsystem.."));
107 pj_log_push_indent();
108
Benny Prijono9f468d12011-07-07 07:46:33 +0000109 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
110 if (pjsua_var.win[i].pool) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000111 free_vid_win(i);
Benny Prijono9f468d12011-07-07 07:46:33 +0000112 pj_pool_release(pjsua_var.win[i].pool);
113 pjsua_var.win[i].pool = NULL;
114 }
115 }
116
117 pjmedia_vid_dev_subsys_shutdown();
118
119#if PJMEDIA_HAS_FFMPEG_CODEC
120 pjmedia_codec_ffmpeg_deinit();
121#endif
122
Benny Prijonob90fd382011-09-18 14:59:56 +0000123 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +0000124 return PJ_SUCCESS;
125}
126
Benny Prijonoe212bc12011-08-15 09:38:42 +0000127PJ_DEF(void)
128pjsua_call_vid_strm_op_param_default(pjsua_call_vid_strm_op_param *param)
129{
130 pj_bzero(param, sizeof(*param));
131 param->med_idx = -1;
132 param->dir = PJMEDIA_DIR_ENCODING_DECODING;
133 param->cap_dev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
134}
Benny Prijono9f468d12011-07-07 07:46:33 +0000135
Benny Prijono2047bd72011-08-25 11:59:39 +0000136PJ_DEF(void) pjsua_vid_preview_param_default(pjsua_vid_preview_param *p)
137{
138 p->rend_id = PJMEDIA_VID_DEFAULT_RENDER_DEV;
139 p->show = PJ_TRUE;
140}
141
142
Benny Prijono9f468d12011-07-07 07:46:33 +0000143/*****************************************************************************
144 * Devices.
145 */
146
147/*
148 * Get the number of video devices installed in the system.
149 */
150PJ_DEF(unsigned) pjsua_vid_dev_count(void)
151{
152 return pjmedia_vid_dev_count();
153}
154
155/*
156 * Retrieve the video device info for the specified device index.
157 */
158PJ_DEF(pj_status_t) pjsua_vid_dev_get_info(pjmedia_vid_dev_index id,
159 pjmedia_vid_dev_info *vdi)
160{
161 return pjmedia_vid_dev_get_info(id, vdi);
162}
163
164/*
165 * Enum all video devices installed in the system.
166 */
167PJ_DEF(pj_status_t) pjsua_vid_enum_devs(pjmedia_vid_dev_info info[],
168 unsigned *count)
169{
170 unsigned i, dev_count;
171
172 dev_count = pjmedia_vid_dev_count();
173
174 if (dev_count > *count) dev_count = *count;
175
176 for (i=0; i<dev_count; ++i) {
177 pj_status_t status;
178
179 status = pjmedia_vid_dev_get_info(i, &info[i]);
180 if (status != PJ_SUCCESS)
181 return status;
182 }
183
184 *count = dev_count;
185
186 return PJ_SUCCESS;
187}
188
189
190/*****************************************************************************
191 * Codecs.
192 */
193
194/*
195 * Enum all supported video codecs in the system.
196 */
197PJ_DEF(pj_status_t) pjsua_vid_enum_codecs( pjsua_codec_info id[],
198 unsigned *p_count )
199{
200 pjmedia_vid_codec_info info[32];
201 unsigned i, j, count, prio[32];
202 pj_status_t status;
203
204 count = PJ_ARRAY_SIZE(info);
205 status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, info, prio);
206 if (status != PJ_SUCCESS) {
207 *p_count = 0;
208 return status;
209 }
210
211 for (i=0, j=0; i<count && j<*p_count; ++i) {
212 if (info[i].has_rtp_pack) {
213 pj_bzero(&id[j], sizeof(pjsua_codec_info));
214
215 pjmedia_vid_codec_info_to_id(&info[i], id[j].buf_, sizeof(id[j].buf_));
216 id[j].codec_id = pj_str(id[j].buf_);
217 id[j].priority = (pj_uint8_t) prio[i];
218
219 if (id[j].codec_id.slen < sizeof(id[j].buf_)) {
220 id[j].desc.ptr = id[j].codec_id.ptr + id[j].codec_id.slen + 1;
221 pj_strncpy(&id[j].desc, &info[i].encoding_desc,
222 sizeof(id[j].buf_) - id[j].codec_id.slen - 1);
223 }
224
225 ++j;
226 }
227 }
228
229 *p_count = j;
230
231 return PJ_SUCCESS;
232}
233
234
235/*
236 * Change video codec priority.
237 */
238PJ_DEF(pj_status_t) pjsua_vid_codec_set_priority( const pj_str_t *codec_id,
239 pj_uint8_t priority )
240{
241 const pj_str_t all = { NULL, 0 };
242
243 if (codec_id->slen==1 && *codec_id->ptr=='*')
244 codec_id = &all;
245
246 return pjmedia_vid_codec_mgr_set_codec_priority(NULL, codec_id,
247 priority);
248}
249
250
251/*
252 * Get video codec parameters.
253 */
254PJ_DEF(pj_status_t) pjsua_vid_codec_get_param(
255 const pj_str_t *codec_id,
256 pjmedia_vid_codec_param *param)
257{
258 const pj_str_t all = { NULL, 0 };
259 const pjmedia_vid_codec_info *info;
260 unsigned count = 1;
261 pj_status_t status;
262
263 if (codec_id->slen==1 && *codec_id->ptr=='*')
264 codec_id = &all;
265
266 status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, codec_id,
267 &count, &info, NULL);
268 if (status != PJ_SUCCESS)
269 return status;
270
271 if (count != 1)
272 return (count > 1? PJ_ETOOMANY : PJ_ENOTFOUND);
273
274 status = pjmedia_vid_codec_mgr_get_default_param(NULL, info, param);
275 return status;
276}
277
278
279/*
280 * Set video codec parameters.
281 */
282PJ_DEF(pj_status_t) pjsua_vid_codec_set_param(
283 const pj_str_t *codec_id,
284 const pjmedia_vid_codec_param *param)
285{
286 const pjmedia_vid_codec_info *info[2];
287 unsigned count = 2;
288 pj_status_t status;
289
290 status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, codec_id,
291 &count, info, NULL);
292 if (status != PJ_SUCCESS)
293 return status;
294
295 /* Codec ID should be specific */
296 if (count > 1) {
297 pj_assert(!"Codec ID is not specific");
298 return PJ_ETOOMANY;
299 }
300
301 status = pjmedia_vid_codec_mgr_set_default_param(NULL, pjsua_var.pool,
302 info[0], param);
303 return status;
304}
305
306
307/*****************************************************************************
308 * Preview
309 */
310
311/*
312 * Get the preview window handle associated with the capture device, if any.
313 */
314PJ_DEF(pjsua_vid_win_id) pjsua_vid_preview_get_win(pjmedia_vid_dev_index id)
315{
316 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
317 unsigned i;
318
319 PJSUA_LOCK();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000320
321 /* Get real capture ID, if set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV */
322 if (id == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
323 pjmedia_vid_dev_info info;
324 pjmedia_vid_dev_get_info(id, &info);
325 id = info.id;
326 }
327
Benny Prijono9f468d12011-07-07 07:46:33 +0000328 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
329 pjsua_vid_win *w = &pjsua_var.win[i];
330 if (w->type == PJSUA_WND_TYPE_PREVIEW && w->preview_cap_id == id) {
331 wid = i;
332 break;
333 }
334 }
335 PJSUA_UNLOCK();
336
337 return wid;
338}
339
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000340
341/* Allocate and initialize pjsua video window:
342 * - If the type is preview, video capture, tee, and render
343 * will be instantiated.
344 * - If the type is stream, only renderer will be created.
345 */
346static pj_status_t create_vid_win(pjsua_vid_win_type type,
347 const pjmedia_format *fmt,
348 pjmedia_vid_dev_index rend_id,
349 pjmedia_vid_dev_index cap_id,
350 pj_bool_t show,
351 pjsua_vid_win_id *id)
Benny Prijono9f468d12011-07-07 07:46:33 +0000352{
353 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000354 pjsua_vid_win *w = NULL;
355 pjmedia_vid_port_param vp_param;
356 pjmedia_format fmt_;
357 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000358 unsigned i;
359
Benny Prijonob90fd382011-09-18 14:59:56 +0000360 PJ_LOG(4,(THIS_FILE, "Creating window, type=%d, cap_dev=%d, rend_dev=%d",
361 type, cap_id, rend_id));
362 pj_log_push_indent();
363
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000364 /* If type is preview, check if it exists already */
365 if (type == PJSUA_WND_TYPE_PREVIEW) {
366 wid = pjsua_vid_preview_get_win(cap_id);
367 if (wid != PJSUA_INVALID_ID) {
368 /* Yes, it exists */
Benny Prijono2047bd72011-08-25 11:59:39 +0000369 /* Show/hide window */
370 pjmedia_vid_dev_stream *rdr;
371 pj_bool_t hide = !show;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000372
Benny Prijono2047bd72011-08-25 11:59:39 +0000373 rdr = pjmedia_vid_port_get_stream(pjsua_var.win[wid].vp_rend);
374 pj_assert(rdr);
375 status = pjmedia_vid_dev_stream_set_cap(
376 rdr, PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE,
377 &hide);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000378
379 /* Done */
380 *id = wid;
Benny Prijonob90fd382011-09-18 14:59:56 +0000381 PJ_LOG(4,(THIS_FILE, "Window already exist: %d", wid));
382 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000383 return PJ_SUCCESS;
384 }
385 }
386
387 /* Allocate window */
Benny Prijono9f468d12011-07-07 07:46:33 +0000388 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000389 w = &pjsua_var.win[i];
Benny Prijono9f468d12011-07-07 07:46:33 +0000390 if (w->type == PJSUA_WND_TYPE_NONE) {
391 wid = i;
392 w->type = type;
393 break;
394 }
395 }
Benny Prijonob90fd382011-09-18 14:59:56 +0000396 if (i == PJSUA_MAX_VID_WINS) {
397 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000398 return PJ_ETOOMANY;
Benny Prijonob90fd382011-09-18 14:59:56 +0000399 }
Benny Prijono9f468d12011-07-07 07:46:33 +0000400
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000401 /* Initialize window */
402 pjmedia_vid_port_param_default(&vp_param);
403
404 if (w->type == PJSUA_WND_TYPE_PREVIEW) {
405 status = pjmedia_vid_dev_default_param(w->pool, cap_id,
406 &vp_param.vidparam);
407 if (status != PJ_SUCCESS)
408 goto on_error;
409
410 /* Normalize capture ID, in case it was set to
411 * PJMEDIA_VID_DEFAULT_CAPTURE_DEV
412 */
413 cap_id = vp_param.vidparam.cap_id;
414
415 /* Assign preview capture device ID */
416 w->preview_cap_id = cap_id;
417
418 /* Create capture video port */
419 vp_param.active = PJ_TRUE;
420 vp_param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
421 if (fmt)
422 vp_param.vidparam.fmt = *fmt;
423
424 status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_cap);
425 if (status != PJ_SUCCESS)
426 goto on_error;
427
428 /* Update format info */
429 fmt_ = vp_param.vidparam.fmt;
430 fmt = &fmt_;
431
432 /* Create video tee */
Nanang Izzuddin15ad7f22011-08-26 04:19:04 +0000433 status = pjmedia_vid_tee_create(w->pool, fmt, VID_TEE_MAX_PORT,
434 &w->tee);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000435 if (status != PJ_SUCCESS)
436 goto on_error;
437 }
438
439 /* Create renderer video port */
440 status = pjmedia_vid_dev_default_param(w->pool, rend_id,
441 &vp_param.vidparam);
442 if (status != PJ_SUCCESS)
443 goto on_error;
444
445 vp_param.active = (w->type == PJSUA_WND_TYPE_STREAM);
446 vp_param.vidparam.dir = PJMEDIA_DIR_RENDER;
447 vp_param.vidparam.fmt = *fmt;
448 vp_param.vidparam.disp_size = fmt->det.vid.size;
449 vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
450 vp_param.vidparam.window_hide = !show;
451
452 status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_rend);
453 if (status != PJ_SUCCESS)
454 goto on_error;
455
456 /* For preview window, connect capturer & renderer (via tee) */
457 if (w->type == PJSUA_WND_TYPE_PREVIEW) {
458 pjmedia_port *rend_port;
459
460 status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
461 if (status != PJ_SUCCESS)
462 goto on_error;
463
464 rend_port = pjmedia_vid_port_get_passive_port(w->vp_rend);
465 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, rend_port);
466 if (status != PJ_SUCCESS)
467 goto on_error;
468 }
469
470 /* Done */
471 *id = wid;
472
Benny Prijonob90fd382011-09-18 14:59:56 +0000473 PJ_LOG(4,(THIS_FILE, "Window %d created", wid));
474 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000475 return PJ_SUCCESS;
476
477on_error:
478 free_vid_win(wid);
Benny Prijonob90fd382011-09-18 14:59:56 +0000479 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000480 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000481}
482
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000483
Benny Prijono9f468d12011-07-07 07:46:33 +0000484static void free_vid_win(pjsua_vid_win_id wid)
485{
486 pjsua_vid_win *w = &pjsua_var.win[wid];
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000487
Benny Prijonob90fd382011-09-18 14:59:56 +0000488 PJ_LOG(4,(THIS_FILE, "Window %d: destroying..", wid));
489 pj_log_push_indent();
490
Benny Prijono9f468d12011-07-07 07:46:33 +0000491 if (w->vp_cap) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000492 pjmedia_vid_port_stop(w->vp_cap);
493 pjmedia_vid_port_disconnect(w->vp_cap);
Benny Prijono9f468d12011-07-07 07:46:33 +0000494 pjmedia_vid_port_destroy(w->vp_cap);
495 }
496 if (w->vp_rend) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000497 pjmedia_vid_port_stop(w->vp_rend);
Benny Prijono9f468d12011-07-07 07:46:33 +0000498 pjmedia_vid_port_destroy(w->vp_rend);
499 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000500 if (w->tee) {
501 pjmedia_port_destroy(w->tee);
502 }
Benny Prijono9f468d12011-07-07 07:46:33 +0000503 pjsua_vid_win_reset(wid);
Benny Prijonob90fd382011-09-18 14:59:56 +0000504
505 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +0000506}
507
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000508
509static void inc_vid_win(pjsua_vid_win_id wid)
510{
511 pjsua_vid_win *w;
512
513 pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
514
515 w = &pjsua_var.win[wid];
516 pj_assert(w->type != PJSUA_WND_TYPE_NONE);
517 ++w->ref_cnt;
518}
519
520static void dec_vid_win(pjsua_vid_win_id wid)
521{
522 pjsua_vid_win *w;
523
524 pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
525
526 w = &pjsua_var.win[wid];
527 pj_assert(w->type != PJSUA_WND_TYPE_NONE);
528 if (--w->ref_cnt == 0)
529 free_vid_win(wid);
530}
531
532
533/* Internal function: update video channel after SDP negotiation */
534pj_status_t video_channel_update(pjsua_call_media *call_med,
535 pj_pool_t *tmp_pool,
536 const pjmedia_sdp_session *local_sdp,
537 const pjmedia_sdp_session *remote_sdp)
538{
539 pjsua_call *call = call_med->call;
540 pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
541 pjmedia_vid_stream_info the_si, *si = &the_si;
542 pjmedia_port *media_port;
543 unsigned strm_idx = call_med->idx;
544 pj_status_t status;
545
Benny Prijonob90fd382011-09-18 14:59:56 +0000546 PJ_LOG(4,(THIS_FILE, "Video channel update.."));
547 pj_log_push_indent();
548
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000549 status = pjmedia_vid_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt,
550 local_sdp, remote_sdp, strm_idx);
551 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000552 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000553
554 /* Check if no media is active */
555 if (si->dir == PJMEDIA_DIR_NONE) {
556 /* Call media state */
557 call_med->state = PJSUA_CALL_MEDIA_NONE;
558
559 /* Call media direction */
560 call_med->dir = PJMEDIA_DIR_NONE;
561
562 } else {
563 pjmedia_transport_info tp_info;
564
565 /* Start/restart media transport */
566 status = pjmedia_transport_media_start(call_med->tp,
567 tmp_pool, local_sdp,
568 remote_sdp, strm_idx);
569 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000570 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000571
572 call_med->tp_st = PJSUA_MED_TP_RUNNING;
573
574 /* Get remote SRTP usage policy */
575 pjmedia_transport_info_init(&tp_info);
576 pjmedia_transport_get_info(call_med->tp, &tp_info);
577 if (tp_info.specific_info_cnt > 0) {
578 unsigned i;
579 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
580 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
581 {
582 pjmedia_srtp_info *srtp_info =
583 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
584
585 call_med->rem_srtp_use = srtp_info->peer_use;
586 break;
587 }
588 }
589 }
590
591 /* Optionally, application may modify other stream settings here
592 * (such as jitter buffer parameters, codec ptime, etc.)
593 */
594 si->jb_init = pjsua_var.media_cfg.jb_init;
595 si->jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
596 si->jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
597 si->jb_max = pjsua_var.media_cfg.jb_max;
598
599 /* Set SSRC */
600 si->ssrc = call_med->ssrc;
601
602 /* Set RTP timestamp & sequence, normally these value are intialized
603 * automatically when stream session created, but for some cases (e.g:
604 * call reinvite, call update) timestamp and sequence need to be kept
605 * contigue.
606 */
607 si->rtp_ts = call_med->rtp_tx_ts;
608 si->rtp_seq = call_med->rtp_tx_seq;
609 si->rtp_seq_ts_set = call_med->rtp_tx_seq_ts_set;
610
611#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
612 /* Enable/disable stream keep-alive and NAT hole punch. */
613 si->use_ka = pjsua_var.acc[call->acc_id].cfg.use_stream_ka;
614#endif
615
616 /* Try to get shared format ID between the capture device and
617 * the encoder to avoid format conversion in the capture device.
618 */
619 if (si->dir & PJMEDIA_DIR_ENCODING) {
620 pjmedia_vid_dev_info dev_info;
621 pjmedia_vid_codec_info *codec_info = &si->codec_info;
622 unsigned i, j;
623
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +0000624 status = pjmedia_vid_dev_get_info(call_med->strm.v.cap_dev,
625 &dev_info);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000626 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000627 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000628
629 /* Find matched format ID */
630 for (i = 0; i < codec_info->dec_fmt_id_cnt; ++i) {
631 for (j = 0; j < dev_info.fmt_cnt; ++j) {
632 if (codec_info->dec_fmt_id[i] ==
633 (pjmedia_format_id)dev_info.fmt[j].id)
634 {
635 /* Apply the matched format ID to the codec */
636 si->codec_param->dec_fmt.id =
637 codec_info->dec_fmt_id[i];
638
639 /* Force outer loop to break */
640 i = codec_info->dec_fmt_id_cnt;
641 break;
642 }
643 }
644 }
645 }
646
647 /* Create session based on session info. */
648 status = pjmedia_vid_stream_create(pjsua_var.med_endpt, NULL, si,
649 call_med->tp, NULL,
650 &call_med->strm.v.stream);
651 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000652 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000653
654 /* Start stream */
655 status = pjmedia_vid_stream_start(call_med->strm.v.stream);
656 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000657 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000658
659 /* Setup decoding direction */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000660 if (si->dir & PJMEDIA_DIR_DECODING)
661 {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000662 pjsua_vid_win_id wid;
663 pjsua_vid_win *w;
664
Benny Prijonob90fd382011-09-18 14:59:56 +0000665 PJ_LOG(4,(THIS_FILE, "Setting up RX.."));
666 pj_log_push_indent();
667
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000668 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
669 PJMEDIA_DIR_DECODING,
670 &media_port);
Benny Prijonob90fd382011-09-18 14:59:56 +0000671 if (status != PJ_SUCCESS) {
672 pj_log_pop_indent();
673 goto on_error;
674 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000675
676 /* Create stream video window */
677 status = create_vid_win(PJSUA_WND_TYPE_STREAM,
678 &media_port->info.fmt,
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000679 call_med->strm.v.rdr_dev,
680 //acc->cfg.vid_rend_dev,
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000681 PJSUA_INVALID_ID,
682 acc->cfg.vid_in_auto_show,
683 &wid);
Benny Prijonob90fd382011-09-18 14:59:56 +0000684 if (status != PJ_SUCCESS) {
685 pj_log_pop_indent();
686 goto on_error;
687 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000688
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000689 w = &pjsua_var.win[wid];
690
Benny Prijono6565b582011-08-29 09:54:02 +0000691#if ENABLE_EVENT
Benny Prijonoee0ba182011-07-15 06:18:29 +0000692 /* Register to video events */
693 pjmedia_event_subscribe(
694 pjmedia_vid_port_get_event_publisher(w->vp_rend),
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000695 &call_med->esub_rend);
Benny Prijono6565b582011-08-29 09:54:02 +0000696#endif
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000697
698 /* Connect renderer to stream */
699 status = pjmedia_vid_port_connect(w->vp_rend, media_port,
700 PJ_FALSE);
Benny Prijonob90fd382011-09-18 14:59:56 +0000701 if (status != PJ_SUCCESS) {
702 pj_log_pop_indent();
703 goto on_error;
704 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000705
706 /* Start renderer */
707 status = pjmedia_vid_port_start(w->vp_rend);
Benny Prijonob90fd382011-09-18 14:59:56 +0000708 if (status != PJ_SUCCESS) {
709 pj_log_pop_indent();
710 goto on_error;
711 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000712
713 /* Done */
714 inc_vid_win(wid);
715 call_med->strm.v.rdr_win_id = wid;
Benny Prijonob90fd382011-09-18 14:59:56 +0000716 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000717 }
718
719 /* Setup encoding direction */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000720 if (si->dir & PJMEDIA_DIR_ENCODING && !call->local_hold)
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000721 {
722 pjsua_vid_win *w;
723 pjsua_vid_win_id wid;
724
Benny Prijonob90fd382011-09-18 14:59:56 +0000725 PJ_LOG(4,(THIS_FILE, "Setting up TX.."));
726 pj_log_push_indent();
727
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000728 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
729 PJMEDIA_DIR_ENCODING,
730 &media_port);
Benny Prijonob90fd382011-09-18 14:59:56 +0000731 if (status != PJ_SUCCESS) {
732 pj_log_pop_indent();
733 goto on_error;
734 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000735
736 /* Create preview video window */
737 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
738 &media_port->info.fmt,
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000739 call_med->strm.v.rdr_dev,
740 call_med->strm.v.cap_dev,
741 //acc->cfg.vid_rend_dev,
742 //acc->cfg.vid_cap_dev,
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000743 PJ_FALSE,
744 &wid);
Benny Prijonob90fd382011-09-18 14:59:56 +0000745 if (status != PJ_SUCCESS) {
746 pj_log_pop_indent();
747 goto on_error;
748 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000749
750 w = &pjsua_var.win[wid];
Benny Prijono6565b582011-08-29 09:54:02 +0000751#if ENABLE_EVENT
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000752 pjmedia_event_subscribe(
753 pjmedia_vid_port_get_event_publisher(w->vp_cap),
754 &call_med->esub_cap);
Benny Prijono6565b582011-08-29 09:54:02 +0000755#endif
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000756
757 /* Connect stream to capturer (via video window tee) */
758 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
Benny Prijonob90fd382011-09-18 14:59:56 +0000759 if (status != PJ_SUCCESS) {
760 pj_log_pop_indent();
761 goto on_error;
762 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000763
764 /* Start renderer */
765 status = pjmedia_vid_port_start(w->vp_rend);
Benny Prijonob90fd382011-09-18 14:59:56 +0000766 if (status != PJ_SUCCESS) {
767 pj_log_pop_indent();
768 goto on_error;
769 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000770
771 /* Start capturer */
772 status = pjmedia_vid_port_start(w->vp_cap);
Benny Prijonob90fd382011-09-18 14:59:56 +0000773 if (status != PJ_SUCCESS) {
774 pj_log_pop_indent();
775 goto on_error;
776 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000777
778 /* Done */
779 inc_vid_win(wid);
780 call_med->strm.v.cap_win_id = wid;
Benny Prijonob90fd382011-09-18 14:59:56 +0000781 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000782 }
783
784 /* Call media direction */
785 call_med->dir = si->dir;
786
787 /* Call media state */
788 if (call->local_hold)
789 call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
790 else if (call_med->dir == PJMEDIA_DIR_DECODING)
791 call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
792 else
793 call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
794 }
795
796 /* Print info. */
797 {
798 char info[80];
799 int info_len = 0;
800 int len;
801 const char *dir;
802
803 switch (si->dir) {
804 case PJMEDIA_DIR_NONE:
805 dir = "inactive";
806 break;
807 case PJMEDIA_DIR_ENCODING:
808 dir = "sendonly";
809 break;
810 case PJMEDIA_DIR_DECODING:
811 dir = "recvonly";
812 break;
813 case PJMEDIA_DIR_ENCODING_DECODING:
814 dir = "sendrecv";
815 break;
816 default:
817 dir = "unknown";
818 break;
819 }
820 len = pj_ansi_sprintf( info+info_len,
821 ", stream #%d: %.*s (%s)", strm_idx,
822 (int)si->codec_info.encoding_name.slen,
823 si->codec_info.encoding_name.ptr,
824 dir);
825 if (len > 0)
826 info_len += len;
Benny Prijonob90fd382011-09-18 14:59:56 +0000827 PJ_LOG(4,(THIS_FILE,"Video updated%s", info));
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000828 }
829
Sauw Mingc7bc3aa2011-07-15 07:22:49 +0000830 if (!acc->cfg.vid_out_auto_transmit && call_med->strm.v.stream) {
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000831 status = pjmedia_vid_stream_pause(call_med->strm.v.stream,
832 PJMEDIA_DIR_ENCODING);
833 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000834 goto on_error;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000835 }
836
Benny Prijonob90fd382011-09-18 14:59:56 +0000837 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000838 return PJ_SUCCESS;
Benny Prijonob90fd382011-09-18 14:59:56 +0000839
840on_error:
841 pj_log_pop_indent();
842 return status;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000843}
844
845
846/* Internal function to stop video stream */
847void stop_video_stream(pjsua_call_media *call_med)
848{
849 pjmedia_vid_stream *strm = call_med->strm.v.stream;
850 pjmedia_rtcp_stat stat;
851
852 pj_assert(call_med->type == PJMEDIA_TYPE_VIDEO);
853
854 if (!strm)
855 return;
856
Benny Prijonob90fd382011-09-18 14:59:56 +0000857 PJ_LOG(4,(THIS_FILE, "Stopping video stream.."));
858 pj_log_push_indent();
859
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000860 /* Unsubscribe events */
861 pjmedia_event_unsubscribe(&call_med->esub_rend);
862 pjmedia_event_unsubscribe(&call_med->esub_cap);
863
Benny Prijonoa0dbe052011-08-29 04:16:14 +0000864 if (call_med->strm.v.cap_win_id != PJSUA_INVALID_ID) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000865 pjmedia_port *media_port;
866 pjsua_vid_win *w =
867 &pjsua_var.win[call_med->strm.v.cap_win_id];
868
869 pjmedia_vid_stream_get_port(call_med->strm.v.stream,
870 PJMEDIA_DIR_ENCODING,
871 &media_port);
872 pj_assert(media_port);
873
874 pjmedia_vid_port_stop(w->vp_cap);
875 pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
876 pjmedia_vid_port_start(w->vp_cap);
877
878 dec_vid_win(call_med->strm.v.cap_win_id);
879 }
880
Benny Prijonoa0dbe052011-08-29 04:16:14 +0000881 if (call_med->strm.v.rdr_win_id != PJSUA_INVALID_ID) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000882 dec_vid_win(call_med->strm.v.rdr_win_id);
883 }
884
885 if ((call_med->dir & PJMEDIA_DIR_ENCODING) &&
886 (pjmedia_vid_stream_get_stat(strm, &stat) == PJ_SUCCESS))
887 {
888 /* Save RTP timestamp & sequence, so when media session is
889 * restarted, those values will be restored as the initial
890 * RTP timestamp & sequence of the new media session. So in
891 * the same call session, RTP timestamp and sequence are
892 * guaranteed to be contigue.
893 */
894 call_med->rtp_tx_seq_ts_set = 1 | (1 << 1);
895 call_med->rtp_tx_seq = stat.rtp_tx_last_seq;
896 call_med->rtp_tx_ts = stat.rtp_tx_last_ts;
897 }
898
899 pjmedia_vid_stream_destroy(strm);
900 call_med->strm.v.stream = NULL;
Benny Prijonob90fd382011-09-18 14:59:56 +0000901
902 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000903}
904
905
Benny Prijono9f468d12011-07-07 07:46:33 +0000906/*
907 * Start video preview window for the specified capture device.
908 */
909PJ_DEF(pj_status_t) pjsua_vid_preview_start(pjmedia_vid_dev_index id,
Benny Prijono2047bd72011-08-25 11:59:39 +0000910 const pjsua_vid_preview_param *prm)
Benny Prijono9f468d12011-07-07 07:46:33 +0000911{
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000912 pjsua_vid_win_id wid;
913 pjsua_vid_win *w;
914 pjmedia_vid_dev_index rend_id;
Benny Prijono2047bd72011-08-25 11:59:39 +0000915 pjsua_vid_preview_param default_param;
Benny Prijono9f468d12011-07-07 07:46:33 +0000916 pj_status_t status;
917
Benny Prijono2047bd72011-08-25 11:59:39 +0000918 if (!prm) {
919 pjsua_vid_preview_param_default(&default_param);
920 prm = &default_param;
Benny Prijono9f468d12011-07-07 07:46:33 +0000921 }
922
Benny Prijonob90fd382011-09-18 14:59:56 +0000923 PJ_LOG(4,(THIS_FILE, "Starting preview for cap_dev=%d, show=%d",
924 id, prm->show));
925 pj_log_push_indent();
926
927 PJSUA_LOCK();
928
Benny Prijono2047bd72011-08-25 11:59:39 +0000929 rend_id = prm->rend_id;
930
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000931 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW, NULL, rend_id, id,
Benny Prijono2047bd72011-08-25 11:59:39 +0000932 prm->show, &wid);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000933 if (status != PJ_SUCCESS) {
Benny Prijono9f468d12011-07-07 07:46:33 +0000934 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +0000935 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000936 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000937 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000938
Benny Prijono9f468d12011-07-07 07:46:33 +0000939 w = &pjsua_var.win[wid];
940
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000941 /* Start capturer */
942 status = pjmedia_vid_port_start(w->vp_rend);
943 if (status != PJ_SUCCESS) {
944 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +0000945 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000946 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000947 }
948
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000949 /* Start renderer */
Benny Prijono9f468d12011-07-07 07:46:33 +0000950 status = pjmedia_vid_port_start(w->vp_cap);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000951 if (status != PJ_SUCCESS) {
952 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +0000953 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000954 return status;
955 }
Benny Prijono9f468d12011-07-07 07:46:33 +0000956
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000957 inc_vid_win(wid);
958
Benny Prijono9f468d12011-07-07 07:46:33 +0000959 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +0000960 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +0000961 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +0000962}
963
964/*
965 * Stop video preview.
966 */
967PJ_DEF(pj_status_t) pjsua_vid_preview_stop(pjmedia_vid_dev_index id)
968{
969 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
Benny Prijono9f468d12011-07-07 07:46:33 +0000970
Benny Prijonob90fd382011-09-18 14:59:56 +0000971 PJ_LOG(4,(THIS_FILE, "Stopping preview for cap_dev=%d", id));
972 pj_log_push_indent();
973
Benny Prijono9f468d12011-07-07 07:46:33 +0000974 PJSUA_LOCK();
975 wid = pjsua_vid_preview_get_win(id);
976 if (wid == PJSUA_INVALID_ID) {
977 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +0000978 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +0000979 return PJ_ENOTFOUND;
980 }
981
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000982 dec_vid_win(wid);
Benny Prijono9f468d12011-07-07 07:46:33 +0000983
984 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +0000985 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +0000986
987 return PJ_SUCCESS;
988}
989
990
991/*****************************************************************************
992 * Window
993 */
994
Nanang Izzuddinf3638022011-07-14 03:47:04 +0000995
996/*
997 * Enumerates all video windows.
998 */
999PJ_DEF(pj_status_t) pjsua_vid_enum_wins( pjsua_vid_win_id wids[],
1000 unsigned *count)
1001{
1002 unsigned i, cnt;
1003
1004 cnt = 0;
1005
1006 for (i=0; i<PJSUA_MAX_VID_WINS && cnt <*count; ++i) {
1007 pjsua_vid_win *w = &pjsua_var.win[i];
1008 if (w->type != PJSUA_WND_TYPE_NONE)
1009 wids[cnt++] = i;
1010 }
1011
1012 *count = cnt;
1013
1014 return PJ_SUCCESS;
1015}
1016
1017
Benny Prijono9f468d12011-07-07 07:46:33 +00001018/*
1019 * Get window info.
1020 */
1021PJ_DEF(pj_status_t) pjsua_vid_win_get_info( pjsua_vid_win_id wid,
1022 pjsua_vid_win_info *wi)
1023{
1024 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001025 pjmedia_vid_dev_stream *s;
Sauw Ming5291a2d2011-07-15 07:52:44 +00001026 pjmedia_vid_dev_param vparam;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001027 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001028
1029 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && wi, PJ_EINVAL);
1030
1031 PJSUA_LOCK();
1032 w = &pjsua_var.win[wid];
1033 if (w->vp_rend == NULL) {
1034 PJSUA_UNLOCK();
1035 return PJ_EINVAL;
1036 }
1037
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001038 s = pjmedia_vid_port_get_stream(w->vp_rend);
1039 if (s == NULL) {
1040 PJSUA_UNLOCK();
1041 return PJ_EINVAL;
1042 }
1043
1044 status = pjmedia_vid_dev_stream_get_param(s, &vparam);
1045 if (status != PJ_SUCCESS) {
1046 PJSUA_UNLOCK();
1047 return status;
1048 }
1049
Nanang Izzuddindb9b0022011-07-26 08:17:25 +00001050 wi->rdr_dev = vparam.rend_id;
Nanang Izzuddin6e2fcc32011-07-22 04:49:36 +00001051 wi->hwnd = vparam.window;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001052 wi->show = !vparam.window_hide;
1053 wi->pos = vparam.window_pos;
1054 wi->size = vparam.disp_size;
1055
Benny Prijono9f468d12011-07-07 07:46:33 +00001056 PJSUA_UNLOCK();
1057
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001058 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +00001059}
1060
1061/*
1062 * Show or hide window.
1063 */
1064PJ_DEF(pj_status_t) pjsua_vid_win_set_show( pjsua_vid_win_id wid,
1065 pj_bool_t show)
1066{
1067 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001068 pjmedia_vid_dev_stream *s;
Nanang Izzuddinc16590a2011-07-14 02:14:25 +00001069 pj_bool_t hide;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001070 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001071
1072 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
1073
1074 PJSUA_LOCK();
1075 w = &pjsua_var.win[wid];
1076 if (w->vp_rend == NULL) {
1077 PJSUA_UNLOCK();
1078 return PJ_EINVAL;
1079 }
1080
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001081 s = pjmedia_vid_port_get_stream(w->vp_rend);
1082 if (s == NULL) {
1083 PJSUA_UNLOCK();
1084 return PJ_EINVAL;
1085 }
1086
Nanang Izzuddinc16590a2011-07-14 02:14:25 +00001087 hide = !show;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001088 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinc16590a2011-07-14 02:14:25 +00001089 PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE, &hide);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001090
Benny Prijono9f468d12011-07-07 07:46:33 +00001091 PJSUA_UNLOCK();
1092
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001093 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001094}
1095
1096/*
1097 * Set video window position.
1098 */
1099PJ_DEF(pj_status_t) pjsua_vid_win_set_pos( pjsua_vid_win_id wid,
1100 const pjmedia_coord *pos)
1101{
1102 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001103 pjmedia_vid_dev_stream *s;
1104 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001105
1106 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && pos, PJ_EINVAL);
1107
1108 PJSUA_LOCK();
1109 w = &pjsua_var.win[wid];
1110 if (w->vp_rend == NULL) {
1111 PJSUA_UNLOCK();
1112 return PJ_EINVAL;
1113 }
1114
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001115 s = pjmedia_vid_port_get_stream(w->vp_rend);
1116 if (s == NULL) {
1117 PJSUA_UNLOCK();
1118 return PJ_EINVAL;
1119 }
1120
1121 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001122 PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION, pos);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001123
Benny Prijono9f468d12011-07-07 07:46:33 +00001124 PJSUA_UNLOCK();
1125
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001126 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001127}
1128
1129/*
1130 * Resize window.
1131 */
1132PJ_DEF(pj_status_t) pjsua_vid_win_set_size( pjsua_vid_win_id wid,
1133 const pjmedia_rect_size *size)
1134{
1135 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001136 pjmedia_vid_dev_stream *s;
1137 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001138
1139 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && size, PJ_EINVAL);
1140
1141 PJSUA_LOCK();
1142 w = &pjsua_var.win[wid];
1143 if (w->vp_rend == NULL) {
1144 PJSUA_UNLOCK();
1145 return PJ_EINVAL;
1146 }
1147
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001148 s = pjmedia_vid_port_get_stream(w->vp_rend);
1149 if (s == NULL) {
1150 PJSUA_UNLOCK();
1151 return PJ_EINVAL;
1152 }
1153
1154 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001155 PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE, size);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001156
Benny Prijono9f468d12011-07-07 07:46:33 +00001157 PJSUA_UNLOCK();
1158
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001159 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001160}
1161
1162
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001163static void call_get_vid_strm_info(pjsua_call *call,
1164 int *first_active,
1165 int *first_inactive,
1166 unsigned *active_cnt,
1167 unsigned *cnt)
1168{
1169 unsigned i, var_cnt = 0;
1170
1171 if (first_active && ++var_cnt)
1172 *first_active = -1;
1173 if (first_inactive && ++var_cnt)
1174 *first_inactive = -1;
1175 if (active_cnt && ++var_cnt)
1176 *active_cnt = 0;
1177 if (cnt && ++var_cnt)
1178 *cnt = 0;
1179
1180 for (i = 0; i < call->med_cnt && var_cnt; ++i) {
1181 if (call->media[i].type == PJMEDIA_TYPE_VIDEO) {
1182 if (call->media[i].dir != PJMEDIA_DIR_NONE)
1183 {
1184 if (first_active && *first_active == -1) {
1185 *first_active = i;
1186 --var_cnt;
1187 }
1188 if (active_cnt)
1189 ++(*active_cnt);
1190 } else if (first_inactive && *first_inactive == -1) {
1191 *first_inactive = i;
1192 --var_cnt;
1193 }
1194 if (cnt)
1195 ++(*cnt);
1196 }
1197 }
1198}
1199
1200
1201/* Send SDP reoffer. */
1202static pj_status_t call_reoffer_sdp(pjsua_call_id call_id,
1203 const pjmedia_sdp_session *sdp)
1204{
1205 pjsua_call *call;
1206 pjsip_tx_data *tdata;
1207 pjsip_dialog *dlg;
1208 pj_status_t status;
1209
1210 status = acquire_call("call_reoffer_sdp()", call_id, &call, &dlg);
1211 if (status != PJ_SUCCESS)
1212 return status;
1213
1214 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1215 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
1216 pjsip_dlg_dec_lock(dlg);
1217 return PJSIP_ESESSIONSTATE;
1218 }
1219
1220 /* Create re-INVITE with new offer */
1221 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1222 if (status != PJ_SUCCESS) {
1223 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
1224 pjsip_dlg_dec_lock(dlg);
1225 return status;
1226 }
1227
1228 /* Send the request */
1229 status = pjsip_inv_send_msg( call->inv, tdata);
1230 if (status != PJ_SUCCESS) {
1231 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
1232 pjsip_dlg_dec_lock(dlg);
1233 return status;
1234 }
1235
1236 pjsip_dlg_dec_lock(dlg);
1237
1238 return PJ_SUCCESS;
1239}
1240
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001241/* Add a new video stream into a call */
1242static pj_status_t call_add_video(pjsua_call *call,
Nanang Izzuddin98085612011-07-15 07:41:02 +00001243 pjmedia_vid_dev_index cap_dev,
1244 pjmedia_dir dir)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001245{
1246 pj_pool_t *pool = call->inv->pool_prov;
1247 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1248 pjsua_call_media *call_med;
1249 pjmedia_sdp_session *sdp;
1250 pjmedia_sdp_media *sdp_m;
1251 pjmedia_transport_info tpinfo;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001252 unsigned active_cnt;
1253 pj_status_t status;
1254
1255 /* Verify media slot availability */
1256 if (call->med_cnt == PJSUA_MAX_CALL_MEDIA)
1257 return PJ_ETOOMANY;
1258
1259 call_get_vid_strm_info(call, NULL, NULL, &active_cnt, NULL);
1260 if (active_cnt == acc_cfg->max_video_cnt)
1261 return PJ_ETOOMANY;
1262
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001263 /* Get active local SDP */
1264 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
1265 if (status != PJ_SUCCESS)
1266 return status;
1267
1268 /* Initialize call media */
1269 call_med = &call->media[call->med_cnt++];
1270
1271 status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1272 &acc_cfg->rtp_cfg, call->secure_level,
1273 NULL);
1274 if (status != PJ_SUCCESS)
1275 goto on_error;
1276
1277 /* Override default capture device setting */
1278 call_med->strm.v.cap_dev = cap_dev;
1279
1280 /* Init transport media */
1281 status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1282 NULL, call_med->idx);
1283 if (status != PJ_SUCCESS)
1284 goto on_error;
1285
1286 call_med->tp_st = PJSUA_MED_TP_INIT;
1287
1288 /* Get transport address info */
1289 pjmedia_transport_info_init(&tpinfo);
1290 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1291
1292 /* Create SDP media line */
1293 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1294 &tpinfo.sock_info, 0, &sdp_m);
1295 if (status != PJ_SUCCESS)
1296 goto on_error;
1297
1298 sdp->media[sdp->media_count++] = sdp_m;
1299
Nanang Izzuddin98085612011-07-15 07:41:02 +00001300 /* Update media direction, if it is not 'sendrecv' */
1301 if (dir != PJMEDIA_DIR_ENCODING_DECODING) {
1302 pjmedia_sdp_attr *a;
1303
1304 /* Remove sendrecv direction attribute, if any */
1305 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
1306
1307 if (dir == PJMEDIA_DIR_ENCODING)
1308 a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
1309 else if (dir == PJMEDIA_DIR_DECODING)
1310 a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
1311 else
1312 a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
1313
1314 pjmedia_sdp_media_add_attr(sdp_m, a);
1315 }
1316
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001317 /* Update SDP media line by media transport */
1318 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1319 sdp, NULL, call_med->idx);
1320 if (status != PJ_SUCCESS)
1321 goto on_error;
1322
1323 status = call_reoffer_sdp(call->index, sdp);
1324 if (status != PJ_SUCCESS)
1325 goto on_error;
1326
1327 return PJ_SUCCESS;
1328
1329on_error:
1330 if (call_med->tp) {
1331 pjmedia_transport_close(call_med->tp);
1332 call_med->tp = call_med->tp_orig = NULL;
1333 }
1334
1335 return status;
1336}
1337
1338
Nanang Izzuddin98085612011-07-15 07:41:02 +00001339/* Modify a video stream from a call, i.e: update direction,
1340 * remove/disable.
1341 */
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001342static pj_status_t call_modify_video(pjsua_call *call,
1343 int med_idx,
Nanang Izzuddin98085612011-07-15 07:41:02 +00001344 pjmedia_dir dir,
1345 pj_bool_t remove)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001346{
1347 pjsua_call_media *call_med;
1348 pjmedia_sdp_session *sdp;
1349 pj_status_t status;
1350
1351 /* Verify and normalize media index */
1352 if (med_idx == -1) {
1353 int first_active;
1354
1355 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1356 if (first_active == -1)
1357 return PJ_ENOTFOUND;
1358
1359 med_idx = first_active;
1360 }
1361
1362 call_med = &call->media[med_idx];
1363
1364 /* Verify if the stream media type is video */
1365 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1366 return PJ_EINVAL;
1367
Nanang Izzuddin98085612011-07-15 07:41:02 +00001368 /* Verify if the stream dir is not changed */
1369 if ((!remove && call_med->dir == dir) ||
1370 ( remove && (call_med->tp_st == PJSUA_MED_TP_DISABLED ||
1371 call_med->tp == NULL)))
1372 {
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001373 return PJ_SUCCESS;
Nanang Izzuddin98085612011-07-15 07:41:02 +00001374 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001375
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001376 /* Get active local SDP */
1377 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
1378 if (status != PJ_SUCCESS)
1379 return status;
1380
1381 pj_assert(med_idx < (int)sdp->media_count);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001382
Nanang Izzuddin98085612011-07-15 07:41:02 +00001383 if (!remove) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001384 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1385 pj_pool_t *pool = call->inv->pool_prov;
1386 pjmedia_sdp_media *sdp_m;
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001387
1388 status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1389 &acc_cfg->rtp_cfg, call->secure_level,
1390 NULL);
1391 if (status != PJ_SUCCESS)
1392 goto on_error;
1393
1394 /* Init transport media */
Nanang Izzuddin98085612011-07-15 07:41:02 +00001395 if (call_med->tp && call_med->tp_st == PJSUA_MED_TP_IDLE) {
1396 status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1397 NULL, call_med->idx);
1398 if (status != PJ_SUCCESS)
1399 goto on_error;
1400 }
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001401
Nanang Izzuddin98085612011-07-15 07:41:02 +00001402 sdp_m = sdp->media[med_idx];
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001403
Nanang Izzuddin98085612011-07-15 07:41:02 +00001404 /* Create new SDP media line if the stream is disabled */
1405 if (sdp->media[med_idx]->desc.port == 0) {
1406 pjmedia_transport_info tpinfo;
1407
1408 /* Get transport address info */
1409 pjmedia_transport_info_init(&tpinfo);
1410 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1411
1412 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1413 &tpinfo.sock_info, 0, &sdp_m);
1414 if (status != PJ_SUCCESS)
1415 goto on_error;
1416 }
1417
1418 {
1419 pjmedia_sdp_attr *a;
1420
1421 /* Remove any direction attributes */
1422 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
1423 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendonly");
1424 pjmedia_sdp_media_remove_all_attr(sdp_m, "recvonly");
1425 pjmedia_sdp_media_remove_all_attr(sdp_m, "inactive");
1426
1427 /* Update media direction */
1428 if (dir == PJMEDIA_DIR_ENCODING_DECODING)
1429 a = pjmedia_sdp_attr_create(pool, "sendrecv", NULL);
1430 else if (dir == PJMEDIA_DIR_ENCODING)
1431 a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
1432 else if (dir == PJMEDIA_DIR_DECODING)
1433 a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
1434 else
1435 a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
1436
1437 pjmedia_sdp_media_add_attr(sdp_m, a);
1438 }
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001439
1440 sdp->media[med_idx] = sdp_m;
1441
1442 /* Update SDP media line by media transport */
1443 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1444 sdp, NULL, call_med->idx);
1445 if (status != PJ_SUCCESS)
1446 goto on_error;
1447
1448on_error:
1449 if (status != PJ_SUCCESS) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001450 return status;
1451 }
Nanang Izzuddin98085612011-07-15 07:41:02 +00001452
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001453 } else {
Nanang Izzuddin98085612011-07-15 07:41:02 +00001454
1455 pj_pool_t *pool = call->inv->pool_prov;
1456
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001457 /* Mark media transport to disabled */
1458 // Don't close this here, as SDP negotiation has not been
1459 // done and stream may be still active.
1460 call_med->tp_st = PJSUA_MED_TP_DISABLED;
1461
Nanang Izzuddin98085612011-07-15 07:41:02 +00001462 /* Deactivate the stream */
1463 pjmedia_sdp_media_deactivate(pool, sdp->media[med_idx]);
1464
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001465 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001466
1467 status = call_reoffer_sdp(call->index, sdp);
1468 if (status != PJ_SUCCESS)
1469 return status;
1470
1471 return PJ_SUCCESS;
1472}
1473
1474
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001475/* Change capture device of a video stream in a call */
1476static pj_status_t call_change_cap_dev(pjsua_call *call,
1477 int med_idx,
1478 pjmedia_vid_dev_index cap_dev)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001479{
1480 pjsua_call_media *call_med;
1481 pjmedia_vid_dev_info info;
1482 pjsua_vid_win *w, *new_w = NULL;
1483 pjsua_vid_win_id wid, new_wid;
1484 pjmedia_port *media_port;
1485 pj_status_t status;
1486
1487 /* Verify and normalize media index */
1488 if (med_idx == -1) {
1489 int first_active;
1490
1491 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1492 if (first_active == -1)
1493 return PJ_ENOTFOUND;
1494
1495 med_idx = first_active;
1496 }
1497
1498 call_med = &call->media[med_idx];
1499
1500 /* Verify if the stream media type is video */
1501 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1502 return PJ_EINVAL;
1503
1504 /* Verify the capture device */
1505 status = pjmedia_vid_dev_get_info(cap_dev, &info);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001506 if (status != PJ_SUCCESS || info.dir != PJMEDIA_DIR_CAPTURE)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001507 return PJ_EINVAL;
1508
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001509 /* The specified capture device is being used already */
1510 if (call_med->strm.v.cap_dev == cap_dev)
1511 return PJ_SUCCESS;
1512
1513 /* == Apply the new capture device == */
1514
1515 wid = call_med->strm.v.cap_win_id;
1516 w = &pjsua_var.win[wid];
1517 pj_assert(w->type == PJSUA_WND_TYPE_PREVIEW && w->vp_cap);
1518
1519 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
1520 PJMEDIA_DIR_ENCODING, &media_port);
1521 if (status != PJ_SUCCESS)
1522 return status;
Benny Prijono1fe04ee2011-07-15 07:27:05 +00001523
1524 pjmedia_event_unsubscribe(&call_med->esub_cap);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001525
1526 /* = Detach stream port from the old capture device = */
1527 status = pjmedia_vid_port_disconnect(w->vp_cap);
1528 if (status != PJ_SUCCESS)
1529 return status;
1530
1531 status = pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
1532 if (status != PJ_SUCCESS) {
1533 /* Connect back the old capturer */
1534 pjmedia_vid_port_connect(w->vp_cap, media_port, PJ_FALSE);
1535 return status;
1536 }
1537
1538 /* = Attach stream port to the new capture device = */
1539
1540 /* Create preview video window */
1541 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
1542 &media_port->info.fmt,
1543 call_med->strm.v.rdr_dev,
1544 cap_dev,
1545 PJ_FALSE,
1546 &new_wid);
1547 if (status != PJ_SUCCESS)
1548 goto on_error;
1549
1550 inc_vid_win(new_wid);
1551 new_w = &pjsua_var.win[new_wid];
1552
1553 /* Connect stream to capturer (via video window tee) */
1554 status = pjmedia_vid_tee_add_dst_port2(new_w->tee, 0, media_port);
1555 if (status != PJ_SUCCESS)
1556 goto on_error;
1557
1558 /* Connect capturer to tee */
1559 status = pjmedia_vid_port_connect(new_w->vp_cap, new_w->tee, PJ_FALSE);
1560 if (status != PJ_SUCCESS)
1561 return status;
1562
Benny Prijono6565b582011-08-29 09:54:02 +00001563#if ENABLE_EVENT
Benny Prijono1fe04ee2011-07-15 07:27:05 +00001564 pjmedia_event_subscribe(
1565 pjmedia_vid_port_get_event_publisher(w->vp_rend),
1566 &call_med->esub_cap);
Benny Prijono6565b582011-08-29 09:54:02 +00001567#endif
Benny Prijono1fe04ee2011-07-15 07:27:05 +00001568
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001569 /* Start renderer */
1570 status = pjmedia_vid_port_start(new_w->vp_rend);
1571 if (status != PJ_SUCCESS)
1572 goto on_error;
1573
1574 /* Start capturer */
1575 status = pjmedia_vid_port_start(new_w->vp_cap);
1576 if (status != PJ_SUCCESS)
1577 goto on_error;
1578
1579 /* Finally */
1580 call_med->strm.v.cap_dev = cap_dev;
1581 call_med->strm.v.cap_win_id = new_wid;
1582 dec_vid_win(wid);
1583
1584 return PJ_SUCCESS;
1585
1586on_error:
1587 if (new_w) {
1588 /* Disconnect media port from the new capturer */
1589 pjmedia_vid_tee_remove_dst_port(new_w->tee, media_port);
1590 /* Release the new capturer */
1591 dec_vid_win(new_wid);
1592 }
1593
1594 /* Revert back to the old capturer */
1595 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
1596 if (status != PJ_SUCCESS)
1597 return status;
1598
1599 status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
1600 if (status != PJ_SUCCESS)
1601 return status;
1602
1603 return status;
1604}
1605
1606
Nanang Izzuddin98085612011-07-15 07:41:02 +00001607/* Start/stop transmitting video stream in a call */
1608static pj_status_t call_set_tx_video(pjsua_call *call,
1609 int med_idx,
1610 pj_bool_t enable)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001611{
1612 pjsua_call_media *call_med;
1613 pj_status_t status;
1614
1615 /* Verify and normalize media index */
1616 if (med_idx == -1) {
1617 int first_active;
1618
1619 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1620 if (first_active == -1)
1621 return PJ_ENOTFOUND;
1622
1623 med_idx = first_active;
1624 }
1625
1626 call_med = &call->media[med_idx];
1627
1628 /* Verify if the stream is transmitting video */
1629 if (call_med->type != PJMEDIA_TYPE_VIDEO ||
Nanang Izzuddin98085612011-07-15 07:41:02 +00001630 (enable && (call_med->dir & PJMEDIA_DIR_ENCODING) == 0))
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001631 {
1632 return PJ_EINVAL;
1633 }
1634
Nanang Izzuddin98085612011-07-15 07:41:02 +00001635 if (enable) {
1636 /* Start stream in encoding direction */
1637 status = pjmedia_vid_stream_resume(call_med->strm.v.stream,
1638 PJMEDIA_DIR_ENCODING);
1639 } else {
1640 /* Pause stream in encoding direction */
1641 status = pjmedia_vid_stream_pause( call_med->strm.v.stream,
1642 PJMEDIA_DIR_ENCODING);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001643 }
1644
Nanang Izzuddin98085612011-07-15 07:41:02 +00001645 return status;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001646}
1647
1648
1649/*
1650 * Start, stop, and/or manipulate video transmission for the specified call.
1651 */
1652PJ_DEF(pj_status_t) pjsua_call_set_vid_strm (
1653 pjsua_call_id call_id,
1654 pjsua_call_vid_strm_op op,
1655 const pjsua_call_vid_strm_op_param *param)
1656{
1657 pjsua_call *call;
1658 pjsua_call_vid_strm_op_param param_;
1659 pj_status_t status;
1660
1661 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1662 PJ_EINVAL);
Benny Prijonoe212bc12011-08-15 09:38:42 +00001663 PJ_ASSERT_RETURN(op != PJSUA_CALL_VID_STRM_NO_OP, PJ_EINVAL);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001664
Benny Prijonob90fd382011-09-18 14:59:56 +00001665 PJ_LOG(4,(THIS_FILE, "Call %d: set video stream, op=%d",
1666 call_id, op));
1667 pj_log_push_indent();
1668
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001669 PJSUA_LOCK();
1670
1671 call = &pjsua_var.calls[call_id];
1672
1673 if (param) {
1674 param_ = *param;
1675 } else {
Benny Prijonoe212bc12011-08-15 09:38:42 +00001676 pjsua_call_vid_strm_op_param_default(&param_);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001677 }
1678
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001679 /* If set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
1680 * account default video capture device.
1681 */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001682 if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001683 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1684 param_.cap_dev = acc_cfg->vid_cap_dev;
1685
1686 /* If the account default video capture device is
1687 * PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
1688 * global default video capture device.
1689 */
1690 if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
1691 pjmedia_vid_dev_info info;
1692 pjmedia_vid_dev_get_info(param_.cap_dev, &info);
1693 pj_assert(info.dir == PJMEDIA_DIR_CAPTURE);
1694 param_.cap_dev = info.id;
1695 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001696 }
1697
1698 switch (op) {
1699 case PJSUA_CALL_VID_STRM_ADD:
Nanang Izzuddin98085612011-07-15 07:41:02 +00001700 status = call_add_video(call, param_.cap_dev, param_.dir);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001701 break;
Nanang Izzuddin98085612011-07-15 07:41:02 +00001702 case PJSUA_CALL_VID_STRM_REMOVE:
1703 status = call_modify_video(call, param_.med_idx, PJMEDIA_DIR_NONE,
1704 PJ_TRUE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001705 break;
Nanang Izzuddin98085612011-07-15 07:41:02 +00001706 case PJSUA_CALL_VID_STRM_CHANGE_DIR:
1707 status = call_modify_video(call, param_.med_idx, param_.dir, PJ_FALSE);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001708 break;
1709 case PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV:
1710 status = call_change_cap_dev(call, param_.med_idx, param_.cap_dev);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001711 break;
1712 case PJSUA_CALL_VID_STRM_START_TRANSMIT:
Nanang Izzuddin98085612011-07-15 07:41:02 +00001713 status = call_set_tx_video(call, param_.med_idx, PJ_TRUE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001714 break;
1715 case PJSUA_CALL_VID_STRM_STOP_TRANSMIT:
Nanang Izzuddin98085612011-07-15 07:41:02 +00001716 status = call_set_tx_video(call, param_.med_idx, PJ_FALSE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001717 break;
1718 default:
1719 status = PJ_EINVALIDOP;
1720 break;
1721 }
1722
1723 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001724 pj_log_pop_indent();
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001725
1726 return status;
1727}
1728
Benny Prijono9f468d12011-07-07 07:46:33 +00001729
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001730/*
1731 * Get the media stream index of the default video stream in the call.
1732 */
1733PJ_DEF(int) pjsua_call_get_vid_stream_idx(pjsua_call_id call_id)
1734{
1735 pjsua_call *call;
1736 int first_active, first_inactive;
1737
1738 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1739 PJ_EINVAL);
1740
1741 PJSUA_LOCK();
1742 call = &pjsua_var.calls[call_id];
1743 call_get_vid_strm_info(call, &first_active, &first_inactive, NULL, NULL);
1744 PJSUA_UNLOCK();
1745
1746 if (first_active == -1)
1747 return first_inactive;
1748
1749 return first_active;
1750}
1751
1752
Benny Prijono9f468d12011-07-07 07:46:33 +00001753#endif /* PJSUA_HAS_VIDEO */
1754