blob: 55f892ec49202baf7abe19c343db089944ae5570 [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
678 call_med->tp_st = PJSUA_MED_TP_RUNNING;
679
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];
982
983 pjmedia_vid_stream_get_port(call_med->strm.v.stream,
984 PJMEDIA_DIR_ENCODING,
985 &media_port);
986 pj_assert(media_port);
987
988 pjmedia_vid_port_stop(w->vp_cap);
989 pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
990 pjmedia_vid_port_start(w->vp_cap);
991
992 dec_vid_win(call_med->strm.v.cap_win_id);
993 }
994
Benny Prijonoa0dbe052011-08-29 04:16:14 +0000995 if (call_med->strm.v.rdr_win_id != PJSUA_INVALID_ID) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000996 dec_vid_win(call_med->strm.v.rdr_win_id);
997 }
998
999 if ((call_med->dir & PJMEDIA_DIR_ENCODING) &&
1000 (pjmedia_vid_stream_get_stat(strm, &stat) == PJ_SUCCESS))
1001 {
1002 /* Save RTP timestamp & sequence, so when media session is
1003 * restarted, those values will be restored as the initial
1004 * RTP timestamp & sequence of the new media session. So in
1005 * the same call session, RTP timestamp and sequence are
1006 * guaranteed to be contigue.
1007 */
1008 call_med->rtp_tx_seq_ts_set = 1 | (1 << 1);
1009 call_med->rtp_tx_seq = stat.rtp_tx_last_seq;
1010 call_med->rtp_tx_ts = stat.rtp_tx_last_ts;
1011 }
1012
1013 pjmedia_vid_stream_destroy(strm);
1014 call_med->strm.v.stream = NULL;
Benny Prijonob90fd382011-09-18 14:59:56 +00001015
1016 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001017}
1018
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001019/*
1020 * Does it have built-in preview support.
1021 */
1022PJ_DEF(pj_bool_t) pjsua_vid_preview_has_native(pjmedia_vid_dev_index id)
1023{
1024 pjmedia_vid_dev_info vdi;
1025
1026 return (pjmedia_vid_dev_get_info(id, &vdi)==PJ_SUCCESS) ?
1027 ((vdi.caps & PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW)!=0) : PJ_FALSE;
1028}
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001029
Benny Prijono9f468d12011-07-07 07:46:33 +00001030/*
1031 * Start video preview window for the specified capture device.
1032 */
1033PJ_DEF(pj_status_t) pjsua_vid_preview_start(pjmedia_vid_dev_index id,
Benny Prijono2047bd72011-08-25 11:59:39 +00001034 const pjsua_vid_preview_param *prm)
Benny Prijono9f468d12011-07-07 07:46:33 +00001035{
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001036 pjsua_vid_win_id wid;
1037 pjsua_vid_win *w;
1038 pjmedia_vid_dev_index rend_id;
Benny Prijono2047bd72011-08-25 11:59:39 +00001039 pjsua_vid_preview_param default_param;
Benny Prijono9f468d12011-07-07 07:46:33 +00001040 pj_status_t status;
1041
Benny Prijono2047bd72011-08-25 11:59:39 +00001042 if (!prm) {
1043 pjsua_vid_preview_param_default(&default_param);
1044 prm = &default_param;
Benny Prijono9f468d12011-07-07 07:46:33 +00001045 }
1046
Benny Prijonob90fd382011-09-18 14:59:56 +00001047 PJ_LOG(4,(THIS_FILE, "Starting preview for cap_dev=%d, show=%d",
1048 id, prm->show));
1049 pj_log_push_indent();
1050
1051 PJSUA_LOCK();
1052
Benny Prijono2047bd72011-08-25 11:59:39 +00001053 rend_id = prm->rend_id;
1054
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001055 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW, NULL, rend_id, id,
Benny Prijono2047bd72011-08-25 11:59:39 +00001056 prm->show, &wid);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001057 if (status != PJ_SUCCESS) {
Benny Prijono9f468d12011-07-07 07:46:33 +00001058 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001059 pj_log_pop_indent();
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001060 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001061 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001062
Benny Prijono9f468d12011-07-07 07:46:33 +00001063 w = &pjsua_var.win[wid];
Benny Prijono39203b82011-09-20 10:07:55 +00001064 if (w->preview_running) {
1065 PJSUA_UNLOCK();
1066 pj_log_pop_indent();
1067 return PJ_SUCCESS;
1068 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001069
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001070 /* Start renderer, unless it's native preview */
Benny Prijono39203b82011-09-20 10:07:55 +00001071 if (w->is_native && !pjmedia_vid_port_is_running(w->vp_cap)) {
1072 pjmedia_vid_dev_stream *cap_dev;
1073 pj_bool_t enabled = PJ_TRUE;
1074
1075 cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
1076 status = pjmedia_vid_dev_stream_set_cap(
1077 cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
1078 &enabled);
1079 if (status != PJ_SUCCESS) {
1080 PJ_PERROR(1,(THIS_FILE, status,
1081 "Error activating native preview, falling back "
1082 "to software preview.."));
1083 w->is_native = PJ_FALSE;
1084 }
1085 }
1086
1087 if (!w->is_native && !pjmedia_vid_port_is_running(w->vp_rend)) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001088 status = pjmedia_vid_port_start(w->vp_rend);
1089 if (status != PJ_SUCCESS) {
1090 PJSUA_UNLOCK();
1091 pj_log_pop_indent();
1092 return status;
1093 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001094 }
1095
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001096 /* Start capturer */
Benny Prijono39203b82011-09-20 10:07:55 +00001097 if (!pjmedia_vid_port_is_running(w->vp_cap)) {
1098 status = pjmedia_vid_port_start(w->vp_cap);
1099 if (status != PJ_SUCCESS) {
1100 PJSUA_UNLOCK();
1101 pj_log_pop_indent();
1102 return status;
1103 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001104 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001105
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001106 inc_vid_win(wid);
Benny Prijono39203b82011-09-20 10:07:55 +00001107 w->preview_running = PJ_TRUE;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001108
Benny Prijono9f468d12011-07-07 07:46:33 +00001109 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001110 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +00001111 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +00001112}
1113
1114/*
1115 * Stop video preview.
1116 */
1117PJ_DEF(pj_status_t) pjsua_vid_preview_stop(pjmedia_vid_dev_index id)
1118{
1119 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
Benny Prijono39203b82011-09-20 10:07:55 +00001120 pjsua_vid_win *w;
1121 pj_status_t status;
Benny Prijonob90fd382011-09-18 14:59:56 +00001122
Benny Prijono9f468d12011-07-07 07:46:33 +00001123 PJSUA_LOCK();
1124 wid = pjsua_vid_preview_get_win(id);
1125 if (wid == PJSUA_INVALID_ID) {
1126 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001127 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +00001128 return PJ_ENOTFOUND;
1129 }
1130
Benny Prijono39203b82011-09-20 10:07:55 +00001131 PJ_LOG(4,(THIS_FILE, "Stopping preview for cap_dev=%d", id));
1132 pj_log_push_indent();
1133
1134 w = &pjsua_var.win[wid];
1135 if (w->preview_running) {
1136 if (w->is_native) {
1137 pjmedia_vid_dev_stream *cap_dev;
1138 pj_bool_t enabled = PJ_FALSE;
1139
1140 cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
1141 status = pjmedia_vid_dev_stream_set_cap(
1142 cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
1143 &enabled);
1144 } else {
1145 status = pjmedia_vid_port_stop(w->vp_rend);
1146 }
1147
1148 if (status != PJ_SUCCESS) {
1149 PJ_PERROR(1,(THIS_FILE, status, "Error stopping %spreview",
1150 (w->is_native ? "native " : "")));
1151 PJSUA_UNLOCK();
1152 pj_log_pop_indent();
1153 return status;
1154 }
1155
1156 dec_vid_win(wid);
1157 w->preview_running = PJ_FALSE;
1158 }
Benny Prijono9f468d12011-07-07 07:46:33 +00001159
1160 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001161 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +00001162
1163 return PJ_SUCCESS;
1164}
1165
1166
1167/*****************************************************************************
1168 * Window
1169 */
1170
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001171
1172/*
1173 * Enumerates all video windows.
1174 */
1175PJ_DEF(pj_status_t) pjsua_vid_enum_wins( pjsua_vid_win_id wids[],
1176 unsigned *count)
1177{
1178 unsigned i, cnt;
1179
1180 cnt = 0;
1181
1182 for (i=0; i<PJSUA_MAX_VID_WINS && cnt <*count; ++i) {
1183 pjsua_vid_win *w = &pjsua_var.win[i];
1184 if (w->type != PJSUA_WND_TYPE_NONE)
1185 wids[cnt++] = i;
1186 }
1187
1188 *count = cnt;
1189
1190 return PJ_SUCCESS;
1191}
1192
1193
Benny Prijono9f468d12011-07-07 07:46:33 +00001194/*
1195 * Get window info.
1196 */
1197PJ_DEF(pj_status_t) pjsua_vid_win_get_info( pjsua_vid_win_id wid,
1198 pjsua_vid_win_info *wi)
1199{
1200 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001201 pjmedia_vid_dev_stream *s;
Sauw Ming5291a2d2011-07-15 07:52:44 +00001202 pjmedia_vid_dev_param vparam;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001203 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001204
1205 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && wi, PJ_EINVAL);
1206
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001207 pj_bzero(wi, sizeof(*wi));
1208
Benny Prijono9f468d12011-07-07 07:46:33 +00001209 PJSUA_LOCK();
1210 w = &pjsua_var.win[wid];
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001211
1212 wi->is_native = w->is_native;
1213
1214 if (w->is_native) {
1215 pjmedia_vid_dev_stream *cap_strm;
1216 pjmedia_vid_dev_cap cap = PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
1217
1218 cap_strm = pjmedia_vid_port_get_stream(w->vp_cap);
1219 if (!cap_strm) {
1220 status = PJ_EINVAL;
1221 } else {
1222 status = pjmedia_vid_dev_stream_get_cap(cap_strm, cap, &wi->hwnd);
1223 }
1224
1225 PJSUA_UNLOCK();
1226 return status;
1227 }
1228
Benny Prijono9f468d12011-07-07 07:46:33 +00001229 if (w->vp_rend == NULL) {
1230 PJSUA_UNLOCK();
1231 return PJ_EINVAL;
1232 }
1233
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001234 s = pjmedia_vid_port_get_stream(w->vp_rend);
1235 if (s == NULL) {
1236 PJSUA_UNLOCK();
1237 return PJ_EINVAL;
1238 }
1239
1240 status = pjmedia_vid_dev_stream_get_param(s, &vparam);
1241 if (status != PJ_SUCCESS) {
1242 PJSUA_UNLOCK();
1243 return status;
1244 }
1245
Nanang Izzuddindb9b0022011-07-26 08:17:25 +00001246 wi->rdr_dev = vparam.rend_id;
Nanang Izzuddin6e2fcc32011-07-22 04:49:36 +00001247 wi->hwnd = vparam.window;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001248 wi->show = !vparam.window_hide;
1249 wi->pos = vparam.window_pos;
1250 wi->size = vparam.disp_size;
1251
Benny Prijono9f468d12011-07-07 07:46:33 +00001252 PJSUA_UNLOCK();
1253
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001254 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +00001255}
1256
1257/*
1258 * Show or hide window.
1259 */
1260PJ_DEF(pj_status_t) pjsua_vid_win_set_show( pjsua_vid_win_id wid,
1261 pj_bool_t show)
1262{
1263 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001264 pjmedia_vid_dev_stream *s;
Nanang Izzuddinc16590a2011-07-14 02:14:25 +00001265 pj_bool_t hide;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001266 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001267
1268 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
1269
1270 PJSUA_LOCK();
1271 w = &pjsua_var.win[wid];
1272 if (w->vp_rend == NULL) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001273 /* Native window */
Benny Prijono9f468d12011-07-07 07:46:33 +00001274 PJSUA_UNLOCK();
1275 return PJ_EINVAL;
1276 }
1277
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001278 s = pjmedia_vid_port_get_stream(w->vp_rend);
1279 if (s == NULL) {
1280 PJSUA_UNLOCK();
1281 return PJ_EINVAL;
1282 }
1283
Nanang Izzuddinc16590a2011-07-14 02:14:25 +00001284 hide = !show;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001285 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinc16590a2011-07-14 02:14:25 +00001286 PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE, &hide);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001287
Benny Prijono9f468d12011-07-07 07:46:33 +00001288 PJSUA_UNLOCK();
1289
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001290 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001291}
1292
1293/*
1294 * Set video window position.
1295 */
1296PJ_DEF(pj_status_t) pjsua_vid_win_set_pos( pjsua_vid_win_id wid,
1297 const pjmedia_coord *pos)
1298{
1299 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001300 pjmedia_vid_dev_stream *s;
1301 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001302
1303 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && pos, PJ_EINVAL);
1304
1305 PJSUA_LOCK();
1306 w = &pjsua_var.win[wid];
1307 if (w->vp_rend == NULL) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001308 /* Native window */
Benny Prijono9f468d12011-07-07 07:46:33 +00001309 PJSUA_UNLOCK();
1310 return PJ_EINVAL;
1311 }
1312
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001313 s = pjmedia_vid_port_get_stream(w->vp_rend);
1314 if (s == NULL) {
1315 PJSUA_UNLOCK();
1316 return PJ_EINVAL;
1317 }
1318
1319 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001320 PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION, pos);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001321
Benny Prijono9f468d12011-07-07 07:46:33 +00001322 PJSUA_UNLOCK();
1323
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001324 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001325}
1326
1327/*
1328 * Resize window.
1329 */
1330PJ_DEF(pj_status_t) pjsua_vid_win_set_size( pjsua_vid_win_id wid,
1331 const pjmedia_rect_size *size)
1332{
1333 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001334 pjmedia_vid_dev_stream *s;
1335 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001336
1337 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && size, PJ_EINVAL);
1338
1339 PJSUA_LOCK();
1340 w = &pjsua_var.win[wid];
1341 if (w->vp_rend == NULL) {
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001342 /* Native window */
Benny Prijono9f468d12011-07-07 07:46:33 +00001343 PJSUA_UNLOCK();
1344 return PJ_EINVAL;
1345 }
1346
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001347 s = pjmedia_vid_port_get_stream(w->vp_rend);
1348 if (s == NULL) {
1349 PJSUA_UNLOCK();
1350 return PJ_EINVAL;
1351 }
1352
1353 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001354 PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE, size);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001355
Benny Prijono9f468d12011-07-07 07:46:33 +00001356 PJSUA_UNLOCK();
1357
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001358 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001359}
1360
1361
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001362static void call_get_vid_strm_info(pjsua_call *call,
1363 int *first_active,
1364 int *first_inactive,
1365 unsigned *active_cnt,
1366 unsigned *cnt)
1367{
1368 unsigned i, var_cnt = 0;
1369
1370 if (first_active && ++var_cnt)
1371 *first_active = -1;
1372 if (first_inactive && ++var_cnt)
1373 *first_inactive = -1;
1374 if (active_cnt && ++var_cnt)
1375 *active_cnt = 0;
1376 if (cnt && ++var_cnt)
1377 *cnt = 0;
1378
1379 for (i = 0; i < call->med_cnt && var_cnt; ++i) {
1380 if (call->media[i].type == PJMEDIA_TYPE_VIDEO) {
1381 if (call->media[i].dir != PJMEDIA_DIR_NONE)
1382 {
1383 if (first_active && *first_active == -1) {
1384 *first_active = i;
1385 --var_cnt;
1386 }
1387 if (active_cnt)
1388 ++(*active_cnt);
1389 } else if (first_inactive && *first_inactive == -1) {
1390 *first_inactive = i;
1391 --var_cnt;
1392 }
1393 if (cnt)
1394 ++(*cnt);
1395 }
1396 }
1397}
1398
1399
1400/* Send SDP reoffer. */
1401static pj_status_t call_reoffer_sdp(pjsua_call_id call_id,
1402 const pjmedia_sdp_session *sdp)
1403{
1404 pjsua_call *call;
1405 pjsip_tx_data *tdata;
1406 pjsip_dialog *dlg;
1407 pj_status_t status;
1408
1409 status = acquire_call("call_reoffer_sdp()", call_id, &call, &dlg);
1410 if (status != PJ_SUCCESS)
1411 return status;
1412
1413 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1414 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
1415 pjsip_dlg_dec_lock(dlg);
1416 return PJSIP_ESESSIONSTATE;
1417 }
1418
1419 /* Create re-INVITE with new offer */
1420 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1421 if (status != PJ_SUCCESS) {
1422 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
1423 pjsip_dlg_dec_lock(dlg);
1424 return status;
1425 }
1426
1427 /* Send the request */
1428 status = pjsip_inv_send_msg( call->inv, tdata);
1429 if (status != PJ_SUCCESS) {
1430 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
1431 pjsip_dlg_dec_lock(dlg);
1432 return status;
1433 }
1434
1435 pjsip_dlg_dec_lock(dlg);
1436
1437 return PJ_SUCCESS;
1438}
1439
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001440/* Add a new video stream into a call */
1441static pj_status_t call_add_video(pjsua_call *call,
Nanang Izzuddin98085612011-07-15 07:41:02 +00001442 pjmedia_vid_dev_index cap_dev,
1443 pjmedia_dir dir)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001444{
1445 pj_pool_t *pool = call->inv->pool_prov;
1446 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1447 pjsua_call_media *call_med;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001448 const pjmedia_sdp_session *current_sdp;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001449 pjmedia_sdp_session *sdp;
1450 pjmedia_sdp_media *sdp_m;
1451 pjmedia_transport_info tpinfo;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001452 unsigned active_cnt;
1453 pj_status_t status;
1454
1455 /* Verify media slot availability */
1456 if (call->med_cnt == PJSUA_MAX_CALL_MEDIA)
1457 return PJ_ETOOMANY;
1458
1459 call_get_vid_strm_info(call, NULL, NULL, &active_cnt, NULL);
1460 if (active_cnt == acc_cfg->max_video_cnt)
1461 return PJ_ETOOMANY;
1462
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001463 /* Get active local SDP and clone it */
1464 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &current_sdp);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001465 if (status != PJ_SUCCESS)
1466 return status;
1467
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001468 sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, current_sdp);
1469
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001470 /* Initialize call media */
1471 call_med = &call->media[call->med_cnt++];
1472
1473 status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1474 &acc_cfg->rtp_cfg, call->secure_level,
1475 NULL);
1476 if (status != PJ_SUCCESS)
1477 goto on_error;
1478
1479 /* Override default capture device setting */
1480 call_med->strm.v.cap_dev = cap_dev;
1481
1482 /* Init transport media */
1483 status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1484 NULL, call_med->idx);
1485 if (status != PJ_SUCCESS)
1486 goto on_error;
1487
1488 call_med->tp_st = PJSUA_MED_TP_INIT;
1489
1490 /* Get transport address info */
1491 pjmedia_transport_info_init(&tpinfo);
1492 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1493
1494 /* Create SDP media line */
1495 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1496 &tpinfo.sock_info, 0, &sdp_m);
1497 if (status != PJ_SUCCESS)
1498 goto on_error;
1499
1500 sdp->media[sdp->media_count++] = sdp_m;
1501
Nanang Izzuddin98085612011-07-15 07:41:02 +00001502 /* Update media direction, if it is not 'sendrecv' */
1503 if (dir != PJMEDIA_DIR_ENCODING_DECODING) {
1504 pjmedia_sdp_attr *a;
1505
1506 /* Remove sendrecv direction attribute, if any */
1507 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
1508
1509 if (dir == PJMEDIA_DIR_ENCODING)
1510 a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
1511 else if (dir == PJMEDIA_DIR_DECODING)
1512 a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
1513 else
1514 a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
1515
1516 pjmedia_sdp_media_add_attr(sdp_m, a);
1517 }
1518
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001519 /* Update SDP media line by media transport */
1520 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1521 sdp, NULL, call_med->idx);
1522 if (status != PJ_SUCCESS)
1523 goto on_error;
1524
1525 status = call_reoffer_sdp(call->index, sdp);
1526 if (status != PJ_SUCCESS)
1527 goto on_error;
1528
1529 return PJ_SUCCESS;
1530
1531on_error:
1532 if (call_med->tp) {
1533 pjmedia_transport_close(call_med->tp);
1534 call_med->tp = call_med->tp_orig = NULL;
1535 }
1536
1537 return status;
1538}
1539
1540
Nanang Izzuddin98085612011-07-15 07:41:02 +00001541/* Modify a video stream from a call, i.e: update direction,
1542 * remove/disable.
1543 */
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001544static pj_status_t call_modify_video(pjsua_call *call,
1545 int med_idx,
Nanang Izzuddin98085612011-07-15 07:41:02 +00001546 pjmedia_dir dir,
1547 pj_bool_t remove)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001548{
1549 pjsua_call_media *call_med;
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001550 const pjmedia_sdp_session *current_sdp;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001551 pjmedia_sdp_session *sdp;
1552 pj_status_t status;
1553
1554 /* Verify and normalize media index */
1555 if (med_idx == -1) {
1556 int first_active;
1557
1558 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1559 if (first_active == -1)
1560 return PJ_ENOTFOUND;
1561
1562 med_idx = first_active;
1563 }
1564
1565 call_med = &call->media[med_idx];
1566
1567 /* Verify if the stream media type is video */
1568 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1569 return PJ_EINVAL;
1570
Nanang Izzuddin98085612011-07-15 07:41:02 +00001571 /* Verify if the stream dir is not changed */
1572 if ((!remove && call_med->dir == dir) ||
1573 ( remove && (call_med->tp_st == PJSUA_MED_TP_DISABLED ||
1574 call_med->tp == NULL)))
1575 {
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001576 return PJ_SUCCESS;
Nanang Izzuddin98085612011-07-15 07:41:02 +00001577 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001578
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001579 /* Get active local SDP and clone it */
1580 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &current_sdp);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001581 if (status != PJ_SUCCESS)
1582 return status;
1583
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001584 sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, current_sdp);
1585
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001586 pj_assert(med_idx < (int)sdp->media_count);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001587
Nanang Izzuddin98085612011-07-15 07:41:02 +00001588 if (!remove) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001589 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1590 pj_pool_t *pool = call->inv->pool_prov;
1591 pjmedia_sdp_media *sdp_m;
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001592
1593 status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1594 &acc_cfg->rtp_cfg, call->secure_level,
1595 NULL);
1596 if (status != PJ_SUCCESS)
1597 goto on_error;
1598
1599 /* Init transport media */
Nanang Izzuddin98085612011-07-15 07:41:02 +00001600 if (call_med->tp && call_med->tp_st == PJSUA_MED_TP_IDLE) {
1601 status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1602 NULL, call_med->idx);
1603 if (status != PJ_SUCCESS)
1604 goto on_error;
1605 }
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001606
Nanang Izzuddin98085612011-07-15 07:41:02 +00001607 sdp_m = sdp->media[med_idx];
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001608
Nanang Izzuddin98085612011-07-15 07:41:02 +00001609 /* Create new SDP media line if the stream is disabled */
1610 if (sdp->media[med_idx]->desc.port == 0) {
1611 pjmedia_transport_info tpinfo;
1612
1613 /* Get transport address info */
1614 pjmedia_transport_info_init(&tpinfo);
1615 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1616
1617 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1618 &tpinfo.sock_info, 0, &sdp_m);
1619 if (status != PJ_SUCCESS)
1620 goto on_error;
1621 }
1622
1623 {
1624 pjmedia_sdp_attr *a;
1625
1626 /* Remove any direction attributes */
1627 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
1628 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendonly");
1629 pjmedia_sdp_media_remove_all_attr(sdp_m, "recvonly");
1630 pjmedia_sdp_media_remove_all_attr(sdp_m, "inactive");
1631
1632 /* Update media direction */
1633 if (dir == PJMEDIA_DIR_ENCODING_DECODING)
1634 a = pjmedia_sdp_attr_create(pool, "sendrecv", NULL);
1635 else if (dir == PJMEDIA_DIR_ENCODING)
1636 a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
1637 else if (dir == PJMEDIA_DIR_DECODING)
1638 a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
1639 else
1640 a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
1641
1642 pjmedia_sdp_media_add_attr(sdp_m, a);
1643 }
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001644
1645 sdp->media[med_idx] = sdp_m;
1646
1647 /* Update SDP media line by media transport */
1648 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1649 sdp, NULL, call_med->idx);
1650 if (status != PJ_SUCCESS)
1651 goto on_error;
1652
1653on_error:
1654 if (status != PJ_SUCCESS) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001655 return status;
1656 }
Nanang Izzuddin98085612011-07-15 07:41:02 +00001657
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001658 } else {
Nanang Izzuddin98085612011-07-15 07:41:02 +00001659
1660 pj_pool_t *pool = call->inv->pool_prov;
1661
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001662 /* Mark media transport to disabled */
1663 // Don't close this here, as SDP negotiation has not been
1664 // done and stream may be still active.
1665 call_med->tp_st = PJSUA_MED_TP_DISABLED;
1666
Nanang Izzuddin98085612011-07-15 07:41:02 +00001667 /* Deactivate the stream */
1668 pjmedia_sdp_media_deactivate(pool, sdp->media[med_idx]);
1669
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001670 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001671
1672 status = call_reoffer_sdp(call->index, sdp);
1673 if (status != PJ_SUCCESS)
1674 return status;
1675
1676 return PJ_SUCCESS;
1677}
1678
1679
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001680/* Change capture device of a video stream in a call */
1681static pj_status_t call_change_cap_dev(pjsua_call *call,
1682 int med_idx,
1683 pjmedia_vid_dev_index cap_dev)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001684{
1685 pjsua_call_media *call_med;
1686 pjmedia_vid_dev_info info;
1687 pjsua_vid_win *w, *new_w = NULL;
1688 pjsua_vid_win_id wid, new_wid;
1689 pjmedia_port *media_port;
1690 pj_status_t status;
1691
1692 /* Verify and normalize media index */
1693 if (med_idx == -1) {
1694 int first_active;
1695
1696 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1697 if (first_active == -1)
1698 return PJ_ENOTFOUND;
1699
1700 med_idx = first_active;
1701 }
1702
1703 call_med = &call->media[med_idx];
1704
1705 /* Verify if the stream media type is video */
1706 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1707 return PJ_EINVAL;
1708
1709 /* Verify the capture device */
1710 status = pjmedia_vid_dev_get_info(cap_dev, &info);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001711 if (status != PJ_SUCCESS || info.dir != PJMEDIA_DIR_CAPTURE)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001712 return PJ_EINVAL;
1713
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001714 /* The specified capture device is being used already */
1715 if (call_med->strm.v.cap_dev == cap_dev)
1716 return PJ_SUCCESS;
1717
1718 /* == Apply the new capture device == */
1719
1720 wid = call_med->strm.v.cap_win_id;
1721 w = &pjsua_var.win[wid];
1722 pj_assert(w->type == PJSUA_WND_TYPE_PREVIEW && w->vp_cap);
1723
1724 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
1725 PJMEDIA_DIR_ENCODING, &media_port);
1726 if (status != PJ_SUCCESS)
1727 return status;
Benny Prijono1fe04ee2011-07-15 07:27:05 +00001728
1729 pjmedia_event_unsubscribe(&call_med->esub_cap);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001730
1731 /* = Detach stream port from the old capture device = */
1732 status = pjmedia_vid_port_disconnect(w->vp_cap);
1733 if (status != PJ_SUCCESS)
1734 return status;
1735
1736 status = pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
1737 if (status != PJ_SUCCESS) {
1738 /* Connect back the old capturer */
1739 pjmedia_vid_port_connect(w->vp_cap, media_port, PJ_FALSE);
1740 return status;
1741 }
1742
1743 /* = Attach stream port to the new capture device = */
1744
Benny Prijono39203b82011-09-20 10:07:55 +00001745 /* Note: calling pjsua_vid_preview_get_win() even though
1746 * create_vid_win() will automatically create the window
1747 * if it doesn't exist, because create_vid_win() will modify
1748 * existing window SHOW/HIDE value.
1749 */
1750 new_wid = vid_preview_get_win(cap_dev, PJ_FALSE);
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001751 if (new_wid == PJSUA_INVALID_ID) {
1752 /* Create preview video window */
1753 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
1754 &media_port->info.fmt,
1755 call_med->strm.v.rdr_dev,
1756 cap_dev,
1757 PJSUA_HIDE_WINDOW,
1758 &new_wid);
1759 if (status != PJ_SUCCESS)
1760 goto on_error;
1761 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001762
1763 inc_vid_win(new_wid);
1764 new_w = &pjsua_var.win[new_wid];
1765
1766 /* Connect stream to capturer (via video window tee) */
1767 status = pjmedia_vid_tee_add_dst_port2(new_w->tee, 0, media_port);
1768 if (status != PJ_SUCCESS)
1769 goto on_error;
1770
1771 /* Connect capturer to tee */
1772 status = pjmedia_vid_port_connect(new_w->vp_cap, new_w->tee, PJ_FALSE);
1773 if (status != PJ_SUCCESS)
1774 return status;
1775
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001776 if (w->vp_rend) {
Benny Prijono6565b582011-08-29 09:54:02 +00001777#if ENABLE_EVENT
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001778 pjmedia_event_subscribe(
1779 pjmedia_vid_port_get_event_publisher(w->vp_rend),
1780 &call_med->esub_cap);
Benny Prijono6565b582011-08-29 09:54:02 +00001781#endif
Benny Prijono1fe04ee2011-07-15 07:27:05 +00001782
Benny Prijonoaa15fbb2011-09-19 08:26:35 +00001783 /* Start renderer */
1784 status = pjmedia_vid_port_start(new_w->vp_rend);
1785 if (status != PJ_SUCCESS)
1786 goto on_error;
1787 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001788
1789 /* Start capturer */
1790 status = pjmedia_vid_port_start(new_w->vp_cap);
1791 if (status != PJ_SUCCESS)
1792 goto on_error;
1793
1794 /* Finally */
1795 call_med->strm.v.cap_dev = cap_dev;
1796 call_med->strm.v.cap_win_id = new_wid;
1797 dec_vid_win(wid);
1798
1799 return PJ_SUCCESS;
1800
1801on_error:
1802 if (new_w) {
1803 /* Disconnect media port from the new capturer */
1804 pjmedia_vid_tee_remove_dst_port(new_w->tee, media_port);
1805 /* Release the new capturer */
1806 dec_vid_win(new_wid);
1807 }
1808
1809 /* Revert back to the old capturer */
1810 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
1811 if (status != PJ_SUCCESS)
1812 return status;
1813
1814 status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
1815 if (status != PJ_SUCCESS)
1816 return status;
1817
1818 return status;
1819}
1820
1821
Nanang Izzuddin98085612011-07-15 07:41:02 +00001822/* Start/stop transmitting video stream in a call */
1823static pj_status_t call_set_tx_video(pjsua_call *call,
1824 int med_idx,
1825 pj_bool_t enable)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001826{
1827 pjsua_call_media *call_med;
1828 pj_status_t status;
1829
1830 /* Verify and normalize media index */
1831 if (med_idx == -1) {
1832 int first_active;
1833
1834 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1835 if (first_active == -1)
1836 return PJ_ENOTFOUND;
1837
1838 med_idx = first_active;
1839 }
1840
1841 call_med = &call->media[med_idx];
1842
1843 /* Verify if the stream is transmitting video */
1844 if (call_med->type != PJMEDIA_TYPE_VIDEO ||
Nanang Izzuddin98085612011-07-15 07:41:02 +00001845 (enable && (call_med->dir & PJMEDIA_DIR_ENCODING) == 0))
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001846 {
1847 return PJ_EINVAL;
1848 }
1849
Nanang Izzuddin98085612011-07-15 07:41:02 +00001850 if (enable) {
1851 /* Start stream in encoding direction */
1852 status = pjmedia_vid_stream_resume(call_med->strm.v.stream,
1853 PJMEDIA_DIR_ENCODING);
1854 } else {
1855 /* Pause stream in encoding direction */
1856 status = pjmedia_vid_stream_pause( call_med->strm.v.stream,
1857 PJMEDIA_DIR_ENCODING);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001858 }
1859
Nanang Izzuddin98085612011-07-15 07:41:02 +00001860 return status;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001861}
1862
1863
1864/*
1865 * Start, stop, and/or manipulate video transmission for the specified call.
1866 */
1867PJ_DEF(pj_status_t) pjsua_call_set_vid_strm (
1868 pjsua_call_id call_id,
1869 pjsua_call_vid_strm_op op,
1870 const pjsua_call_vid_strm_op_param *param)
1871{
1872 pjsua_call *call;
1873 pjsua_call_vid_strm_op_param param_;
1874 pj_status_t status;
1875
1876 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1877 PJ_EINVAL);
Benny Prijonoe212bc12011-08-15 09:38:42 +00001878 PJ_ASSERT_RETURN(op != PJSUA_CALL_VID_STRM_NO_OP, PJ_EINVAL);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001879
Benny Prijonob90fd382011-09-18 14:59:56 +00001880 PJ_LOG(4,(THIS_FILE, "Call %d: set video stream, op=%d",
1881 call_id, op));
1882 pj_log_push_indent();
1883
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001884 PJSUA_LOCK();
1885
1886 call = &pjsua_var.calls[call_id];
1887
1888 if (param) {
1889 param_ = *param;
1890 } else {
Benny Prijonoe212bc12011-08-15 09:38:42 +00001891 pjsua_call_vid_strm_op_param_default(&param_);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001892 }
1893
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001894 /* If set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
1895 * account default video capture device.
1896 */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001897 if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001898 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1899 param_.cap_dev = acc_cfg->vid_cap_dev;
1900
1901 /* If the account default video capture device is
1902 * PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
1903 * global default video capture device.
1904 */
1905 if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
1906 pjmedia_vid_dev_info info;
1907 pjmedia_vid_dev_get_info(param_.cap_dev, &info);
1908 pj_assert(info.dir == PJMEDIA_DIR_CAPTURE);
1909 param_.cap_dev = info.id;
1910 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001911 }
1912
1913 switch (op) {
1914 case PJSUA_CALL_VID_STRM_ADD:
Nanang Izzuddin98085612011-07-15 07:41:02 +00001915 status = call_add_video(call, param_.cap_dev, param_.dir);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001916 break;
Nanang Izzuddin98085612011-07-15 07:41:02 +00001917 case PJSUA_CALL_VID_STRM_REMOVE:
1918 status = call_modify_video(call, param_.med_idx, PJMEDIA_DIR_NONE,
1919 PJ_TRUE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001920 break;
Nanang Izzuddin98085612011-07-15 07:41:02 +00001921 case PJSUA_CALL_VID_STRM_CHANGE_DIR:
1922 status = call_modify_video(call, param_.med_idx, param_.dir, PJ_FALSE);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001923 break;
1924 case PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV:
1925 status = call_change_cap_dev(call, param_.med_idx, param_.cap_dev);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001926 break;
1927 case PJSUA_CALL_VID_STRM_START_TRANSMIT:
Nanang Izzuddin98085612011-07-15 07:41:02 +00001928 status = call_set_tx_video(call, param_.med_idx, PJ_TRUE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001929 break;
1930 case PJSUA_CALL_VID_STRM_STOP_TRANSMIT:
Nanang Izzuddin98085612011-07-15 07:41:02 +00001931 status = call_set_tx_video(call, param_.med_idx, PJ_FALSE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001932 break;
1933 default:
1934 status = PJ_EINVALIDOP;
1935 break;
1936 }
1937
1938 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001939 pj_log_pop_indent();
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001940
1941 return status;
1942}
1943
Benny Prijono9f468d12011-07-07 07:46:33 +00001944
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001945/*
1946 * Get the media stream index of the default video stream in the call.
1947 */
1948PJ_DEF(int) pjsua_call_get_vid_stream_idx(pjsua_call_id call_id)
1949{
1950 pjsua_call *call;
1951 int first_active, first_inactive;
1952
1953 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1954 PJ_EINVAL);
1955
1956 PJSUA_LOCK();
1957 call = &pjsua_var.calls[call_id];
1958 call_get_vid_strm_info(call, &first_active, &first_inactive, NULL, NULL);
1959 PJSUA_UNLOCK();
1960
1961 if (first_active == -1)
1962 return first_inactive;
1963
1964 return first_active;
1965}
1966
1967
Benny Prijono9f468d12011-07-07 07:46:33 +00001968#endif /* PJSUA_HAS_VIDEO */
1969