blob: 95d02de3121095f1715393886daf68841c42599c [file] [log] [blame]
Benny Prijono9f468d12011-07-07 07:46:33 +00001/* $Id$ */
2/*
3 * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjsua-lib/pjsua.h>
20#include <pjsua-lib/pjsua_internal.h>
21
22#define THIS_FILE "pjsua_vid.c"
23
24#if PJSUA_HAS_VIDEO
25
Nanang Izzuddin62053a62011-07-12 11:08:32 +000026static void free_vid_win(pjsua_vid_win_id wid);
27
Benny Prijono9f468d12011-07-07 07:46:33 +000028/*****************************************************************************
29 * pjsua video subsystem.
30 */
31pj_status_t pjsua_vid_subsys_init(void)
32{
33 unsigned i;
34 pj_status_t status;
35
36 status = pjmedia_video_format_mgr_create(pjsua_var.pool, 64, 0, NULL);
37 if (status != PJ_SUCCESS) {
38 PJ_PERROR(1,(THIS_FILE, status,
39 "Error creating PJMEDIA video format manager"));
40 return status;
41 }
42
43 status = pjmedia_converter_mgr_create(pjsua_var.pool, NULL);
44 if (status != PJ_SUCCESS) {
45 PJ_PERROR(1,(THIS_FILE, status,
46 "Error creating PJMEDIA converter manager"));
47 return status;
48 }
49
50 status = pjmedia_vid_codec_mgr_create(pjsua_var.pool, NULL);
51 if (status != PJ_SUCCESS) {
52 PJ_PERROR(1,(THIS_FILE, status,
53 "Error creating PJMEDIA video codec manager"));
54 return status;
55 }
56
57 status = pjmedia_vid_dev_subsys_init(&pjsua_var.cp.factory);
58 if (status != PJ_SUCCESS) {
59 PJ_PERROR(1,(THIS_FILE, status,
60 "Error creating PJMEDIA video subsystem"));
61 return status;
62 }
63
64#if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_FFMPEG_CODEC
65 status = pjmedia_codec_ffmpeg_init(NULL, &pjsua_var.cp.factory);
66 if (status != PJ_SUCCESS) {
67 PJ_PERROR(1,(THIS_FILE, status,
68 "Error initializing ffmpeg library"));
69 return status;
70 }
71#endif
72
73 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
74 if (pjsua_var.win[i].pool == NULL) {
75 pjsua_var.win[i].pool = pjsua_pool_create("win%p", 512, 512);
76 if (pjsua_var.win[i].pool == NULL)
77 return PJ_ENOMEM;
78 }
79 }
80
81 return PJ_SUCCESS;
82}
83
84pj_status_t pjsua_vid_subsys_start(void)
85{
86 return PJ_SUCCESS;
87}
88
89pj_status_t pjsua_vid_subsys_destroy(void)
90{
91 unsigned i;
92
93 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
94 if (pjsua_var.win[i].pool) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +000095 free_vid_win(i);
Benny Prijono9f468d12011-07-07 07:46:33 +000096 pj_pool_release(pjsua_var.win[i].pool);
97 pjsua_var.win[i].pool = NULL;
98 }
99 }
100
101 pjmedia_vid_dev_subsys_shutdown();
102
103#if PJMEDIA_HAS_FFMPEG_CODEC
104 pjmedia_codec_ffmpeg_deinit();
105#endif
106
107 return PJ_SUCCESS;
108}
109
110
111/*****************************************************************************
112 * Devices.
113 */
114
115/*
116 * Get the number of video devices installed in the system.
117 */
118PJ_DEF(unsigned) pjsua_vid_dev_count(void)
119{
120 return pjmedia_vid_dev_count();
121}
122
123/*
124 * Retrieve the video device info for the specified device index.
125 */
126PJ_DEF(pj_status_t) pjsua_vid_dev_get_info(pjmedia_vid_dev_index id,
127 pjmedia_vid_dev_info *vdi)
128{
129 return pjmedia_vid_dev_get_info(id, vdi);
130}
131
132/*
133 * Enum all video devices installed in the system.
134 */
135PJ_DEF(pj_status_t) pjsua_vid_enum_devs(pjmedia_vid_dev_info info[],
136 unsigned *count)
137{
138 unsigned i, dev_count;
139
140 dev_count = pjmedia_vid_dev_count();
141
142 if (dev_count > *count) dev_count = *count;
143
144 for (i=0; i<dev_count; ++i) {
145 pj_status_t status;
146
147 status = pjmedia_vid_dev_get_info(i, &info[i]);
148 if (status != PJ_SUCCESS)
149 return status;
150 }
151
152 *count = dev_count;
153
154 return PJ_SUCCESS;
155}
156
157
158/*****************************************************************************
159 * Codecs.
160 */
161
162/*
163 * Enum all supported video codecs in the system.
164 */
165PJ_DEF(pj_status_t) pjsua_vid_enum_codecs( pjsua_codec_info id[],
166 unsigned *p_count )
167{
168 pjmedia_vid_codec_info info[32];
169 unsigned i, j, count, prio[32];
170 pj_status_t status;
171
172 count = PJ_ARRAY_SIZE(info);
173 status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, info, prio);
174 if (status != PJ_SUCCESS) {
175 *p_count = 0;
176 return status;
177 }
178
179 for (i=0, j=0; i<count && j<*p_count; ++i) {
180 if (info[i].has_rtp_pack) {
181 pj_bzero(&id[j], sizeof(pjsua_codec_info));
182
183 pjmedia_vid_codec_info_to_id(&info[i], id[j].buf_, sizeof(id[j].buf_));
184 id[j].codec_id = pj_str(id[j].buf_);
185 id[j].priority = (pj_uint8_t) prio[i];
186
187 if (id[j].codec_id.slen < sizeof(id[j].buf_)) {
188 id[j].desc.ptr = id[j].codec_id.ptr + id[j].codec_id.slen + 1;
189 pj_strncpy(&id[j].desc, &info[i].encoding_desc,
190 sizeof(id[j].buf_) - id[j].codec_id.slen - 1);
191 }
192
193 ++j;
194 }
195 }
196
197 *p_count = j;
198
199 return PJ_SUCCESS;
200}
201
202
203/*
204 * Change video codec priority.
205 */
206PJ_DEF(pj_status_t) pjsua_vid_codec_set_priority( const pj_str_t *codec_id,
207 pj_uint8_t priority )
208{
209 const pj_str_t all = { NULL, 0 };
210
211 if (codec_id->slen==1 && *codec_id->ptr=='*')
212 codec_id = &all;
213
214 return pjmedia_vid_codec_mgr_set_codec_priority(NULL, codec_id,
215 priority);
216}
217
218
219/*
220 * Get video codec parameters.
221 */
222PJ_DEF(pj_status_t) pjsua_vid_codec_get_param(
223 const pj_str_t *codec_id,
224 pjmedia_vid_codec_param *param)
225{
226 const pj_str_t all = { NULL, 0 };
227 const pjmedia_vid_codec_info *info;
228 unsigned count = 1;
229 pj_status_t status;
230
231 if (codec_id->slen==1 && *codec_id->ptr=='*')
232 codec_id = &all;
233
234 status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, codec_id,
235 &count, &info, NULL);
236 if (status != PJ_SUCCESS)
237 return status;
238
239 if (count != 1)
240 return (count > 1? PJ_ETOOMANY : PJ_ENOTFOUND);
241
242 status = pjmedia_vid_codec_mgr_get_default_param(NULL, info, param);
243 return status;
244}
245
246
247/*
248 * Set video codec parameters.
249 */
250PJ_DEF(pj_status_t) pjsua_vid_codec_set_param(
251 const pj_str_t *codec_id,
252 const pjmedia_vid_codec_param *param)
253{
254 const pjmedia_vid_codec_info *info[2];
255 unsigned count = 2;
256 pj_status_t status;
257
258 status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, codec_id,
259 &count, info, NULL);
260 if (status != PJ_SUCCESS)
261 return status;
262
263 /* Codec ID should be specific */
264 if (count > 1) {
265 pj_assert(!"Codec ID is not specific");
266 return PJ_ETOOMANY;
267 }
268
269 status = pjmedia_vid_codec_mgr_set_default_param(NULL, pjsua_var.pool,
270 info[0], param);
271 return status;
272}
273
274
275/*****************************************************************************
276 * Preview
277 */
278
279/*
280 * Get the preview window handle associated with the capture device, if any.
281 */
282PJ_DEF(pjsua_vid_win_id) pjsua_vid_preview_get_win(pjmedia_vid_dev_index id)
283{
284 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
285 unsigned i;
286
287 PJSUA_LOCK();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000288
289 /* Get real capture ID, if set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV */
290 if (id == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
291 pjmedia_vid_dev_info info;
292 pjmedia_vid_dev_get_info(id, &info);
293 id = info.id;
294 }
295
Benny Prijono9f468d12011-07-07 07:46:33 +0000296 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
297 pjsua_vid_win *w = &pjsua_var.win[i];
298 if (w->type == PJSUA_WND_TYPE_PREVIEW && w->preview_cap_id == id) {
299 wid = i;
300 break;
301 }
302 }
303 PJSUA_UNLOCK();
304
305 return wid;
306}
307
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000308
309/* Allocate and initialize pjsua video window:
310 * - If the type is preview, video capture, tee, and render
311 * will be instantiated.
312 * - If the type is stream, only renderer will be created.
313 */
314static pj_status_t create_vid_win(pjsua_vid_win_type type,
315 const pjmedia_format *fmt,
316 pjmedia_vid_dev_index rend_id,
317 pjmedia_vid_dev_index cap_id,
318 pj_bool_t show,
319 pjsua_vid_win_id *id)
Benny Prijono9f468d12011-07-07 07:46:33 +0000320{
321 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000322 pjsua_vid_win *w = NULL;
323 pjmedia_vid_port_param vp_param;
324 pjmedia_format fmt_;
325 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000326 unsigned i;
327
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000328 /* If type is preview, check if it exists already */
329 if (type == PJSUA_WND_TYPE_PREVIEW) {
330 wid = pjsua_vid_preview_get_win(cap_id);
331 if (wid != PJSUA_INVALID_ID) {
332 /* Yes, it exists */
333
334 /* Show window if requested */
335 if (show) {
336 pjmedia_vid_dev_stream *rdr;
Nanang Izzuddinc16590a2011-07-14 02:14:25 +0000337 pj_bool_t hide = PJ_FALSE;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000338
339 rdr = pjmedia_vid_port_get_stream(pjsua_var.win[wid].vp_rend);
340 pj_assert(rdr);
341 status = pjmedia_vid_dev_stream_set_cap(
342 rdr,
343 PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE,
Nanang Izzuddinc16590a2011-07-14 02:14:25 +0000344 &hide);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000345 }
346
347 /* Done */
348 *id = wid;
349 return PJ_SUCCESS;
350 }
351 }
352
353 /* Allocate window */
Benny Prijono9f468d12011-07-07 07:46:33 +0000354 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000355 w = &pjsua_var.win[i];
Benny Prijono9f468d12011-07-07 07:46:33 +0000356 if (w->type == PJSUA_WND_TYPE_NONE) {
357 wid = i;
358 w->type = type;
359 break;
360 }
361 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000362 if (i == PJSUA_MAX_VID_WINS)
363 return PJ_ETOOMANY;
Benny Prijono9f468d12011-07-07 07:46:33 +0000364
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000365 /* Initialize window */
366 pjmedia_vid_port_param_default(&vp_param);
367
368 if (w->type == PJSUA_WND_TYPE_PREVIEW) {
369 status = pjmedia_vid_dev_default_param(w->pool, cap_id,
370 &vp_param.vidparam);
371 if (status != PJ_SUCCESS)
372 goto on_error;
373
374 /* Normalize capture ID, in case it was set to
375 * PJMEDIA_VID_DEFAULT_CAPTURE_DEV
376 */
377 cap_id = vp_param.vidparam.cap_id;
378
379 /* Assign preview capture device ID */
380 w->preview_cap_id = cap_id;
381
382 /* Create capture video port */
383 vp_param.active = PJ_TRUE;
384 vp_param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
385 if (fmt)
386 vp_param.vidparam.fmt = *fmt;
387
388 status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_cap);
389 if (status != PJ_SUCCESS)
390 goto on_error;
391
392 /* Update format info */
393 fmt_ = vp_param.vidparam.fmt;
394 fmt = &fmt_;
395
396 /* Create video tee */
397 status = pjmedia_vid_tee_create(w->pool, fmt, 2, &w->tee);
398 if (status != PJ_SUCCESS)
399 goto on_error;
400 }
401
402 /* Create renderer video port */
403 status = pjmedia_vid_dev_default_param(w->pool, rend_id,
404 &vp_param.vidparam);
405 if (status != PJ_SUCCESS)
406 goto on_error;
407
408 vp_param.active = (w->type == PJSUA_WND_TYPE_STREAM);
409 vp_param.vidparam.dir = PJMEDIA_DIR_RENDER;
410 vp_param.vidparam.fmt = *fmt;
411 vp_param.vidparam.disp_size = fmt->det.vid.size;
412 vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
413 vp_param.vidparam.window_hide = !show;
414
415 status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_rend);
416 if (status != PJ_SUCCESS)
417 goto on_error;
418
419 /* For preview window, connect capturer & renderer (via tee) */
420 if (w->type == PJSUA_WND_TYPE_PREVIEW) {
421 pjmedia_port *rend_port;
422
423 status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
424 if (status != PJ_SUCCESS)
425 goto on_error;
426
427 rend_port = pjmedia_vid_port_get_passive_port(w->vp_rend);
428 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, rend_port);
429 if (status != PJ_SUCCESS)
430 goto on_error;
431 }
432
433 /* Done */
434 *id = wid;
435
436 return PJ_SUCCESS;
437
438on_error:
439 free_vid_win(wid);
440 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000441}
442
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000443
Benny Prijono9f468d12011-07-07 07:46:33 +0000444static void free_vid_win(pjsua_vid_win_id wid)
445{
446 pjsua_vid_win *w = &pjsua_var.win[wid];
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000447
Benny Prijono9f468d12011-07-07 07:46:33 +0000448 if (w->vp_cap) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000449 pjmedia_vid_port_stop(w->vp_cap);
450 pjmedia_vid_port_disconnect(w->vp_cap);
Benny Prijono9f468d12011-07-07 07:46:33 +0000451 pjmedia_vid_port_destroy(w->vp_cap);
452 }
453 if (w->vp_rend) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000454 pjmedia_vid_port_stop(w->vp_rend);
Benny Prijono9f468d12011-07-07 07:46:33 +0000455 pjmedia_vid_port_destroy(w->vp_rend);
456 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000457 if (w->tee) {
458 pjmedia_port_destroy(w->tee);
459 }
Benny Prijono9f468d12011-07-07 07:46:33 +0000460 pjsua_vid_win_reset(wid);
461}
462
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000463
464static void inc_vid_win(pjsua_vid_win_id wid)
465{
466 pjsua_vid_win *w;
467
468 pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
469
470 w = &pjsua_var.win[wid];
471 pj_assert(w->type != PJSUA_WND_TYPE_NONE);
472 ++w->ref_cnt;
473}
474
475static void dec_vid_win(pjsua_vid_win_id wid)
476{
477 pjsua_vid_win *w;
478
479 pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
480
481 w = &pjsua_var.win[wid];
482 pj_assert(w->type != PJSUA_WND_TYPE_NONE);
483 if (--w->ref_cnt == 0)
484 free_vid_win(wid);
485}
486
487
488/* Internal function: update video channel after SDP negotiation */
489pj_status_t video_channel_update(pjsua_call_media *call_med,
490 pj_pool_t *tmp_pool,
491 const pjmedia_sdp_session *local_sdp,
492 const pjmedia_sdp_session *remote_sdp)
493{
494 pjsua_call *call = call_med->call;
495 pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
496 pjmedia_vid_stream_info the_si, *si = &the_si;
497 pjmedia_port *media_port;
498 unsigned strm_idx = call_med->idx;
499 pj_status_t status;
500
501 status = pjmedia_vid_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt,
502 local_sdp, remote_sdp, strm_idx);
503 if (status != PJ_SUCCESS)
504 return status;
505
506 /* Check if no media is active */
507 if (si->dir == PJMEDIA_DIR_NONE) {
508 /* Call media state */
509 call_med->state = PJSUA_CALL_MEDIA_NONE;
510
511 /* Call media direction */
512 call_med->dir = PJMEDIA_DIR_NONE;
513
514 } else {
515 pjmedia_transport_info tp_info;
516
517 /* Start/restart media transport */
518 status = pjmedia_transport_media_start(call_med->tp,
519 tmp_pool, local_sdp,
520 remote_sdp, strm_idx);
521 if (status != PJ_SUCCESS)
522 return status;
523
524 call_med->tp_st = PJSUA_MED_TP_RUNNING;
525
526 /* Get remote SRTP usage policy */
527 pjmedia_transport_info_init(&tp_info);
528 pjmedia_transport_get_info(call_med->tp, &tp_info);
529 if (tp_info.specific_info_cnt > 0) {
530 unsigned i;
531 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
532 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
533 {
534 pjmedia_srtp_info *srtp_info =
535 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
536
537 call_med->rem_srtp_use = srtp_info->peer_use;
538 break;
539 }
540 }
541 }
542
543 /* Optionally, application may modify other stream settings here
544 * (such as jitter buffer parameters, codec ptime, etc.)
545 */
546 si->jb_init = pjsua_var.media_cfg.jb_init;
547 si->jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
548 si->jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
549 si->jb_max = pjsua_var.media_cfg.jb_max;
550
551 /* Set SSRC */
552 si->ssrc = call_med->ssrc;
553
554 /* Set RTP timestamp & sequence, normally these value are intialized
555 * automatically when stream session created, but for some cases (e.g:
556 * call reinvite, call update) timestamp and sequence need to be kept
557 * contigue.
558 */
559 si->rtp_ts = call_med->rtp_tx_ts;
560 si->rtp_seq = call_med->rtp_tx_seq;
561 si->rtp_seq_ts_set = call_med->rtp_tx_seq_ts_set;
562
563#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
564 /* Enable/disable stream keep-alive and NAT hole punch. */
565 si->use_ka = pjsua_var.acc[call->acc_id].cfg.use_stream_ka;
566#endif
567
568 /* Try to get shared format ID between the capture device and
569 * the encoder to avoid format conversion in the capture device.
570 */
571 if (si->dir & PJMEDIA_DIR_ENCODING) {
572 pjmedia_vid_dev_info dev_info;
573 pjmedia_vid_codec_info *codec_info = &si->codec_info;
574 unsigned i, j;
575
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +0000576 status = pjmedia_vid_dev_get_info(call_med->strm.v.cap_dev,
577 &dev_info);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000578 if (status != PJ_SUCCESS)
579 return status;
580
581 /* Find matched format ID */
582 for (i = 0; i < codec_info->dec_fmt_id_cnt; ++i) {
583 for (j = 0; j < dev_info.fmt_cnt; ++j) {
584 if (codec_info->dec_fmt_id[i] ==
585 (pjmedia_format_id)dev_info.fmt[j].id)
586 {
587 /* Apply the matched format ID to the codec */
588 si->codec_param->dec_fmt.id =
589 codec_info->dec_fmt_id[i];
590
591 /* Force outer loop to break */
592 i = codec_info->dec_fmt_id_cnt;
593 break;
594 }
595 }
596 }
597 }
598
599 /* Create session based on session info. */
600 status = pjmedia_vid_stream_create(pjsua_var.med_endpt, NULL, si,
601 call_med->tp, NULL,
602 &call_med->strm.v.stream);
603 if (status != PJ_SUCCESS)
604 return status;
605
606 /* Start stream */
607 status = pjmedia_vid_stream_start(call_med->strm.v.stream);
608 if (status != PJ_SUCCESS)
609 return status;
610
611 /* Setup decoding direction */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000612 if (si->dir & PJMEDIA_DIR_DECODING)
613 {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000614 pjsua_vid_win_id wid;
615 pjsua_vid_win *w;
616
617 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
618 PJMEDIA_DIR_DECODING,
619 &media_port);
620 if (status != PJ_SUCCESS)
621 return status;
622
623 /* Create stream video window */
624 status = create_vid_win(PJSUA_WND_TYPE_STREAM,
625 &media_port->info.fmt,
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000626 call_med->strm.v.rdr_dev,
627 //acc->cfg.vid_rend_dev,
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000628 PJSUA_INVALID_ID,
629 acc->cfg.vid_in_auto_show,
630 &wid);
631 if (status != PJ_SUCCESS)
632 return status;
633
Benny Prijonoee0ba182011-07-15 06:18:29 +0000634 /* Register to video events */
635 pjmedia_event_subscribe(
636 pjmedia_vid_port_get_event_publisher(w->vp_rend),
637 &call_med->esub);
638
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000639 w = &pjsua_var.win[wid];
640
641 /* Connect renderer to stream */
642 status = pjmedia_vid_port_connect(w->vp_rend, media_port,
643 PJ_FALSE);
644 if (status != PJ_SUCCESS)
645 return status;
646
647 /* Start renderer */
648 status = pjmedia_vid_port_start(w->vp_rend);
649 if (status != PJ_SUCCESS)
650 return status;
651
652 /* Done */
653 inc_vid_win(wid);
654 call_med->strm.v.rdr_win_id = wid;
655 }
656
657 /* Setup encoding direction */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000658 if (si->dir & PJMEDIA_DIR_ENCODING && !call->local_hold)
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000659 {
660 pjsua_vid_win *w;
661 pjsua_vid_win_id wid;
662
663 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
664 PJMEDIA_DIR_ENCODING,
665 &media_port);
666 if (status != PJ_SUCCESS)
667 return status;
668
669 /* Create preview video window */
670 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
671 &media_port->info.fmt,
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000672 call_med->strm.v.rdr_dev,
673 call_med->strm.v.cap_dev,
674 //acc->cfg.vid_rend_dev,
675 //acc->cfg.vid_cap_dev,
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000676 PJ_FALSE,
677 &wid);
678 if (status != PJ_SUCCESS)
679 return status;
680
681 w = &pjsua_var.win[wid];
682
683 /* Connect stream to capturer (via video window tee) */
684 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
685 if (status != PJ_SUCCESS)
686 return status;
687
688 /* Start renderer */
689 status = pjmedia_vid_port_start(w->vp_rend);
690 if (status != PJ_SUCCESS)
691 return status;
692
693 /* Start capturer */
694 status = pjmedia_vid_port_start(w->vp_cap);
695 if (status != PJ_SUCCESS)
696 return status;
697
698 /* Done */
699 inc_vid_win(wid);
700 call_med->strm.v.cap_win_id = wid;
701 }
702
703 /* Call media direction */
704 call_med->dir = si->dir;
705
706 /* Call media state */
707 if (call->local_hold)
708 call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
709 else if (call_med->dir == PJMEDIA_DIR_DECODING)
710 call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
711 else
712 call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
713 }
714
715 /* Print info. */
716 {
717 char info[80];
718 int info_len = 0;
719 int len;
720 const char *dir;
721
722 switch (si->dir) {
723 case PJMEDIA_DIR_NONE:
724 dir = "inactive";
725 break;
726 case PJMEDIA_DIR_ENCODING:
727 dir = "sendonly";
728 break;
729 case PJMEDIA_DIR_DECODING:
730 dir = "recvonly";
731 break;
732 case PJMEDIA_DIR_ENCODING_DECODING:
733 dir = "sendrecv";
734 break;
735 default:
736 dir = "unknown";
737 break;
738 }
739 len = pj_ansi_sprintf( info+info_len,
740 ", stream #%d: %.*s (%s)", strm_idx,
741 (int)si->codec_info.encoding_name.slen,
742 si->codec_info.encoding_name.ptr,
743 dir);
744 if (len > 0)
745 info_len += len;
746 PJ_LOG(4,(THIS_FILE,"Media updates%s", info));
747 }
748
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +0000749 if (!acc->cfg.vid_out_auto_transmit) {
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000750 status = pjmedia_vid_stream_pause(call_med->strm.v.stream,
751 PJMEDIA_DIR_ENCODING);
752 if (status != PJ_SUCCESS)
753 return status;
754 }
755
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000756 return PJ_SUCCESS;
757}
758
759
760/* Internal function to stop video stream */
761void stop_video_stream(pjsua_call_media *call_med)
762{
763 pjmedia_vid_stream *strm = call_med->strm.v.stream;
764 pjmedia_rtcp_stat stat;
765
766 pj_assert(call_med->type == PJMEDIA_TYPE_VIDEO);
767
768 if (!strm)
769 return;
770
771 if (call_med->strm.v.cap_win_id != PJSUA_INVALID_ID) {
772 pjmedia_port *media_port;
773 pjsua_vid_win *w =
774 &pjsua_var.win[call_med->strm.v.cap_win_id];
775
776 pjmedia_vid_stream_get_port(call_med->strm.v.stream,
777 PJMEDIA_DIR_ENCODING,
778 &media_port);
779 pj_assert(media_port);
780
781 pjmedia_vid_port_stop(w->vp_cap);
782 pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
783 pjmedia_vid_port_start(w->vp_cap);
784
785 dec_vid_win(call_med->strm.v.cap_win_id);
786 }
787
788 if (call_med->strm.v.rdr_win_id != PJSUA_INVALID_ID) {
789 dec_vid_win(call_med->strm.v.rdr_win_id);
790 }
791
792 if ((call_med->dir & PJMEDIA_DIR_ENCODING) &&
793 (pjmedia_vid_stream_get_stat(strm, &stat) == PJ_SUCCESS))
794 {
795 /* Save RTP timestamp & sequence, so when media session is
796 * restarted, those values will be restored as the initial
797 * RTP timestamp & sequence of the new media session. So in
798 * the same call session, RTP timestamp and sequence are
799 * guaranteed to be contigue.
800 */
801 call_med->rtp_tx_seq_ts_set = 1 | (1 << 1);
802 call_med->rtp_tx_seq = stat.rtp_tx_last_seq;
803 call_med->rtp_tx_ts = stat.rtp_tx_last_ts;
804 }
805
806 pjmedia_vid_stream_destroy(strm);
807 call_med->strm.v.stream = NULL;
808}
809
810
Benny Prijono9f468d12011-07-07 07:46:33 +0000811/*
812 * Start video preview window for the specified capture device.
813 */
814PJ_DEF(pj_status_t) pjsua_vid_preview_start(pjmedia_vid_dev_index id,
815 pjsua_vid_preview_param *prm)
816{
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000817 pjsua_vid_win_id wid;
818 pjsua_vid_win *w;
819 pjmedia_vid_dev_index rend_id;
Benny Prijono9f468d12011-07-07 07:46:33 +0000820 pj_status_t status;
821
822 PJSUA_LOCK();
823
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000824 if (prm) {
825 rend_id = prm->rend_id;
826 } else {
827 rend_id = PJMEDIA_VID_DEFAULT_RENDER_DEV;
Benny Prijono9f468d12011-07-07 07:46:33 +0000828 }
829
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000830 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW, NULL, rend_id, id,
831 PJ_TRUE, &wid);
832 if (status != PJ_SUCCESS) {
Benny Prijono9f468d12011-07-07 07:46:33 +0000833 PJSUA_UNLOCK();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000834 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000835 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000836
Benny Prijono9f468d12011-07-07 07:46:33 +0000837 w = &pjsua_var.win[wid];
838
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000839 /* Start capturer */
840 status = pjmedia_vid_port_start(w->vp_rend);
841 if (status != PJ_SUCCESS) {
842 PJSUA_UNLOCK();
843 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000844 }
845
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000846 /* Start renderer */
Benny Prijono9f468d12011-07-07 07:46:33 +0000847 status = pjmedia_vid_port_start(w->vp_cap);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000848 if (status != PJ_SUCCESS) {
849 PJSUA_UNLOCK();
850 return status;
851 }
Benny Prijono9f468d12011-07-07 07:46:33 +0000852
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000853 inc_vid_win(wid);
854
Benny Prijono9f468d12011-07-07 07:46:33 +0000855 PJSUA_UNLOCK();
856 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +0000857}
858
859/*
860 * Stop video preview.
861 */
862PJ_DEF(pj_status_t) pjsua_vid_preview_stop(pjmedia_vid_dev_index id)
863{
864 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
Benny Prijono9f468d12011-07-07 07:46:33 +0000865
866 PJSUA_LOCK();
867 wid = pjsua_vid_preview_get_win(id);
868 if (wid == PJSUA_INVALID_ID) {
869 PJSUA_UNLOCK();
870 return PJ_ENOTFOUND;
871 }
872
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000873 dec_vid_win(wid);
Benny Prijono9f468d12011-07-07 07:46:33 +0000874
875 PJSUA_UNLOCK();
876
877 return PJ_SUCCESS;
878}
879
880
881/*****************************************************************************
882 * Window
883 */
884
Nanang Izzuddinf3638022011-07-14 03:47:04 +0000885
886/*
887 * Enumerates all video windows.
888 */
889PJ_DEF(pj_status_t) pjsua_vid_enum_wins( pjsua_vid_win_id wids[],
890 unsigned *count)
891{
892 unsigned i, cnt;
893
894 cnt = 0;
895
896 for (i=0; i<PJSUA_MAX_VID_WINS && cnt <*count; ++i) {
897 pjsua_vid_win *w = &pjsua_var.win[i];
898 if (w->type != PJSUA_WND_TYPE_NONE)
899 wids[cnt++] = i;
900 }
901
902 *count = cnt;
903
904 return PJ_SUCCESS;
905}
906
907
Benny Prijono9f468d12011-07-07 07:46:33 +0000908/*
909 * Get window info.
910 */
911PJ_DEF(pj_status_t) pjsua_vid_win_get_info( pjsua_vid_win_id wid,
912 pjsua_vid_win_info *wi)
913{
914 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000915 pjmedia_vid_dev_stream *s;
916 pjmedia_vid_param vparam;
917 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000918
919 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && wi, PJ_EINVAL);
920
921 PJSUA_LOCK();
922 w = &pjsua_var.win[wid];
923 if (w->vp_rend == NULL) {
924 PJSUA_UNLOCK();
925 return PJ_EINVAL;
926 }
927
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000928 s = pjmedia_vid_port_get_stream(w->vp_rend);
929 if (s == NULL) {
930 PJSUA_UNLOCK();
931 return PJ_EINVAL;
932 }
933
934 status = pjmedia_vid_dev_stream_get_param(s, &vparam);
935 if (status != PJ_SUCCESS) {
936 PJSUA_UNLOCK();
937 return status;
938 }
939
940 wi->show = !vparam.window_hide;
941 wi->pos = vparam.window_pos;
942 wi->size = vparam.disp_size;
943
Benny Prijono9f468d12011-07-07 07:46:33 +0000944 PJSUA_UNLOCK();
945
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000946 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +0000947}
948
949/*
950 * Show or hide window.
951 */
952PJ_DEF(pj_status_t) pjsua_vid_win_set_show( pjsua_vid_win_id wid,
953 pj_bool_t show)
954{
955 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000956 pjmedia_vid_dev_stream *s;
Nanang Izzuddinc16590a2011-07-14 02:14:25 +0000957 pj_bool_t hide;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000958 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000959
960 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
961
962 PJSUA_LOCK();
963 w = &pjsua_var.win[wid];
964 if (w->vp_rend == NULL) {
965 PJSUA_UNLOCK();
966 return PJ_EINVAL;
967 }
968
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000969 s = pjmedia_vid_port_get_stream(w->vp_rend);
970 if (s == NULL) {
971 PJSUA_UNLOCK();
972 return PJ_EINVAL;
973 }
974
Nanang Izzuddinc16590a2011-07-14 02:14:25 +0000975 hide = !show;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000976 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinc16590a2011-07-14 02:14:25 +0000977 PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE, &hide);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000978
Benny Prijono9f468d12011-07-07 07:46:33 +0000979 PJSUA_UNLOCK();
980
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000981 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000982}
983
984/*
985 * Set video window position.
986 */
987PJ_DEF(pj_status_t) pjsua_vid_win_set_pos( pjsua_vid_win_id wid,
988 const pjmedia_coord *pos)
989{
990 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000991 pjmedia_vid_dev_stream *s;
992 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000993
994 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && pos, PJ_EINVAL);
995
996 PJSUA_LOCK();
997 w = &pjsua_var.win[wid];
998 if (w->vp_rend == NULL) {
999 PJSUA_UNLOCK();
1000 return PJ_EINVAL;
1001 }
1002
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001003 s = pjmedia_vid_port_get_stream(w->vp_rend);
1004 if (s == NULL) {
1005 PJSUA_UNLOCK();
1006 return PJ_EINVAL;
1007 }
1008
1009 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001010 PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION, pos);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001011
Benny Prijono9f468d12011-07-07 07:46:33 +00001012 PJSUA_UNLOCK();
1013
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001014 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001015}
1016
1017/*
1018 * Resize window.
1019 */
1020PJ_DEF(pj_status_t) pjsua_vid_win_set_size( pjsua_vid_win_id wid,
1021 const pjmedia_rect_size *size)
1022{
1023 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001024 pjmedia_vid_dev_stream *s;
1025 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001026
1027 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && size, PJ_EINVAL);
1028
1029 PJSUA_LOCK();
1030 w = &pjsua_var.win[wid];
1031 if (w->vp_rend == NULL) {
1032 PJSUA_UNLOCK();
1033 return PJ_EINVAL;
1034 }
1035
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001036 s = pjmedia_vid_port_get_stream(w->vp_rend);
1037 if (s == NULL) {
1038 PJSUA_UNLOCK();
1039 return PJ_EINVAL;
1040 }
1041
1042 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001043 PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE, size);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001044
Benny Prijono9f468d12011-07-07 07:46:33 +00001045 PJSUA_UNLOCK();
1046
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001047 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001048}
1049
1050
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001051static void call_get_vid_strm_info(pjsua_call *call,
1052 int *first_active,
1053 int *first_inactive,
1054 unsigned *active_cnt,
1055 unsigned *cnt)
1056{
1057 unsigned i, var_cnt = 0;
1058
1059 if (first_active && ++var_cnt)
1060 *first_active = -1;
1061 if (first_inactive && ++var_cnt)
1062 *first_inactive = -1;
1063 if (active_cnt && ++var_cnt)
1064 *active_cnt = 0;
1065 if (cnt && ++var_cnt)
1066 *cnt = 0;
1067
1068 for (i = 0; i < call->med_cnt && var_cnt; ++i) {
1069 if (call->media[i].type == PJMEDIA_TYPE_VIDEO) {
1070 if (call->media[i].dir != PJMEDIA_DIR_NONE)
1071 {
1072 if (first_active && *first_active == -1) {
1073 *first_active = i;
1074 --var_cnt;
1075 }
1076 if (active_cnt)
1077 ++(*active_cnt);
1078 } else if (first_inactive && *first_inactive == -1) {
1079 *first_inactive = i;
1080 --var_cnt;
1081 }
1082 if (cnt)
1083 ++(*cnt);
1084 }
1085 }
1086}
1087
1088
1089/* Send SDP reoffer. */
1090static pj_status_t call_reoffer_sdp(pjsua_call_id call_id,
1091 const pjmedia_sdp_session *sdp)
1092{
1093 pjsua_call *call;
1094 pjsip_tx_data *tdata;
1095 pjsip_dialog *dlg;
1096 pj_status_t status;
1097
1098 status = acquire_call("call_reoffer_sdp()", call_id, &call, &dlg);
1099 if (status != PJ_SUCCESS)
1100 return status;
1101
1102 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1103 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
1104 pjsip_dlg_dec_lock(dlg);
1105 return PJSIP_ESESSIONSTATE;
1106 }
1107
1108 /* Create re-INVITE with new offer */
1109 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1110 if (status != PJ_SUCCESS) {
1111 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
1112 pjsip_dlg_dec_lock(dlg);
1113 return status;
1114 }
1115
1116 /* Send the request */
1117 status = pjsip_inv_send_msg( call->inv, tdata);
1118 if (status != PJ_SUCCESS) {
1119 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
1120 pjsip_dlg_dec_lock(dlg);
1121 return status;
1122 }
1123
1124 pjsip_dlg_dec_lock(dlg);
1125
1126 return PJ_SUCCESS;
1127}
1128
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001129/* Add a new video stream into a call */
1130static pj_status_t call_add_video(pjsua_call *call,
1131 pjmedia_vid_dev_index cap_dev)
1132{
1133 pj_pool_t *pool = call->inv->pool_prov;
1134 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1135 pjsua_call_media *call_med;
1136 pjmedia_sdp_session *sdp;
1137 pjmedia_sdp_media *sdp_m;
1138 pjmedia_transport_info tpinfo;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001139 unsigned active_cnt;
1140 pj_status_t status;
1141
1142 /* Verify media slot availability */
1143 if (call->med_cnt == PJSUA_MAX_CALL_MEDIA)
1144 return PJ_ETOOMANY;
1145
1146 call_get_vid_strm_info(call, NULL, NULL, &active_cnt, NULL);
1147 if (active_cnt == acc_cfg->max_video_cnt)
1148 return PJ_ETOOMANY;
1149
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001150 /* Get active local SDP */
1151 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
1152 if (status != PJ_SUCCESS)
1153 return status;
1154
1155 /* Initialize call media */
1156 call_med = &call->media[call->med_cnt++];
1157
1158 status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1159 &acc_cfg->rtp_cfg, call->secure_level,
1160 NULL);
1161 if (status != PJ_SUCCESS)
1162 goto on_error;
1163
1164 /* Override default capture device setting */
1165 call_med->strm.v.cap_dev = cap_dev;
1166
1167 /* Init transport media */
1168 status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1169 NULL, call_med->idx);
1170 if (status != PJ_SUCCESS)
1171 goto on_error;
1172
1173 call_med->tp_st = PJSUA_MED_TP_INIT;
1174
1175 /* Get transport address info */
1176 pjmedia_transport_info_init(&tpinfo);
1177 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1178
1179 /* Create SDP media line */
1180 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1181 &tpinfo.sock_info, 0, &sdp_m);
1182 if (status != PJ_SUCCESS)
1183 goto on_error;
1184
1185 sdp->media[sdp->media_count++] = sdp_m;
1186
1187 /* Update SDP media line by media transport */
1188 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1189 sdp, NULL, call_med->idx);
1190 if (status != PJ_SUCCESS)
1191 goto on_error;
1192
1193 status = call_reoffer_sdp(call->index, sdp);
1194 if (status != PJ_SUCCESS)
1195 goto on_error;
1196
1197 return PJ_SUCCESS;
1198
1199on_error:
1200 if (call_med->tp) {
1201 pjmedia_transport_close(call_med->tp);
1202 call_med->tp = call_med->tp_orig = NULL;
1203 }
1204
1205 return status;
1206}
1207
1208
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001209/* Modify a video stream from a call, i.e: enable/disable */
1210static pj_status_t call_modify_video(pjsua_call *call,
1211 int med_idx,
1212 pj_bool_t enable)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001213{
1214 pjsua_call_media *call_med;
1215 pjmedia_sdp_session *sdp;
1216 pj_status_t status;
1217
1218 /* Verify and normalize media index */
1219 if (med_idx == -1) {
1220 int first_active;
1221
1222 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1223 if (first_active == -1)
1224 return PJ_ENOTFOUND;
1225
1226 med_idx = first_active;
1227 }
1228
1229 call_med = &call->media[med_idx];
1230
1231 /* Verify if the stream media type is video */
1232 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1233 return PJ_EINVAL;
1234
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001235 /* Verify if the stream already enabled/disabled */
1236 if (( enable && call_med->dir != PJMEDIA_DIR_NONE) ||
1237 (!enable && call_med->dir == PJMEDIA_DIR_NONE))
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001238 return PJ_SUCCESS;
1239
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001240 /* Get active local SDP */
1241 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
1242 if (status != PJ_SUCCESS)
1243 return status;
1244
1245 pj_assert(med_idx < (int)sdp->media_count);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001246
1247 if (enable) {
1248 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1249 pj_pool_t *pool = call->inv->pool_prov;
1250 pjmedia_sdp_media *sdp_m;
1251 pjmedia_transport_info tpinfo;
1252
1253 status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1254 &acc_cfg->rtp_cfg, call->secure_level,
1255 NULL);
1256 if (status != PJ_SUCCESS)
1257 goto on_error;
1258
1259 /* Init transport media */
1260 status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1261 NULL, call_med->idx);
1262 if (status != PJ_SUCCESS)
1263 goto on_error;
1264
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001265 /* Get transport address info */
1266 pjmedia_transport_info_init(&tpinfo);
1267 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1268
1269 /* Create SDP media line */
1270 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1271 &tpinfo.sock_info, 0, &sdp_m);
1272 if (status != PJ_SUCCESS)
1273 goto on_error;
1274
1275 sdp->media[med_idx] = sdp_m;
1276
1277 /* Update SDP media line by media transport */
1278 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1279 sdp, NULL, call_med->idx);
1280 if (status != PJ_SUCCESS)
1281 goto on_error;
1282
1283on_error:
1284 if (status != PJ_SUCCESS) {
1285 if (call_med->tp) {
1286 pjmedia_transport_close(call_med->tp);
1287 call_med->tp = call_med->tp_orig = NULL;
1288 }
1289 return status;
1290 }
1291 } else {
1292 /* Mark media transport to disabled */
1293 // Don't close this here, as SDP negotiation has not been
1294 // done and stream may be still active.
1295 call_med->tp_st = PJSUA_MED_TP_DISABLED;
1296
1297 /* Disable the stream in SDP by setting port to 0 */
1298 sdp->media[med_idx]->desc.port = 0;
1299 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001300
1301 status = call_reoffer_sdp(call->index, sdp);
1302 if (status != PJ_SUCCESS)
1303 return status;
1304
1305 return PJ_SUCCESS;
1306}
1307
1308
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001309/* Change capture device of a video stream in a call */
1310static pj_status_t call_change_cap_dev(pjsua_call *call,
1311 int med_idx,
1312 pjmedia_vid_dev_index cap_dev)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001313{
1314 pjsua_call_media *call_med;
1315 pjmedia_vid_dev_info info;
1316 pjsua_vid_win *w, *new_w = NULL;
1317 pjsua_vid_win_id wid, new_wid;
1318 pjmedia_port *media_port;
1319 pj_status_t status;
1320
1321 /* Verify and normalize media index */
1322 if (med_idx == -1) {
1323 int first_active;
1324
1325 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1326 if (first_active == -1)
1327 return PJ_ENOTFOUND;
1328
1329 med_idx = first_active;
1330 }
1331
1332 call_med = &call->media[med_idx];
1333
1334 /* Verify if the stream media type is video */
1335 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1336 return PJ_EINVAL;
1337
1338 /* Verify the capture device */
1339 status = pjmedia_vid_dev_get_info(cap_dev, &info);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001340 if (status != PJ_SUCCESS || info.dir != PJMEDIA_DIR_CAPTURE)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001341 return PJ_EINVAL;
1342
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001343 /* The specified capture device is being used already */
1344 if (call_med->strm.v.cap_dev == cap_dev)
1345 return PJ_SUCCESS;
1346
1347 /* == Apply the new capture device == */
1348
1349 wid = call_med->strm.v.cap_win_id;
1350 w = &pjsua_var.win[wid];
1351 pj_assert(w->type == PJSUA_WND_TYPE_PREVIEW && w->vp_cap);
1352
1353 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
1354 PJMEDIA_DIR_ENCODING, &media_port);
1355 if (status != PJ_SUCCESS)
1356 return status;
1357
1358 /* = Detach stream port from the old capture device = */
1359 status = pjmedia_vid_port_disconnect(w->vp_cap);
1360 if (status != PJ_SUCCESS)
1361 return status;
1362
1363 status = pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
1364 if (status != PJ_SUCCESS) {
1365 /* Connect back the old capturer */
1366 pjmedia_vid_port_connect(w->vp_cap, media_port, PJ_FALSE);
1367 return status;
1368 }
1369
1370 /* = Attach stream port to the new capture device = */
1371
1372 /* Create preview video window */
1373 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
1374 &media_port->info.fmt,
1375 call_med->strm.v.rdr_dev,
1376 cap_dev,
1377 PJ_FALSE,
1378 &new_wid);
1379 if (status != PJ_SUCCESS)
1380 goto on_error;
1381
1382 inc_vid_win(new_wid);
1383 new_w = &pjsua_var.win[new_wid];
1384
1385 /* Connect stream to capturer (via video window tee) */
1386 status = pjmedia_vid_tee_add_dst_port2(new_w->tee, 0, media_port);
1387 if (status != PJ_SUCCESS)
1388 goto on_error;
1389
1390 /* Connect capturer to tee */
1391 status = pjmedia_vid_port_connect(new_w->vp_cap, new_w->tee, PJ_FALSE);
1392 if (status != PJ_SUCCESS)
1393 return status;
1394
1395 /* Start renderer */
1396 status = pjmedia_vid_port_start(new_w->vp_rend);
1397 if (status != PJ_SUCCESS)
1398 goto on_error;
1399
1400 /* Start capturer */
1401 status = pjmedia_vid_port_start(new_w->vp_cap);
1402 if (status != PJ_SUCCESS)
1403 goto on_error;
1404
1405 /* Finally */
1406 call_med->strm.v.cap_dev = cap_dev;
1407 call_med->strm.v.cap_win_id = new_wid;
1408 dec_vid_win(wid);
1409
1410 return PJ_SUCCESS;
1411
1412on_error:
1413 if (new_w) {
1414 /* Disconnect media port from the new capturer */
1415 pjmedia_vid_tee_remove_dst_port(new_w->tee, media_port);
1416 /* Release the new capturer */
1417 dec_vid_win(new_wid);
1418 }
1419
1420 /* Revert back to the old capturer */
1421 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
1422 if (status != PJ_SUCCESS)
1423 return status;
1424
1425 status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
1426 if (status != PJ_SUCCESS)
1427 return status;
1428
1429 return status;
1430}
1431
1432
1433/* Start transmitting video stream in a call */
1434static pj_status_t call_start_tx_video(pjsua_call *call,
1435 int med_idx,
1436 pjmedia_vid_dev_index cap_dev)
1437{
1438 pjsua_call_media *call_med;
1439 pj_status_t status;
1440
1441 /* Verify and normalize media index */
1442 if (med_idx == -1) {
1443 int first_active;
1444
1445 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1446 if (first_active == -1)
1447 return PJ_ENOTFOUND;
1448
1449 med_idx = first_active;
1450 }
1451
1452 call_med = &call->media[med_idx];
1453
1454 /* Verify if the stream is transmitting video */
1455 if (call_med->type != PJMEDIA_TYPE_VIDEO ||
1456 (call_med->dir & PJMEDIA_DIR_ENCODING) == 0)
1457 {
1458 return PJ_EINVAL;
1459 }
1460
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001461 /* Apply the capture device, it may be changed! */
1462 status = call_change_cap_dev(call, med_idx, cap_dev);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001463 if (status != PJ_SUCCESS)
1464 return status;
1465
1466 /* Start stream in encoding direction */
1467 status = pjmedia_vid_stream_resume(call_med->strm.v.stream,
1468 PJMEDIA_DIR_ENCODING);
1469 if (status != PJ_SUCCESS)
1470 return status;
1471
1472 return PJ_SUCCESS;
1473}
1474
1475
1476/* Stop transmitting video stream in a call */
1477static pj_status_t call_stop_tx_video(pjsua_call *call,
1478 int med_idx)
1479{
1480 pjsua_call_media *call_med;
1481 pj_status_t status;
1482
1483 /* Verify and normalize media index */
1484 if (med_idx == -1) {
1485 int first_active;
1486
1487 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1488 if (first_active == -1)
1489 return PJ_ENOTFOUND;
1490
1491 med_idx = first_active;
1492 }
1493
1494 call_med = &call->media[med_idx];
1495
1496 /* Verify if the stream is transmitting video */
1497 if (call_med->type != PJMEDIA_TYPE_VIDEO ||
1498 (call_med->dir & PJMEDIA_DIR_ENCODING) == 0)
1499 {
1500 return PJ_EINVAL;
1501 }
1502
1503 /* Pause stream in encoding direction */
1504 status = pjmedia_vid_stream_pause( call_med->strm.v.stream,
1505 PJMEDIA_DIR_ENCODING);
1506 if (status != PJ_SUCCESS)
1507 return status;
1508
1509 return PJ_SUCCESS;
1510}
1511
1512
1513/*
1514 * Start, stop, and/or manipulate video transmission for the specified call.
1515 */
1516PJ_DEF(pj_status_t) pjsua_call_set_vid_strm (
1517 pjsua_call_id call_id,
1518 pjsua_call_vid_strm_op op,
1519 const pjsua_call_vid_strm_op_param *param)
1520{
1521 pjsua_call *call;
1522 pjsua_call_vid_strm_op_param param_;
1523 pj_status_t status;
1524
1525 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1526 PJ_EINVAL);
1527
1528 PJSUA_LOCK();
1529
1530 call = &pjsua_var.calls[call_id];
1531
1532 if (param) {
1533 param_ = *param;
1534 } else {
1535 param_.med_idx = -1;
1536 param_.cap_dev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
1537 }
1538
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001539 /* If set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
1540 * account default video capture device.
1541 */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001542 if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001543 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1544 param_.cap_dev = acc_cfg->vid_cap_dev;
1545
1546 /* If the account default video capture device is
1547 * PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
1548 * global default video capture device.
1549 */
1550 if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
1551 pjmedia_vid_dev_info info;
1552 pjmedia_vid_dev_get_info(param_.cap_dev, &info);
1553 pj_assert(info.dir == PJMEDIA_DIR_CAPTURE);
1554 param_.cap_dev = info.id;
1555 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001556 }
1557
1558 switch (op) {
1559 case PJSUA_CALL_VID_STRM_ADD:
1560 status = call_add_video(call, param_.cap_dev);
1561 break;
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001562 case PJSUA_CALL_VID_STRM_ENABLE:
1563 status = call_modify_video(call, param_.med_idx, PJ_TRUE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001564 break;
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001565 case PJSUA_CALL_VID_STRM_DISABLE:
1566 status = call_modify_video(call, param_.med_idx, PJ_FALSE);
1567 break;
1568 case PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV:
1569 status = call_change_cap_dev(call, param_.med_idx, param_.cap_dev);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001570 break;
1571 case PJSUA_CALL_VID_STRM_START_TRANSMIT:
1572 status = call_start_tx_video(call, param_.med_idx, param_.cap_dev);
1573 break;
1574 case PJSUA_CALL_VID_STRM_STOP_TRANSMIT:
1575 status = call_stop_tx_video(call, param_.med_idx);
1576 break;
1577 default:
1578 status = PJ_EINVALIDOP;
1579 break;
1580 }
1581
1582 PJSUA_UNLOCK();
1583
1584 return status;
1585}
1586
Benny Prijono9f468d12011-07-07 07:46:33 +00001587
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001588/*
1589 * Get the media stream index of the default video stream in the call.
1590 */
1591PJ_DEF(int) pjsua_call_get_vid_stream_idx(pjsua_call_id call_id)
1592{
1593 pjsua_call *call;
1594 int first_active, first_inactive;
1595
1596 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1597 PJ_EINVAL);
1598
1599 PJSUA_LOCK();
1600 call = &pjsua_var.calls[call_id];
1601 call_get_vid_strm_info(call, &first_active, &first_inactive, NULL, NULL);
1602 PJSUA_UNLOCK();
1603
1604 if (first_active == -1)
1605 return first_inactive;
1606
1607 return first_active;
1608}
1609
1610
Benny Prijono9f468d12011-07-07 07:46:33 +00001611#endif /* PJSUA_HAS_VIDEO */
1612