blob: fe9c59251d71060f89c22fccdb3e358df888a2a6 [file] [log] [blame]
Benny Prijono9f468d12011-07-07 07:46:33 +00001/* $Id$ */
2/*
3 * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjsua-lib/pjsua.h>
20#include <pjsua-lib/pjsua_internal.h>
21
22#define THIS_FILE "pjsua_vid.c"
23
24#if PJSUA_HAS_VIDEO
25
Benny Prijonoaa15fbb2011-09-19 08:26:35 +000026#define ENABLE_EVENT 0
27#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) {
227 if (info[i].has_rtp_pack) {
228 pj_bzero(&id[j], sizeof(pjsua_codec_info));
229
230 pjmedia_vid_codec_info_to_id(&info[i], id[j].buf_, sizeof(id[j].buf_));
231 id[j].codec_id = pj_str(id[j].buf_);
232 id[j].priority = (pj_uint8_t) prio[i];
233
234 if (id[j].codec_id.slen < sizeof(id[j].buf_)) {
235 id[j].desc.ptr = id[j].codec_id.ptr + id[j].codec_id.slen + 1;
236 pj_strncpy(&id[j].desc, &info[i].encoding_desc,
237 sizeof(id[j].buf_) - id[j].codec_id.slen - 1);
238 }
239
240 ++j;
241 }
242 }
243
244 *p_count = j;
245
246 return PJ_SUCCESS;
247}
248
249
250/*
251 * Change video codec priority.
252 */
253PJ_DEF(pj_status_t) pjsua_vid_codec_set_priority( const pj_str_t *codec_id,
254 pj_uint8_t priority )
255{
256 const pj_str_t all = { NULL, 0 };
257
258 if (codec_id->slen==1 && *codec_id->ptr=='*')
259 codec_id = &all;
260
261 return pjmedia_vid_codec_mgr_set_codec_priority(NULL, codec_id,
262 priority);
263}
264
265
266/*
267 * Get video codec parameters.
268 */
269PJ_DEF(pj_status_t) pjsua_vid_codec_get_param(
270 const pj_str_t *codec_id,
271 pjmedia_vid_codec_param *param)
272{
273 const pj_str_t all = { NULL, 0 };
274 const pjmedia_vid_codec_info *info;
275 unsigned count = 1;
276 pj_status_t status;
277
278 if (codec_id->slen==1 && *codec_id->ptr=='*')
279 codec_id = &all;
280
281 status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, codec_id,
282 &count, &info, NULL);
283 if (status != PJ_SUCCESS)
284 return status;
285
286 if (count != 1)
287 return (count > 1? PJ_ETOOMANY : PJ_ENOTFOUND);
288
289 status = pjmedia_vid_codec_mgr_get_default_param(NULL, info, param);
290 return status;
291}
292
293
294/*
295 * Set video codec parameters.
296 */
297PJ_DEF(pj_status_t) pjsua_vid_codec_set_param(
298 const pj_str_t *codec_id,
299 const pjmedia_vid_codec_param *param)
300{
301 const pjmedia_vid_codec_info *info[2];
302 unsigned count = 2;
303 pj_status_t status;
304
305 status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, codec_id,
306 &count, info, NULL);
307 if (status != PJ_SUCCESS)
308 return status;
309
310 /* Codec ID should be specific */
311 if (count > 1) {
312 pj_assert(!"Codec ID is not specific");
313 return PJ_ETOOMANY;
314 }
315
316 status = pjmedia_vid_codec_mgr_set_default_param(NULL, pjsua_var.pool,
317 info[0], param);
318 return status;
319}
320
321
322/*****************************************************************************
323 * Preview
324 */
325
Benny Prijono39203b82011-09-20 10:07:55 +0000326static pjsua_vid_win_id vid_preview_get_win(pjmedia_vid_dev_index id,
327 pj_bool_t running_only)
Benny Prijono9f468d12011-07-07 07:46:33 +0000328{
329 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
330 unsigned i;
331
332 PJSUA_LOCK();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000333
334 /* Get real capture ID, if set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV */
335 if (id == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
336 pjmedia_vid_dev_info info;
337 pjmedia_vid_dev_get_info(id, &info);
338 id = info.id;
339 }
340
Benny Prijono9f468d12011-07-07 07:46:33 +0000341 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
342 pjsua_vid_win *w = &pjsua_var.win[i];
343 if (w->type == PJSUA_WND_TYPE_PREVIEW && w->preview_cap_id == id) {
344 wid = i;
345 break;
346 }
347 }
Benny Prijono39203b82011-09-20 10:07:55 +0000348
349 if (wid != PJSUA_INVALID_ID && running_only) {
350 pjsua_vid_win *w = &pjsua_var.win[wid];
351 wid = w->preview_running ? wid : PJSUA_INVALID_ID;
352 }
353
Benny Prijono9f468d12011-07-07 07:46:33 +0000354 PJSUA_UNLOCK();
355
356 return wid;
357}
358
Benny Prijono39203b82011-09-20 10:07:55 +0000359/*
360 * NOTE: internal function don't use this!!! Use vid_preview_get_win()
361 * instead. This is because this function will only return window ID
362 * if preview is currently running.
363 */
364PJ_DEF(pjsua_vid_win_id) pjsua_vid_preview_get_win(pjmedia_vid_dev_index id)
365{
366 return vid_preview_get_win(id, PJ_TRUE);
367}
368
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000369PJ_DEF(void) pjsua_vid_win_reset(pjsua_vid_win_id wid)
370{
371 pjsua_vid_win *w = &pjsua_var.win[wid];
372 pj_pool_t *pool = w->pool;
373
374 pj_bzero(w, sizeof(*w));
375 if (pool) pj_pool_reset(pool);
376 w->ref_cnt = 0;
377 w->pool = pool;
378 w->preview_cap_id = PJMEDIA_VID_INVALID_DEV;
379}
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000380
381/* Allocate and initialize pjsua video window:
382 * - If the type is preview, video capture, tee, and render
383 * will be instantiated.
384 * - If the type is stream, only renderer will be created.
385 */
386static pj_status_t create_vid_win(pjsua_vid_win_type type,
387 const pjmedia_format *fmt,
388 pjmedia_vid_dev_index rend_id,
389 pjmedia_vid_dev_index cap_id,
390 pj_bool_t show,
391 pjsua_vid_win_id *id)
Benny Prijono9f468d12011-07-07 07:46:33 +0000392{
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000393 pj_bool_t enable_native_preview;
Benny Prijono9f468d12011-07-07 07:46:33 +0000394 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000395 pjsua_vid_win *w = NULL;
396 pjmedia_vid_port_param vp_param;
397 pjmedia_format fmt_;
398 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000399 unsigned i;
400
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000401 enable_native_preview = pjsua_var.media_cfg.vid_preview_enable_native;
402
403 PJ_LOG(4,(THIS_FILE,
404 "Creating video window: type=%s, cap_id=%d, rend_id=%d",
405 pjsua_vid_win_type_name(type), cap_id, rend_id));
Benny Prijonob90fd382011-09-18 14:59:56 +0000406 pj_log_push_indent();
407
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000408 /* If type is preview, check if it exists already */
409 if (type == PJSUA_WND_TYPE_PREVIEW) {
Benny Prijono39203b82011-09-20 10:07:55 +0000410 wid = vid_preview_get_win(cap_id, PJ_FALSE);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000411 if (wid != PJSUA_INVALID_ID) {
412 /* Yes, it exists */
Benny Prijono2047bd72011-08-25 11:59:39 +0000413 /* Show/hide window */
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000414 pjmedia_vid_dev_stream *strm;
Benny Prijono2047bd72011-08-25 11:59:39 +0000415 pj_bool_t hide = !show;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000416
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000417 w = &pjsua_var.win[wid];
418
419 PJ_LOG(4,(THIS_FILE,
420 "Window already exists for cap_dev=%d, returning wid=%d",
421 cap_id, wid));
422
423
424 if (w->is_native) {
425 strm = pjmedia_vid_port_get_stream(w->vp_cap);
426 } else {
427 strm = pjmedia_vid_port_get_stream(w->vp_rend);
428 }
429
430 pj_assert(strm);
Benny Prijono2047bd72011-08-25 11:59:39 +0000431 status = pjmedia_vid_dev_stream_set_cap(
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000432 strm, PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE,
Benny Prijono2047bd72011-08-25 11:59:39 +0000433 &hide);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000434
435 /* Done */
436 *id = wid;
Benny Prijonob90fd382011-09-18 14:59:56 +0000437 pj_log_pop_indent();
Benny Prijono39203b82011-09-20 10:07:55 +0000438
439 return status;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000440 }
441 }
442
443 /* Allocate window */
Benny Prijono9f468d12011-07-07 07:46:33 +0000444 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000445 w = &pjsua_var.win[i];
Benny Prijono9f468d12011-07-07 07:46:33 +0000446 if (w->type == PJSUA_WND_TYPE_NONE) {
447 wid = i;
448 w->type = type;
449 break;
450 }
451 }
Benny Prijonob90fd382011-09-18 14:59:56 +0000452 if (i == PJSUA_MAX_VID_WINS) {
453 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000454 return PJ_ETOOMANY;
Benny Prijonob90fd382011-09-18 14:59:56 +0000455 }
Benny Prijono9f468d12011-07-07 07:46:33 +0000456
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000457 /* Initialize window */
458 pjmedia_vid_port_param_default(&vp_param);
459
460 if (w->type == PJSUA_WND_TYPE_PREVIEW) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000461 pjmedia_vid_dev_info vdi;
462
463 /*
464 * Determine if the device supports native preview.
465 */
466 status = pjmedia_vid_dev_get_info(cap_id, &vdi);
467 if (status != PJ_SUCCESS)
468 goto on_error;
469
470 if (enable_native_preview &&
471 (vdi.caps & PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW))
472 {
473 /* Device supports native preview! */
474 w->is_native = PJ_TRUE;
475 }
476
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000477 status = pjmedia_vid_dev_default_param(w->pool, cap_id,
478 &vp_param.vidparam);
479 if (status != PJ_SUCCESS)
480 goto on_error;
481
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000482 if (w->is_native) {
483 vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
484 vp_param.vidparam.window_hide = !show;
485 }
486
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000487 /* Normalize capture ID, in case it was set to
488 * PJMEDIA_VID_DEFAULT_CAPTURE_DEV
489 */
490 cap_id = vp_param.vidparam.cap_id;
491
492 /* Assign preview capture device ID */
493 w->preview_cap_id = cap_id;
494
495 /* Create capture video port */
496 vp_param.active = PJ_TRUE;
497 vp_param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
498 if (fmt)
499 vp_param.vidparam.fmt = *fmt;
500
501 status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_cap);
502 if (status != PJ_SUCCESS)
503 goto on_error;
504
505 /* Update format info */
506 fmt_ = vp_param.vidparam.fmt;
507 fmt = &fmt_;
508
509 /* Create video tee */
Nanang Izzuddin15ad7f22011-08-26 04:19:04 +0000510 status = pjmedia_vid_tee_create(w->pool, fmt, VID_TEE_MAX_PORT,
511 &w->tee);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000512 if (status != PJ_SUCCESS)
513 goto on_error;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000514
515 /* If device supports native preview, enable it */
516 if (w->is_native) {
517 pjmedia_vid_dev_stream *cap_dev;
518 pj_bool_t enabled = PJ_TRUE;
519
520 cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
521 status = pjmedia_vid_dev_stream_set_cap(
522 cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
523 &enabled);
524 if (status != PJ_SUCCESS) {
525 PJ_PERROR(1,(THIS_FILE, status,
526 "Error activating native preview, falling back "
527 "to software preview.."));
528 w->is_native = PJ_FALSE;
529 }
530 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000531 }
532
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000533 /* Create renderer video port, only if it's not a native preview */
534 if (!w->is_native) {
535 status = pjmedia_vid_dev_default_param(w->pool, rend_id,
536 &vp_param.vidparam);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000537 if (status != PJ_SUCCESS)
538 goto on_error;
539
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000540 vp_param.active = (w->type == PJSUA_WND_TYPE_STREAM);
541 vp_param.vidparam.dir = PJMEDIA_DIR_RENDER;
542 vp_param.vidparam.fmt = *fmt;
543 vp_param.vidparam.disp_size = fmt->det.vid.size;
544 vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
545 vp_param.vidparam.window_hide = !show;
546
547 status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_rend);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000548 if (status != PJ_SUCCESS)
549 goto on_error;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000550
551 /* For preview window, connect capturer & renderer (via tee) */
552 if (w->type == PJSUA_WND_TYPE_PREVIEW) {
553 pjmedia_port *rend_port;
554
555 status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
556 if (status != PJ_SUCCESS)
557 goto on_error;
558
559 rend_port = pjmedia_vid_port_get_passive_port(w->vp_rend);
560 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, rend_port);
561 if (status != PJ_SUCCESS)
562 goto on_error;
563 }
564
565 PJ_LOG(4,(THIS_FILE,
566 "%s window id %d created for cap_dev=%d rend_dev=%d",
567 pjsua_vid_win_type_name(type), wid, cap_id, rend_id));
568 } else {
569 PJ_LOG(4,(THIS_FILE,
570 "Preview window id %d created for cap_dev %d, "
571 "using built-in preview!",
572 wid, cap_id));
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000573 }
574
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000575
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000576 /* Done */
577 *id = wid;
578
Benny Prijonob90fd382011-09-18 14:59:56 +0000579 PJ_LOG(4,(THIS_FILE, "Window %d created", wid));
580 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000581 return PJ_SUCCESS;
582
583on_error:
584 free_vid_win(wid);
Benny Prijonob90fd382011-09-18 14:59:56 +0000585 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000586 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000587}
588
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000589
Benny Prijono9f468d12011-07-07 07:46:33 +0000590static void free_vid_win(pjsua_vid_win_id wid)
591{
592 pjsua_vid_win *w = &pjsua_var.win[wid];
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000593
Benny Prijonob90fd382011-09-18 14:59:56 +0000594 PJ_LOG(4,(THIS_FILE, "Window %d: destroying..", wid));
595 pj_log_push_indent();
596
Benny Prijono9f468d12011-07-07 07:46:33 +0000597 if (w->vp_cap) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000598 pjmedia_vid_port_stop(w->vp_cap);
599 pjmedia_vid_port_disconnect(w->vp_cap);
Benny Prijono9f468d12011-07-07 07:46:33 +0000600 pjmedia_vid_port_destroy(w->vp_cap);
601 }
602 if (w->vp_rend) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000603 pjmedia_vid_port_stop(w->vp_rend);
Benny Prijono9f468d12011-07-07 07:46:33 +0000604 pjmedia_vid_port_destroy(w->vp_rend);
605 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000606 if (w->tee) {
607 pjmedia_port_destroy(w->tee);
608 }
Benny Prijono9f468d12011-07-07 07:46:33 +0000609 pjsua_vid_win_reset(wid);
Benny Prijonob90fd382011-09-18 14:59:56 +0000610
611 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +0000612}
613
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000614
615static void inc_vid_win(pjsua_vid_win_id wid)
616{
617 pjsua_vid_win *w;
618
619 pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
620
621 w = &pjsua_var.win[wid];
622 pj_assert(w->type != PJSUA_WND_TYPE_NONE);
623 ++w->ref_cnt;
624}
625
626static void dec_vid_win(pjsua_vid_win_id wid)
627{
628 pjsua_vid_win *w;
629
630 pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
631
632 w = &pjsua_var.win[wid];
633 pj_assert(w->type != PJSUA_WND_TYPE_NONE);
634 if (--w->ref_cnt == 0)
635 free_vid_win(wid);
636}
637
638
639/* Internal function: update video channel after SDP negotiation */
640pj_status_t video_channel_update(pjsua_call_media *call_med,
641 pj_pool_t *tmp_pool,
642 const pjmedia_sdp_session *local_sdp,
643 const pjmedia_sdp_session *remote_sdp)
644{
645 pjsua_call *call = call_med->call;
646 pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
647 pjmedia_vid_stream_info the_si, *si = &the_si;
648 pjmedia_port *media_port;
649 unsigned strm_idx = call_med->idx;
650 pj_status_t status;
651
Benny Prijonob90fd382011-09-18 14:59:56 +0000652 PJ_LOG(4,(THIS_FILE, "Video channel update.."));
653 pj_log_push_indent();
654
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000655 status = pjmedia_vid_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt,
656 local_sdp, remote_sdp, strm_idx);
657 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000658 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000659
660 /* Check if no media is active */
661 if (si->dir == PJMEDIA_DIR_NONE) {
662 /* Call media state */
663 call_med->state = PJSUA_CALL_MEDIA_NONE;
664
665 /* Call media direction */
666 call_med->dir = PJMEDIA_DIR_NONE;
667
668 } else {
669 pjmedia_transport_info tp_info;
670
671 /* Start/restart media transport */
672 status = pjmedia_transport_media_start(call_med->tp,
673 tmp_pool, local_sdp,
674 remote_sdp, strm_idx);
675 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000676 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000677
Sauw Ming73ecfe82011-09-21 10:20:01 +0000678 set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000679
680 /* Get remote SRTP usage policy */
681 pjmedia_transport_info_init(&tp_info);
682 pjmedia_transport_get_info(call_med->tp, &tp_info);
683 if (tp_info.specific_info_cnt > 0) {
684 unsigned i;
685 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
686 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
687 {
688 pjmedia_srtp_info *srtp_info =
689 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
690
691 call_med->rem_srtp_use = srtp_info->peer_use;
692 break;
693 }
694 }
695 }
696
697 /* Optionally, application may modify other stream settings here
698 * (such as jitter buffer parameters, codec ptime, etc.)
699 */
700 si->jb_init = pjsua_var.media_cfg.jb_init;
701 si->jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
702 si->jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
703 si->jb_max = pjsua_var.media_cfg.jb_max;
704
705 /* Set SSRC */
706 si->ssrc = call_med->ssrc;
707
708 /* Set RTP timestamp & sequence, normally these value are intialized
709 * automatically when stream session created, but for some cases (e.g:
710 * call reinvite, call update) timestamp and sequence need to be kept
711 * contigue.
712 */
713 si->rtp_ts = call_med->rtp_tx_ts;
714 si->rtp_seq = call_med->rtp_tx_seq;
715 si->rtp_seq_ts_set = call_med->rtp_tx_seq_ts_set;
716
717#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
718 /* Enable/disable stream keep-alive and NAT hole punch. */
719 si->use_ka = pjsua_var.acc[call->acc_id].cfg.use_stream_ka;
720#endif
721
722 /* Try to get shared format ID between the capture device and
723 * the encoder to avoid format conversion in the capture device.
724 */
725 if (si->dir & PJMEDIA_DIR_ENCODING) {
726 pjmedia_vid_dev_info dev_info;
727 pjmedia_vid_codec_info *codec_info = &si->codec_info;
728 unsigned i, j;
729
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +0000730 status = pjmedia_vid_dev_get_info(call_med->strm.v.cap_dev,
731 &dev_info);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000732 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000733 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000734
735 /* Find matched format ID */
736 for (i = 0; i < codec_info->dec_fmt_id_cnt; ++i) {
737 for (j = 0; j < dev_info.fmt_cnt; ++j) {
738 if (codec_info->dec_fmt_id[i] ==
739 (pjmedia_format_id)dev_info.fmt[j].id)
740 {
741 /* Apply the matched format ID to the codec */
742 si->codec_param->dec_fmt.id =
743 codec_info->dec_fmt_id[i];
744
745 /* Force outer loop to break */
746 i = codec_info->dec_fmt_id_cnt;
747 break;
748 }
749 }
750 }
751 }
752
753 /* Create session based on session info. */
754 status = pjmedia_vid_stream_create(pjsua_var.med_endpt, NULL, si,
755 call_med->tp, NULL,
756 &call_med->strm.v.stream);
757 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000758 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000759
760 /* Start stream */
761 status = pjmedia_vid_stream_start(call_med->strm.v.stream);
762 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000763 goto on_error;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000764
765 /* Setup decoding direction */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000766 if (si->dir & PJMEDIA_DIR_DECODING)
767 {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000768 pjsua_vid_win_id wid;
769 pjsua_vid_win *w;
770
Benny Prijonob90fd382011-09-18 14:59:56 +0000771 PJ_LOG(4,(THIS_FILE, "Setting up RX.."));
772 pj_log_push_indent();
773
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000774 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
775 PJMEDIA_DIR_DECODING,
776 &media_port);
Benny Prijonob90fd382011-09-18 14:59:56 +0000777 if (status != PJ_SUCCESS) {
778 pj_log_pop_indent();
779 goto on_error;
780 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000781
782 /* Create stream video window */
783 status = create_vid_win(PJSUA_WND_TYPE_STREAM,
784 &media_port->info.fmt,
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000785 call_med->strm.v.rdr_dev,
786 //acc->cfg.vid_rend_dev,
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000787 PJSUA_INVALID_ID,
788 acc->cfg.vid_in_auto_show,
789 &wid);
Benny Prijonob90fd382011-09-18 14:59:56 +0000790 if (status != PJ_SUCCESS) {
791 pj_log_pop_indent();
792 goto on_error;
793 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000794
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000795 w = &pjsua_var.win[wid];
796
Benny Prijono6565b582011-08-29 09:54:02 +0000797#if ENABLE_EVENT
Benny Prijonoee0ba182011-07-15 06:18:29 +0000798 /* Register to video events */
799 pjmedia_event_subscribe(
800 pjmedia_vid_port_get_event_publisher(w->vp_rend),
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000801 &call_med->esub_rend);
Benny Prijono6565b582011-08-29 09:54:02 +0000802#endif
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000803
804 /* Connect renderer to stream */
805 status = pjmedia_vid_port_connect(w->vp_rend, media_port,
806 PJ_FALSE);
Benny Prijonob90fd382011-09-18 14:59:56 +0000807 if (status != PJ_SUCCESS) {
808 pj_log_pop_indent();
809 goto on_error;
810 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000811
812 /* Start renderer */
813 status = pjmedia_vid_port_start(w->vp_rend);
Benny Prijonob90fd382011-09-18 14:59:56 +0000814 if (status != PJ_SUCCESS) {
815 pj_log_pop_indent();
816 goto on_error;
817 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000818
819 /* Done */
820 inc_vid_win(wid);
821 call_med->strm.v.rdr_win_id = wid;
Benny Prijonob90fd382011-09-18 14:59:56 +0000822 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000823 }
824
825 /* Setup encoding direction */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000826 if (si->dir & PJMEDIA_DIR_ENCODING && !call->local_hold)
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000827 {
828 pjsua_vid_win *w;
829 pjsua_vid_win_id wid;
830
Benny Prijonob90fd382011-09-18 14:59:56 +0000831 PJ_LOG(4,(THIS_FILE, "Setting up TX.."));
832 pj_log_push_indent();
833
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000834 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
835 PJMEDIA_DIR_ENCODING,
836 &media_port);
Benny Prijonob90fd382011-09-18 14:59:56 +0000837 if (status != PJ_SUCCESS) {
838 pj_log_pop_indent();
839 goto on_error;
840 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000841
Benny Prijono39203b82011-09-20 10:07:55 +0000842 /* Note: calling pjsua_vid_preview_get_win() even though
843 * create_vid_win() will automatically create the window
844 * if it doesn't exist, because create_vid_win() will modify
845 * existing window SHOW/HIDE value.
846 */
847 wid = vid_preview_get_win(call_med->strm.v.cap_dev, PJ_FALSE);
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000848 if (wid == PJSUA_INVALID_ID) {
849 /* Create preview video window */
850 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
851 &media_port->info.fmt,
852 call_med->strm.v.rdr_dev,
853 call_med->strm.v.cap_dev,
854 //acc->cfg.vid_rend_dev,
855 //acc->cfg.vid_cap_dev,
856 PJSUA_HIDE_WINDOW,
857 &wid);
858 if (status != PJ_SUCCESS) {
Benny Prijonob90fd382011-09-18 14:59:56 +0000859 pj_log_pop_indent();
Benny Prijonoaa15fbb2011-09-19 08:26:35 +0000860 return status;
861 }
Benny Prijonob90fd382011-09-18 14:59:56 +0000862 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000863
864 w = &pjsua_var.win[wid];
Benny Prijono6565b582011-08-29 09:54:02 +0000865#if ENABLE_EVENT
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000866 pjmedia_event_subscribe(
867 pjmedia_vid_port_get_event_publisher(w->vp_cap),
868 &call_med->esub_cap);
Benny Prijono6565b582011-08-29 09:54:02 +0000869#endif
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000870
871 /* Connect stream to capturer (via video window tee) */
872 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
Benny Prijonob90fd382011-09-18 14:59:56 +0000873 if (status != PJ_SUCCESS) {
874 pj_log_pop_indent();
875 goto on_error;
876 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000877
878 /* Start renderer */
879 status = pjmedia_vid_port_start(w->vp_rend);
Benny Prijonob90fd382011-09-18 14:59:56 +0000880 if (status != PJ_SUCCESS) {
881 pj_log_pop_indent();
882 goto on_error;
883 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000884
885 /* Start capturer */
886 status = pjmedia_vid_port_start(w->vp_cap);
Benny Prijonob90fd382011-09-18 14:59:56 +0000887 if (status != PJ_SUCCESS) {
888 pj_log_pop_indent();
889 goto on_error;
890 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000891
892 /* Done */
893 inc_vid_win(wid);
894 call_med->strm.v.cap_win_id = wid;
Benny Prijonob90fd382011-09-18 14:59:56 +0000895 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000896 }
897
898 /* Call media direction */
899 call_med->dir = si->dir;
900
901 /* Call media state */
902 if (call->local_hold)
903 call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
904 else if (call_med->dir == PJMEDIA_DIR_DECODING)
905 call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
906 else
907 call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
908 }
909
910 /* Print info. */
911 {
912 char info[80];
913 int info_len = 0;
914 int len;
915 const char *dir;
916
917 switch (si->dir) {
918 case PJMEDIA_DIR_NONE:
919 dir = "inactive";
920 break;
921 case PJMEDIA_DIR_ENCODING:
922 dir = "sendonly";
923 break;
924 case PJMEDIA_DIR_DECODING:
925 dir = "recvonly";
926 break;
927 case PJMEDIA_DIR_ENCODING_DECODING:
928 dir = "sendrecv";
929 break;
930 default:
931 dir = "unknown";
932 break;
933 }
934 len = pj_ansi_sprintf( info+info_len,
935 ", stream #%d: %.*s (%s)", strm_idx,
936 (int)si->codec_info.encoding_name.slen,
937 si->codec_info.encoding_name.ptr,
938 dir);
939 if (len > 0)
940 info_len += len;
Benny Prijonob90fd382011-09-18 14:59:56 +0000941 PJ_LOG(4,(THIS_FILE,"Video updated%s", info));
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000942 }
943
Sauw Mingc7bc3aa2011-07-15 07:22:49 +0000944 if (!acc->cfg.vid_out_auto_transmit && call_med->strm.v.stream) {
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000945 status = pjmedia_vid_stream_pause(call_med->strm.v.stream,
946 PJMEDIA_DIR_ENCODING);
947 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000948 goto on_error;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000949 }
950
Benny Prijonob90fd382011-09-18 14:59:56 +0000951 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000952 return PJ_SUCCESS;
Benny Prijonob90fd382011-09-18 14:59:56 +0000953
954on_error:
955 pj_log_pop_indent();
956 return status;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000957}
958
959
960/* Internal function to stop video stream */
961void stop_video_stream(pjsua_call_media *call_med)
962{
963 pjmedia_vid_stream *strm = call_med->strm.v.stream;
964 pjmedia_rtcp_stat stat;
965
966 pj_assert(call_med->type == PJMEDIA_TYPE_VIDEO);
967
968 if (!strm)
969 return;
970
Benny Prijonob90fd382011-09-18 14:59:56 +0000971 PJ_LOG(4,(THIS_FILE, "Stopping video stream.."));
972 pj_log_push_indent();
973
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000974 /* Unsubscribe events */
975 pjmedia_event_unsubscribe(&call_med->esub_rend);
976 pjmedia_event_unsubscribe(&call_med->esub_cap);
977
Benny Prijonoa0dbe052011-08-29 04:16:14 +0000978 if (call_med->strm.v.cap_win_id != PJSUA_INVALID_ID) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000979 pjmedia_port *media_port;
980 pjsua_vid_win *w =
981 &pjsua_var.win[call_med->strm.v.cap_win_id];
Nanang Izzuddinca356182011-09-23 04:00:27 +0000982 pj_status_t status;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000983
Nanang Izzuddinca356182011-09-23 04:00:27 +0000984 /* Disconnect video stream from capture device */
985 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
986 PJMEDIA_DIR_ENCODING,
987 &media_port);
988 if (status == PJ_SUCCESS) {
989 /* Video tee is not threadsafe, so stop the capture first */
990 pjmedia_vid_port_stop(w->vp_cap);
991 pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
992 pjmedia_vid_port_start(w->vp_cap);
993 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000994
995 dec_vid_win(call_med->strm.v.cap_win_id);
Nanang Izzuddinca356182011-09-23 04:00:27 +0000996 call_med->strm.v.cap_win_id = PJSUA_INVALID_ID;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000997 }
998
Benny Prijonoa0dbe052011-08-29 04:16:14 +0000999 if (call_med->strm.v.rdr_win_id != PJSUA_INVALID_ID) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001000 dec_vid_win(call_med->strm.v.rdr_win_id);
Nanang Izzuddinca356182011-09-23 04:00:27 +00001001 call_med->strm.v.rdr_win_id = PJSUA_INVALID_ID;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001002 }
1003
1004 if ((call_med->dir & PJMEDIA_DIR_ENCODING) &&
1005 (pjmedia_vid_stream_get_stat(strm, &stat) == PJ_SUCCESS))
1006 {
1007 /* Save RTP timestamp & sequence, so when media session is
1008 * restarted, those values will be restored as the initial
1009 * RTP timestamp & sequence of the new media session. So in
1010 * the same call session, RTP timestamp and sequence are
1011 * guaranteed to be contigue.
1012 */
1013 call_med->rtp_tx_seq_ts_set = 1 | (1 << 1);
1014 call_med->rtp_tx_seq = stat.rtp_tx_last_seq;
1015 call_med->rtp_tx_ts = stat.rtp_tx_last_ts;
1016 }
1017
1018 pjmedia_vid_stream_destroy(strm);
1019 call_med->strm.v.stream = NULL;
Benny Prijonob90fd382011-09-18 14:59:56 +00001020
1021 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001022}
1023
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001024/*
1025 * Does it have built-in preview support.
1026 */
1027PJ_DEF(pj_bool_t) pjsua_vid_preview_has_native(pjmedia_vid_dev_index id)
1028{
1029 pjmedia_vid_dev_info vdi;
1030
1031 return (pjmedia_vid_dev_get_info(id, &vdi)==PJ_SUCCESS) ?
1032 ((vdi.caps & PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW)!=0) : PJ_FALSE;
1033}
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001034
Benny Prijono9f468d12011-07-07 07:46:33 +00001035/*
1036 * Start video preview window for the specified capture device.
1037 */
1038PJ_DEF(pj_status_t) pjsua_vid_preview_start(pjmedia_vid_dev_index id,
Benny Prijono2047bd72011-08-25 11:59:39 +00001039 const pjsua_vid_preview_param *prm)
Benny Prijono9f468d12011-07-07 07:46:33 +00001040{
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001041 pjsua_vid_win_id wid;
1042 pjsua_vid_win *w;
1043 pjmedia_vid_dev_index rend_id;
Benny Prijono2047bd72011-08-25 11:59:39 +00001044 pjsua_vid_preview_param default_param;
Benny Prijono9f468d12011-07-07 07:46:33 +00001045 pj_status_t status;
1046
Benny Prijono2047bd72011-08-25 11:59:39 +00001047 if (!prm) {
1048 pjsua_vid_preview_param_default(&default_param);
1049 prm = &default_param;
Benny Prijono9f468d12011-07-07 07:46:33 +00001050 }
1051
Benny Prijonob90fd382011-09-18 14:59:56 +00001052 PJ_LOG(4,(THIS_FILE, "Starting preview for cap_dev=%d, show=%d",
1053 id, prm->show));
1054 pj_log_push_indent();
1055
1056 PJSUA_LOCK();
1057
Benny Prijono2047bd72011-08-25 11:59:39 +00001058 rend_id = prm->rend_id;
1059
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001060 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW, NULL, rend_id, id,
Benny Prijono2047bd72011-08-25 11:59:39 +00001061 prm->show, &wid);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001062 if (status != PJ_SUCCESS) {
Benny Prijono9f468d12011-07-07 07:46:33 +00001063 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001064 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001065 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001066 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001067
Benny Prijono9f468d12011-07-07 07:46:33 +00001068 w = &pjsua_var.win[wid];
Benny Prijono39203b82011-09-20 10:07:55 +00001069 if (w->preview_running) {
1070 PJSUA_UNLOCK();
1071 pj_log_pop_indent();
1072 return PJ_SUCCESS;
1073 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001074
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001075 /* Start renderer, unless it's native preview */
Benny Prijono39203b82011-09-20 10:07:55 +00001076 if (w->is_native && !pjmedia_vid_port_is_running(w->vp_cap)) {
1077 pjmedia_vid_dev_stream *cap_dev;
1078 pj_bool_t enabled = PJ_TRUE;
1079
1080 cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
1081 status = pjmedia_vid_dev_stream_set_cap(
1082 cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
1083 &enabled);
1084 if (status != PJ_SUCCESS) {
1085 PJ_PERROR(1,(THIS_FILE, status,
1086 "Error activating native preview, falling back "
1087 "to software preview.."));
1088 w->is_native = PJ_FALSE;
1089 }
1090 }
1091
1092 if (!w->is_native && !pjmedia_vid_port_is_running(w->vp_rend)) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001093 status = pjmedia_vid_port_start(w->vp_rend);
1094 if (status != PJ_SUCCESS) {
1095 PJSUA_UNLOCK();
1096 pj_log_pop_indent();
1097 return status;
1098 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001099 }
1100
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001101 /* Start capturer */
Benny Prijono39203b82011-09-20 10:07:55 +00001102 if (!pjmedia_vid_port_is_running(w->vp_cap)) {
1103 status = pjmedia_vid_port_start(w->vp_cap);
1104 if (status != PJ_SUCCESS) {
1105 PJSUA_UNLOCK();
1106 pj_log_pop_indent();
1107 return status;
1108 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001109 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001110
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001111 inc_vid_win(wid);
Benny Prijono39203b82011-09-20 10:07:55 +00001112 w->preview_running = PJ_TRUE;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001113
Benny Prijono9f468d12011-07-07 07:46:33 +00001114 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001115 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +00001116 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +00001117}
1118
1119/*
1120 * Stop video preview.
1121 */
1122PJ_DEF(pj_status_t) pjsua_vid_preview_stop(pjmedia_vid_dev_index id)
1123{
1124 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
Benny Prijono39203b82011-09-20 10:07:55 +00001125 pjsua_vid_win *w;
1126 pj_status_t status;
Benny Prijonob90fd382011-09-18 14:59:56 +00001127
Benny Prijono9f468d12011-07-07 07:46:33 +00001128 PJSUA_LOCK();
1129 wid = pjsua_vid_preview_get_win(id);
1130 if (wid == PJSUA_INVALID_ID) {
1131 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001132 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +00001133 return PJ_ENOTFOUND;
1134 }
1135
Benny Prijono39203b82011-09-20 10:07:55 +00001136 PJ_LOG(4,(THIS_FILE, "Stopping preview for cap_dev=%d", id));
1137 pj_log_push_indent();
1138
1139 w = &pjsua_var.win[wid];
1140 if (w->preview_running) {
1141 if (w->is_native) {
1142 pjmedia_vid_dev_stream *cap_dev;
1143 pj_bool_t enabled = PJ_FALSE;
1144
1145 cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
1146 status = pjmedia_vid_dev_stream_set_cap(
1147 cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
1148 &enabled);
1149 } else {
1150 status = pjmedia_vid_port_stop(w->vp_rend);
1151 }
1152
1153 if (status != PJ_SUCCESS) {
1154 PJ_PERROR(1,(THIS_FILE, status, "Error stopping %spreview",
1155 (w->is_native ? "native " : "")));
1156 PJSUA_UNLOCK();
1157 pj_log_pop_indent();
1158 return status;
1159 }
1160
1161 dec_vid_win(wid);
1162 w->preview_running = PJ_FALSE;
1163 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001164
1165 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001166 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +00001167
1168 return PJ_SUCCESS;
1169}
1170
1171
1172/*****************************************************************************
1173 * Window
1174 */
1175
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001176
1177/*
1178 * Enumerates all video windows.
1179 */
1180PJ_DEF(pj_status_t) pjsua_vid_enum_wins( pjsua_vid_win_id wids[],
1181 unsigned *count)
1182{
1183 unsigned i, cnt;
1184
1185 cnt = 0;
1186
1187 for (i=0; i<PJSUA_MAX_VID_WINS && cnt <*count; ++i) {
1188 pjsua_vid_win *w = &pjsua_var.win[i];
1189 if (w->type != PJSUA_WND_TYPE_NONE)
1190 wids[cnt++] = i;
1191 }
1192
1193 *count = cnt;
1194
1195 return PJ_SUCCESS;
1196}
1197
1198
Benny Prijono9f468d12011-07-07 07:46:33 +00001199/*
1200 * Get window info.
1201 */
1202PJ_DEF(pj_status_t) pjsua_vid_win_get_info( pjsua_vid_win_id wid,
1203 pjsua_vid_win_info *wi)
1204{
1205 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001206 pjmedia_vid_dev_stream *s;
Sauw Ming5291a2d2011-07-15 07:52:44 +00001207 pjmedia_vid_dev_param vparam;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001208 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001209
1210 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && wi, PJ_EINVAL);
1211
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001212 pj_bzero(wi, sizeof(*wi));
1213
Benny Prijono9f468d12011-07-07 07:46:33 +00001214 PJSUA_LOCK();
1215 w = &pjsua_var.win[wid];
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001216
1217 wi->is_native = w->is_native;
1218
1219 if (w->is_native) {
1220 pjmedia_vid_dev_stream *cap_strm;
1221 pjmedia_vid_dev_cap cap = PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
1222
1223 cap_strm = pjmedia_vid_port_get_stream(w->vp_cap);
1224 if (!cap_strm) {
1225 status = PJ_EINVAL;
1226 } else {
1227 status = pjmedia_vid_dev_stream_get_cap(cap_strm, cap, &wi->hwnd);
1228 }
1229
1230 PJSUA_UNLOCK();
1231 return status;
1232 }
1233
Benny Prijono9f468d12011-07-07 07:46:33 +00001234 if (w->vp_rend == NULL) {
1235 PJSUA_UNLOCK();
1236 return PJ_EINVAL;
1237 }
1238
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001239 s = pjmedia_vid_port_get_stream(w->vp_rend);
1240 if (s == NULL) {
1241 PJSUA_UNLOCK();
1242 return PJ_EINVAL;
1243 }
1244
1245 status = pjmedia_vid_dev_stream_get_param(s, &vparam);
1246 if (status != PJ_SUCCESS) {
1247 PJSUA_UNLOCK();
1248 return status;
1249 }
1250
Nanang Izzuddindb9b0022011-07-26 08:17:25 +00001251 wi->rdr_dev = vparam.rend_id;
Nanang Izzuddin6e2fcc32011-07-22 04:49:36 +00001252 wi->hwnd = vparam.window;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001253 wi->show = !vparam.window_hide;
1254 wi->pos = vparam.window_pos;
1255 wi->size = vparam.disp_size;
1256
Benny Prijono9f468d12011-07-07 07:46:33 +00001257 PJSUA_UNLOCK();
1258
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001259 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +00001260}
1261
1262/*
1263 * Show or hide window.
1264 */
1265PJ_DEF(pj_status_t) pjsua_vid_win_set_show( pjsua_vid_win_id wid,
1266 pj_bool_t show)
1267{
1268 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001269 pjmedia_vid_dev_stream *s;
Nanang Izzuddinc16590a2011-07-14 02:14:25 +00001270 pj_bool_t hide;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001271 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001272
1273 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
1274
1275 PJSUA_LOCK();
1276 w = &pjsua_var.win[wid];
1277 if (w->vp_rend == NULL) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001278 /* Native window */
Benny Prijono9f468d12011-07-07 07:46:33 +00001279 PJSUA_UNLOCK();
1280 return PJ_EINVAL;
1281 }
1282
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001283 s = pjmedia_vid_port_get_stream(w->vp_rend);
1284 if (s == NULL) {
1285 PJSUA_UNLOCK();
1286 return PJ_EINVAL;
1287 }
1288
Nanang Izzuddinc16590a2011-07-14 02:14:25 +00001289 hide = !show;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001290 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinc16590a2011-07-14 02:14:25 +00001291 PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE, &hide);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001292
Benny Prijono9f468d12011-07-07 07:46:33 +00001293 PJSUA_UNLOCK();
1294
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001295 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001296}
1297
1298/*
1299 * Set video window position.
1300 */
1301PJ_DEF(pj_status_t) pjsua_vid_win_set_pos( pjsua_vid_win_id wid,
1302 const pjmedia_coord *pos)
1303{
1304 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001305 pjmedia_vid_dev_stream *s;
1306 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001307
1308 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && pos, PJ_EINVAL);
1309
1310 PJSUA_LOCK();
1311 w = &pjsua_var.win[wid];
1312 if (w->vp_rend == NULL) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001313 /* Native window */
Benny Prijono9f468d12011-07-07 07:46:33 +00001314 PJSUA_UNLOCK();
1315 return PJ_EINVAL;
1316 }
1317
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001318 s = pjmedia_vid_port_get_stream(w->vp_rend);
1319 if (s == NULL) {
1320 PJSUA_UNLOCK();
1321 return PJ_EINVAL;
1322 }
1323
1324 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001325 PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION, pos);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001326
Benny Prijono9f468d12011-07-07 07:46:33 +00001327 PJSUA_UNLOCK();
1328
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001329 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001330}
1331
1332/*
1333 * Resize window.
1334 */
1335PJ_DEF(pj_status_t) pjsua_vid_win_set_size( pjsua_vid_win_id wid,
1336 const pjmedia_rect_size *size)
1337{
1338 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001339 pjmedia_vid_dev_stream *s;
1340 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001341
1342 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && size, PJ_EINVAL);
1343
1344 PJSUA_LOCK();
1345 w = &pjsua_var.win[wid];
1346 if (w->vp_rend == NULL) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001347 /* Native window */
Benny Prijono9f468d12011-07-07 07:46:33 +00001348 PJSUA_UNLOCK();
1349 return PJ_EINVAL;
1350 }
1351
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001352 s = pjmedia_vid_port_get_stream(w->vp_rend);
1353 if (s == NULL) {
1354 PJSUA_UNLOCK();
1355 return PJ_EINVAL;
1356 }
1357
1358 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001359 PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE, size);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001360
Benny Prijono9f468d12011-07-07 07:46:33 +00001361 PJSUA_UNLOCK();
1362
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001363 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001364}
1365
Nanang Izzuddine9b279b2011-09-27 05:24:06 +00001366/*
1367 * Set video orientation.
1368 */
1369PJ_DEF(pj_status_t) pjsua_vid_win_rotate( pjsua_vid_win_id wid,
1370 int angle)
1371{
1372 pjsua_vid_win *w;
1373 pjmedia_vid_dev_stream *s;
1374 pjmedia_orient orient;
1375 pj_status_t status;
1376
1377 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
1378 PJ_ASSERT_RETURN((angle % 90) == 0, PJ_EINVAL);
1379
1380 /* Normalize angle, so it must be 0, 90, 180, or 270. */
1381 angle %= 360;
1382 if (angle < 0)
1383 angle += 360;
1384
1385 /* Convert angle to pjmedia_orient */
1386 switch(angle) {
1387 case 0:
1388 /* No rotation */
1389 return PJ_SUCCESS;
1390 case 90:
1391 orient = PJMEDIA_ORIENT_ROTATE_90DEG;
1392 break;
1393 case 180:
1394 orient = PJMEDIA_ORIENT_ROTATE_180DEG;
1395 break;
1396 case 270:
1397 orient = PJMEDIA_ORIENT_ROTATE_270DEG;
1398 break;
1399 default:
1400 pj_assert(!"Angle must have been validated");
1401 return PJ_EBUG;
1402 }
1403
1404 PJSUA_LOCK();
1405 w = &pjsua_var.win[wid];
1406 if (w->vp_rend == NULL) {
1407 /* Native window */
1408 PJSUA_UNLOCK();
1409 return PJ_EINVAL;
1410 }
1411
1412 s = pjmedia_vid_port_get_stream(w->vp_rend);
1413 if (s == NULL) {
1414 PJSUA_UNLOCK();
1415 return PJ_EINVAL;
1416 }
1417
1418 status = pjmedia_vid_dev_stream_set_cap(s,
1419 PJMEDIA_VID_DEV_CAP_ORIENTATION, &orient);
1420
1421 PJSUA_UNLOCK();
1422
1423 return status;
1424}
1425
Benny Prijono9f468d12011-07-07 07:46:33 +00001426
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001427static void call_get_vid_strm_info(pjsua_call *call,
1428 int *first_active,
1429 int *first_inactive,
1430 unsigned *active_cnt,
1431 unsigned *cnt)
1432{
1433 unsigned i, var_cnt = 0;
1434
1435 if (first_active && ++var_cnt)
1436 *first_active = -1;
1437 if (first_inactive && ++var_cnt)
1438 *first_inactive = -1;
1439 if (active_cnt && ++var_cnt)
1440 *active_cnt = 0;
1441 if (cnt && ++var_cnt)
1442 *cnt = 0;
1443
1444 for (i = 0; i < call->med_cnt && var_cnt; ++i) {
1445 if (call->media[i].type == PJMEDIA_TYPE_VIDEO) {
1446 if (call->media[i].dir != PJMEDIA_DIR_NONE)
1447 {
1448 if (first_active && *first_active == -1) {
1449 *first_active = i;
1450 --var_cnt;
1451 }
1452 if (active_cnt)
1453 ++(*active_cnt);
1454 } else if (first_inactive && *first_inactive == -1) {
1455 *first_inactive = i;
1456 --var_cnt;
1457 }
1458 if (cnt)
1459 ++(*cnt);
1460 }
1461 }
1462}
1463
1464
1465/* Send SDP reoffer. */
1466static pj_status_t call_reoffer_sdp(pjsua_call_id call_id,
1467 const pjmedia_sdp_session *sdp)
1468{
1469 pjsua_call *call;
1470 pjsip_tx_data *tdata;
1471 pjsip_dialog *dlg;
1472 pj_status_t status;
1473
1474 status = acquire_call("call_reoffer_sdp()", call_id, &call, &dlg);
1475 if (status != PJ_SUCCESS)
1476 return status;
1477
1478 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1479 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
1480 pjsip_dlg_dec_lock(dlg);
1481 return PJSIP_ESESSIONSTATE;
1482 }
1483
1484 /* Create re-INVITE with new offer */
1485 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1486 if (status != PJ_SUCCESS) {
1487 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
1488 pjsip_dlg_dec_lock(dlg);
1489 return status;
1490 }
1491
1492 /* Send the request */
1493 status = pjsip_inv_send_msg( call->inv, tdata);
1494 if (status != PJ_SUCCESS) {
1495 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
1496 pjsip_dlg_dec_lock(dlg);
1497 return status;
1498 }
1499
1500 pjsip_dlg_dec_lock(dlg);
1501
1502 return PJ_SUCCESS;
1503}
1504
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001505/* Add a new video stream into a call */
1506static pj_status_t call_add_video(pjsua_call *call,
Nanang Izzuddin98085612011-07-15 07:41:02 +00001507 pjmedia_vid_dev_index cap_dev,
1508 pjmedia_dir dir)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001509{
1510 pj_pool_t *pool = call->inv->pool_prov;
1511 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1512 pjsua_call_media *call_med;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001513 const pjmedia_sdp_session *current_sdp;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001514 pjmedia_sdp_session *sdp;
1515 pjmedia_sdp_media *sdp_m;
1516 pjmedia_transport_info tpinfo;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001517 unsigned active_cnt;
1518 pj_status_t status;
1519
1520 /* Verify media slot availability */
1521 if (call->med_cnt == PJSUA_MAX_CALL_MEDIA)
1522 return PJ_ETOOMANY;
1523
1524 call_get_vid_strm_info(call, NULL, NULL, &active_cnt, NULL);
1525 if (active_cnt == acc_cfg->max_video_cnt)
1526 return PJ_ETOOMANY;
1527
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001528 /* Get active local SDP and clone it */
1529 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &current_sdp);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001530 if (status != PJ_SUCCESS)
1531 return status;
1532
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001533 sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, current_sdp);
1534
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001535 /* Initialize call media */
1536 call_med = &call->media[call->med_cnt++];
1537
1538 status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1539 &acc_cfg->rtp_cfg, call->secure_level,
Sauw Ming73ecfe82011-09-21 10:20:01 +00001540 NULL, PJ_FALSE, NULL);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001541 if (status != PJ_SUCCESS)
1542 goto on_error;
1543
1544 /* Override default capture device setting */
1545 call_med->strm.v.cap_dev = cap_dev;
1546
1547 /* Init transport media */
1548 status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1549 NULL, call_med->idx);
1550 if (status != PJ_SUCCESS)
1551 goto on_error;
1552
Sauw Ming73ecfe82011-09-21 10:20:01 +00001553 set_media_tp_state(call_med, PJSUA_MED_TP_INIT);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001554
1555 /* Get transport address info */
1556 pjmedia_transport_info_init(&tpinfo);
1557 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1558
1559 /* Create SDP media line */
1560 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1561 &tpinfo.sock_info, 0, &sdp_m);
1562 if (status != PJ_SUCCESS)
1563 goto on_error;
1564
1565 sdp->media[sdp->media_count++] = sdp_m;
1566
Nanang Izzuddin98085612011-07-15 07:41:02 +00001567 /* Update media direction, if it is not 'sendrecv' */
1568 if (dir != PJMEDIA_DIR_ENCODING_DECODING) {
1569 pjmedia_sdp_attr *a;
1570
1571 /* Remove sendrecv direction attribute, if any */
1572 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
1573
1574 if (dir == PJMEDIA_DIR_ENCODING)
1575 a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
1576 else if (dir == PJMEDIA_DIR_DECODING)
1577 a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
1578 else
1579 a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
1580
1581 pjmedia_sdp_media_add_attr(sdp_m, a);
1582 }
1583
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001584 /* Update SDP media line by media transport */
1585 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1586 sdp, NULL, call_med->idx);
1587 if (status != PJ_SUCCESS)
1588 goto on_error;
1589
1590 status = call_reoffer_sdp(call->index, sdp);
1591 if (status != PJ_SUCCESS)
1592 goto on_error;
1593
1594 return PJ_SUCCESS;
1595
1596on_error:
1597 if (call_med->tp) {
1598 pjmedia_transport_close(call_med->tp);
1599 call_med->tp = call_med->tp_orig = NULL;
1600 }
1601
1602 return status;
1603}
1604
1605
Nanang Izzuddin98085612011-07-15 07:41:02 +00001606/* Modify a video stream from a call, i.e: update direction,
1607 * remove/disable.
1608 */
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001609static pj_status_t call_modify_video(pjsua_call *call,
1610 int med_idx,
Nanang Izzuddin98085612011-07-15 07:41:02 +00001611 pjmedia_dir dir,
1612 pj_bool_t remove)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001613{
1614 pjsua_call_media *call_med;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001615 const pjmedia_sdp_session *current_sdp;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001616 pjmedia_sdp_session *sdp;
1617 pj_status_t status;
1618
1619 /* Verify and normalize media index */
1620 if (med_idx == -1) {
1621 int first_active;
1622
1623 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1624 if (first_active == -1)
1625 return PJ_ENOTFOUND;
1626
1627 med_idx = first_active;
1628 }
1629
1630 call_med = &call->media[med_idx];
1631
1632 /* Verify if the stream media type is video */
1633 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1634 return PJ_EINVAL;
1635
Nanang Izzuddin98085612011-07-15 07:41:02 +00001636 /* Verify if the stream dir is not changed */
1637 if ((!remove && call_med->dir == dir) ||
1638 ( remove && (call_med->tp_st == PJSUA_MED_TP_DISABLED ||
1639 call_med->tp == NULL)))
1640 {
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001641 return PJ_SUCCESS;
Nanang Izzuddin98085612011-07-15 07:41:02 +00001642 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001643
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001644 /* Get active local SDP and clone it */
1645 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &current_sdp);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001646 if (status != PJ_SUCCESS)
1647 return status;
1648
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001649 sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, current_sdp);
1650
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001651 pj_assert(med_idx < (int)sdp->media_count);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001652
Nanang Izzuddin98085612011-07-15 07:41:02 +00001653 if (!remove) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001654 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1655 pj_pool_t *pool = call->inv->pool_prov;
1656 pjmedia_sdp_media *sdp_m;
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001657
1658 status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1659 &acc_cfg->rtp_cfg, call->secure_level,
Sauw Ming73ecfe82011-09-21 10:20:01 +00001660 NULL, PJ_FALSE, NULL);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001661 if (status != PJ_SUCCESS)
1662 goto on_error;
1663
1664 /* Init transport media */
Nanang Izzuddin98085612011-07-15 07:41:02 +00001665 if (call_med->tp && call_med->tp_st == PJSUA_MED_TP_IDLE) {
1666 status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1667 NULL, call_med->idx);
1668 if (status != PJ_SUCCESS)
1669 goto on_error;
1670 }
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001671
Nanang Izzuddin98085612011-07-15 07:41:02 +00001672 sdp_m = sdp->media[med_idx];
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001673
Nanang Izzuddin98085612011-07-15 07:41:02 +00001674 /* Create new SDP media line if the stream is disabled */
1675 if (sdp->media[med_idx]->desc.port == 0) {
1676 pjmedia_transport_info tpinfo;
1677
1678 /* Get transport address info */
1679 pjmedia_transport_info_init(&tpinfo);
1680 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1681
1682 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1683 &tpinfo.sock_info, 0, &sdp_m);
1684 if (status != PJ_SUCCESS)
1685 goto on_error;
1686 }
1687
1688 {
1689 pjmedia_sdp_attr *a;
1690
1691 /* Remove any direction attributes */
1692 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
1693 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendonly");
1694 pjmedia_sdp_media_remove_all_attr(sdp_m, "recvonly");
1695 pjmedia_sdp_media_remove_all_attr(sdp_m, "inactive");
1696
1697 /* Update media direction */
1698 if (dir == PJMEDIA_DIR_ENCODING_DECODING)
1699 a = pjmedia_sdp_attr_create(pool, "sendrecv", NULL);
1700 else if (dir == PJMEDIA_DIR_ENCODING)
1701 a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
1702 else if (dir == PJMEDIA_DIR_DECODING)
1703 a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
1704 else
1705 a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
1706
1707 pjmedia_sdp_media_add_attr(sdp_m, a);
1708 }
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001709
1710 sdp->media[med_idx] = sdp_m;
1711
1712 /* Update SDP media line by media transport */
1713 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1714 sdp, NULL, call_med->idx);
1715 if (status != PJ_SUCCESS)
1716 goto on_error;
1717
1718on_error:
1719 if (status != PJ_SUCCESS) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001720 return status;
1721 }
Nanang Izzuddin98085612011-07-15 07:41:02 +00001722
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001723 } else {
Nanang Izzuddin98085612011-07-15 07:41:02 +00001724
1725 pj_pool_t *pool = call->inv->pool_prov;
1726
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001727 /* Mark media transport to disabled */
1728 // Don't close this here, as SDP negotiation has not been
1729 // done and stream may be still active.
Sauw Ming73ecfe82011-09-21 10:20:01 +00001730 set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001731
Nanang Izzuddin98085612011-07-15 07:41:02 +00001732 /* Deactivate the stream */
1733 pjmedia_sdp_media_deactivate(pool, sdp->media[med_idx]);
1734
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001735 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001736
1737 status = call_reoffer_sdp(call->index, sdp);
1738 if (status != PJ_SUCCESS)
1739 return status;
1740
1741 return PJ_SUCCESS;
1742}
1743
1744
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001745/* Change capture device of a video stream in a call */
1746static pj_status_t call_change_cap_dev(pjsua_call *call,
1747 int med_idx,
1748 pjmedia_vid_dev_index cap_dev)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001749{
1750 pjsua_call_media *call_med;
1751 pjmedia_vid_dev_info info;
1752 pjsua_vid_win *w, *new_w = NULL;
1753 pjsua_vid_win_id wid, new_wid;
1754 pjmedia_port *media_port;
1755 pj_status_t status;
1756
1757 /* Verify and normalize media index */
1758 if (med_idx == -1) {
1759 int first_active;
1760
1761 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1762 if (first_active == -1)
1763 return PJ_ENOTFOUND;
1764
1765 med_idx = first_active;
1766 }
1767
1768 call_med = &call->media[med_idx];
1769
1770 /* Verify if the stream media type is video */
1771 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1772 return PJ_EINVAL;
1773
1774 /* Verify the capture device */
1775 status = pjmedia_vid_dev_get_info(cap_dev, &info);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001776 if (status != PJ_SUCCESS || info.dir != PJMEDIA_DIR_CAPTURE)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001777 return PJ_EINVAL;
1778
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001779 /* The specified capture device is being used already */
1780 if (call_med->strm.v.cap_dev == cap_dev)
1781 return PJ_SUCCESS;
1782
1783 /* == Apply the new capture device == */
1784
1785 wid = call_med->strm.v.cap_win_id;
1786 w = &pjsua_var.win[wid];
1787 pj_assert(w->type == PJSUA_WND_TYPE_PREVIEW && w->vp_cap);
1788
1789 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
1790 PJMEDIA_DIR_ENCODING, &media_port);
1791 if (status != PJ_SUCCESS)
1792 return status;
Benny Prijono1fe04ee2011-07-15 07:27:05 +00001793
1794 pjmedia_event_unsubscribe(&call_med->esub_cap);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001795
1796 /* = Detach stream port from the old capture device = */
1797 status = pjmedia_vid_port_disconnect(w->vp_cap);
1798 if (status != PJ_SUCCESS)
1799 return status;
1800
1801 status = pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
1802 if (status != PJ_SUCCESS) {
1803 /* Connect back the old capturer */
1804 pjmedia_vid_port_connect(w->vp_cap, media_port, PJ_FALSE);
1805 return status;
1806 }
1807
1808 /* = Attach stream port to the new capture device = */
1809
Benny Prijono39203b82011-09-20 10:07:55 +00001810 /* Note: calling pjsua_vid_preview_get_win() even though
1811 * create_vid_win() will automatically create the window
1812 * if it doesn't exist, because create_vid_win() will modify
1813 * existing window SHOW/HIDE value.
1814 */
1815 new_wid = vid_preview_get_win(cap_dev, PJ_FALSE);
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001816 if (new_wid == PJSUA_INVALID_ID) {
1817 /* Create preview video window */
1818 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
1819 &media_port->info.fmt,
1820 call_med->strm.v.rdr_dev,
1821 cap_dev,
1822 PJSUA_HIDE_WINDOW,
1823 &new_wid);
1824 if (status != PJ_SUCCESS)
1825 goto on_error;
1826 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001827
1828 inc_vid_win(new_wid);
1829 new_w = &pjsua_var.win[new_wid];
1830
1831 /* Connect stream to capturer (via video window tee) */
1832 status = pjmedia_vid_tee_add_dst_port2(new_w->tee, 0, media_port);
1833 if (status != PJ_SUCCESS)
1834 goto on_error;
1835
1836 /* Connect capturer to tee */
1837 status = pjmedia_vid_port_connect(new_w->vp_cap, new_w->tee, PJ_FALSE);
1838 if (status != PJ_SUCCESS)
1839 return status;
1840
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001841 if (w->vp_rend) {
Benny Prijono6565b582011-08-29 09:54:02 +00001842#if ENABLE_EVENT
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001843 pjmedia_event_subscribe(
1844 pjmedia_vid_port_get_event_publisher(w->vp_rend),
1845 &call_med->esub_cap);
Benny Prijono6565b582011-08-29 09:54:02 +00001846#endif
Benny Prijono1fe04ee2011-07-15 07:27:05 +00001847
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001848 /* Start renderer */
1849 status = pjmedia_vid_port_start(new_w->vp_rend);
1850 if (status != PJ_SUCCESS)
1851 goto on_error;
1852 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001853
1854 /* Start capturer */
1855 status = pjmedia_vid_port_start(new_w->vp_cap);
1856 if (status != PJ_SUCCESS)
1857 goto on_error;
1858
1859 /* Finally */
1860 call_med->strm.v.cap_dev = cap_dev;
1861 call_med->strm.v.cap_win_id = new_wid;
1862 dec_vid_win(wid);
1863
1864 return PJ_SUCCESS;
1865
1866on_error:
1867 if (new_w) {
1868 /* Disconnect media port from the new capturer */
1869 pjmedia_vid_tee_remove_dst_port(new_w->tee, media_port);
1870 /* Release the new capturer */
1871 dec_vid_win(new_wid);
1872 }
1873
1874 /* Revert back to the old capturer */
1875 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
1876 if (status != PJ_SUCCESS)
1877 return status;
1878
1879 status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
1880 if (status != PJ_SUCCESS)
1881 return status;
1882
1883 return status;
1884}
1885
1886
Nanang Izzuddin98085612011-07-15 07:41:02 +00001887/* Start/stop transmitting video stream in a call */
1888static pj_status_t call_set_tx_video(pjsua_call *call,
1889 int med_idx,
1890 pj_bool_t enable)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001891{
1892 pjsua_call_media *call_med;
1893 pj_status_t status;
1894
1895 /* Verify and normalize media index */
1896 if (med_idx == -1) {
1897 int first_active;
1898
1899 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1900 if (first_active == -1)
1901 return PJ_ENOTFOUND;
1902
1903 med_idx = first_active;
1904 }
1905
1906 call_med = &call->media[med_idx];
1907
1908 /* Verify if the stream is transmitting video */
1909 if (call_med->type != PJMEDIA_TYPE_VIDEO ||
Nanang Izzuddin98085612011-07-15 07:41:02 +00001910 (enable && (call_med->dir & PJMEDIA_DIR_ENCODING) == 0))
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001911 {
1912 return PJ_EINVAL;
1913 }
1914
Nanang Izzuddin98085612011-07-15 07:41:02 +00001915 if (enable) {
1916 /* Start stream in encoding direction */
1917 status = pjmedia_vid_stream_resume(call_med->strm.v.stream,
1918 PJMEDIA_DIR_ENCODING);
1919 } else {
1920 /* Pause stream in encoding direction */
1921 status = pjmedia_vid_stream_pause( call_med->strm.v.stream,
1922 PJMEDIA_DIR_ENCODING);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001923 }
1924
Nanang Izzuddin98085612011-07-15 07:41:02 +00001925 return status;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001926}
1927
1928
1929/*
1930 * Start, stop, and/or manipulate video transmission for the specified call.
1931 */
1932PJ_DEF(pj_status_t) pjsua_call_set_vid_strm (
1933 pjsua_call_id call_id,
1934 pjsua_call_vid_strm_op op,
1935 const pjsua_call_vid_strm_op_param *param)
1936{
1937 pjsua_call *call;
1938 pjsua_call_vid_strm_op_param param_;
1939 pj_status_t status;
1940
1941 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1942 PJ_EINVAL);
Benny Prijonoe212bc12011-08-15 09:38:42 +00001943 PJ_ASSERT_RETURN(op != PJSUA_CALL_VID_STRM_NO_OP, PJ_EINVAL);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001944
Benny Prijonob90fd382011-09-18 14:59:56 +00001945 PJ_LOG(4,(THIS_FILE, "Call %d: set video stream, op=%d",
1946 call_id, op));
1947 pj_log_push_indent();
1948
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001949 PJSUA_LOCK();
1950
1951 call = &pjsua_var.calls[call_id];
1952
1953 if (param) {
1954 param_ = *param;
1955 } else {
Benny Prijonoe212bc12011-08-15 09:38:42 +00001956 pjsua_call_vid_strm_op_param_default(&param_);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001957 }
1958
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001959 /* If set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
1960 * account default video capture device.
1961 */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001962 if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001963 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1964 param_.cap_dev = acc_cfg->vid_cap_dev;
1965
1966 /* If the account default video capture device is
1967 * PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
1968 * global default video capture device.
1969 */
1970 if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
1971 pjmedia_vid_dev_info info;
1972 pjmedia_vid_dev_get_info(param_.cap_dev, &info);
1973 pj_assert(info.dir == PJMEDIA_DIR_CAPTURE);
1974 param_.cap_dev = info.id;
1975 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001976 }
1977
1978 switch (op) {
1979 case PJSUA_CALL_VID_STRM_ADD:
Nanang Izzuddin98085612011-07-15 07:41:02 +00001980 status = call_add_video(call, param_.cap_dev, param_.dir);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001981 break;
Nanang Izzuddin98085612011-07-15 07:41:02 +00001982 case PJSUA_CALL_VID_STRM_REMOVE:
1983 status = call_modify_video(call, param_.med_idx, PJMEDIA_DIR_NONE,
1984 PJ_TRUE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001985 break;
Nanang Izzuddin98085612011-07-15 07:41:02 +00001986 case PJSUA_CALL_VID_STRM_CHANGE_DIR:
1987 status = call_modify_video(call, param_.med_idx, param_.dir, PJ_FALSE);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001988 break;
1989 case PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV:
1990 status = call_change_cap_dev(call, param_.med_idx, param_.cap_dev);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001991 break;
1992 case PJSUA_CALL_VID_STRM_START_TRANSMIT:
Nanang Izzuddin98085612011-07-15 07:41:02 +00001993 status = call_set_tx_video(call, param_.med_idx, PJ_TRUE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001994 break;
1995 case PJSUA_CALL_VID_STRM_STOP_TRANSMIT:
Nanang Izzuddin98085612011-07-15 07:41:02 +00001996 status = call_set_tx_video(call, param_.med_idx, PJ_FALSE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001997 break;
1998 default:
1999 status = PJ_EINVALIDOP;
2000 break;
2001 }
2002
2003 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00002004 pj_log_pop_indent();
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00002005
2006 return status;
2007}
2008
Benny Prijono9f468d12011-07-07 07:46:33 +00002009
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00002010/*
2011 * Get the media stream index of the default video stream in the call.
2012 */
2013PJ_DEF(int) pjsua_call_get_vid_stream_idx(pjsua_call_id call_id)
2014{
2015 pjsua_call *call;
2016 int first_active, first_inactive;
2017
2018 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2019 PJ_EINVAL);
2020
2021 PJSUA_LOCK();
2022 call = &pjsua_var.calls[call_id];
2023 call_get_vid_strm_info(call, &first_active, &first_inactive, NULL, NULL);
2024 PJSUA_UNLOCK();
2025
2026 if (first_active == -1)
2027 return first_inactive;
2028
2029 return first_active;
2030}
2031
2032
Benny Prijono9f468d12011-07-07 07:46:33 +00002033#endif /* PJSUA_HAS_VIDEO */
2034