blob: 81b943e17f31fc35a60b7ba309aaad21c6851255 [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
Benny Prijono9f468d12011-07-07 07:46:33 +000067#if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_FFMPEG_CODEC
68 status = pjmedia_codec_ffmpeg_init(NULL, &pjsua_var.cp.factory);
69 if (status != PJ_SUCCESS) {
70 PJ_PERROR(1,(THIS_FILE, status,
71 "Error initializing ffmpeg library"));
Benny Prijonob90fd382011-09-18 14:59:56 +000072 goto on_error;
Benny Prijono9f468d12011-07-07 07:46:33 +000073 }
74#endif
75
Benny Prijono58366a02011-11-17 14:25:26 +000076 status = pjmedia_vid_dev_subsys_init(&pjsua_var.cp.factory);
77 if (status != PJ_SUCCESS) {
78 PJ_PERROR(1,(THIS_FILE, status,
79 "Error creating PJMEDIA video subsystem"));
80 goto on_error;
81 }
82
Benny Prijono9f468d12011-07-07 07:46:33 +000083 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
Benny Prijono58366a02011-11-17 14:25:26 +0000124 pjmedia_codec_ffmpeg_deinit();
Benny Prijono9f468d12011-07-07 07:46:33 +0000125#endif
126
Benny Prijono58366a02011-11-17 14:25:26 +0000127 if (pjmedia_vid_codec_mgr_instance())
128 pjmedia_vid_codec_mgr_destroy(NULL);
129
130 if (pjmedia_converter_mgr_instance())
131 pjmedia_converter_mgr_destroy(NULL);
132
133 if (pjmedia_video_format_mgr_instance())
134 pjmedia_video_format_mgr_destroy(NULL);
135
Benny Prijonob90fd382011-09-18 14:59:56 +0000136 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +0000137 return PJ_SUCCESS;
138}
139
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000140PJ_DEF(const char*) pjsua_vid_win_type_name(pjsua_vid_win_type wt)
141{
142 const char *win_type_names[] = {
143 "none",
144 "preview",
145 "stream"
146 };
147
148 return (wt < PJ_ARRAY_SIZE(win_type_names)) ? win_type_names[wt] : "??";
149}
150
Benny Prijonoe212bc12011-08-15 09:38:42 +0000151PJ_DEF(void)
152pjsua_call_vid_strm_op_param_default(pjsua_call_vid_strm_op_param *param)
153{
154 pj_bzero(param, sizeof(*param));
155 param->med_idx = -1;
156 param->dir = PJMEDIA_DIR_ENCODING_DECODING;
157 param->cap_dev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
158}
Benny Prijono9f468d12011-07-07 07:46:33 +0000159
Benny Prijono2047bd72011-08-25 11:59:39 +0000160PJ_DEF(void) pjsua_vid_preview_param_default(pjsua_vid_preview_param *p)
161{
162 p->rend_id = PJMEDIA_VID_DEFAULT_RENDER_DEV;
163 p->show = PJ_TRUE;
164}
165
166
Benny Prijono9f468d12011-07-07 07:46:33 +0000167/*****************************************************************************
168 * Devices.
169 */
170
171/*
172 * Get the number of video devices installed in the system.
173 */
174PJ_DEF(unsigned) pjsua_vid_dev_count(void)
175{
176 return pjmedia_vid_dev_count();
177}
178
179/*
180 * Retrieve the video device info for the specified device index.
181 */
182PJ_DEF(pj_status_t) pjsua_vid_dev_get_info(pjmedia_vid_dev_index id,
183 pjmedia_vid_dev_info *vdi)
184{
185 return pjmedia_vid_dev_get_info(id, vdi);
186}
187
188/*
189 * Enum all video devices installed in the system.
190 */
191PJ_DEF(pj_status_t) pjsua_vid_enum_devs(pjmedia_vid_dev_info info[],
192 unsigned *count)
193{
194 unsigned i, dev_count;
195
196 dev_count = pjmedia_vid_dev_count();
197
198 if (dev_count > *count) dev_count = *count;
199
200 for (i=0; i<dev_count; ++i) {
201 pj_status_t status;
202
203 status = pjmedia_vid_dev_get_info(i, &info[i]);
204 if (status != PJ_SUCCESS)
205 return status;
206 }
207
208 *count = dev_count;
209
210 return PJ_SUCCESS;
211}
212
213
214/*****************************************************************************
215 * Codecs.
216 */
217
218/*
219 * Enum all supported video codecs in the system.
220 */
221PJ_DEF(pj_status_t) pjsua_vid_enum_codecs( pjsua_codec_info id[],
222 unsigned *p_count )
223{
224 pjmedia_vid_codec_info info[32];
225 unsigned i, j, count, prio[32];
226 pj_status_t status;
227
228 count = PJ_ARRAY_SIZE(info);
229 status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, info, prio);
230 if (status != PJ_SUCCESS) {
231 *p_count = 0;
232 return status;
233 }
234
235 for (i=0, j=0; i<count && j<*p_count; ++i) {
Benny Prijono84685572011-09-29 08:31:15 +0000236 if (info[i].packings & PJMEDIA_VID_PACKING_PACKETS) {
Benny Prijono9f468d12011-07-07 07:46:33 +0000237 pj_bzero(&id[j], sizeof(pjsua_codec_info));
238
239 pjmedia_vid_codec_info_to_id(&info[i], id[j].buf_, sizeof(id[j].buf_));
240 id[j].codec_id = pj_str(id[j].buf_);
241 id[j].priority = (pj_uint8_t) prio[i];
242
243 if (id[j].codec_id.slen < sizeof(id[j].buf_)) {
244 id[j].desc.ptr = id[j].codec_id.ptr + id[j].codec_id.slen + 1;
245 pj_strncpy(&id[j].desc, &info[i].encoding_desc,
246 sizeof(id[j].buf_) - id[j].codec_id.slen - 1);
247 }
248
249 ++j;
250 }
251 }
252
253 *p_count = j;
254
255 return PJ_SUCCESS;
256}
257
258
259/*
260 * Change video codec priority.
261 */
262PJ_DEF(pj_status_t) pjsua_vid_codec_set_priority( const pj_str_t *codec_id,
263 pj_uint8_t priority )
264{
265 const pj_str_t all = { NULL, 0 };
266
267 if (codec_id->slen==1 && *codec_id->ptr=='*')
268 codec_id = &all;
269
270 return pjmedia_vid_codec_mgr_set_codec_priority(NULL, codec_id,
271 priority);
272}
273
274
275/*
276 * Get video codec parameters.
277 */
278PJ_DEF(pj_status_t) pjsua_vid_codec_get_param(
279 const pj_str_t *codec_id,
280 pjmedia_vid_codec_param *param)
281{
282 const pj_str_t all = { NULL, 0 };
283 const pjmedia_vid_codec_info *info;
284 unsigned count = 1;
285 pj_status_t status;
286
287 if (codec_id->slen==1 && *codec_id->ptr=='*')
288 codec_id = &all;
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 if (count != 1)
296 return (count > 1? PJ_ETOOMANY : PJ_ENOTFOUND);
297
298 status = pjmedia_vid_codec_mgr_get_default_param(NULL, info, param);
299 return status;
300}
301
302
303/*
304 * Set video codec parameters.
305 */
306PJ_DEF(pj_status_t) pjsua_vid_codec_set_param(
307 const pj_str_t *codec_id,
308 const pjmedia_vid_codec_param *param)
309{
310 const pjmedia_vid_codec_info *info[2];
311 unsigned count = 2;
312 pj_status_t status;
313
314 status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, codec_id,
315 &count, info, NULL);
316 if (status != PJ_SUCCESS)
317 return status;
318
319 /* Codec ID should be specific */
320 if (count > 1) {
321 pj_assert(!"Codec ID is not specific");
322 return PJ_ETOOMANY;
323 }
324
325 status = pjmedia_vid_codec_mgr_set_default_param(NULL, pjsua_var.pool,
326 info[0], param);
327 return status;
328}
329
330
331/*****************************************************************************
332 * Preview
333 */
334
Benny Prijono39203b82011-09-20 10:07:55 +0000335static pjsua_vid_win_id vid_preview_get_win(pjmedia_vid_dev_index id,
336 pj_bool_t running_only)
Benny Prijono9f468d12011-07-07 07:46:33 +0000337{
338 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
339 unsigned i;
340
341 PJSUA_LOCK();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000342
343 /* Get real capture ID, if set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV */
344 if (id == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
345 pjmedia_vid_dev_info info;
346 pjmedia_vid_dev_get_info(id, &info);
347 id = info.id;
348 }
349
Benny Prijono9f468d12011-07-07 07:46:33 +0000350 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
351 pjsua_vid_win *w = &pjsua_var.win[i];
352 if (w->type == PJSUA_WND_TYPE_PREVIEW && w->preview_cap_id == id) {
353 wid = i;
354 break;
355 }
356 }
Benny Prijono39203b82011-09-20 10:07:55 +0000357
358 if (wid != PJSUA_INVALID_ID && running_only) {
359 pjsua_vid_win *w = &pjsua_var.win[wid];
360 wid = w->preview_running ? wid : PJSUA_INVALID_ID;
361 }
362
Benny Prijono9f468d12011-07-07 07:46:33 +0000363 PJSUA_UNLOCK();
364
365 return wid;
366}
367
Benny Prijono39203b82011-09-20 10:07:55 +0000368/*
369 * NOTE: internal function don't use this!!! Use vid_preview_get_win()
370 * instead. This is because this function will only return window ID
371 * if preview is currently running.
372 */
373PJ_DEF(pjsua_vid_win_id) pjsua_vid_preview_get_win(pjmedia_vid_dev_index id)
374{
375 return vid_preview_get_win(id, PJ_TRUE);
376}
377
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000378PJ_DEF(void) pjsua_vid_win_reset(pjsua_vid_win_id wid)
379{
380 pjsua_vid_win *w = &pjsua_var.win[wid];
381 pj_pool_t *pool = w->pool;
382
383 pj_bzero(w, sizeof(*w));
384 if (pool) pj_pool_reset(pool);
385 w->ref_cnt = 0;
386 w->pool = pool;
387 w->preview_cap_id = PJMEDIA_VID_INVALID_DEV;
388}
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000389
390/* Allocate and initialize pjsua video window:
391 * - If the type is preview, video capture, tee, and render
392 * will be instantiated.
393 * - If the type is stream, only renderer will be created.
394 */
395static pj_status_t create_vid_win(pjsua_vid_win_type type,
396 const pjmedia_format *fmt,
397 pjmedia_vid_dev_index rend_id,
398 pjmedia_vid_dev_index cap_id,
399 pj_bool_t show,
Sauw Ming0ee072f2011-10-26 09:01:53 +0000400 unsigned wnd_flags,
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000401 pjsua_vid_win_id *id)
Benny Prijono9f468d12011-07-07 07:46:33 +0000402{
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000403 pj_bool_t enable_native_preview;
Benny Prijono9f468d12011-07-07 07:46:33 +0000404 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000405 pjsua_vid_win *w = NULL;
406 pjmedia_vid_port_param vp_param;
407 pjmedia_format fmt_;
408 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000409 unsigned i;
410
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000411 enable_native_preview = pjsua_var.media_cfg.vid_preview_enable_native;
412
413 PJ_LOG(4,(THIS_FILE,
414 "Creating video window: type=%s, cap_id=%d, rend_id=%d",
415 pjsua_vid_win_type_name(type), cap_id, rend_id));
Benny Prijonob90fd382011-09-18 14:59:56 +0000416 pj_log_push_indent();
417
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000418 /* If type is preview, check if it exists already */
419 if (type == PJSUA_WND_TYPE_PREVIEW) {
Benny Prijono39203b82011-09-20 10:07:55 +0000420 wid = vid_preview_get_win(cap_id, PJ_FALSE);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000421 if (wid != PJSUA_INVALID_ID) {
422 /* Yes, it exists */
Benny Prijono2047bd72011-08-25 11:59:39 +0000423 /* Show/hide window */
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000424 pjmedia_vid_dev_stream *strm;
Benny Prijono2047bd72011-08-25 11:59:39 +0000425 pj_bool_t hide = !show;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000426
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000427 w = &pjsua_var.win[wid];
428
429 PJ_LOG(4,(THIS_FILE,
430 "Window already exists for cap_dev=%d, returning wid=%d",
431 cap_id, wid));
432
433
434 if (w->is_native) {
435 strm = pjmedia_vid_port_get_stream(w->vp_cap);
436 } else {
437 strm = pjmedia_vid_port_get_stream(w->vp_rend);
438 }
439
440 pj_assert(strm);
Benny Prijono2047bd72011-08-25 11:59:39 +0000441 status = pjmedia_vid_dev_stream_set_cap(
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000442 strm, PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE,
Benny Prijono2047bd72011-08-25 11:59:39 +0000443 &hide);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000444
Sauw Ming0ee072f2011-10-26 09:01:53 +0000445 pjmedia_vid_dev_stream_set_cap(
446 strm, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS,
447 &wnd_flags);
448
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000449 /* Done */
450 *id = wid;
Benny Prijonob90fd382011-09-18 14:59:56 +0000451 pj_log_pop_indent();
Benny Prijono39203b82011-09-20 10:07:55 +0000452
453 return status;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000454 }
455 }
456
457 /* Allocate window */
Benny Prijono9f468d12011-07-07 07:46:33 +0000458 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000459 w = &pjsua_var.win[i];
Benny Prijono9f468d12011-07-07 07:46:33 +0000460 if (w->type == PJSUA_WND_TYPE_NONE) {
461 wid = i;
462 w->type = type;
463 break;
464 }
465 }
Benny Prijonob90fd382011-09-18 14:59:56 +0000466 if (i == PJSUA_MAX_VID_WINS) {
467 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000468 return PJ_ETOOMANY;
Benny Prijonob90fd382011-09-18 14:59:56 +0000469 }
Benny Prijono9f468d12011-07-07 07:46:33 +0000470
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000471 /* Initialize window */
472 pjmedia_vid_port_param_default(&vp_param);
473
474 if (w->type == PJSUA_WND_TYPE_PREVIEW) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000475 pjmedia_vid_dev_info vdi;
476
477 /*
478 * Determine if the device supports native preview.
479 */
480 status = pjmedia_vid_dev_get_info(cap_id, &vdi);
481 if (status != PJ_SUCCESS)
482 goto on_error;
483
484 if (enable_native_preview &&
485 (vdi.caps & PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW))
486 {
487 /* Device supports native preview! */
488 w->is_native = PJ_TRUE;
489 }
490
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000491 status = pjmedia_vid_dev_default_param(w->pool, cap_id,
492 &vp_param.vidparam);
493 if (status != PJ_SUCCESS)
494 goto on_error;
495
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000496 if (w->is_native) {
497 vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
498 vp_param.vidparam.window_hide = !show;
Sauw Ming0ee072f2011-10-26 09:01:53 +0000499 vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS;
500 vp_param.vidparam.window_flags = wnd_flags;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000501 }
502
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000503 /* Normalize capture ID, in case it was set to
504 * PJMEDIA_VID_DEFAULT_CAPTURE_DEV
505 */
506 cap_id = vp_param.vidparam.cap_id;
507
508 /* Assign preview capture device ID */
509 w->preview_cap_id = cap_id;
510
511 /* Create capture video port */
512 vp_param.active = PJ_TRUE;
513 vp_param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
514 if (fmt)
515 vp_param.vidparam.fmt = *fmt;
516
517 status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_cap);
518 if (status != PJ_SUCCESS)
519 goto on_error;
520
521 /* Update format info */
522 fmt_ = vp_param.vidparam.fmt;
523 fmt = &fmt_;
524
525 /* Create video tee */
Nanang Izzuddin15ad7f22011-08-26 04:19:04 +0000526 status = pjmedia_vid_tee_create(w->pool, fmt, VID_TEE_MAX_PORT,
527 &w->tee);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000528 if (status != PJ_SUCCESS)
529 goto on_error;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000530
531 /* If device supports native preview, enable it */
532 if (w->is_native) {
533 pjmedia_vid_dev_stream *cap_dev;
534 pj_bool_t enabled = PJ_TRUE;
535
536 cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
537 status = pjmedia_vid_dev_stream_set_cap(
538 cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
539 &enabled);
540 if (status != PJ_SUCCESS) {
541 PJ_PERROR(1,(THIS_FILE, status,
542 "Error activating native preview, falling back "
543 "to software preview.."));
544 w->is_native = PJ_FALSE;
545 }
546 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000547 }
548
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000549 /* Create renderer video port, only if it's not a native preview */
550 if (!w->is_native) {
551 status = pjmedia_vid_dev_default_param(w->pool, rend_id,
552 &vp_param.vidparam);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000553 if (status != PJ_SUCCESS)
554 goto on_error;
555
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000556 vp_param.active = (w->type == PJSUA_WND_TYPE_STREAM);
557 vp_param.vidparam.dir = PJMEDIA_DIR_RENDER;
558 vp_param.vidparam.fmt = *fmt;
559 vp_param.vidparam.disp_size = fmt->det.vid.size;
560 vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
561 vp_param.vidparam.window_hide = !show;
Sauw Ming0ee072f2011-10-26 09:01:53 +0000562 vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS;
563 vp_param.vidparam.window_flags = wnd_flags;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000564
565 status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_rend);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000566 if (status != PJ_SUCCESS)
567 goto on_error;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000568
569 /* For preview window, connect capturer & renderer (via tee) */
570 if (w->type == PJSUA_WND_TYPE_PREVIEW) {
571 pjmedia_port *rend_port;
572
573 status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
574 if (status != PJ_SUCCESS)
575 goto on_error;
576
577 rend_port = pjmedia_vid_port_get_passive_port(w->vp_rend);
578 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, rend_port);
579 if (status != PJ_SUCCESS)
580 goto on_error;
581 }
582
583 PJ_LOG(4,(THIS_FILE,
584 "%s window id %d created for cap_dev=%d rend_dev=%d",
585 pjsua_vid_win_type_name(type), wid, cap_id, rend_id));
586 } else {
587 PJ_LOG(4,(THIS_FILE,
588 "Preview window id %d created for cap_dev %d, "
589 "using built-in preview!",
590 wid, cap_id));
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000591 }
592
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000593
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000594 /* Done */
595 *id = wid;
596
Benny Prijonob90fd382011-09-18 14:59:56 +0000597 PJ_LOG(4,(THIS_FILE, "Window %d created", wid));
598 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000599 return PJ_SUCCESS;
600
601on_error:
602 free_vid_win(wid);
Benny Prijonob90fd382011-09-18 14:59:56 +0000603 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000604 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000605}
606
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000607
Benny Prijono9f468d12011-07-07 07:46:33 +0000608static void free_vid_win(pjsua_vid_win_id wid)
609{
610 pjsua_vid_win *w = &pjsua_var.win[wid];
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000611
Benny Prijonob90fd382011-09-18 14:59:56 +0000612 PJ_LOG(4,(THIS_FILE, "Window %d: destroying..", wid));
613 pj_log_push_indent();
614
Benny Prijono9f468d12011-07-07 07:46:33 +0000615 if (w->vp_cap) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000616 pjmedia_vid_port_stop(w->vp_cap);
617 pjmedia_vid_port_disconnect(w->vp_cap);
Benny Prijono9f468d12011-07-07 07:46:33 +0000618 pjmedia_vid_port_destroy(w->vp_cap);
619 }
620 if (w->vp_rend) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000621 pjmedia_vid_port_stop(w->vp_rend);
Benny Prijono9f468d12011-07-07 07:46:33 +0000622 pjmedia_vid_port_destroy(w->vp_rend);
623 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000624 if (w->tee) {
625 pjmedia_port_destroy(w->tee);
626 }
Benny Prijono9f468d12011-07-07 07:46:33 +0000627 pjsua_vid_win_reset(wid);
Benny Prijonob90fd382011-09-18 14:59:56 +0000628
629 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +0000630}
631
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000632
633static void inc_vid_win(pjsua_vid_win_id wid)
634{
635 pjsua_vid_win *w;
636
637 pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
638
639 w = &pjsua_var.win[wid];
640 pj_assert(w->type != PJSUA_WND_TYPE_NONE);
641 ++w->ref_cnt;
642}
643
644static void dec_vid_win(pjsua_vid_win_id wid)
645{
646 pjsua_vid_win *w;
647
648 pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
649
650 w = &pjsua_var.win[wid];
651 pj_assert(w->type != PJSUA_WND_TYPE_NONE);
652 if (--w->ref_cnt == 0)
653 free_vid_win(wid);
654}
655
656
657/* Internal function: update video channel after SDP negotiation */
658pj_status_t video_channel_update(pjsua_call_media *call_med,
659 pj_pool_t *tmp_pool,
660 const pjmedia_sdp_session *local_sdp,
661 const pjmedia_sdp_session *remote_sdp)
662{
663 pjsua_call *call = call_med->call;
664 pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
665 pjmedia_vid_stream_info the_si, *si = &the_si;
666 pjmedia_port *media_port;
667 unsigned strm_idx = call_med->idx;
668 pj_status_t status;
669
Benny Prijonob90fd382011-09-18 14:59:56 +0000670 PJ_LOG(4,(THIS_FILE, "Video channel update.."));
671 pj_log_push_indent();
672
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000673 status = pjmedia_vid_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt,
674 local_sdp, remote_sdp, strm_idx);
675 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000676 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000677
678 /* Check if no media is active */
679 if (si->dir == PJMEDIA_DIR_NONE) {
680 /* Call media state */
681 call_med->state = PJSUA_CALL_MEDIA_NONE;
682
683 /* Call media direction */
684 call_med->dir = PJMEDIA_DIR_NONE;
685
686 } else {
687 pjmedia_transport_info tp_info;
688
689 /* Start/restart media transport */
690 status = pjmedia_transport_media_start(call_med->tp,
691 tmp_pool, local_sdp,
692 remote_sdp, strm_idx);
693 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000694 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000695
Sauw Ming73ecfe82011-09-21 10:20:01 +0000696 set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000697
698 /* Get remote SRTP usage policy */
699 pjmedia_transport_info_init(&tp_info);
700 pjmedia_transport_get_info(call_med->tp, &tp_info);
701 if (tp_info.specific_info_cnt > 0) {
702 unsigned i;
703 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
704 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
705 {
706 pjmedia_srtp_info *srtp_info =
707 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
708
709 call_med->rem_srtp_use = srtp_info->peer_use;
710 break;
711 }
712 }
713 }
714
715 /* Optionally, application may modify other stream settings here
716 * (such as jitter buffer parameters, codec ptime, etc.)
717 */
718 si->jb_init = pjsua_var.media_cfg.jb_init;
719 si->jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
720 si->jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
721 si->jb_max = pjsua_var.media_cfg.jb_max;
722
723 /* Set SSRC */
724 si->ssrc = call_med->ssrc;
725
726 /* Set RTP timestamp & sequence, normally these value are intialized
727 * automatically when stream session created, but for some cases (e.g:
728 * call reinvite, call update) timestamp and sequence need to be kept
729 * contigue.
730 */
731 si->rtp_ts = call_med->rtp_tx_ts;
732 si->rtp_seq = call_med->rtp_tx_seq;
733 si->rtp_seq_ts_set = call_med->rtp_tx_seq_ts_set;
734
735#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
736 /* Enable/disable stream keep-alive and NAT hole punch. */
737 si->use_ka = pjsua_var.acc[call->acc_id].cfg.use_stream_ka;
738#endif
739
740 /* Try to get shared format ID between the capture device and
741 * the encoder to avoid format conversion in the capture device.
742 */
743 if (si->dir & PJMEDIA_DIR_ENCODING) {
744 pjmedia_vid_dev_info dev_info;
745 pjmedia_vid_codec_info *codec_info = &si->codec_info;
746 unsigned i, j;
747
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +0000748 status = pjmedia_vid_dev_get_info(call_med->strm.v.cap_dev,
749 &dev_info);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000750 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000751 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000752
753 /* Find matched format ID */
754 for (i = 0; i < codec_info->dec_fmt_id_cnt; ++i) {
755 for (j = 0; j < dev_info.fmt_cnt; ++j) {
756 if (codec_info->dec_fmt_id[i] ==
757 (pjmedia_format_id)dev_info.fmt[j].id)
758 {
759 /* Apply the matched format ID to the codec */
760 si->codec_param->dec_fmt.id =
761 codec_info->dec_fmt_id[i];
762
763 /* Force outer loop to break */
764 i = codec_info->dec_fmt_id_cnt;
765 break;
766 }
767 }
768 }
769 }
770
771 /* Create session based on session info. */
772 status = pjmedia_vid_stream_create(pjsua_var.med_endpt, NULL, si,
773 call_med->tp, NULL,
774 &call_med->strm.v.stream);
775 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000776 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000777
778 /* Start stream */
779 status = pjmedia_vid_stream_start(call_med->strm.v.stream);
780 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000781 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000782
783 /* Setup decoding direction */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000784 if (si->dir & PJMEDIA_DIR_DECODING)
785 {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000786 pjsua_vid_win_id wid;
787 pjsua_vid_win *w;
788
Benny Prijonob90fd382011-09-18 14:59:56 +0000789 PJ_LOG(4,(THIS_FILE, "Setting up RX.."));
790 pj_log_push_indent();
791
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000792 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
793 PJMEDIA_DIR_DECODING,
794 &media_port);
Benny Prijonob90fd382011-09-18 14:59:56 +0000795 if (status != PJ_SUCCESS) {
796 pj_log_pop_indent();
797 goto on_error;
798 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000799
800 /* Create stream video window */
801 status = create_vid_win(PJSUA_WND_TYPE_STREAM,
802 &media_port->info.fmt,
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000803 call_med->strm.v.rdr_dev,
804 //acc->cfg.vid_rend_dev,
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000805 PJSUA_INVALID_ID,
806 acc->cfg.vid_in_auto_show,
Sauw Ming0ee072f2011-10-26 09:01:53 +0000807 acc->cfg.vid_wnd_flags,
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000808 &wid);
Benny Prijonob90fd382011-09-18 14:59:56 +0000809 if (status != PJ_SUCCESS) {
810 pj_log_pop_indent();
811 goto on_error;
812 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000813
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000814 w = &pjsua_var.win[wid];
815
Benny Prijono6565b582011-08-29 09:54:02 +0000816#if ENABLE_EVENT
Benny Prijonoee0ba182011-07-15 06:18:29 +0000817 /* Register to video events */
818 pjmedia_event_subscribe(
819 pjmedia_vid_port_get_event_publisher(w->vp_rend),
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000820 &call_med->esub_rend);
Benny Prijono6565b582011-08-29 09:54:02 +0000821#endif
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000822
823 /* Connect renderer to stream */
824 status = pjmedia_vid_port_connect(w->vp_rend, media_port,
825 PJ_FALSE);
Benny Prijonob90fd382011-09-18 14:59:56 +0000826 if (status != PJ_SUCCESS) {
827 pj_log_pop_indent();
828 goto on_error;
829 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000830
831 /* Start renderer */
832 status = pjmedia_vid_port_start(w->vp_rend);
Benny Prijonob90fd382011-09-18 14:59:56 +0000833 if (status != PJ_SUCCESS) {
834 pj_log_pop_indent();
835 goto on_error;
836 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000837
838 /* Done */
839 inc_vid_win(wid);
840 call_med->strm.v.rdr_win_id = wid;
Benny Prijonob90fd382011-09-18 14:59:56 +0000841 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000842 }
843
844 /* Setup encoding direction */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000845 if (si->dir & PJMEDIA_DIR_ENCODING && !call->local_hold)
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000846 {
Sauw Ming0ee072f2011-10-26 09:01:53 +0000847 pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000848 pjsua_vid_win *w;
849 pjsua_vid_win_id wid;
Benny Prijonoeb663982011-10-06 06:05:36 +0000850 pj_bool_t just_created = PJ_FALSE;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000851
Benny Prijonob90fd382011-09-18 14:59:56 +0000852 PJ_LOG(4,(THIS_FILE, "Setting up TX.."));
853 pj_log_push_indent();
854
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000855 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
856 PJMEDIA_DIR_ENCODING,
857 &media_port);
Benny Prijonob90fd382011-09-18 14:59:56 +0000858 if (status != PJ_SUCCESS) {
859 pj_log_pop_indent();
860 goto on_error;
861 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000862
Benny Prijono39203b82011-09-20 10:07:55 +0000863 /* Note: calling pjsua_vid_preview_get_win() even though
864 * create_vid_win() will automatically create the window
865 * if it doesn't exist, because create_vid_win() will modify
866 * existing window SHOW/HIDE value.
867 */
868 wid = vid_preview_get_win(call_med->strm.v.cap_dev, PJ_FALSE);
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000869 if (wid == PJSUA_INVALID_ID) {
870 /* Create preview video window */
871 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
872 &media_port->info.fmt,
873 call_med->strm.v.rdr_dev,
874 call_med->strm.v.cap_dev,
875 //acc->cfg.vid_rend_dev,
876 //acc->cfg.vid_cap_dev,
877 PJSUA_HIDE_WINDOW,
Sauw Ming0ee072f2011-10-26 09:01:53 +0000878 acc->cfg.vid_wnd_flags,
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000879 &wid);
880 if (status != PJ_SUCCESS) {
Benny Prijonob90fd382011-09-18 14:59:56 +0000881 pj_log_pop_indent();
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000882 return status;
883 }
Benny Prijonoeb663982011-10-06 06:05:36 +0000884 just_created = PJ_TRUE;
Benny Prijonob90fd382011-09-18 14:59:56 +0000885 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000886
887 w = &pjsua_var.win[wid];
Benny Prijono6565b582011-08-29 09:54:02 +0000888#if ENABLE_EVENT
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000889 pjmedia_event_subscribe(
890 pjmedia_vid_port_get_event_publisher(w->vp_cap),
891 &call_med->esub_cap);
Benny Prijono6565b582011-08-29 09:54:02 +0000892#endif
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000893
894 /* Connect stream to capturer (via video window tee) */
895 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
Benny Prijonob90fd382011-09-18 14:59:56 +0000896 if (status != PJ_SUCCESS) {
897 pj_log_pop_indent();
898 goto on_error;
899 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000900
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000901 /* Start capturer */
Benny Prijonoeb663982011-10-06 06:05:36 +0000902 if (just_created) {
903 status = pjmedia_vid_port_start(w->vp_cap);
904 if (status != PJ_SUCCESS) {
905 pj_log_pop_indent();
906 goto on_error;
907 }
Benny Prijonob90fd382011-09-18 14:59:56 +0000908 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000909
910 /* Done */
911 inc_vid_win(wid);
912 call_med->strm.v.cap_win_id = wid;
Benny Prijonob90fd382011-09-18 14:59:56 +0000913 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000914 }
915
916 /* Call media direction */
917 call_med->dir = si->dir;
918
919 /* Call media state */
920 if (call->local_hold)
921 call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
922 else if (call_med->dir == PJMEDIA_DIR_DECODING)
923 call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
924 else
925 call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
926 }
927
928 /* Print info. */
929 {
930 char info[80];
931 int info_len = 0;
932 int len;
933 const char *dir;
934
935 switch (si->dir) {
936 case PJMEDIA_DIR_NONE:
937 dir = "inactive";
938 break;
939 case PJMEDIA_DIR_ENCODING:
940 dir = "sendonly";
941 break;
942 case PJMEDIA_DIR_DECODING:
943 dir = "recvonly";
944 break;
945 case PJMEDIA_DIR_ENCODING_DECODING:
946 dir = "sendrecv";
947 break;
948 default:
949 dir = "unknown";
950 break;
951 }
952 len = pj_ansi_sprintf( info+info_len,
953 ", stream #%d: %.*s (%s)", strm_idx,
954 (int)si->codec_info.encoding_name.slen,
955 si->codec_info.encoding_name.ptr,
956 dir);
957 if (len > 0)
958 info_len += len;
Benny Prijonob90fd382011-09-18 14:59:56 +0000959 PJ_LOG(4,(THIS_FILE,"Video updated%s", info));
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000960 }
961
Sauw Mingc7bc3aa2011-07-15 07:22:49 +0000962 if (!acc->cfg.vid_out_auto_transmit && call_med->strm.v.stream) {
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000963 status = pjmedia_vid_stream_pause(call_med->strm.v.stream,
964 PJMEDIA_DIR_ENCODING);
965 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000966 goto on_error;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000967 }
968
Benny Prijonob90fd382011-09-18 14:59:56 +0000969 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000970 return PJ_SUCCESS;
Benny Prijonob90fd382011-09-18 14:59:56 +0000971
972on_error:
973 pj_log_pop_indent();
974 return status;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000975}
976
977
978/* Internal function to stop video stream */
979void stop_video_stream(pjsua_call_media *call_med)
980{
981 pjmedia_vid_stream *strm = call_med->strm.v.stream;
982 pjmedia_rtcp_stat stat;
983
984 pj_assert(call_med->type == PJMEDIA_TYPE_VIDEO);
985
986 if (!strm)
987 return;
988
Benny Prijonob90fd382011-09-18 14:59:56 +0000989 PJ_LOG(4,(THIS_FILE, "Stopping video stream.."));
990 pj_log_push_indent();
991
Benny Prijonoa0dbe052011-08-29 04:16:14 +0000992 if (call_med->strm.v.cap_win_id != PJSUA_INVALID_ID) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000993 pjmedia_port *media_port;
Nanang Izzuddinc12a19d2011-10-03 05:23:59 +0000994 pjsua_vid_win *w = &pjsua_var.win[call_med->strm.v.cap_win_id];
Nanang Izzuddinca356182011-09-23 04:00:27 +0000995 pj_status_t status;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000996
Nanang Izzuddinc12a19d2011-10-03 05:23:59 +0000997 /* Stop the capture before detaching stream and unsubscribing event */
998 pjmedia_vid_port_stop(w->vp_cap);
999
Nanang Izzuddinca356182011-09-23 04:00:27 +00001000 /* Disconnect video stream from capture device */
1001 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
1002 PJMEDIA_DIR_ENCODING,
1003 &media_port);
1004 if (status == PJ_SUCCESS) {
Nanang Izzuddinca356182011-09-23 04:00:27 +00001005 pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
Nanang Izzuddinca356182011-09-23 04:00:27 +00001006 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001007
Nanang Izzuddinc12a19d2011-10-03 05:23:59 +00001008 /* Unsubscribe event */
1009 pjmedia_event_unsubscribe(&call_med->esub_cap);
1010
1011 /* Re-start capture again, if it is used by other stream */
1012 if (w->ref_cnt > 1)
1013 pjmedia_vid_port_start(w->vp_cap);
1014
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001015 dec_vid_win(call_med->strm.v.cap_win_id);
Nanang Izzuddinca356182011-09-23 04:00:27 +00001016 call_med->strm.v.cap_win_id = PJSUA_INVALID_ID;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001017 }
1018
Benny Prijonoa0dbe052011-08-29 04:16:14 +00001019 if (call_med->strm.v.rdr_win_id != PJSUA_INVALID_ID) {
Nanang Izzuddinc12a19d2011-10-03 05:23:59 +00001020 pjsua_vid_win *w = &pjsua_var.win[call_med->strm.v.rdr_win_id];
1021
1022 /* Stop the render before unsubscribing event */
1023 pjmedia_vid_port_stop(w->vp_rend);
1024 pjmedia_event_unsubscribe(&call_med->esub_rend);
1025
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001026 dec_vid_win(call_med->strm.v.rdr_win_id);
Nanang Izzuddinca356182011-09-23 04:00:27 +00001027 call_med->strm.v.rdr_win_id = PJSUA_INVALID_ID;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001028 }
1029
1030 if ((call_med->dir & PJMEDIA_DIR_ENCODING) &&
1031 (pjmedia_vid_stream_get_stat(strm, &stat) == PJ_SUCCESS))
1032 {
1033 /* Save RTP timestamp & sequence, so when media session is
1034 * restarted, those values will be restored as the initial
1035 * RTP timestamp & sequence of the new media session. So in
1036 * the same call session, RTP timestamp and sequence are
1037 * guaranteed to be contigue.
1038 */
1039 call_med->rtp_tx_seq_ts_set = 1 | (1 << 1);
1040 call_med->rtp_tx_seq = stat.rtp_tx_last_seq;
1041 call_med->rtp_tx_ts = stat.rtp_tx_last_ts;
1042 }
1043
1044 pjmedia_vid_stream_destroy(strm);
1045 call_med->strm.v.stream = NULL;
Benny Prijonob90fd382011-09-18 14:59:56 +00001046
1047 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001048}
1049
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001050/*
1051 * Does it have built-in preview support.
1052 */
1053PJ_DEF(pj_bool_t) pjsua_vid_preview_has_native(pjmedia_vid_dev_index id)
1054{
1055 pjmedia_vid_dev_info vdi;
1056
1057 return (pjmedia_vid_dev_get_info(id, &vdi)==PJ_SUCCESS) ?
1058 ((vdi.caps & PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW)!=0) : PJ_FALSE;
1059}
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001060
Benny Prijono9f468d12011-07-07 07:46:33 +00001061/*
1062 * Start video preview window for the specified capture device.
1063 */
1064PJ_DEF(pj_status_t) pjsua_vid_preview_start(pjmedia_vid_dev_index id,
Benny Prijono2047bd72011-08-25 11:59:39 +00001065 const pjsua_vid_preview_param *prm)
Benny Prijono9f468d12011-07-07 07:46:33 +00001066{
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001067 pjsua_vid_win_id wid;
1068 pjsua_vid_win *w;
1069 pjmedia_vid_dev_index rend_id;
Benny Prijono2047bd72011-08-25 11:59:39 +00001070 pjsua_vid_preview_param default_param;
Benny Prijono9f468d12011-07-07 07:46:33 +00001071 pj_status_t status;
1072
Benny Prijono2047bd72011-08-25 11:59:39 +00001073 if (!prm) {
1074 pjsua_vid_preview_param_default(&default_param);
1075 prm = &default_param;
Benny Prijono9f468d12011-07-07 07:46:33 +00001076 }
1077
Benny Prijonob90fd382011-09-18 14:59:56 +00001078 PJ_LOG(4,(THIS_FILE, "Starting preview for cap_dev=%d, show=%d",
1079 id, prm->show));
1080 pj_log_push_indent();
1081
1082 PJSUA_LOCK();
1083
Benny Prijono2047bd72011-08-25 11:59:39 +00001084 rend_id = prm->rend_id;
1085
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001086 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW, NULL, rend_id, id,
Sauw Ming0ee072f2011-10-26 09:01:53 +00001087 prm->show, prm->wnd_flags, &wid);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001088 if (status != PJ_SUCCESS) {
Benny Prijono9f468d12011-07-07 07:46:33 +00001089 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001090 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001091 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001092 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001093
Benny Prijono9f468d12011-07-07 07:46:33 +00001094 w = &pjsua_var.win[wid];
Benny Prijono39203b82011-09-20 10:07:55 +00001095 if (w->preview_running) {
1096 PJSUA_UNLOCK();
1097 pj_log_pop_indent();
1098 return PJ_SUCCESS;
1099 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001100
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001101 /* Start renderer, unless it's native preview */
Benny Prijono39203b82011-09-20 10:07:55 +00001102 if (w->is_native && !pjmedia_vid_port_is_running(w->vp_cap)) {
1103 pjmedia_vid_dev_stream *cap_dev;
1104 pj_bool_t enabled = PJ_TRUE;
1105
1106 cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
1107 status = pjmedia_vid_dev_stream_set_cap(
1108 cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
1109 &enabled);
1110 if (status != PJ_SUCCESS) {
1111 PJ_PERROR(1,(THIS_FILE, status,
1112 "Error activating native preview, falling back "
1113 "to software preview.."));
1114 w->is_native = PJ_FALSE;
1115 }
1116 }
1117
1118 if (!w->is_native && !pjmedia_vid_port_is_running(w->vp_rend)) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001119 status = pjmedia_vid_port_start(w->vp_rend);
1120 if (status != PJ_SUCCESS) {
1121 PJSUA_UNLOCK();
1122 pj_log_pop_indent();
1123 return status;
1124 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001125 }
1126
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001127 /* Start capturer */
Benny Prijono39203b82011-09-20 10:07:55 +00001128 if (!pjmedia_vid_port_is_running(w->vp_cap)) {
1129 status = pjmedia_vid_port_start(w->vp_cap);
1130 if (status != PJ_SUCCESS) {
1131 PJSUA_UNLOCK();
1132 pj_log_pop_indent();
1133 return status;
1134 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001135 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001136
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001137 inc_vid_win(wid);
Benny Prijono39203b82011-09-20 10:07:55 +00001138 w->preview_running = PJ_TRUE;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001139
Benny Prijono9f468d12011-07-07 07:46:33 +00001140 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001141 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +00001142 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +00001143}
1144
1145/*
1146 * Stop video preview.
1147 */
1148PJ_DEF(pj_status_t) pjsua_vid_preview_stop(pjmedia_vid_dev_index id)
1149{
1150 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
Benny Prijono39203b82011-09-20 10:07:55 +00001151 pjsua_vid_win *w;
1152 pj_status_t status;
Benny Prijonob90fd382011-09-18 14:59:56 +00001153
Benny Prijono9f468d12011-07-07 07:46:33 +00001154 PJSUA_LOCK();
1155 wid = pjsua_vid_preview_get_win(id);
1156 if (wid == PJSUA_INVALID_ID) {
1157 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001158 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +00001159 return PJ_ENOTFOUND;
1160 }
1161
Benny Prijono39203b82011-09-20 10:07:55 +00001162 PJ_LOG(4,(THIS_FILE, "Stopping preview for cap_dev=%d", id));
1163 pj_log_push_indent();
1164
1165 w = &pjsua_var.win[wid];
1166 if (w->preview_running) {
1167 if (w->is_native) {
1168 pjmedia_vid_dev_stream *cap_dev;
1169 pj_bool_t enabled = PJ_FALSE;
1170
1171 cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
1172 status = pjmedia_vid_dev_stream_set_cap(
1173 cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
1174 &enabled);
1175 } else {
1176 status = pjmedia_vid_port_stop(w->vp_rend);
1177 }
1178
1179 if (status != PJ_SUCCESS) {
1180 PJ_PERROR(1,(THIS_FILE, status, "Error stopping %spreview",
1181 (w->is_native ? "native " : "")));
1182 PJSUA_UNLOCK();
1183 pj_log_pop_indent();
1184 return status;
1185 }
1186
1187 dec_vid_win(wid);
1188 w->preview_running = PJ_FALSE;
1189 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001190
1191 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001192 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +00001193
1194 return PJ_SUCCESS;
1195}
1196
1197
1198/*****************************************************************************
1199 * Window
1200 */
1201
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001202
1203/*
1204 * Enumerates all video windows.
1205 */
1206PJ_DEF(pj_status_t) pjsua_vid_enum_wins( pjsua_vid_win_id wids[],
1207 unsigned *count)
1208{
1209 unsigned i, cnt;
1210
1211 cnt = 0;
1212
1213 for (i=0; i<PJSUA_MAX_VID_WINS && cnt <*count; ++i) {
1214 pjsua_vid_win *w = &pjsua_var.win[i];
1215 if (w->type != PJSUA_WND_TYPE_NONE)
1216 wids[cnt++] = i;
1217 }
1218
1219 *count = cnt;
1220
1221 return PJ_SUCCESS;
1222}
1223
1224
Benny Prijono9f468d12011-07-07 07:46:33 +00001225/*
1226 * Get window info.
1227 */
1228PJ_DEF(pj_status_t) pjsua_vid_win_get_info( pjsua_vid_win_id wid,
1229 pjsua_vid_win_info *wi)
1230{
1231 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001232 pjmedia_vid_dev_stream *s;
Sauw Ming5291a2d2011-07-15 07:52:44 +00001233 pjmedia_vid_dev_param vparam;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001234 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001235
1236 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && wi, PJ_EINVAL);
1237
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001238 pj_bzero(wi, sizeof(*wi));
1239
Benny Prijono9f468d12011-07-07 07:46:33 +00001240 PJSUA_LOCK();
1241 w = &pjsua_var.win[wid];
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001242
1243 wi->is_native = w->is_native;
1244
1245 if (w->is_native) {
1246 pjmedia_vid_dev_stream *cap_strm;
1247 pjmedia_vid_dev_cap cap = PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
1248
1249 cap_strm = pjmedia_vid_port_get_stream(w->vp_cap);
1250 if (!cap_strm) {
1251 status = PJ_EINVAL;
1252 } else {
1253 status = pjmedia_vid_dev_stream_get_cap(cap_strm, cap, &wi->hwnd);
1254 }
1255
1256 PJSUA_UNLOCK();
1257 return status;
1258 }
1259
Benny Prijono9f468d12011-07-07 07:46:33 +00001260 if (w->vp_rend == NULL) {
1261 PJSUA_UNLOCK();
1262 return PJ_EINVAL;
1263 }
1264
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001265 s = pjmedia_vid_port_get_stream(w->vp_rend);
1266 if (s == NULL) {
1267 PJSUA_UNLOCK();
1268 return PJ_EINVAL;
1269 }
1270
1271 status = pjmedia_vid_dev_stream_get_param(s, &vparam);
1272 if (status != PJ_SUCCESS) {
1273 PJSUA_UNLOCK();
1274 return status;
1275 }
1276
Nanang Izzuddindb9b0022011-07-26 08:17:25 +00001277 wi->rdr_dev = vparam.rend_id;
Nanang Izzuddin6e2fcc32011-07-22 04:49:36 +00001278 wi->hwnd = vparam.window;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001279 wi->show = !vparam.window_hide;
1280 wi->pos = vparam.window_pos;
1281 wi->size = vparam.disp_size;
1282
Benny Prijono9f468d12011-07-07 07:46:33 +00001283 PJSUA_UNLOCK();
1284
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001285 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +00001286}
1287
1288/*
1289 * Show or hide window.
1290 */
1291PJ_DEF(pj_status_t) pjsua_vid_win_set_show( pjsua_vid_win_id wid,
1292 pj_bool_t show)
1293{
1294 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001295 pjmedia_vid_dev_stream *s;
Nanang Izzuddinc16590a2011-07-14 02:14:25 +00001296 pj_bool_t hide;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001297 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001298
1299 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
1300
1301 PJSUA_LOCK();
1302 w = &pjsua_var.win[wid];
1303 if (w->vp_rend == NULL) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001304 /* Native window */
Benny Prijono9f468d12011-07-07 07:46:33 +00001305 PJSUA_UNLOCK();
1306 return PJ_EINVAL;
1307 }
1308
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001309 s = pjmedia_vid_port_get_stream(w->vp_rend);
1310 if (s == NULL) {
1311 PJSUA_UNLOCK();
1312 return PJ_EINVAL;
1313 }
1314
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001315 /* Make sure that renderer gets started before shown up */
1316 if (show && !pjmedia_vid_port_is_running(w->vp_rend))
1317 status = pjmedia_vid_port_start(w->vp_rend);
1318
Nanang Izzuddinc16590a2011-07-14 02:14:25 +00001319 hide = !show;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001320 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinc16590a2011-07-14 02:14:25 +00001321 PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE, &hide);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001322
Benny Prijono9f468d12011-07-07 07:46:33 +00001323 PJSUA_UNLOCK();
1324
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001325 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001326}
1327
1328/*
1329 * Set video window position.
1330 */
1331PJ_DEF(pj_status_t) pjsua_vid_win_set_pos( pjsua_vid_win_id wid,
1332 const pjmedia_coord *pos)
1333{
1334 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001335 pjmedia_vid_dev_stream *s;
1336 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001337
1338 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && pos, PJ_EINVAL);
1339
1340 PJSUA_LOCK();
1341 w = &pjsua_var.win[wid];
1342 if (w->vp_rend == NULL) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001343 /* Native window */
Benny Prijono9f468d12011-07-07 07:46:33 +00001344 PJSUA_UNLOCK();
1345 return PJ_EINVAL;
1346 }
1347
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001348 s = pjmedia_vid_port_get_stream(w->vp_rend);
1349 if (s == NULL) {
1350 PJSUA_UNLOCK();
1351 return PJ_EINVAL;
1352 }
1353
1354 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001355 PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION, pos);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001356
Benny Prijono9f468d12011-07-07 07:46:33 +00001357 PJSUA_UNLOCK();
1358
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001359 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001360}
1361
1362/*
1363 * Resize window.
1364 */
1365PJ_DEF(pj_status_t) pjsua_vid_win_set_size( pjsua_vid_win_id wid,
1366 const pjmedia_rect_size *size)
1367{
1368 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001369 pjmedia_vid_dev_stream *s;
1370 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001371
1372 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && size, PJ_EINVAL);
1373
1374 PJSUA_LOCK();
1375 w = &pjsua_var.win[wid];
1376 if (w->vp_rend == NULL) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001377 /* Native window */
Benny Prijono9f468d12011-07-07 07:46:33 +00001378 PJSUA_UNLOCK();
1379 return PJ_EINVAL;
1380 }
1381
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001382 s = pjmedia_vid_port_get_stream(w->vp_rend);
1383 if (s == NULL) {
1384 PJSUA_UNLOCK();
1385 return PJ_EINVAL;
1386 }
1387
1388 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001389 PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE, size);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001390
Benny Prijono9f468d12011-07-07 07:46:33 +00001391 PJSUA_UNLOCK();
1392
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001393 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001394}
1395
Nanang Izzuddine9b279b2011-09-27 05:24:06 +00001396/*
1397 * Set video orientation.
1398 */
1399PJ_DEF(pj_status_t) pjsua_vid_win_rotate( pjsua_vid_win_id wid,
1400 int angle)
1401{
1402 pjsua_vid_win *w;
1403 pjmedia_vid_dev_stream *s;
1404 pjmedia_orient orient;
1405 pj_status_t status;
1406
1407 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
1408 PJ_ASSERT_RETURN((angle % 90) == 0, PJ_EINVAL);
1409
1410 /* Normalize angle, so it must be 0, 90, 180, or 270. */
1411 angle %= 360;
1412 if (angle < 0)
1413 angle += 360;
1414
1415 /* Convert angle to pjmedia_orient */
1416 switch(angle) {
1417 case 0:
1418 /* No rotation */
1419 return PJ_SUCCESS;
1420 case 90:
1421 orient = PJMEDIA_ORIENT_ROTATE_90DEG;
1422 break;
1423 case 180:
1424 orient = PJMEDIA_ORIENT_ROTATE_180DEG;
1425 break;
1426 case 270:
1427 orient = PJMEDIA_ORIENT_ROTATE_270DEG;
1428 break;
1429 default:
1430 pj_assert(!"Angle must have been validated");
1431 return PJ_EBUG;
1432 }
1433
1434 PJSUA_LOCK();
1435 w = &pjsua_var.win[wid];
1436 if (w->vp_rend == NULL) {
1437 /* Native window */
1438 PJSUA_UNLOCK();
1439 return PJ_EINVAL;
1440 }
1441
1442 s = pjmedia_vid_port_get_stream(w->vp_rend);
1443 if (s == NULL) {
1444 PJSUA_UNLOCK();
1445 return PJ_EINVAL;
1446 }
1447
1448 status = pjmedia_vid_dev_stream_set_cap(s,
1449 PJMEDIA_VID_DEV_CAP_ORIENTATION, &orient);
1450
1451 PJSUA_UNLOCK();
1452
1453 return status;
1454}
1455
Benny Prijono9f468d12011-07-07 07:46:33 +00001456
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001457static void call_get_vid_strm_info(pjsua_call *call,
1458 int *first_active,
1459 int *first_inactive,
1460 unsigned *active_cnt,
1461 unsigned *cnt)
1462{
1463 unsigned i, var_cnt = 0;
1464
1465 if (first_active && ++var_cnt)
1466 *first_active = -1;
1467 if (first_inactive && ++var_cnt)
1468 *first_inactive = -1;
1469 if (active_cnt && ++var_cnt)
1470 *active_cnt = 0;
1471 if (cnt && ++var_cnt)
1472 *cnt = 0;
1473
1474 for (i = 0; i < call->med_cnt && var_cnt; ++i) {
1475 if (call->media[i].type == PJMEDIA_TYPE_VIDEO) {
1476 if (call->media[i].dir != PJMEDIA_DIR_NONE)
1477 {
1478 if (first_active && *first_active == -1) {
1479 *first_active = i;
1480 --var_cnt;
1481 }
1482 if (active_cnt)
1483 ++(*active_cnt);
1484 } else if (first_inactive && *first_inactive == -1) {
1485 *first_inactive = i;
1486 --var_cnt;
1487 }
1488 if (cnt)
1489 ++(*cnt);
1490 }
1491 }
1492}
1493
1494
1495/* Send SDP reoffer. */
1496static pj_status_t call_reoffer_sdp(pjsua_call_id call_id,
1497 const pjmedia_sdp_session *sdp)
1498{
1499 pjsua_call *call;
1500 pjsip_tx_data *tdata;
1501 pjsip_dialog *dlg;
1502 pj_status_t status;
1503
1504 status = acquire_call("call_reoffer_sdp()", call_id, &call, &dlg);
1505 if (status != PJ_SUCCESS)
1506 return status;
1507
1508 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1509 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
1510 pjsip_dlg_dec_lock(dlg);
1511 return PJSIP_ESESSIONSTATE;
1512 }
1513
1514 /* Create re-INVITE with new offer */
1515 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1516 if (status != PJ_SUCCESS) {
1517 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
1518 pjsip_dlg_dec_lock(dlg);
1519 return status;
1520 }
1521
1522 /* Send the request */
1523 status = pjsip_inv_send_msg( call->inv, tdata);
1524 if (status != PJ_SUCCESS) {
1525 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
1526 pjsip_dlg_dec_lock(dlg);
1527 return status;
1528 }
1529
1530 pjsip_dlg_dec_lock(dlg);
1531
1532 return PJ_SUCCESS;
1533}
1534
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001535/* Add a new video stream into a call */
1536static pj_status_t call_add_video(pjsua_call *call,
Nanang Izzuddin98085612011-07-15 07:41:02 +00001537 pjmedia_vid_dev_index cap_dev,
1538 pjmedia_dir dir)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001539{
1540 pj_pool_t *pool = call->inv->pool_prov;
1541 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1542 pjsua_call_media *call_med;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001543 const pjmedia_sdp_session *current_sdp;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001544 pjmedia_sdp_session *sdp;
1545 pjmedia_sdp_media *sdp_m;
1546 pjmedia_transport_info tpinfo;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001547 pj_status_t status;
1548
1549 /* Verify media slot availability */
1550 if (call->med_cnt == PJSUA_MAX_CALL_MEDIA)
1551 return PJ_ETOOMANY;
1552
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001553 /* Get active local SDP and clone it */
1554 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &current_sdp);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001555 if (status != PJ_SUCCESS)
1556 return status;
1557
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001558 sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, current_sdp);
1559
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001560 /* Initialize call media */
1561 call_med = &call->media[call->med_cnt++];
1562
1563 status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1564 &acc_cfg->rtp_cfg, call->secure_level,
Sauw Ming73ecfe82011-09-21 10:20:01 +00001565 NULL, PJ_FALSE, NULL);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001566 if (status != PJ_SUCCESS)
1567 goto on_error;
1568
1569 /* Override default capture device setting */
1570 call_med->strm.v.cap_dev = cap_dev;
1571
1572 /* Init transport media */
1573 status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1574 NULL, call_med->idx);
1575 if (status != PJ_SUCCESS)
1576 goto on_error;
1577
Sauw Ming73ecfe82011-09-21 10:20:01 +00001578 set_media_tp_state(call_med, PJSUA_MED_TP_INIT);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001579
1580 /* Get transport address info */
1581 pjmedia_transport_info_init(&tpinfo);
1582 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1583
1584 /* Create SDP media line */
1585 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1586 &tpinfo.sock_info, 0, &sdp_m);
1587 if (status != PJ_SUCCESS)
1588 goto on_error;
1589
1590 sdp->media[sdp->media_count++] = sdp_m;
1591
Nanang Izzuddin98085612011-07-15 07:41:02 +00001592 /* Update media direction, if it is not 'sendrecv' */
1593 if (dir != PJMEDIA_DIR_ENCODING_DECODING) {
1594 pjmedia_sdp_attr *a;
1595
1596 /* Remove sendrecv direction attribute, if any */
1597 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
1598
1599 if (dir == PJMEDIA_DIR_ENCODING)
1600 a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
1601 else if (dir == PJMEDIA_DIR_DECODING)
1602 a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
1603 else
1604 a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
1605
1606 pjmedia_sdp_media_add_attr(sdp_m, a);
1607 }
1608
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001609 /* Update SDP media line by media transport */
1610 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1611 sdp, NULL, call_med->idx);
1612 if (status != PJ_SUCCESS)
1613 goto on_error;
1614
1615 status = call_reoffer_sdp(call->index, sdp);
1616 if (status != PJ_SUCCESS)
1617 goto on_error;
1618
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001619 call->opt.video_cnt++;
1620
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001621 return PJ_SUCCESS;
1622
1623on_error:
1624 if (call_med->tp) {
1625 pjmedia_transport_close(call_med->tp);
1626 call_med->tp = call_med->tp_orig = NULL;
1627 }
1628
1629 return status;
1630}
1631
1632
Nanang Izzuddin98085612011-07-15 07:41:02 +00001633/* Modify a video stream from a call, i.e: update direction,
1634 * remove/disable.
1635 */
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001636static pj_status_t call_modify_video(pjsua_call *call,
1637 int med_idx,
Nanang Izzuddin98085612011-07-15 07:41:02 +00001638 pjmedia_dir dir,
1639 pj_bool_t remove)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001640{
1641 pjsua_call_media *call_med;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001642 const pjmedia_sdp_session *current_sdp;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001643 pjmedia_sdp_session *sdp;
1644 pj_status_t status;
1645
1646 /* Verify and normalize media index */
1647 if (med_idx == -1) {
1648 int first_active;
1649
1650 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1651 if (first_active == -1)
1652 return PJ_ENOTFOUND;
1653
1654 med_idx = first_active;
1655 }
1656
1657 call_med = &call->media[med_idx];
1658
1659 /* Verify if the stream media type is video */
1660 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1661 return PJ_EINVAL;
1662
Nanang Izzuddin98085612011-07-15 07:41:02 +00001663 /* Verify if the stream dir is not changed */
1664 if ((!remove && call_med->dir == dir) ||
1665 ( remove && (call_med->tp_st == PJSUA_MED_TP_DISABLED ||
1666 call_med->tp == NULL)))
1667 {
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001668 return PJ_SUCCESS;
Nanang Izzuddin98085612011-07-15 07:41:02 +00001669 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001670
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001671 /* Get active local SDP and clone it */
1672 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &current_sdp);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001673 if (status != PJ_SUCCESS)
1674 return status;
1675
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001676 sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, current_sdp);
1677
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001678 pj_assert(med_idx < (int)sdp->media_count);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001679
Nanang Izzuddin98085612011-07-15 07:41:02 +00001680 if (!remove) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001681 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1682 pj_pool_t *pool = call->inv->pool_prov;
1683 pjmedia_sdp_media *sdp_m;
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001684
1685 status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1686 &acc_cfg->rtp_cfg, call->secure_level,
Sauw Ming73ecfe82011-09-21 10:20:01 +00001687 NULL, PJ_FALSE, NULL);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001688 if (status != PJ_SUCCESS)
1689 goto on_error;
1690
1691 /* Init transport media */
Nanang Izzuddin98085612011-07-15 07:41:02 +00001692 if (call_med->tp && call_med->tp_st == PJSUA_MED_TP_IDLE) {
1693 status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1694 NULL, call_med->idx);
1695 if (status != PJ_SUCCESS)
1696 goto on_error;
1697 }
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001698
Nanang Izzuddin98085612011-07-15 07:41:02 +00001699 sdp_m = sdp->media[med_idx];
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001700
Nanang Izzuddin98085612011-07-15 07:41:02 +00001701 /* Create new SDP media line if the stream is disabled */
1702 if (sdp->media[med_idx]->desc.port == 0) {
1703 pjmedia_transport_info tpinfo;
1704
1705 /* Get transport address info */
1706 pjmedia_transport_info_init(&tpinfo);
1707 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1708
1709 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1710 &tpinfo.sock_info, 0, &sdp_m);
1711 if (status != PJ_SUCCESS)
1712 goto on_error;
1713 }
1714
1715 {
1716 pjmedia_sdp_attr *a;
1717
1718 /* Remove any direction attributes */
1719 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
1720 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendonly");
1721 pjmedia_sdp_media_remove_all_attr(sdp_m, "recvonly");
1722 pjmedia_sdp_media_remove_all_attr(sdp_m, "inactive");
1723
1724 /* Update media direction */
1725 if (dir == PJMEDIA_DIR_ENCODING_DECODING)
1726 a = pjmedia_sdp_attr_create(pool, "sendrecv", NULL);
1727 else if (dir == PJMEDIA_DIR_ENCODING)
1728 a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
1729 else if (dir == PJMEDIA_DIR_DECODING)
1730 a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
1731 else
1732 a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
1733
1734 pjmedia_sdp_media_add_attr(sdp_m, a);
1735 }
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001736
1737 sdp->media[med_idx] = sdp_m;
1738
1739 /* Update SDP media line by media transport */
1740 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1741 sdp, NULL, call_med->idx);
1742 if (status != PJ_SUCCESS)
1743 goto on_error;
1744
1745on_error:
1746 if (status != PJ_SUCCESS) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001747 return status;
1748 }
Nanang Izzuddin98085612011-07-15 07:41:02 +00001749
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001750 } else {
Nanang Izzuddin98085612011-07-15 07:41:02 +00001751
1752 pj_pool_t *pool = call->inv->pool_prov;
1753
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001754 /* Mark media transport to disabled */
1755 // Don't close this here, as SDP negotiation has not been
1756 // done and stream may be still active.
Sauw Ming73ecfe82011-09-21 10:20:01 +00001757 set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001758
Nanang Izzuddin98085612011-07-15 07:41:02 +00001759 /* Deactivate the stream */
1760 pjmedia_sdp_media_deactivate(pool, sdp->media[med_idx]);
1761
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001762 call->opt.video_cnt--;
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001763 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001764
1765 status = call_reoffer_sdp(call->index, sdp);
1766 if (status != PJ_SUCCESS)
1767 return status;
1768
1769 return PJ_SUCCESS;
1770}
1771
1772
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001773/* Change capture device of a video stream in a call */
1774static pj_status_t call_change_cap_dev(pjsua_call *call,
1775 int med_idx,
1776 pjmedia_vid_dev_index cap_dev)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001777{
1778 pjsua_call_media *call_med;
Benny Prijono10ff56e2011-10-04 13:35:41 +00001779 pjmedia_vid_dev_stream *old_dev;
1780 pjmedia_vid_dev_switch_param switch_prm;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001781 pjmedia_vid_dev_info info;
1782 pjsua_vid_win *w, *new_w = NULL;
1783 pjsua_vid_win_id wid, new_wid;
1784 pjmedia_port *media_port;
1785 pj_status_t status;
1786
1787 /* Verify and normalize media index */
1788 if (med_idx == -1) {
1789 int first_active;
1790
1791 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1792 if (first_active == -1)
1793 return PJ_ENOTFOUND;
1794
1795 med_idx = first_active;
1796 }
1797
1798 call_med = &call->media[med_idx];
1799
1800 /* Verify if the stream media type is video */
1801 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1802 return PJ_EINVAL;
1803
1804 /* Verify the capture device */
1805 status = pjmedia_vid_dev_get_info(cap_dev, &info);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001806 if (status != PJ_SUCCESS || info.dir != PJMEDIA_DIR_CAPTURE)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001807 return PJ_EINVAL;
1808
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001809 /* The specified capture device is being used already */
1810 if (call_med->strm.v.cap_dev == cap_dev)
1811 return PJ_SUCCESS;
1812
1813 /* == Apply the new capture device == */
1814
1815 wid = call_med->strm.v.cap_win_id;
1816 w = &pjsua_var.win[wid];
1817 pj_assert(w->type == PJSUA_WND_TYPE_PREVIEW && w->vp_cap);
1818
Benny Prijono10ff56e2011-10-04 13:35:41 +00001819 /* If the old device supports fast switching, then that's excellent! */
1820 old_dev = pjmedia_vid_port_get_stream(w->vp_cap);
1821 pjmedia_vid_dev_switch_param_default(&switch_prm);
1822 switch_prm.target_id = cap_dev;
1823 status = pjmedia_vid_dev_stream_set_cap(old_dev,
1824 PJMEDIA_VID_DEV_CAP_SWITCH,
1825 &switch_prm);
1826 if (status == PJ_SUCCESS) {
1827 w->preview_cap_id = cap_dev;
Benny Prijono86e91622011-10-04 13:49:40 +00001828 call_med->strm.v.cap_dev = cap_dev;
Benny Prijono10ff56e2011-10-04 13:35:41 +00001829 return PJ_SUCCESS;
1830 }
1831
1832 /* No it doesn't support fast switching. Do slow switching then.. */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001833 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
1834 PJMEDIA_DIR_ENCODING, &media_port);
1835 if (status != PJ_SUCCESS)
1836 return status;
Benny Prijono1fe04ee2011-07-15 07:27:05 +00001837
1838 pjmedia_event_unsubscribe(&call_med->esub_cap);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001839
Benny Prijono65030232011-10-06 07:37:46 +00001840 /* temporarily disconnect while we operate on the tee. */
1841 pjmedia_vid_port_disconnect(w->vp_cap);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001842
Benny Prijono65030232011-10-06 07:37:46 +00001843 /* = Detach stream port from the old capture device's tee = */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001844 status = pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
1845 if (status != PJ_SUCCESS) {
Benny Prijono65030232011-10-06 07:37:46 +00001846 /* Something wrong, assume that media_port has been removed
1847 * and continue.
1848 */
1849 PJ_PERROR(4,(THIS_FILE, status,
1850 "Warning: call %d: unable to remove video from tee",
1851 call->index));
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001852 }
1853
Benny Prijono65030232011-10-06 07:37:46 +00001854 /* Reconnect again immediately. We're done with w->tee */
1855 pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
1856
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001857 /* = Attach stream port to the new capture device = */
1858
Benny Prijono39203b82011-09-20 10:07:55 +00001859 /* Note: calling pjsua_vid_preview_get_win() even though
1860 * create_vid_win() will automatically create the window
1861 * if it doesn't exist, because create_vid_win() will modify
1862 * existing window SHOW/HIDE value.
1863 */
1864 new_wid = vid_preview_get_win(cap_dev, PJ_FALSE);
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001865 if (new_wid == PJSUA_INVALID_ID) {
Sauw Ming0ee072f2011-10-26 09:01:53 +00001866 pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
1867
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001868 /* Create preview video window */
1869 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
1870 &media_port->info.fmt,
1871 call_med->strm.v.rdr_dev,
1872 cap_dev,
1873 PJSUA_HIDE_WINDOW,
Sauw Ming0ee072f2011-10-26 09:01:53 +00001874 acc->cfg.vid_wnd_flags,
1875 &new_wid);
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001876 if (status != PJ_SUCCESS)
1877 goto on_error;
1878 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001879
1880 inc_vid_win(new_wid);
1881 new_w = &pjsua_var.win[new_wid];
1882
1883 /* Connect stream to capturer (via video window tee) */
1884 status = pjmedia_vid_tee_add_dst_port2(new_w->tee, 0, media_port);
1885 if (status != PJ_SUCCESS)
1886 goto on_error;
1887
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001888 if (w->vp_rend) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001889 /* Start renderer */
1890 status = pjmedia_vid_port_start(new_w->vp_rend);
1891 if (status != PJ_SUCCESS)
1892 goto on_error;
1893 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001894
Nanang Izzuddinc12a19d2011-10-03 05:23:59 +00001895#if ENABLE_EVENT
1896 pjmedia_event_subscribe(
1897 pjmedia_vid_port_get_event_publisher(new_w->vp_cap),
1898 &call_med->esub_cap);
1899#endif
1900
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001901 /* Start capturer */
Benny Prijono65030232011-10-06 07:37:46 +00001902 if (!pjmedia_vid_port_is_running(new_w->vp_cap)) {
1903 status = pjmedia_vid_port_start(new_w->vp_cap);
1904 if (status != PJ_SUCCESS)
1905 goto on_error;
1906 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001907
1908 /* Finally */
1909 call_med->strm.v.cap_dev = cap_dev;
1910 call_med->strm.v.cap_win_id = new_wid;
1911 dec_vid_win(wid);
1912
1913 return PJ_SUCCESS;
1914
1915on_error:
Benny Prijono65030232011-10-06 07:37:46 +00001916 PJ_PERROR(4,(THIS_FILE, status,
1917 "Call %d: error changing capture device to %d",
1918 call->index, cap_dev));
1919
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001920 if (new_w) {
Benny Prijono65030232011-10-06 07:37:46 +00001921 /* Unsubscribe, just in case */
1922 pjmedia_event_unsubscribe(&call_med->esub_cap);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001923 /* Disconnect media port from the new capturer */
1924 pjmedia_vid_tee_remove_dst_port(new_w->tee, media_port);
1925 /* Release the new capturer */
1926 dec_vid_win(new_wid);
1927 }
1928
1929 /* Revert back to the old capturer */
Benny Prijono65030232011-10-06 07:37:46 +00001930 pjmedia_vid_port_disconnect(w->vp_cap);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001931 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
Benny Prijono65030232011-10-06 07:37:46 +00001932 pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001933 if (status != PJ_SUCCESS)
1934 return status;
1935
Benny Prijono65030232011-10-06 07:37:46 +00001936#if ENABLE_EVENT
1937 /* Resubscribe */
1938 pjmedia_event_subscribe(
1939 pjmedia_vid_port_get_event_publisher(w->vp_cap),
1940 &call_med->esub_cap);
1941#endif
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001942
1943 return status;
1944}
1945
1946
Nanang Izzuddin98085612011-07-15 07:41:02 +00001947/* Start/stop transmitting video stream in a call */
1948static pj_status_t call_set_tx_video(pjsua_call *call,
1949 int med_idx,
1950 pj_bool_t enable)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001951{
1952 pjsua_call_media *call_med;
1953 pj_status_t status;
1954
1955 /* Verify and normalize media index */
1956 if (med_idx == -1) {
1957 int first_active;
1958
1959 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1960 if (first_active == -1)
1961 return PJ_ENOTFOUND;
1962
1963 med_idx = first_active;
1964 }
1965
1966 call_med = &call->media[med_idx];
1967
1968 /* Verify if the stream is transmitting video */
1969 if (call_med->type != PJMEDIA_TYPE_VIDEO ||
Nanang Izzuddin98085612011-07-15 07:41:02 +00001970 (enable && (call_med->dir & PJMEDIA_DIR_ENCODING) == 0))
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001971 {
1972 return PJ_EINVAL;
1973 }
1974
Nanang Izzuddin98085612011-07-15 07:41:02 +00001975 if (enable) {
1976 /* Start stream in encoding direction */
1977 status = pjmedia_vid_stream_resume(call_med->strm.v.stream,
1978 PJMEDIA_DIR_ENCODING);
1979 } else {
1980 /* Pause stream in encoding direction */
1981 status = pjmedia_vid_stream_pause( call_med->strm.v.stream,
1982 PJMEDIA_DIR_ENCODING);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001983 }
1984
Nanang Izzuddin98085612011-07-15 07:41:02 +00001985 return status;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001986}
1987
1988
1989/*
1990 * Start, stop, and/or manipulate video transmission for the specified call.
1991 */
1992PJ_DEF(pj_status_t) pjsua_call_set_vid_strm (
1993 pjsua_call_id call_id,
1994 pjsua_call_vid_strm_op op,
1995 const pjsua_call_vid_strm_op_param *param)
1996{
1997 pjsua_call *call;
1998 pjsua_call_vid_strm_op_param param_;
1999 pj_status_t status;
2000
2001 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2002 PJ_EINVAL);
Benny Prijonoe212bc12011-08-15 09:38:42 +00002003 PJ_ASSERT_RETURN(op != PJSUA_CALL_VID_STRM_NO_OP, PJ_EINVAL);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002004
Benny Prijonob90fd382011-09-18 14:59:56 +00002005 PJ_LOG(4,(THIS_FILE, "Call %d: set video stream, op=%d",
2006 call_id, op));
2007 pj_log_push_indent();
2008
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002009 PJSUA_LOCK();
2010
2011 call = &pjsua_var.calls[call_id];
2012
2013 if (param) {
2014 param_ = *param;
2015 } else {
Benny Prijonoe212bc12011-08-15 09:38:42 +00002016 pjsua_call_vid_strm_op_param_default(&param_);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002017 }
2018
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00002019 /* If set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
2020 * account default video capture device.
2021 */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002022 if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00002023 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
2024 param_.cap_dev = acc_cfg->vid_cap_dev;
2025
2026 /* If the account default video capture device is
2027 * PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
2028 * global default video capture device.
2029 */
2030 if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
2031 pjmedia_vid_dev_info info;
2032 pjmedia_vid_dev_get_info(param_.cap_dev, &info);
2033 pj_assert(info.dir == PJMEDIA_DIR_CAPTURE);
2034 param_.cap_dev = info.id;
2035 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002036 }
2037
2038 switch (op) {
2039 case PJSUA_CALL_VID_STRM_ADD:
Nanang Izzuddin98085612011-07-15 07:41:02 +00002040 status = call_add_video(call, param_.cap_dev, param_.dir);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002041 break;
Nanang Izzuddin98085612011-07-15 07:41:02 +00002042 case PJSUA_CALL_VID_STRM_REMOVE:
2043 status = call_modify_video(call, param_.med_idx, PJMEDIA_DIR_NONE,
2044 PJ_TRUE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002045 break;
Nanang Izzuddin98085612011-07-15 07:41:02 +00002046 case PJSUA_CALL_VID_STRM_CHANGE_DIR:
2047 status = call_modify_video(call, param_.med_idx, param_.dir, PJ_FALSE);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00002048 break;
2049 case PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV:
2050 status = call_change_cap_dev(call, param_.med_idx, param_.cap_dev);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002051 break;
2052 case PJSUA_CALL_VID_STRM_START_TRANSMIT:
Nanang Izzuddin98085612011-07-15 07:41:02 +00002053 status = call_set_tx_video(call, param_.med_idx, PJ_TRUE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002054 break;
2055 case PJSUA_CALL_VID_STRM_STOP_TRANSMIT:
Nanang Izzuddin98085612011-07-15 07:41:02 +00002056 status = call_set_tx_video(call, param_.med_idx, PJ_FALSE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002057 break;
2058 default:
2059 status = PJ_EINVALIDOP;
2060 break;
2061 }
2062
2063 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00002064 pj_log_pop_indent();
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002065
2066 return status;
2067}
2068
Benny Prijono9f468d12011-07-07 07:46:33 +00002069
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00002070/*
2071 * Get the media stream index of the default video stream in the call.
2072 */
2073PJ_DEF(int) pjsua_call_get_vid_stream_idx(pjsua_call_id call_id)
2074{
2075 pjsua_call *call;
2076 int first_active, first_inactive;
2077
2078 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2079 PJ_EINVAL);
2080
2081 PJSUA_LOCK();
2082 call = &pjsua_var.calls[call_id];
2083 call_get_vid_strm_info(call, &first_active, &first_inactive, NULL, NULL);
2084 PJSUA_UNLOCK();
2085
2086 if (first_active == -1)
2087 return first_inactive;
2088
2089 return first_active;
2090}
2091
2092
Benny Prijono83a9eaf2011-10-04 08:23:07 +00002093/*
2094 * Determine if video stream for the specified call is currently running
2095 * for the specified direction.
2096 */
2097PJ_DEF(pj_bool_t) pjsua_call_vid_stream_is_running( pjsua_call_id call_id,
2098 int med_idx,
2099 pjmedia_dir dir)
2100{
2101 pjsua_call *call;
2102 pjsua_call_media *call_med;
2103
2104 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2105 PJ_EINVAL);
2106
2107 /* Verify and normalize media index */
2108 if (med_idx == -1) {
2109 med_idx = pjsua_call_get_vid_stream_idx(call_id);
2110 }
2111
2112 call = &pjsua_var.calls[call_id];
Nanang Izzuddina5964e72011-10-06 05:39:18 +00002113 PJ_ASSERT_RETURN(med_idx >= 0 && med_idx < (int)call->med_cnt, PJ_EINVAL);
Benny Prijono83a9eaf2011-10-04 08:23:07 +00002114
2115 call_med = &call->media[med_idx];
2116
2117 /* Verify if the stream is transmitting video */
2118 if (call_med->type != PJMEDIA_TYPE_VIDEO || (call_med->dir & dir) == 0 ||
2119 !call_med->strm.v.stream)
2120 {
2121 return PJ_FALSE;
2122 }
2123
2124 return pjmedia_vid_stream_is_running(call_med->strm.v.stream, dir);
2125}
2126
Benny Prijono9f468d12011-07-07 07:46:33 +00002127#endif /* PJSUA_HAS_VIDEO */
2128