blob: 1cd890d0add73a2f56370dc06a9b50174cf935aa [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,
Sauw Ming0ee072f2011-10-26 09:01:53 +0000391 unsigned wnd_flags,
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000392 pjsua_vid_win_id *id)
Benny Prijono9f468d12011-07-07 07:46:33 +0000393{
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000394 pj_bool_t enable_native_preview;
Benny Prijono9f468d12011-07-07 07:46:33 +0000395 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000396 pjsua_vid_win *w = NULL;
397 pjmedia_vid_port_param vp_param;
398 pjmedia_format fmt_;
399 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000400 unsigned i;
401
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000402 enable_native_preview = pjsua_var.media_cfg.vid_preview_enable_native;
403
404 PJ_LOG(4,(THIS_FILE,
405 "Creating video window: type=%s, cap_id=%d, rend_id=%d",
406 pjsua_vid_win_type_name(type), cap_id, rend_id));
Benny Prijonob90fd382011-09-18 14:59:56 +0000407 pj_log_push_indent();
408
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000409 /* If type is preview, check if it exists already */
410 if (type == PJSUA_WND_TYPE_PREVIEW) {
Benny Prijono39203b82011-09-20 10:07:55 +0000411 wid = vid_preview_get_win(cap_id, PJ_FALSE);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000412 if (wid != PJSUA_INVALID_ID) {
413 /* Yes, it exists */
Benny Prijono2047bd72011-08-25 11:59:39 +0000414 /* Show/hide window */
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000415 pjmedia_vid_dev_stream *strm;
Benny Prijono2047bd72011-08-25 11:59:39 +0000416 pj_bool_t hide = !show;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000417
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000418 w = &pjsua_var.win[wid];
419
420 PJ_LOG(4,(THIS_FILE,
421 "Window already exists for cap_dev=%d, returning wid=%d",
422 cap_id, wid));
423
424
425 if (w->is_native) {
426 strm = pjmedia_vid_port_get_stream(w->vp_cap);
427 } else {
428 strm = pjmedia_vid_port_get_stream(w->vp_rend);
429 }
430
431 pj_assert(strm);
Benny Prijono2047bd72011-08-25 11:59:39 +0000432 status = pjmedia_vid_dev_stream_set_cap(
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000433 strm, PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE,
Benny Prijono2047bd72011-08-25 11:59:39 +0000434 &hide);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000435
Sauw Ming0ee072f2011-10-26 09:01:53 +0000436 pjmedia_vid_dev_stream_set_cap(
437 strm, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS,
438 &wnd_flags);
439
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000440 /* Done */
441 *id = wid;
Benny Prijonob90fd382011-09-18 14:59:56 +0000442 pj_log_pop_indent();
Benny Prijono39203b82011-09-20 10:07:55 +0000443
444 return status;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000445 }
446 }
447
448 /* Allocate window */
Benny Prijono9f468d12011-07-07 07:46:33 +0000449 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000450 w = &pjsua_var.win[i];
Benny Prijono9f468d12011-07-07 07:46:33 +0000451 if (w->type == PJSUA_WND_TYPE_NONE) {
452 wid = i;
453 w->type = type;
454 break;
455 }
456 }
Benny Prijonob90fd382011-09-18 14:59:56 +0000457 if (i == PJSUA_MAX_VID_WINS) {
458 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000459 return PJ_ETOOMANY;
Benny Prijonob90fd382011-09-18 14:59:56 +0000460 }
Benny Prijono9f468d12011-07-07 07:46:33 +0000461
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000462 /* Initialize window */
463 pjmedia_vid_port_param_default(&vp_param);
464
465 if (w->type == PJSUA_WND_TYPE_PREVIEW) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000466 pjmedia_vid_dev_info vdi;
467
468 /*
469 * Determine if the device supports native preview.
470 */
471 status = pjmedia_vid_dev_get_info(cap_id, &vdi);
472 if (status != PJ_SUCCESS)
473 goto on_error;
474
475 if (enable_native_preview &&
476 (vdi.caps & PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW))
477 {
478 /* Device supports native preview! */
479 w->is_native = PJ_TRUE;
480 }
481
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000482 status = pjmedia_vid_dev_default_param(w->pool, cap_id,
483 &vp_param.vidparam);
484 if (status != PJ_SUCCESS)
485 goto on_error;
486
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000487 if (w->is_native) {
488 vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
489 vp_param.vidparam.window_hide = !show;
Sauw Ming0ee072f2011-10-26 09:01:53 +0000490 vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS;
491 vp_param.vidparam.window_flags = wnd_flags;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000492 }
493
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000494 /* Normalize capture ID, in case it was set to
495 * PJMEDIA_VID_DEFAULT_CAPTURE_DEV
496 */
497 cap_id = vp_param.vidparam.cap_id;
498
499 /* Assign preview capture device ID */
500 w->preview_cap_id = cap_id;
501
502 /* Create capture video port */
503 vp_param.active = PJ_TRUE;
504 vp_param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
505 if (fmt)
506 vp_param.vidparam.fmt = *fmt;
507
508 status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_cap);
509 if (status != PJ_SUCCESS)
510 goto on_error;
511
512 /* Update format info */
513 fmt_ = vp_param.vidparam.fmt;
514 fmt = &fmt_;
515
516 /* Create video tee */
Nanang Izzuddin15ad7f22011-08-26 04:19:04 +0000517 status = pjmedia_vid_tee_create(w->pool, fmt, VID_TEE_MAX_PORT,
518 &w->tee);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000519 if (status != PJ_SUCCESS)
520 goto on_error;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000521
522 /* If device supports native preview, enable it */
523 if (w->is_native) {
524 pjmedia_vid_dev_stream *cap_dev;
525 pj_bool_t enabled = PJ_TRUE;
526
527 cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
528 status = pjmedia_vid_dev_stream_set_cap(
529 cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
530 &enabled);
531 if (status != PJ_SUCCESS) {
532 PJ_PERROR(1,(THIS_FILE, status,
533 "Error activating native preview, falling back "
534 "to software preview.."));
535 w->is_native = PJ_FALSE;
536 }
537 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000538 }
539
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000540 /* Create renderer video port, only if it's not a native preview */
541 if (!w->is_native) {
542 status = pjmedia_vid_dev_default_param(w->pool, rend_id,
543 &vp_param.vidparam);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000544 if (status != PJ_SUCCESS)
545 goto on_error;
546
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000547 vp_param.active = (w->type == PJSUA_WND_TYPE_STREAM);
548 vp_param.vidparam.dir = PJMEDIA_DIR_RENDER;
549 vp_param.vidparam.fmt = *fmt;
550 vp_param.vidparam.disp_size = fmt->det.vid.size;
551 vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
552 vp_param.vidparam.window_hide = !show;
Sauw Ming0ee072f2011-10-26 09:01:53 +0000553 vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS;
554 vp_param.vidparam.window_flags = wnd_flags;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000555
556 status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_rend);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000557 if (status != PJ_SUCCESS)
558 goto on_error;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000559
560 /* For preview window, connect capturer & renderer (via tee) */
561 if (w->type == PJSUA_WND_TYPE_PREVIEW) {
562 pjmedia_port *rend_port;
563
564 status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
565 if (status != PJ_SUCCESS)
566 goto on_error;
567
568 rend_port = pjmedia_vid_port_get_passive_port(w->vp_rend);
569 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, rend_port);
570 if (status != PJ_SUCCESS)
571 goto on_error;
572 }
573
574 PJ_LOG(4,(THIS_FILE,
575 "%s window id %d created for cap_dev=%d rend_dev=%d",
576 pjsua_vid_win_type_name(type), wid, cap_id, rend_id));
577 } else {
578 PJ_LOG(4,(THIS_FILE,
579 "Preview window id %d created for cap_dev %d, "
580 "using built-in preview!",
581 wid, cap_id));
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000582 }
583
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000584
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000585 /* Done */
586 *id = wid;
587
Benny Prijonob90fd382011-09-18 14:59:56 +0000588 PJ_LOG(4,(THIS_FILE, "Window %d created", wid));
589 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000590 return PJ_SUCCESS;
591
592on_error:
593 free_vid_win(wid);
Benny Prijonob90fd382011-09-18 14:59:56 +0000594 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000595 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000596}
597
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000598
Benny Prijono9f468d12011-07-07 07:46:33 +0000599static void free_vid_win(pjsua_vid_win_id wid)
600{
601 pjsua_vid_win *w = &pjsua_var.win[wid];
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000602
Benny Prijonob90fd382011-09-18 14:59:56 +0000603 PJ_LOG(4,(THIS_FILE, "Window %d: destroying..", wid));
604 pj_log_push_indent();
605
Benny Prijono9f468d12011-07-07 07:46:33 +0000606 if (w->vp_cap) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000607 pjmedia_vid_port_stop(w->vp_cap);
608 pjmedia_vid_port_disconnect(w->vp_cap);
Benny Prijono9f468d12011-07-07 07:46:33 +0000609 pjmedia_vid_port_destroy(w->vp_cap);
610 }
611 if (w->vp_rend) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000612 pjmedia_vid_port_stop(w->vp_rend);
Benny Prijono9f468d12011-07-07 07:46:33 +0000613 pjmedia_vid_port_destroy(w->vp_rend);
614 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000615 if (w->tee) {
616 pjmedia_port_destroy(w->tee);
617 }
Benny Prijono9f468d12011-07-07 07:46:33 +0000618 pjsua_vid_win_reset(wid);
Benny Prijonob90fd382011-09-18 14:59:56 +0000619
620 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +0000621}
622
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000623
624static void inc_vid_win(pjsua_vid_win_id wid)
625{
626 pjsua_vid_win *w;
627
628 pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
629
630 w = &pjsua_var.win[wid];
631 pj_assert(w->type != PJSUA_WND_TYPE_NONE);
632 ++w->ref_cnt;
633}
634
635static void dec_vid_win(pjsua_vid_win_id wid)
636{
637 pjsua_vid_win *w;
638
639 pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
640
641 w = &pjsua_var.win[wid];
642 pj_assert(w->type != PJSUA_WND_TYPE_NONE);
643 if (--w->ref_cnt == 0)
644 free_vid_win(wid);
645}
646
647
648/* Internal function: update video channel after SDP negotiation */
649pj_status_t video_channel_update(pjsua_call_media *call_med,
650 pj_pool_t *tmp_pool,
651 const pjmedia_sdp_session *local_sdp,
652 const pjmedia_sdp_session *remote_sdp)
653{
654 pjsua_call *call = call_med->call;
655 pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
656 pjmedia_vid_stream_info the_si, *si = &the_si;
657 pjmedia_port *media_port;
658 unsigned strm_idx = call_med->idx;
659 pj_status_t status;
660
Benny Prijonob90fd382011-09-18 14:59:56 +0000661 PJ_LOG(4,(THIS_FILE, "Video channel update.."));
662 pj_log_push_indent();
663
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000664 status = pjmedia_vid_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt,
665 local_sdp, remote_sdp, strm_idx);
666 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000667 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000668
669 /* Check if no media is active */
670 if (si->dir == PJMEDIA_DIR_NONE) {
671 /* Call media state */
672 call_med->state = PJSUA_CALL_MEDIA_NONE;
673
674 /* Call media direction */
675 call_med->dir = PJMEDIA_DIR_NONE;
676
677 } else {
678 pjmedia_transport_info tp_info;
679
680 /* Start/restart media transport */
681 status = pjmedia_transport_media_start(call_med->tp,
682 tmp_pool, local_sdp,
683 remote_sdp, strm_idx);
684 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000685 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000686
Sauw Ming73ecfe82011-09-21 10:20:01 +0000687 set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000688
689 /* Get remote SRTP usage policy */
690 pjmedia_transport_info_init(&tp_info);
691 pjmedia_transport_get_info(call_med->tp, &tp_info);
692 if (tp_info.specific_info_cnt > 0) {
693 unsigned i;
694 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
695 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
696 {
697 pjmedia_srtp_info *srtp_info =
698 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
699
700 call_med->rem_srtp_use = srtp_info->peer_use;
701 break;
702 }
703 }
704 }
705
706 /* Optionally, application may modify other stream settings here
707 * (such as jitter buffer parameters, codec ptime, etc.)
708 */
709 si->jb_init = pjsua_var.media_cfg.jb_init;
710 si->jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
711 si->jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
712 si->jb_max = pjsua_var.media_cfg.jb_max;
713
714 /* Set SSRC */
715 si->ssrc = call_med->ssrc;
716
717 /* Set RTP timestamp & sequence, normally these value are intialized
718 * automatically when stream session created, but for some cases (e.g:
719 * call reinvite, call update) timestamp and sequence need to be kept
720 * contigue.
721 */
722 si->rtp_ts = call_med->rtp_tx_ts;
723 si->rtp_seq = call_med->rtp_tx_seq;
724 si->rtp_seq_ts_set = call_med->rtp_tx_seq_ts_set;
725
726#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
727 /* Enable/disable stream keep-alive and NAT hole punch. */
728 si->use_ka = pjsua_var.acc[call->acc_id].cfg.use_stream_ka;
729#endif
730
731 /* Try to get shared format ID between the capture device and
732 * the encoder to avoid format conversion in the capture device.
733 */
734 if (si->dir & PJMEDIA_DIR_ENCODING) {
735 pjmedia_vid_dev_info dev_info;
736 pjmedia_vid_codec_info *codec_info = &si->codec_info;
737 unsigned i, j;
738
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +0000739 status = pjmedia_vid_dev_get_info(call_med->strm.v.cap_dev,
740 &dev_info);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000741 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000742 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000743
744 /* Find matched format ID */
745 for (i = 0; i < codec_info->dec_fmt_id_cnt; ++i) {
746 for (j = 0; j < dev_info.fmt_cnt; ++j) {
747 if (codec_info->dec_fmt_id[i] ==
748 (pjmedia_format_id)dev_info.fmt[j].id)
749 {
750 /* Apply the matched format ID to the codec */
751 si->codec_param->dec_fmt.id =
752 codec_info->dec_fmt_id[i];
753
754 /* Force outer loop to break */
755 i = codec_info->dec_fmt_id_cnt;
756 break;
757 }
758 }
759 }
760 }
761
762 /* Create session based on session info. */
763 status = pjmedia_vid_stream_create(pjsua_var.med_endpt, NULL, si,
764 call_med->tp, NULL,
765 &call_med->strm.v.stream);
766 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000767 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000768
769 /* Start stream */
770 status = pjmedia_vid_stream_start(call_med->strm.v.stream);
771 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000772 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000773
774 /* Setup decoding direction */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000775 if (si->dir & PJMEDIA_DIR_DECODING)
776 {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000777 pjsua_vid_win_id wid;
778 pjsua_vid_win *w;
779
Benny Prijonob90fd382011-09-18 14:59:56 +0000780 PJ_LOG(4,(THIS_FILE, "Setting up RX.."));
781 pj_log_push_indent();
782
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000783 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
784 PJMEDIA_DIR_DECODING,
785 &media_port);
Benny Prijonob90fd382011-09-18 14:59:56 +0000786 if (status != PJ_SUCCESS) {
787 pj_log_pop_indent();
788 goto on_error;
789 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000790
791 /* Create stream video window */
792 status = create_vid_win(PJSUA_WND_TYPE_STREAM,
793 &media_port->info.fmt,
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000794 call_med->strm.v.rdr_dev,
795 //acc->cfg.vid_rend_dev,
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000796 PJSUA_INVALID_ID,
797 acc->cfg.vid_in_auto_show,
Sauw Ming0ee072f2011-10-26 09:01:53 +0000798 acc->cfg.vid_wnd_flags,
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000799 &wid);
Benny Prijonob90fd382011-09-18 14:59:56 +0000800 if (status != PJ_SUCCESS) {
801 pj_log_pop_indent();
802 goto on_error;
803 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000804
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000805 w = &pjsua_var.win[wid];
806
Benny Prijono6565b582011-08-29 09:54:02 +0000807#if ENABLE_EVENT
Benny Prijonoee0ba182011-07-15 06:18:29 +0000808 /* Register to video events */
809 pjmedia_event_subscribe(
810 pjmedia_vid_port_get_event_publisher(w->vp_rend),
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000811 &call_med->esub_rend);
Benny Prijono6565b582011-08-29 09:54:02 +0000812#endif
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000813
814 /* Connect renderer to stream */
815 status = pjmedia_vid_port_connect(w->vp_rend, media_port,
816 PJ_FALSE);
Benny Prijonob90fd382011-09-18 14:59:56 +0000817 if (status != PJ_SUCCESS) {
818 pj_log_pop_indent();
819 goto on_error;
820 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000821
822 /* Start renderer */
823 status = pjmedia_vid_port_start(w->vp_rend);
Benny Prijonob90fd382011-09-18 14:59:56 +0000824 if (status != PJ_SUCCESS) {
825 pj_log_pop_indent();
826 goto on_error;
827 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000828
829 /* Done */
830 inc_vid_win(wid);
831 call_med->strm.v.rdr_win_id = wid;
Benny Prijonob90fd382011-09-18 14:59:56 +0000832 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000833 }
834
835 /* Setup encoding direction */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000836 if (si->dir & PJMEDIA_DIR_ENCODING && !call->local_hold)
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000837 {
Sauw Ming0ee072f2011-10-26 09:01:53 +0000838 pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000839 pjsua_vid_win *w;
840 pjsua_vid_win_id wid;
Benny Prijonoeb663982011-10-06 06:05:36 +0000841 pj_bool_t just_created = PJ_FALSE;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000842
Benny Prijonob90fd382011-09-18 14:59:56 +0000843 PJ_LOG(4,(THIS_FILE, "Setting up TX.."));
844 pj_log_push_indent();
845
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000846 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
847 PJMEDIA_DIR_ENCODING,
848 &media_port);
Benny Prijonob90fd382011-09-18 14:59:56 +0000849 if (status != PJ_SUCCESS) {
850 pj_log_pop_indent();
851 goto on_error;
852 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000853
Benny Prijono39203b82011-09-20 10:07:55 +0000854 /* Note: calling pjsua_vid_preview_get_win() even though
855 * create_vid_win() will automatically create the window
856 * if it doesn't exist, because create_vid_win() will modify
857 * existing window SHOW/HIDE value.
858 */
859 wid = vid_preview_get_win(call_med->strm.v.cap_dev, PJ_FALSE);
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000860 if (wid == PJSUA_INVALID_ID) {
861 /* Create preview video window */
862 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
863 &media_port->info.fmt,
864 call_med->strm.v.rdr_dev,
865 call_med->strm.v.cap_dev,
866 //acc->cfg.vid_rend_dev,
867 //acc->cfg.vid_cap_dev,
868 PJSUA_HIDE_WINDOW,
Sauw Ming0ee072f2011-10-26 09:01:53 +0000869 acc->cfg.vid_wnd_flags,
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000870 &wid);
871 if (status != PJ_SUCCESS) {
Benny Prijonob90fd382011-09-18 14:59:56 +0000872 pj_log_pop_indent();
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000873 return status;
874 }
Benny Prijonoeb663982011-10-06 06:05:36 +0000875 just_created = PJ_TRUE;
Benny Prijonob90fd382011-09-18 14:59:56 +0000876 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000877
878 w = &pjsua_var.win[wid];
Benny Prijono6565b582011-08-29 09:54:02 +0000879#if ENABLE_EVENT
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000880 pjmedia_event_subscribe(
881 pjmedia_vid_port_get_event_publisher(w->vp_cap),
882 &call_med->esub_cap);
Benny Prijono6565b582011-08-29 09:54:02 +0000883#endif
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000884
885 /* Connect stream to capturer (via video window tee) */
886 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
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 /* Start renderer */
893 status = pjmedia_vid_port_start(w->vp_rend);
Benny Prijonob90fd382011-09-18 14:59:56 +0000894 if (status != PJ_SUCCESS) {
895 pj_log_pop_indent();
896 goto on_error;
897 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000898
899 /* Start capturer */
Benny Prijonoeb663982011-10-06 06:05:36 +0000900 if (just_created) {
901 status = pjmedia_vid_port_start(w->vp_cap);
902 if (status != PJ_SUCCESS) {
903 pj_log_pop_indent();
904 goto on_error;
905 }
Benny Prijonob90fd382011-09-18 14:59:56 +0000906 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000907
908 /* Done */
909 inc_vid_win(wid);
910 call_med->strm.v.cap_win_id = wid;
Benny Prijonob90fd382011-09-18 14:59:56 +0000911 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000912 }
913
914 /* Call media direction */
915 call_med->dir = si->dir;
916
917 /* Call media state */
918 if (call->local_hold)
919 call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
920 else if (call_med->dir == PJMEDIA_DIR_DECODING)
921 call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
922 else
923 call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
924 }
925
926 /* Print info. */
927 {
928 char info[80];
929 int info_len = 0;
930 int len;
931 const char *dir;
932
933 switch (si->dir) {
934 case PJMEDIA_DIR_NONE:
935 dir = "inactive";
936 break;
937 case PJMEDIA_DIR_ENCODING:
938 dir = "sendonly";
939 break;
940 case PJMEDIA_DIR_DECODING:
941 dir = "recvonly";
942 break;
943 case PJMEDIA_DIR_ENCODING_DECODING:
944 dir = "sendrecv";
945 break;
946 default:
947 dir = "unknown";
948 break;
949 }
950 len = pj_ansi_sprintf( info+info_len,
951 ", stream #%d: %.*s (%s)", strm_idx,
952 (int)si->codec_info.encoding_name.slen,
953 si->codec_info.encoding_name.ptr,
954 dir);
955 if (len > 0)
956 info_len += len;
Benny Prijonob90fd382011-09-18 14:59:56 +0000957 PJ_LOG(4,(THIS_FILE,"Video updated%s", info));
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000958 }
959
Sauw Mingc7bc3aa2011-07-15 07:22:49 +0000960 if (!acc->cfg.vid_out_auto_transmit && call_med->strm.v.stream) {
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000961 status = pjmedia_vid_stream_pause(call_med->strm.v.stream,
962 PJMEDIA_DIR_ENCODING);
963 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000964 goto on_error;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000965 }
966
Benny Prijonob90fd382011-09-18 14:59:56 +0000967 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000968 return PJ_SUCCESS;
Benny Prijonob90fd382011-09-18 14:59:56 +0000969
970on_error:
971 pj_log_pop_indent();
972 return status;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000973}
974
975
976/* Internal function to stop video stream */
977void stop_video_stream(pjsua_call_media *call_med)
978{
979 pjmedia_vid_stream *strm = call_med->strm.v.stream;
980 pjmedia_rtcp_stat stat;
981
982 pj_assert(call_med->type == PJMEDIA_TYPE_VIDEO);
983
984 if (!strm)
985 return;
986
Benny Prijonob90fd382011-09-18 14:59:56 +0000987 PJ_LOG(4,(THIS_FILE, "Stopping video stream.."));
988 pj_log_push_indent();
989
Benny Prijonoa0dbe052011-08-29 04:16:14 +0000990 if (call_med->strm.v.cap_win_id != PJSUA_INVALID_ID) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000991 pjmedia_port *media_port;
Nanang Izzuddinc12a19d2011-10-03 05:23:59 +0000992 pjsua_vid_win *w = &pjsua_var.win[call_med->strm.v.cap_win_id];
Nanang Izzuddinca356182011-09-23 04:00:27 +0000993 pj_status_t status;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000994
Nanang Izzuddinc12a19d2011-10-03 05:23:59 +0000995 /* Stop the capture before detaching stream and unsubscribing event */
996 pjmedia_vid_port_stop(w->vp_cap);
997
Nanang Izzuddinca356182011-09-23 04:00:27 +0000998 /* Disconnect video stream from capture device */
999 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
1000 PJMEDIA_DIR_ENCODING,
1001 &media_port);
1002 if (status == PJ_SUCCESS) {
Nanang Izzuddinca356182011-09-23 04:00:27 +00001003 pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
Nanang Izzuddinca356182011-09-23 04:00:27 +00001004 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001005
Nanang Izzuddinc12a19d2011-10-03 05:23:59 +00001006 /* Unsubscribe event */
1007 pjmedia_event_unsubscribe(&call_med->esub_cap);
1008
1009 /* Re-start capture again, if it is used by other stream */
1010 if (w->ref_cnt > 1)
1011 pjmedia_vid_port_start(w->vp_cap);
1012
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001013 dec_vid_win(call_med->strm.v.cap_win_id);
Nanang Izzuddinca356182011-09-23 04:00:27 +00001014 call_med->strm.v.cap_win_id = PJSUA_INVALID_ID;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001015 }
1016
Benny Prijonoa0dbe052011-08-29 04:16:14 +00001017 if (call_med->strm.v.rdr_win_id != PJSUA_INVALID_ID) {
Nanang Izzuddinc12a19d2011-10-03 05:23:59 +00001018 pjsua_vid_win *w = &pjsua_var.win[call_med->strm.v.rdr_win_id];
1019
1020 /* Stop the render before unsubscribing event */
1021 pjmedia_vid_port_stop(w->vp_rend);
1022 pjmedia_event_unsubscribe(&call_med->esub_rend);
1023
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001024 dec_vid_win(call_med->strm.v.rdr_win_id);
Nanang Izzuddinca356182011-09-23 04:00:27 +00001025 call_med->strm.v.rdr_win_id = PJSUA_INVALID_ID;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001026 }
1027
1028 if ((call_med->dir & PJMEDIA_DIR_ENCODING) &&
1029 (pjmedia_vid_stream_get_stat(strm, &stat) == PJ_SUCCESS))
1030 {
1031 /* Save RTP timestamp & sequence, so when media session is
1032 * restarted, those values will be restored as the initial
1033 * RTP timestamp & sequence of the new media session. So in
1034 * the same call session, RTP timestamp and sequence are
1035 * guaranteed to be contigue.
1036 */
1037 call_med->rtp_tx_seq_ts_set = 1 | (1 << 1);
1038 call_med->rtp_tx_seq = stat.rtp_tx_last_seq;
1039 call_med->rtp_tx_ts = stat.rtp_tx_last_ts;
1040 }
1041
1042 pjmedia_vid_stream_destroy(strm);
1043 call_med->strm.v.stream = NULL;
Benny Prijonob90fd382011-09-18 14:59:56 +00001044
1045 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001046}
1047
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001048/*
1049 * Does it have built-in preview support.
1050 */
1051PJ_DEF(pj_bool_t) pjsua_vid_preview_has_native(pjmedia_vid_dev_index id)
1052{
1053 pjmedia_vid_dev_info vdi;
1054
1055 return (pjmedia_vid_dev_get_info(id, &vdi)==PJ_SUCCESS) ?
1056 ((vdi.caps & PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW)!=0) : PJ_FALSE;
1057}
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001058
Benny Prijono9f468d12011-07-07 07:46:33 +00001059/*
1060 * Start video preview window for the specified capture device.
1061 */
1062PJ_DEF(pj_status_t) pjsua_vid_preview_start(pjmedia_vid_dev_index id,
Benny Prijono2047bd72011-08-25 11:59:39 +00001063 const pjsua_vid_preview_param *prm)
Benny Prijono9f468d12011-07-07 07:46:33 +00001064{
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001065 pjsua_vid_win_id wid;
1066 pjsua_vid_win *w;
1067 pjmedia_vid_dev_index rend_id;
Benny Prijono2047bd72011-08-25 11:59:39 +00001068 pjsua_vid_preview_param default_param;
Benny Prijono9f468d12011-07-07 07:46:33 +00001069 pj_status_t status;
1070
Benny Prijono2047bd72011-08-25 11:59:39 +00001071 if (!prm) {
1072 pjsua_vid_preview_param_default(&default_param);
1073 prm = &default_param;
Benny Prijono9f468d12011-07-07 07:46:33 +00001074 }
1075
Benny Prijonob90fd382011-09-18 14:59:56 +00001076 PJ_LOG(4,(THIS_FILE, "Starting preview for cap_dev=%d, show=%d",
1077 id, prm->show));
1078 pj_log_push_indent();
1079
1080 PJSUA_LOCK();
1081
Benny Prijono2047bd72011-08-25 11:59:39 +00001082 rend_id = prm->rend_id;
1083
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001084 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW, NULL, rend_id, id,
Sauw Ming0ee072f2011-10-26 09:01:53 +00001085 prm->show, prm->wnd_flags, &wid);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001086 if (status != PJ_SUCCESS) {
Benny Prijono9f468d12011-07-07 07:46:33 +00001087 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001088 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001089 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001090 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001091
Benny Prijono9f468d12011-07-07 07:46:33 +00001092 w = &pjsua_var.win[wid];
Benny Prijono39203b82011-09-20 10:07:55 +00001093 if (w->preview_running) {
1094 PJSUA_UNLOCK();
1095 pj_log_pop_indent();
1096 return PJ_SUCCESS;
1097 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001098
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001099 /* Start renderer, unless it's native preview */
Benny Prijono39203b82011-09-20 10:07:55 +00001100 if (w->is_native && !pjmedia_vid_port_is_running(w->vp_cap)) {
1101 pjmedia_vid_dev_stream *cap_dev;
1102 pj_bool_t enabled = PJ_TRUE;
1103
1104 cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
1105 status = pjmedia_vid_dev_stream_set_cap(
1106 cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
1107 &enabled);
1108 if (status != PJ_SUCCESS) {
1109 PJ_PERROR(1,(THIS_FILE, status,
1110 "Error activating native preview, falling back "
1111 "to software preview.."));
1112 w->is_native = PJ_FALSE;
1113 }
1114 }
1115
1116 if (!w->is_native && !pjmedia_vid_port_is_running(w->vp_rend)) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001117 status = pjmedia_vid_port_start(w->vp_rend);
1118 if (status != PJ_SUCCESS) {
1119 PJSUA_UNLOCK();
1120 pj_log_pop_indent();
1121 return status;
1122 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001123 }
1124
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001125 /* Start capturer */
Benny Prijono39203b82011-09-20 10:07:55 +00001126 if (!pjmedia_vid_port_is_running(w->vp_cap)) {
1127 status = pjmedia_vid_port_start(w->vp_cap);
1128 if (status != PJ_SUCCESS) {
1129 PJSUA_UNLOCK();
1130 pj_log_pop_indent();
1131 return status;
1132 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001133 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001134
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001135 inc_vid_win(wid);
Benny Prijono39203b82011-09-20 10:07:55 +00001136 w->preview_running = PJ_TRUE;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001137
Benny Prijono9f468d12011-07-07 07:46:33 +00001138 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001139 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +00001140 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +00001141}
1142
1143/*
1144 * Stop video preview.
1145 */
1146PJ_DEF(pj_status_t) pjsua_vid_preview_stop(pjmedia_vid_dev_index id)
1147{
1148 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
Benny Prijono39203b82011-09-20 10:07:55 +00001149 pjsua_vid_win *w;
1150 pj_status_t status;
Benny Prijonob90fd382011-09-18 14:59:56 +00001151
Benny Prijono9f468d12011-07-07 07:46:33 +00001152 PJSUA_LOCK();
1153 wid = pjsua_vid_preview_get_win(id);
1154 if (wid == PJSUA_INVALID_ID) {
1155 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001156 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +00001157 return PJ_ENOTFOUND;
1158 }
1159
Benny Prijono39203b82011-09-20 10:07:55 +00001160 PJ_LOG(4,(THIS_FILE, "Stopping preview for cap_dev=%d", id));
1161 pj_log_push_indent();
1162
1163 w = &pjsua_var.win[wid];
1164 if (w->preview_running) {
1165 if (w->is_native) {
1166 pjmedia_vid_dev_stream *cap_dev;
1167 pj_bool_t enabled = PJ_FALSE;
1168
1169 cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
1170 status = pjmedia_vid_dev_stream_set_cap(
1171 cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
1172 &enabled);
1173 } else {
1174 status = pjmedia_vid_port_stop(w->vp_rend);
1175 }
1176
1177 if (status != PJ_SUCCESS) {
1178 PJ_PERROR(1,(THIS_FILE, status, "Error stopping %spreview",
1179 (w->is_native ? "native " : "")));
1180 PJSUA_UNLOCK();
1181 pj_log_pop_indent();
1182 return status;
1183 }
1184
1185 dec_vid_win(wid);
1186 w->preview_running = PJ_FALSE;
1187 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001188
1189 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001190 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +00001191
1192 return PJ_SUCCESS;
1193}
1194
1195
1196/*****************************************************************************
1197 * Window
1198 */
1199
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001200
1201/*
1202 * Enumerates all video windows.
1203 */
1204PJ_DEF(pj_status_t) pjsua_vid_enum_wins( pjsua_vid_win_id wids[],
1205 unsigned *count)
1206{
1207 unsigned i, cnt;
1208
1209 cnt = 0;
1210
1211 for (i=0; i<PJSUA_MAX_VID_WINS && cnt <*count; ++i) {
1212 pjsua_vid_win *w = &pjsua_var.win[i];
1213 if (w->type != PJSUA_WND_TYPE_NONE)
1214 wids[cnt++] = i;
1215 }
1216
1217 *count = cnt;
1218
1219 return PJ_SUCCESS;
1220}
1221
1222
Benny Prijono9f468d12011-07-07 07:46:33 +00001223/*
1224 * Get window info.
1225 */
1226PJ_DEF(pj_status_t) pjsua_vid_win_get_info( pjsua_vid_win_id wid,
1227 pjsua_vid_win_info *wi)
1228{
1229 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001230 pjmedia_vid_dev_stream *s;
Sauw Ming5291a2d2011-07-15 07:52:44 +00001231 pjmedia_vid_dev_param vparam;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001232 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001233
1234 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && wi, PJ_EINVAL);
1235
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001236 pj_bzero(wi, sizeof(*wi));
1237
Benny Prijono9f468d12011-07-07 07:46:33 +00001238 PJSUA_LOCK();
1239 w = &pjsua_var.win[wid];
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001240
1241 wi->is_native = w->is_native;
1242
1243 if (w->is_native) {
1244 pjmedia_vid_dev_stream *cap_strm;
1245 pjmedia_vid_dev_cap cap = PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
1246
1247 cap_strm = pjmedia_vid_port_get_stream(w->vp_cap);
1248 if (!cap_strm) {
1249 status = PJ_EINVAL;
1250 } else {
1251 status = pjmedia_vid_dev_stream_get_cap(cap_strm, cap, &wi->hwnd);
1252 }
1253
1254 PJSUA_UNLOCK();
1255 return status;
1256 }
1257
Benny Prijono9f468d12011-07-07 07:46:33 +00001258 if (w->vp_rend == NULL) {
1259 PJSUA_UNLOCK();
1260 return PJ_EINVAL;
1261 }
1262
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001263 s = pjmedia_vid_port_get_stream(w->vp_rend);
1264 if (s == NULL) {
1265 PJSUA_UNLOCK();
1266 return PJ_EINVAL;
1267 }
1268
1269 status = pjmedia_vid_dev_stream_get_param(s, &vparam);
1270 if (status != PJ_SUCCESS) {
1271 PJSUA_UNLOCK();
1272 return status;
1273 }
1274
Nanang Izzuddindb9b0022011-07-26 08:17:25 +00001275 wi->rdr_dev = vparam.rend_id;
Nanang Izzuddin6e2fcc32011-07-22 04:49:36 +00001276 wi->hwnd = vparam.window;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001277 wi->show = !vparam.window_hide;
1278 wi->pos = vparam.window_pos;
1279 wi->size = vparam.disp_size;
1280
Benny Prijono9f468d12011-07-07 07:46:33 +00001281 PJSUA_UNLOCK();
1282
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001283 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +00001284}
1285
1286/*
1287 * Show or hide window.
1288 */
1289PJ_DEF(pj_status_t) pjsua_vid_win_set_show( pjsua_vid_win_id wid,
1290 pj_bool_t show)
1291{
1292 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001293 pjmedia_vid_dev_stream *s;
Nanang Izzuddinc16590a2011-07-14 02:14:25 +00001294 pj_bool_t hide;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001295 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001296
1297 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
1298
1299 PJSUA_LOCK();
1300 w = &pjsua_var.win[wid];
1301 if (w->vp_rend == NULL) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001302 /* Native window */
Benny Prijono9f468d12011-07-07 07:46:33 +00001303 PJSUA_UNLOCK();
1304 return PJ_EINVAL;
1305 }
1306
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001307 s = pjmedia_vid_port_get_stream(w->vp_rend);
1308 if (s == NULL) {
1309 PJSUA_UNLOCK();
1310 return PJ_EINVAL;
1311 }
1312
Nanang Izzuddinc16590a2011-07-14 02:14:25 +00001313 hide = !show;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001314 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinc16590a2011-07-14 02:14:25 +00001315 PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE, &hide);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001316
Benny Prijono9f468d12011-07-07 07:46:33 +00001317 PJSUA_UNLOCK();
1318
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001319 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001320}
1321
1322/*
1323 * Set video window position.
1324 */
1325PJ_DEF(pj_status_t) pjsua_vid_win_set_pos( pjsua_vid_win_id wid,
1326 const pjmedia_coord *pos)
1327{
1328 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001329 pjmedia_vid_dev_stream *s;
1330 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001331
1332 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && pos, PJ_EINVAL);
1333
1334 PJSUA_LOCK();
1335 w = &pjsua_var.win[wid];
1336 if (w->vp_rend == NULL) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001337 /* Native window */
Benny Prijono9f468d12011-07-07 07:46:33 +00001338 PJSUA_UNLOCK();
1339 return PJ_EINVAL;
1340 }
1341
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001342 s = pjmedia_vid_port_get_stream(w->vp_rend);
1343 if (s == NULL) {
1344 PJSUA_UNLOCK();
1345 return PJ_EINVAL;
1346 }
1347
1348 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001349 PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION, pos);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001350
Benny Prijono9f468d12011-07-07 07:46:33 +00001351 PJSUA_UNLOCK();
1352
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001353 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001354}
1355
1356/*
1357 * Resize window.
1358 */
1359PJ_DEF(pj_status_t) pjsua_vid_win_set_size( pjsua_vid_win_id wid,
1360 const pjmedia_rect_size *size)
1361{
1362 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001363 pjmedia_vid_dev_stream *s;
1364 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001365
1366 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && size, PJ_EINVAL);
1367
1368 PJSUA_LOCK();
1369 w = &pjsua_var.win[wid];
1370 if (w->vp_rend == NULL) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001371 /* Native window */
Benny Prijono9f468d12011-07-07 07:46:33 +00001372 PJSUA_UNLOCK();
1373 return PJ_EINVAL;
1374 }
1375
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001376 s = pjmedia_vid_port_get_stream(w->vp_rend);
1377 if (s == NULL) {
1378 PJSUA_UNLOCK();
1379 return PJ_EINVAL;
1380 }
1381
1382 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001383 PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE, size);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001384
Benny Prijono9f468d12011-07-07 07:46:33 +00001385 PJSUA_UNLOCK();
1386
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001387 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001388}
1389
Nanang Izzuddine9b279b2011-09-27 05:24:06 +00001390/*
1391 * Set video orientation.
1392 */
1393PJ_DEF(pj_status_t) pjsua_vid_win_rotate( pjsua_vid_win_id wid,
1394 int angle)
1395{
1396 pjsua_vid_win *w;
1397 pjmedia_vid_dev_stream *s;
1398 pjmedia_orient orient;
1399 pj_status_t status;
1400
1401 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
1402 PJ_ASSERT_RETURN((angle % 90) == 0, PJ_EINVAL);
1403
1404 /* Normalize angle, so it must be 0, 90, 180, or 270. */
1405 angle %= 360;
1406 if (angle < 0)
1407 angle += 360;
1408
1409 /* Convert angle to pjmedia_orient */
1410 switch(angle) {
1411 case 0:
1412 /* No rotation */
1413 return PJ_SUCCESS;
1414 case 90:
1415 orient = PJMEDIA_ORIENT_ROTATE_90DEG;
1416 break;
1417 case 180:
1418 orient = PJMEDIA_ORIENT_ROTATE_180DEG;
1419 break;
1420 case 270:
1421 orient = PJMEDIA_ORIENT_ROTATE_270DEG;
1422 break;
1423 default:
1424 pj_assert(!"Angle must have been validated");
1425 return PJ_EBUG;
1426 }
1427
1428 PJSUA_LOCK();
1429 w = &pjsua_var.win[wid];
1430 if (w->vp_rend == NULL) {
1431 /* Native window */
1432 PJSUA_UNLOCK();
1433 return PJ_EINVAL;
1434 }
1435
1436 s = pjmedia_vid_port_get_stream(w->vp_rend);
1437 if (s == NULL) {
1438 PJSUA_UNLOCK();
1439 return PJ_EINVAL;
1440 }
1441
1442 status = pjmedia_vid_dev_stream_set_cap(s,
1443 PJMEDIA_VID_DEV_CAP_ORIENTATION, &orient);
1444
1445 PJSUA_UNLOCK();
1446
1447 return status;
1448}
1449
Benny Prijono9f468d12011-07-07 07:46:33 +00001450
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001451static void call_get_vid_strm_info(pjsua_call *call,
1452 int *first_active,
1453 int *first_inactive,
1454 unsigned *active_cnt,
1455 unsigned *cnt)
1456{
1457 unsigned i, var_cnt = 0;
1458
1459 if (first_active && ++var_cnt)
1460 *first_active = -1;
1461 if (first_inactive && ++var_cnt)
1462 *first_inactive = -1;
1463 if (active_cnt && ++var_cnt)
1464 *active_cnt = 0;
1465 if (cnt && ++var_cnt)
1466 *cnt = 0;
1467
1468 for (i = 0; i < call->med_cnt && var_cnt; ++i) {
1469 if (call->media[i].type == PJMEDIA_TYPE_VIDEO) {
1470 if (call->media[i].dir != PJMEDIA_DIR_NONE)
1471 {
1472 if (first_active && *first_active == -1) {
1473 *first_active = i;
1474 --var_cnt;
1475 }
1476 if (active_cnt)
1477 ++(*active_cnt);
1478 } else if (first_inactive && *first_inactive == -1) {
1479 *first_inactive = i;
1480 --var_cnt;
1481 }
1482 if (cnt)
1483 ++(*cnt);
1484 }
1485 }
1486}
1487
1488
1489/* Send SDP reoffer. */
1490static pj_status_t call_reoffer_sdp(pjsua_call_id call_id,
1491 const pjmedia_sdp_session *sdp)
1492{
1493 pjsua_call *call;
1494 pjsip_tx_data *tdata;
1495 pjsip_dialog *dlg;
1496 pj_status_t status;
1497
1498 status = acquire_call("call_reoffer_sdp()", call_id, &call, &dlg);
1499 if (status != PJ_SUCCESS)
1500 return status;
1501
1502 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1503 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
1504 pjsip_dlg_dec_lock(dlg);
1505 return PJSIP_ESESSIONSTATE;
1506 }
1507
1508 /* Create re-INVITE with new offer */
1509 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1510 if (status != PJ_SUCCESS) {
1511 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
1512 pjsip_dlg_dec_lock(dlg);
1513 return status;
1514 }
1515
1516 /* Send the request */
1517 status = pjsip_inv_send_msg( call->inv, tdata);
1518 if (status != PJ_SUCCESS) {
1519 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
1520 pjsip_dlg_dec_lock(dlg);
1521 return status;
1522 }
1523
1524 pjsip_dlg_dec_lock(dlg);
1525
1526 return PJ_SUCCESS;
1527}
1528
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001529/* Add a new video stream into a call */
1530static pj_status_t call_add_video(pjsua_call *call,
Nanang Izzuddin98085612011-07-15 07:41:02 +00001531 pjmedia_vid_dev_index cap_dev,
1532 pjmedia_dir dir)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001533{
1534 pj_pool_t *pool = call->inv->pool_prov;
1535 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1536 pjsua_call_media *call_med;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001537 const pjmedia_sdp_session *current_sdp;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001538 pjmedia_sdp_session *sdp;
1539 pjmedia_sdp_media *sdp_m;
1540 pjmedia_transport_info tpinfo;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001541 unsigned active_cnt;
1542 pj_status_t status;
1543
1544 /* Verify media slot availability */
1545 if (call->med_cnt == PJSUA_MAX_CALL_MEDIA)
1546 return PJ_ETOOMANY;
1547
1548 call_get_vid_strm_info(call, NULL, NULL, &active_cnt, NULL);
1549 if (active_cnt == acc_cfg->max_video_cnt)
1550 return PJ_ETOOMANY;
1551
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001552 /* Get active local SDP and clone it */
1553 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &current_sdp);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001554 if (status != PJ_SUCCESS)
1555 return status;
1556
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001557 sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, current_sdp);
1558
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001559 /* Initialize call media */
1560 call_med = &call->media[call->med_cnt++];
1561
1562 status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1563 &acc_cfg->rtp_cfg, call->secure_level,
Sauw Ming73ecfe82011-09-21 10:20:01 +00001564 NULL, PJ_FALSE, NULL);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001565 if (status != PJ_SUCCESS)
1566 goto on_error;
1567
1568 /* Override default capture device setting */
1569 call_med->strm.v.cap_dev = cap_dev;
1570
1571 /* Init transport media */
1572 status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1573 NULL, call_med->idx);
1574 if (status != PJ_SUCCESS)
1575 goto on_error;
1576
Sauw Ming73ecfe82011-09-21 10:20:01 +00001577 set_media_tp_state(call_med, PJSUA_MED_TP_INIT);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001578
1579 /* Get transport address info */
1580 pjmedia_transport_info_init(&tpinfo);
1581 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1582
1583 /* Create SDP media line */
1584 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1585 &tpinfo.sock_info, 0, &sdp_m);
1586 if (status != PJ_SUCCESS)
1587 goto on_error;
1588
1589 sdp->media[sdp->media_count++] = sdp_m;
1590
Nanang Izzuddin98085612011-07-15 07:41:02 +00001591 /* Update media direction, if it is not 'sendrecv' */
1592 if (dir != PJMEDIA_DIR_ENCODING_DECODING) {
1593 pjmedia_sdp_attr *a;
1594
1595 /* Remove sendrecv direction attribute, if any */
1596 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
1597
1598 if (dir == PJMEDIA_DIR_ENCODING)
1599 a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
1600 else if (dir == PJMEDIA_DIR_DECODING)
1601 a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
1602 else
1603 a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
1604
1605 pjmedia_sdp_media_add_attr(sdp_m, a);
1606 }
1607
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001608 /* Update SDP media line by media transport */
1609 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1610 sdp, NULL, call_med->idx);
1611 if (status != PJ_SUCCESS)
1612 goto on_error;
1613
1614 status = call_reoffer_sdp(call->index, sdp);
1615 if (status != PJ_SUCCESS)
1616 goto on_error;
1617
1618 return PJ_SUCCESS;
1619
1620on_error:
1621 if (call_med->tp) {
1622 pjmedia_transport_close(call_med->tp);
1623 call_med->tp = call_med->tp_orig = NULL;
1624 }
1625
1626 return status;
1627}
1628
1629
Nanang Izzuddin98085612011-07-15 07:41:02 +00001630/* Modify a video stream from a call, i.e: update direction,
1631 * remove/disable.
1632 */
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001633static pj_status_t call_modify_video(pjsua_call *call,
1634 int med_idx,
Nanang Izzuddin98085612011-07-15 07:41:02 +00001635 pjmedia_dir dir,
1636 pj_bool_t remove)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001637{
1638 pjsua_call_media *call_med;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001639 const pjmedia_sdp_session *current_sdp;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001640 pjmedia_sdp_session *sdp;
1641 pj_status_t status;
1642
1643 /* Verify and normalize media index */
1644 if (med_idx == -1) {
1645 int first_active;
1646
1647 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1648 if (first_active == -1)
1649 return PJ_ENOTFOUND;
1650
1651 med_idx = first_active;
1652 }
1653
1654 call_med = &call->media[med_idx];
1655
1656 /* Verify if the stream media type is video */
1657 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1658 return PJ_EINVAL;
1659
Nanang Izzuddin98085612011-07-15 07:41:02 +00001660 /* Verify if the stream dir is not changed */
1661 if ((!remove && call_med->dir == dir) ||
1662 ( remove && (call_med->tp_st == PJSUA_MED_TP_DISABLED ||
1663 call_med->tp == NULL)))
1664 {
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001665 return PJ_SUCCESS;
Nanang Izzuddin98085612011-07-15 07:41:02 +00001666 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001667
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001668 /* Get active local SDP and clone it */
1669 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &current_sdp);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001670 if (status != PJ_SUCCESS)
1671 return status;
1672
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001673 sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, current_sdp);
1674
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001675 pj_assert(med_idx < (int)sdp->media_count);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001676
Nanang Izzuddin98085612011-07-15 07:41:02 +00001677 if (!remove) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001678 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1679 pj_pool_t *pool = call->inv->pool_prov;
1680 pjmedia_sdp_media *sdp_m;
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001681
1682 status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1683 &acc_cfg->rtp_cfg, call->secure_level,
Sauw Ming73ecfe82011-09-21 10:20:01 +00001684 NULL, PJ_FALSE, NULL);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001685 if (status != PJ_SUCCESS)
1686 goto on_error;
1687
1688 /* Init transport media */
Nanang Izzuddin98085612011-07-15 07:41:02 +00001689 if (call_med->tp && call_med->tp_st == PJSUA_MED_TP_IDLE) {
1690 status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1691 NULL, call_med->idx);
1692 if (status != PJ_SUCCESS)
1693 goto on_error;
1694 }
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001695
Nanang Izzuddin98085612011-07-15 07:41:02 +00001696 sdp_m = sdp->media[med_idx];
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001697
Nanang Izzuddin98085612011-07-15 07:41:02 +00001698 /* Create new SDP media line if the stream is disabled */
1699 if (sdp->media[med_idx]->desc.port == 0) {
1700 pjmedia_transport_info tpinfo;
1701
1702 /* Get transport address info */
1703 pjmedia_transport_info_init(&tpinfo);
1704 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1705
1706 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1707 &tpinfo.sock_info, 0, &sdp_m);
1708 if (status != PJ_SUCCESS)
1709 goto on_error;
1710 }
1711
1712 {
1713 pjmedia_sdp_attr *a;
1714
1715 /* Remove any direction attributes */
1716 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
1717 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendonly");
1718 pjmedia_sdp_media_remove_all_attr(sdp_m, "recvonly");
1719 pjmedia_sdp_media_remove_all_attr(sdp_m, "inactive");
1720
1721 /* Update media direction */
1722 if (dir == PJMEDIA_DIR_ENCODING_DECODING)
1723 a = pjmedia_sdp_attr_create(pool, "sendrecv", NULL);
1724 else if (dir == PJMEDIA_DIR_ENCODING)
1725 a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
1726 else if (dir == PJMEDIA_DIR_DECODING)
1727 a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
1728 else
1729 a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
1730
1731 pjmedia_sdp_media_add_attr(sdp_m, a);
1732 }
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001733
1734 sdp->media[med_idx] = sdp_m;
1735
1736 /* Update SDP media line by media transport */
1737 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1738 sdp, NULL, call_med->idx);
1739 if (status != PJ_SUCCESS)
1740 goto on_error;
1741
1742on_error:
1743 if (status != PJ_SUCCESS) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001744 return status;
1745 }
Nanang Izzuddin98085612011-07-15 07:41:02 +00001746
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001747 } else {
Nanang Izzuddin98085612011-07-15 07:41:02 +00001748
1749 pj_pool_t *pool = call->inv->pool_prov;
1750
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001751 /* Mark media transport to disabled */
1752 // Don't close this here, as SDP negotiation has not been
1753 // done and stream may be still active.
Sauw Ming73ecfe82011-09-21 10:20:01 +00001754 set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001755
Nanang Izzuddin98085612011-07-15 07:41:02 +00001756 /* Deactivate the stream */
1757 pjmedia_sdp_media_deactivate(pool, sdp->media[med_idx]);
1758
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001759 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001760
1761 status = call_reoffer_sdp(call->index, sdp);
1762 if (status != PJ_SUCCESS)
1763 return status;
1764
1765 return PJ_SUCCESS;
1766}
1767
1768
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001769/* Change capture device of a video stream in a call */
1770static pj_status_t call_change_cap_dev(pjsua_call *call,
1771 int med_idx,
1772 pjmedia_vid_dev_index cap_dev)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001773{
1774 pjsua_call_media *call_med;
Benny Prijono10ff56e2011-10-04 13:35:41 +00001775 pjmedia_vid_dev_stream *old_dev;
1776 pjmedia_vid_dev_switch_param switch_prm;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001777 pjmedia_vid_dev_info info;
1778 pjsua_vid_win *w, *new_w = NULL;
1779 pjsua_vid_win_id wid, new_wid;
1780 pjmedia_port *media_port;
1781 pj_status_t status;
1782
1783 /* Verify and normalize media index */
1784 if (med_idx == -1) {
1785 int first_active;
1786
1787 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1788 if (first_active == -1)
1789 return PJ_ENOTFOUND;
1790
1791 med_idx = first_active;
1792 }
1793
1794 call_med = &call->media[med_idx];
1795
1796 /* Verify if the stream media type is video */
1797 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1798 return PJ_EINVAL;
1799
1800 /* Verify the capture device */
1801 status = pjmedia_vid_dev_get_info(cap_dev, &info);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001802 if (status != PJ_SUCCESS || info.dir != PJMEDIA_DIR_CAPTURE)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001803 return PJ_EINVAL;
1804
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001805 /* The specified capture device is being used already */
1806 if (call_med->strm.v.cap_dev == cap_dev)
1807 return PJ_SUCCESS;
1808
1809 /* == Apply the new capture device == */
1810
1811 wid = call_med->strm.v.cap_win_id;
1812 w = &pjsua_var.win[wid];
1813 pj_assert(w->type == PJSUA_WND_TYPE_PREVIEW && w->vp_cap);
1814
Benny Prijono10ff56e2011-10-04 13:35:41 +00001815 /* If the old device supports fast switching, then that's excellent! */
1816 old_dev = pjmedia_vid_port_get_stream(w->vp_cap);
1817 pjmedia_vid_dev_switch_param_default(&switch_prm);
1818 switch_prm.target_id = cap_dev;
1819 status = pjmedia_vid_dev_stream_set_cap(old_dev,
1820 PJMEDIA_VID_DEV_CAP_SWITCH,
1821 &switch_prm);
1822 if (status == PJ_SUCCESS) {
1823 w->preview_cap_id = cap_dev;
Benny Prijono86e91622011-10-04 13:49:40 +00001824 call_med->strm.v.cap_dev = cap_dev;
Benny Prijono10ff56e2011-10-04 13:35:41 +00001825 return PJ_SUCCESS;
1826 }
1827
1828 /* No it doesn't support fast switching. Do slow switching then.. */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001829 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
1830 PJMEDIA_DIR_ENCODING, &media_port);
1831 if (status != PJ_SUCCESS)
1832 return status;
Benny Prijono1fe04ee2011-07-15 07:27:05 +00001833
1834 pjmedia_event_unsubscribe(&call_med->esub_cap);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001835
Benny Prijono65030232011-10-06 07:37:46 +00001836 /* temporarily disconnect while we operate on the tee. */
1837 pjmedia_vid_port_disconnect(w->vp_cap);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001838
Benny Prijono65030232011-10-06 07:37:46 +00001839 /* = Detach stream port from the old capture device's tee = */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001840 status = pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
1841 if (status != PJ_SUCCESS) {
Benny Prijono65030232011-10-06 07:37:46 +00001842 /* Something wrong, assume that media_port has been removed
1843 * and continue.
1844 */
1845 PJ_PERROR(4,(THIS_FILE, status,
1846 "Warning: call %d: unable to remove video from tee",
1847 call->index));
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001848 }
1849
Benny Prijono65030232011-10-06 07:37:46 +00001850 /* Reconnect again immediately. We're done with w->tee */
1851 pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
1852
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001853 /* = Attach stream port to the new capture device = */
1854
Benny Prijono39203b82011-09-20 10:07:55 +00001855 /* Note: calling pjsua_vid_preview_get_win() even though
1856 * create_vid_win() will automatically create the window
1857 * if it doesn't exist, because create_vid_win() will modify
1858 * existing window SHOW/HIDE value.
1859 */
1860 new_wid = vid_preview_get_win(cap_dev, PJ_FALSE);
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001861 if (new_wid == PJSUA_INVALID_ID) {
Sauw Ming0ee072f2011-10-26 09:01:53 +00001862 pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
1863
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001864 /* Create preview video window */
1865 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
1866 &media_port->info.fmt,
1867 call_med->strm.v.rdr_dev,
1868 cap_dev,
1869 PJSUA_HIDE_WINDOW,
Sauw Ming0ee072f2011-10-26 09:01:53 +00001870 acc->cfg.vid_wnd_flags,
1871 &new_wid);
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001872 if (status != PJ_SUCCESS)
1873 goto on_error;
1874 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001875
1876 inc_vid_win(new_wid);
1877 new_w = &pjsua_var.win[new_wid];
1878
1879 /* Connect stream to capturer (via video window tee) */
1880 status = pjmedia_vid_tee_add_dst_port2(new_w->tee, 0, media_port);
1881 if (status != PJ_SUCCESS)
1882 goto on_error;
1883
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001884 if (w->vp_rend) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001885 /* Start renderer */
1886 status = pjmedia_vid_port_start(new_w->vp_rend);
1887 if (status != PJ_SUCCESS)
1888 goto on_error;
1889 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001890
Nanang Izzuddinc12a19d2011-10-03 05:23:59 +00001891#if ENABLE_EVENT
1892 pjmedia_event_subscribe(
1893 pjmedia_vid_port_get_event_publisher(new_w->vp_cap),
1894 &call_med->esub_cap);
1895#endif
1896
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001897 /* Start capturer */
Benny Prijono65030232011-10-06 07:37:46 +00001898 if (!pjmedia_vid_port_is_running(new_w->vp_cap)) {
1899 status = pjmedia_vid_port_start(new_w->vp_cap);
1900 if (status != PJ_SUCCESS)
1901 goto on_error;
1902 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001903
1904 /* Finally */
1905 call_med->strm.v.cap_dev = cap_dev;
1906 call_med->strm.v.cap_win_id = new_wid;
1907 dec_vid_win(wid);
1908
1909 return PJ_SUCCESS;
1910
1911on_error:
Benny Prijono65030232011-10-06 07:37:46 +00001912 PJ_PERROR(4,(THIS_FILE, status,
1913 "Call %d: error changing capture device to %d",
1914 call->index, cap_dev));
1915
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001916 if (new_w) {
Benny Prijono65030232011-10-06 07:37:46 +00001917 /* Unsubscribe, just in case */
1918 pjmedia_event_unsubscribe(&call_med->esub_cap);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001919 /* Disconnect media port from the new capturer */
1920 pjmedia_vid_tee_remove_dst_port(new_w->tee, media_port);
1921 /* Release the new capturer */
1922 dec_vid_win(new_wid);
1923 }
1924
1925 /* Revert back to the old capturer */
Benny Prijono65030232011-10-06 07:37:46 +00001926 pjmedia_vid_port_disconnect(w->vp_cap);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001927 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
Benny Prijono65030232011-10-06 07:37:46 +00001928 pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001929 if (status != PJ_SUCCESS)
1930 return status;
1931
Benny Prijono65030232011-10-06 07:37:46 +00001932#if ENABLE_EVENT
1933 /* Resubscribe */
1934 pjmedia_event_subscribe(
1935 pjmedia_vid_port_get_event_publisher(w->vp_cap),
1936 &call_med->esub_cap);
1937#endif
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001938
1939 return status;
1940}
1941
1942
Nanang Izzuddin98085612011-07-15 07:41:02 +00001943/* Start/stop transmitting video stream in a call */
1944static pj_status_t call_set_tx_video(pjsua_call *call,
1945 int med_idx,
1946 pj_bool_t enable)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001947{
1948 pjsua_call_media *call_med;
1949 pj_status_t status;
1950
1951 /* Verify and normalize media index */
1952 if (med_idx == -1) {
1953 int first_active;
1954
1955 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1956 if (first_active == -1)
1957 return PJ_ENOTFOUND;
1958
1959 med_idx = first_active;
1960 }
1961
1962 call_med = &call->media[med_idx];
1963
1964 /* Verify if the stream is transmitting video */
1965 if (call_med->type != PJMEDIA_TYPE_VIDEO ||
Nanang Izzuddin98085612011-07-15 07:41:02 +00001966 (enable && (call_med->dir & PJMEDIA_DIR_ENCODING) == 0))
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001967 {
1968 return PJ_EINVAL;
1969 }
1970
Nanang Izzuddin98085612011-07-15 07:41:02 +00001971 if (enable) {
1972 /* Start stream in encoding direction */
1973 status = pjmedia_vid_stream_resume(call_med->strm.v.stream,
1974 PJMEDIA_DIR_ENCODING);
1975 } else {
1976 /* Pause stream in encoding direction */
1977 status = pjmedia_vid_stream_pause( call_med->strm.v.stream,
1978 PJMEDIA_DIR_ENCODING);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001979 }
1980
Nanang Izzuddin98085612011-07-15 07:41:02 +00001981 return status;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001982}
1983
1984
1985/*
1986 * Start, stop, and/or manipulate video transmission for the specified call.
1987 */
1988PJ_DEF(pj_status_t) pjsua_call_set_vid_strm (
1989 pjsua_call_id call_id,
1990 pjsua_call_vid_strm_op op,
1991 const pjsua_call_vid_strm_op_param *param)
1992{
1993 pjsua_call *call;
1994 pjsua_call_vid_strm_op_param param_;
1995 pj_status_t status;
1996
1997 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1998 PJ_EINVAL);
Benny Prijonoe212bc12011-08-15 09:38:42 +00001999 PJ_ASSERT_RETURN(op != PJSUA_CALL_VID_STRM_NO_OP, PJ_EINVAL);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002000
Benny Prijonob90fd382011-09-18 14:59:56 +00002001 PJ_LOG(4,(THIS_FILE, "Call %d: set video stream, op=%d",
2002 call_id, op));
2003 pj_log_push_indent();
2004
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002005 PJSUA_LOCK();
2006
2007 call = &pjsua_var.calls[call_id];
2008
2009 if (param) {
2010 param_ = *param;
2011 } else {
Benny Prijonoe212bc12011-08-15 09:38:42 +00002012 pjsua_call_vid_strm_op_param_default(&param_);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002013 }
2014
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00002015 /* If set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
2016 * account default video capture device.
2017 */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002018 if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00002019 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
2020 param_.cap_dev = acc_cfg->vid_cap_dev;
2021
2022 /* If the account default video capture device is
2023 * PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
2024 * global default video capture device.
2025 */
2026 if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
2027 pjmedia_vid_dev_info info;
2028 pjmedia_vid_dev_get_info(param_.cap_dev, &info);
2029 pj_assert(info.dir == PJMEDIA_DIR_CAPTURE);
2030 param_.cap_dev = info.id;
2031 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002032 }
2033
2034 switch (op) {
2035 case PJSUA_CALL_VID_STRM_ADD:
Nanang Izzuddin98085612011-07-15 07:41:02 +00002036 status = call_add_video(call, param_.cap_dev, param_.dir);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002037 break;
Nanang Izzuddin98085612011-07-15 07:41:02 +00002038 case PJSUA_CALL_VID_STRM_REMOVE:
2039 status = call_modify_video(call, param_.med_idx, PJMEDIA_DIR_NONE,
2040 PJ_TRUE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002041 break;
Nanang Izzuddin98085612011-07-15 07:41:02 +00002042 case PJSUA_CALL_VID_STRM_CHANGE_DIR:
2043 status = call_modify_video(call, param_.med_idx, param_.dir, PJ_FALSE);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00002044 break;
2045 case PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV:
2046 status = call_change_cap_dev(call, param_.med_idx, param_.cap_dev);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002047 break;
2048 case PJSUA_CALL_VID_STRM_START_TRANSMIT:
Nanang Izzuddin98085612011-07-15 07:41:02 +00002049 status = call_set_tx_video(call, param_.med_idx, PJ_TRUE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002050 break;
2051 case PJSUA_CALL_VID_STRM_STOP_TRANSMIT:
Nanang Izzuddin98085612011-07-15 07:41:02 +00002052 status = call_set_tx_video(call, param_.med_idx, PJ_FALSE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002053 break;
2054 default:
2055 status = PJ_EINVALIDOP;
2056 break;
2057 }
2058
2059 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00002060 pj_log_pop_indent();
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002061
2062 return status;
2063}
2064
Benny Prijono9f468d12011-07-07 07:46:33 +00002065
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00002066/*
2067 * Get the media stream index of the default video stream in the call.
2068 */
2069PJ_DEF(int) pjsua_call_get_vid_stream_idx(pjsua_call_id call_id)
2070{
2071 pjsua_call *call;
2072 int first_active, first_inactive;
2073
2074 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2075 PJ_EINVAL);
2076
2077 PJSUA_LOCK();
2078 call = &pjsua_var.calls[call_id];
2079 call_get_vid_strm_info(call, &first_active, &first_inactive, NULL, NULL);
2080 PJSUA_UNLOCK();
2081
2082 if (first_active == -1)
2083 return first_inactive;
2084
2085 return first_active;
2086}
2087
2088
Benny Prijono83a9eaf2011-10-04 08:23:07 +00002089/*
2090 * Determine if video stream for the specified call is currently running
2091 * for the specified direction.
2092 */
2093PJ_DEF(pj_bool_t) pjsua_call_vid_stream_is_running( pjsua_call_id call_id,
2094 int med_idx,
2095 pjmedia_dir dir)
2096{
2097 pjsua_call *call;
2098 pjsua_call_media *call_med;
2099
2100 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2101 PJ_EINVAL);
2102
2103 /* Verify and normalize media index */
2104 if (med_idx == -1) {
2105 med_idx = pjsua_call_get_vid_stream_idx(call_id);
2106 }
2107
2108 call = &pjsua_var.calls[call_id];
Nanang Izzuddina5964e72011-10-06 05:39:18 +00002109 PJ_ASSERT_RETURN(med_idx >= 0 && med_idx < (int)call->med_cnt, PJ_EINVAL);
Benny Prijono83a9eaf2011-10-04 08:23:07 +00002110
2111 call_med = &call->media[med_idx];
2112
2113 /* Verify if the stream is transmitting video */
2114 if (call_med->type != PJMEDIA_TYPE_VIDEO || (call_med->dir & dir) == 0 ||
2115 !call_med->strm.v.stream)
2116 {
2117 return PJ_FALSE;
2118 }
2119
2120 return pjmedia_vid_stream_is_running(call_med->strm.v.stream, dir);
2121}
2122
Benny Prijono9f468d12011-07-07 07:46:33 +00002123#endif /* PJSUA_HAS_VIDEO */
2124