blob: 65a5998f8f20d20ae43f6cdf317cad6f8e5d2ee1 [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;
337
338 rdr = pjmedia_vid_port_get_stream(pjsua_var.win[wid].vp_rend);
339 pj_assert(rdr);
340 status = pjmedia_vid_dev_stream_set_cap(
341 rdr,
342 PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE,
343 &show);
344 }
345
346 /* Done */
347 *id = wid;
348 return PJ_SUCCESS;
349 }
350 }
351
352 /* Allocate window */
Benny Prijono9f468d12011-07-07 07:46:33 +0000353 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000354 w = &pjsua_var.win[i];
Benny Prijono9f468d12011-07-07 07:46:33 +0000355 if (w->type == PJSUA_WND_TYPE_NONE) {
356 wid = i;
357 w->type = type;
358 break;
359 }
360 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000361 if (i == PJSUA_MAX_VID_WINS)
362 return PJ_ETOOMANY;
Benny Prijono9f468d12011-07-07 07:46:33 +0000363
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000364 /* Initialize window */
365 pjmedia_vid_port_param_default(&vp_param);
366
367 if (w->type == PJSUA_WND_TYPE_PREVIEW) {
368 status = pjmedia_vid_dev_default_param(w->pool, cap_id,
369 &vp_param.vidparam);
370 if (status != PJ_SUCCESS)
371 goto on_error;
372
373 /* Normalize capture ID, in case it was set to
374 * PJMEDIA_VID_DEFAULT_CAPTURE_DEV
375 */
376 cap_id = vp_param.vidparam.cap_id;
377
378 /* Assign preview capture device ID */
379 w->preview_cap_id = cap_id;
380
381 /* Create capture video port */
382 vp_param.active = PJ_TRUE;
383 vp_param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
384 if (fmt)
385 vp_param.vidparam.fmt = *fmt;
386
387 status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_cap);
388 if (status != PJ_SUCCESS)
389 goto on_error;
390
391 /* Update format info */
392 fmt_ = vp_param.vidparam.fmt;
393 fmt = &fmt_;
394
395 /* Create video tee */
396 status = pjmedia_vid_tee_create(w->pool, fmt, 2, &w->tee);
397 if (status != PJ_SUCCESS)
398 goto on_error;
399 }
400
401 /* Create renderer video port */
402 status = pjmedia_vid_dev_default_param(w->pool, rend_id,
403 &vp_param.vidparam);
404 if (status != PJ_SUCCESS)
405 goto on_error;
406
407 vp_param.active = (w->type == PJSUA_WND_TYPE_STREAM);
408 vp_param.vidparam.dir = PJMEDIA_DIR_RENDER;
409 vp_param.vidparam.fmt = *fmt;
410 vp_param.vidparam.disp_size = fmt->det.vid.size;
411 vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
412 vp_param.vidparam.window_hide = !show;
413
414 status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_rend);
415 if (status != PJ_SUCCESS)
416 goto on_error;
417
418 /* For preview window, connect capturer & renderer (via tee) */
419 if (w->type == PJSUA_WND_TYPE_PREVIEW) {
420 pjmedia_port *rend_port;
421
422 status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
423 if (status != PJ_SUCCESS)
424 goto on_error;
425
426 rend_port = pjmedia_vid_port_get_passive_port(w->vp_rend);
427 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, rend_port);
428 if (status != PJ_SUCCESS)
429 goto on_error;
430 }
431
432 /* Done */
433 *id = wid;
434
435 return PJ_SUCCESS;
436
437on_error:
438 free_vid_win(wid);
439 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000440}
441
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000442
Benny Prijono9f468d12011-07-07 07:46:33 +0000443static void free_vid_win(pjsua_vid_win_id wid)
444{
445 pjsua_vid_win *w = &pjsua_var.win[wid];
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000446
Benny Prijono9f468d12011-07-07 07:46:33 +0000447 if (w->vp_cap) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000448 pjmedia_vid_port_stop(w->vp_cap);
449 pjmedia_vid_port_disconnect(w->vp_cap);
Benny Prijono9f468d12011-07-07 07:46:33 +0000450 pjmedia_vid_port_destroy(w->vp_cap);
451 }
452 if (w->vp_rend) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000453 pjmedia_vid_port_stop(w->vp_rend);
Benny Prijono9f468d12011-07-07 07:46:33 +0000454 pjmedia_vid_port_destroy(w->vp_rend);
455 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000456 if (w->tee) {
457 pjmedia_port_destroy(w->tee);
458 }
Benny Prijono9f468d12011-07-07 07:46:33 +0000459 pjsua_vid_win_reset(wid);
460}
461
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000462
463static void inc_vid_win(pjsua_vid_win_id wid)
464{
465 pjsua_vid_win *w;
466
467 pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
468
469 w = &pjsua_var.win[wid];
470 pj_assert(w->type != PJSUA_WND_TYPE_NONE);
471 ++w->ref_cnt;
472}
473
474static void dec_vid_win(pjsua_vid_win_id wid)
475{
476 pjsua_vid_win *w;
477
478 pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
479
480 w = &pjsua_var.win[wid];
481 pj_assert(w->type != PJSUA_WND_TYPE_NONE);
482 if (--w->ref_cnt == 0)
483 free_vid_win(wid);
484}
485
486
487/* Internal function: update video channel after SDP negotiation */
488pj_status_t video_channel_update(pjsua_call_media *call_med,
489 pj_pool_t *tmp_pool,
490 const pjmedia_sdp_session *local_sdp,
491 const pjmedia_sdp_session *remote_sdp)
492{
493 pjsua_call *call = call_med->call;
494 pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
495 pjmedia_vid_stream_info the_si, *si = &the_si;
496 pjmedia_port *media_port;
497 unsigned strm_idx = call_med->idx;
498 pj_status_t status;
499
500 status = pjmedia_vid_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt,
501 local_sdp, remote_sdp, strm_idx);
502 if (status != PJ_SUCCESS)
503 return status;
504
505 /* Check if no media is active */
506 if (si->dir == PJMEDIA_DIR_NONE) {
507 /* Call media state */
508 call_med->state = PJSUA_CALL_MEDIA_NONE;
509
510 /* Call media direction */
511 call_med->dir = PJMEDIA_DIR_NONE;
512
513 } else {
514 pjmedia_transport_info tp_info;
515
516 /* Start/restart media transport */
517 status = pjmedia_transport_media_start(call_med->tp,
518 tmp_pool, local_sdp,
519 remote_sdp, strm_idx);
520 if (status != PJ_SUCCESS)
521 return status;
522
523 call_med->tp_st = PJSUA_MED_TP_RUNNING;
524
525 /* Get remote SRTP usage policy */
526 pjmedia_transport_info_init(&tp_info);
527 pjmedia_transport_get_info(call_med->tp, &tp_info);
528 if (tp_info.specific_info_cnt > 0) {
529 unsigned i;
530 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
531 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
532 {
533 pjmedia_srtp_info *srtp_info =
534 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
535
536 call_med->rem_srtp_use = srtp_info->peer_use;
537 break;
538 }
539 }
540 }
541
542 /* Optionally, application may modify other stream settings here
543 * (such as jitter buffer parameters, codec ptime, etc.)
544 */
545 si->jb_init = pjsua_var.media_cfg.jb_init;
546 si->jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
547 si->jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
548 si->jb_max = pjsua_var.media_cfg.jb_max;
549
550 /* Set SSRC */
551 si->ssrc = call_med->ssrc;
552
553 /* Set RTP timestamp & sequence, normally these value are intialized
554 * automatically when stream session created, but for some cases (e.g:
555 * call reinvite, call update) timestamp and sequence need to be kept
556 * contigue.
557 */
558 si->rtp_ts = call_med->rtp_tx_ts;
559 si->rtp_seq = call_med->rtp_tx_seq;
560 si->rtp_seq_ts_set = call_med->rtp_tx_seq_ts_set;
561
562#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
563 /* Enable/disable stream keep-alive and NAT hole punch. */
564 si->use_ka = pjsua_var.acc[call->acc_id].cfg.use_stream_ka;
565#endif
566
567 /* Try to get shared format ID between the capture device and
568 * the encoder to avoid format conversion in the capture device.
569 */
570 if (si->dir & PJMEDIA_DIR_ENCODING) {
571 pjmedia_vid_dev_info dev_info;
572 pjmedia_vid_codec_info *codec_info = &si->codec_info;
573 unsigned i, j;
574
575 status = pjmedia_vid_dev_get_info(acc->cfg.vid_cap_dev, &dev_info);
576 if (status != PJ_SUCCESS)
577 return status;
578
579 /* Find matched format ID */
580 for (i = 0; i < codec_info->dec_fmt_id_cnt; ++i) {
581 for (j = 0; j < dev_info.fmt_cnt; ++j) {
582 if (codec_info->dec_fmt_id[i] ==
583 (pjmedia_format_id)dev_info.fmt[j].id)
584 {
585 /* Apply the matched format ID to the codec */
586 si->codec_param->dec_fmt.id =
587 codec_info->dec_fmt_id[i];
588
589 /* Force outer loop to break */
590 i = codec_info->dec_fmt_id_cnt;
591 break;
592 }
593 }
594 }
595 }
596
597 /* Create session based on session info. */
598 status = pjmedia_vid_stream_create(pjsua_var.med_endpt, NULL, si,
599 call_med->tp, NULL,
600 &call_med->strm.v.stream);
601 if (status != PJ_SUCCESS)
602 return status;
603
604 /* Start stream */
605 status = pjmedia_vid_stream_start(call_med->strm.v.stream);
606 if (status != PJ_SUCCESS)
607 return status;
608
609 /* Setup decoding direction */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000610 if (si->dir & PJMEDIA_DIR_DECODING)
611 {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000612 pjsua_vid_win_id wid;
613 pjsua_vid_win *w;
614
615 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
616 PJMEDIA_DIR_DECODING,
617 &media_port);
618 if (status != PJ_SUCCESS)
619 return status;
620
621 /* Create stream video window */
622 status = create_vid_win(PJSUA_WND_TYPE_STREAM,
623 &media_port->info.fmt,
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000624 call_med->strm.v.rdr_dev,
625 //acc->cfg.vid_rend_dev,
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000626 PJSUA_INVALID_ID,
627 acc->cfg.vid_in_auto_show,
628 &wid);
629 if (status != PJ_SUCCESS)
630 return status;
631
632 w = &pjsua_var.win[wid];
633
634 /* Connect renderer to stream */
635 status = pjmedia_vid_port_connect(w->vp_rend, media_port,
636 PJ_FALSE);
637 if (status != PJ_SUCCESS)
638 return status;
639
640 /* Start renderer */
641 status = pjmedia_vid_port_start(w->vp_rend);
642 if (status != PJ_SUCCESS)
643 return status;
644
645 /* Done */
646 inc_vid_win(wid);
647 call_med->strm.v.rdr_win_id = wid;
648 }
649
650 /* Setup encoding direction */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000651 if (si->dir & PJMEDIA_DIR_ENCODING && !call->local_hold)
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000652 {
653 pjsua_vid_win *w;
654 pjsua_vid_win_id wid;
655
656 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
657 PJMEDIA_DIR_ENCODING,
658 &media_port);
659 if (status != PJ_SUCCESS)
660 return status;
661
662 /* Create preview video window */
663 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
664 &media_port->info.fmt,
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000665 call_med->strm.v.rdr_dev,
666 call_med->strm.v.cap_dev,
667 //acc->cfg.vid_rend_dev,
668 //acc->cfg.vid_cap_dev,
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000669 PJ_FALSE,
670 &wid);
671 if (status != PJ_SUCCESS)
672 return status;
673
674 w = &pjsua_var.win[wid];
675
676 /* Connect stream to capturer (via video window tee) */
677 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
678 if (status != PJ_SUCCESS)
679 return status;
680
681 /* Start renderer */
682 status = pjmedia_vid_port_start(w->vp_rend);
683 if (status != PJ_SUCCESS)
684 return status;
685
686 /* Start capturer */
687 status = pjmedia_vid_port_start(w->vp_cap);
688 if (status != PJ_SUCCESS)
689 return status;
690
691 /* Done */
692 inc_vid_win(wid);
693 call_med->strm.v.cap_win_id = wid;
694 }
695
696 /* Call media direction */
697 call_med->dir = si->dir;
698
699 /* Call media state */
700 if (call->local_hold)
701 call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
702 else if (call_med->dir == PJMEDIA_DIR_DECODING)
703 call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
704 else
705 call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
706 }
707
708 /* Print info. */
709 {
710 char info[80];
711 int info_len = 0;
712 int len;
713 const char *dir;
714
715 switch (si->dir) {
716 case PJMEDIA_DIR_NONE:
717 dir = "inactive";
718 break;
719 case PJMEDIA_DIR_ENCODING:
720 dir = "sendonly";
721 break;
722 case PJMEDIA_DIR_DECODING:
723 dir = "recvonly";
724 break;
725 case PJMEDIA_DIR_ENCODING_DECODING:
726 dir = "sendrecv";
727 break;
728 default:
729 dir = "unknown";
730 break;
731 }
732 len = pj_ansi_sprintf( info+info_len,
733 ", stream #%d: %.*s (%s)", strm_idx,
734 (int)si->codec_info.encoding_name.slen,
735 si->codec_info.encoding_name.ptr,
736 dir);
737 if (len > 0)
738 info_len += len;
739 PJ_LOG(4,(THIS_FILE,"Media updates%s", info));
740 }
741
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000742 if (acc->cfg.vid_out_auto_transmit) {
743 status = pjmedia_vid_stream_pause(call_med->strm.v.stream,
744 PJMEDIA_DIR_ENCODING);
745 if (status != PJ_SUCCESS)
746 return status;
747 }
748
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000749 return PJ_SUCCESS;
750}
751
752
753/* Internal function to stop video stream */
754void stop_video_stream(pjsua_call_media *call_med)
755{
756 pjmedia_vid_stream *strm = call_med->strm.v.stream;
757 pjmedia_rtcp_stat stat;
758
759 pj_assert(call_med->type == PJMEDIA_TYPE_VIDEO);
760
761 if (!strm)
762 return;
763
764 if (call_med->strm.v.cap_win_id != PJSUA_INVALID_ID) {
765 pjmedia_port *media_port;
766 pjsua_vid_win *w =
767 &pjsua_var.win[call_med->strm.v.cap_win_id];
768
769 pjmedia_vid_stream_get_port(call_med->strm.v.stream,
770 PJMEDIA_DIR_ENCODING,
771 &media_port);
772 pj_assert(media_port);
773
774 pjmedia_vid_port_stop(w->vp_cap);
775 pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
776 pjmedia_vid_port_start(w->vp_cap);
777
778 dec_vid_win(call_med->strm.v.cap_win_id);
779 }
780
781 if (call_med->strm.v.rdr_win_id != PJSUA_INVALID_ID) {
782 dec_vid_win(call_med->strm.v.rdr_win_id);
783 }
784
785 if ((call_med->dir & PJMEDIA_DIR_ENCODING) &&
786 (pjmedia_vid_stream_get_stat(strm, &stat) == PJ_SUCCESS))
787 {
788 /* Save RTP timestamp & sequence, so when media session is
789 * restarted, those values will be restored as the initial
790 * RTP timestamp & sequence of the new media session. So in
791 * the same call session, RTP timestamp and sequence are
792 * guaranteed to be contigue.
793 */
794 call_med->rtp_tx_seq_ts_set = 1 | (1 << 1);
795 call_med->rtp_tx_seq = stat.rtp_tx_last_seq;
796 call_med->rtp_tx_ts = stat.rtp_tx_last_ts;
797 }
798
799 pjmedia_vid_stream_destroy(strm);
800 call_med->strm.v.stream = NULL;
801}
802
803
Benny Prijono9f468d12011-07-07 07:46:33 +0000804/*
805 * Start video preview window for the specified capture device.
806 */
807PJ_DEF(pj_status_t) pjsua_vid_preview_start(pjmedia_vid_dev_index id,
808 pjsua_vid_preview_param *prm)
809{
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000810 pjsua_vid_win_id wid;
811 pjsua_vid_win *w;
812 pjmedia_vid_dev_index rend_id;
Benny Prijono9f468d12011-07-07 07:46:33 +0000813 pj_status_t status;
814
815 PJSUA_LOCK();
816
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000817 if (prm) {
818 rend_id = prm->rend_id;
819 } else {
820 rend_id = PJMEDIA_VID_DEFAULT_RENDER_DEV;
Benny Prijono9f468d12011-07-07 07:46:33 +0000821 }
822
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000823 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW, NULL, rend_id, id,
824 PJ_TRUE, &wid);
825 if (status != PJ_SUCCESS) {
Benny Prijono9f468d12011-07-07 07:46:33 +0000826 PJSUA_UNLOCK();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000827 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000828 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000829
Benny Prijono9f468d12011-07-07 07:46:33 +0000830 w = &pjsua_var.win[wid];
831
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000832 /* Start capturer */
833 status = pjmedia_vid_port_start(w->vp_rend);
834 if (status != PJ_SUCCESS) {
835 PJSUA_UNLOCK();
836 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000837 }
838
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000839 /* Start renderer */
Benny Prijono9f468d12011-07-07 07:46:33 +0000840 status = pjmedia_vid_port_start(w->vp_cap);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000841 if (status != PJ_SUCCESS) {
842 PJSUA_UNLOCK();
843 return status;
844 }
Benny Prijono9f468d12011-07-07 07:46:33 +0000845
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000846 inc_vid_win(wid);
847
Benny Prijono9f468d12011-07-07 07:46:33 +0000848 PJSUA_UNLOCK();
849 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +0000850}
851
852/*
853 * Stop video preview.
854 */
855PJ_DEF(pj_status_t) pjsua_vid_preview_stop(pjmedia_vid_dev_index id)
856{
857 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
Benny Prijono9f468d12011-07-07 07:46:33 +0000858
859 PJSUA_LOCK();
860 wid = pjsua_vid_preview_get_win(id);
861 if (wid == PJSUA_INVALID_ID) {
862 PJSUA_UNLOCK();
863 return PJ_ENOTFOUND;
864 }
865
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000866 dec_vid_win(wid);
Benny Prijono9f468d12011-07-07 07:46:33 +0000867
868 PJSUA_UNLOCK();
869
870 return PJ_SUCCESS;
871}
872
873
874/*****************************************************************************
875 * Window
876 */
877
878/*
879 * Get window info.
880 */
881PJ_DEF(pj_status_t) pjsua_vid_win_get_info( pjsua_vid_win_id wid,
882 pjsua_vid_win_info *wi)
883{
884 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000885 pjmedia_vid_dev_stream *s;
886 pjmedia_vid_param vparam;
887 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000888
889 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && wi, PJ_EINVAL);
890
891 PJSUA_LOCK();
892 w = &pjsua_var.win[wid];
893 if (w->vp_rend == NULL) {
894 PJSUA_UNLOCK();
895 return PJ_EINVAL;
896 }
897
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000898 s = pjmedia_vid_port_get_stream(w->vp_rend);
899 if (s == NULL) {
900 PJSUA_UNLOCK();
901 return PJ_EINVAL;
902 }
903
904 status = pjmedia_vid_dev_stream_get_param(s, &vparam);
905 if (status != PJ_SUCCESS) {
906 PJSUA_UNLOCK();
907 return status;
908 }
909
910 wi->show = !vparam.window_hide;
911 wi->pos = vparam.window_pos;
912 wi->size = vparam.disp_size;
913
Benny Prijono9f468d12011-07-07 07:46:33 +0000914 PJSUA_UNLOCK();
915
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000916 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +0000917}
918
919/*
920 * Show or hide window.
921 */
922PJ_DEF(pj_status_t) pjsua_vid_win_set_show( pjsua_vid_win_id wid,
923 pj_bool_t show)
924{
925 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000926 pjmedia_vid_dev_stream *s;
927 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000928
929 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
930
931 PJSUA_LOCK();
932 w = &pjsua_var.win[wid];
933 if (w->vp_rend == NULL) {
934 PJSUA_UNLOCK();
935 return PJ_EINVAL;
936 }
937
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000938 s = pjmedia_vid_port_get_stream(w->vp_rend);
939 if (s == NULL) {
940 PJSUA_UNLOCK();
941 return PJ_EINVAL;
942 }
943
944 status = pjmedia_vid_dev_stream_set_cap(s,
945 PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE, &show);
946
Benny Prijono9f468d12011-07-07 07:46:33 +0000947 PJSUA_UNLOCK();
948
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000949 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000950}
951
952/*
953 * Set video window position.
954 */
955PJ_DEF(pj_status_t) pjsua_vid_win_set_pos( pjsua_vid_win_id wid,
956 const pjmedia_coord *pos)
957{
958 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000959 pjmedia_vid_dev_stream *s;
960 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000961
962 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && pos, PJ_EINVAL);
963
964 PJSUA_LOCK();
965 w = &pjsua_var.win[wid];
966 if (w->vp_rend == NULL) {
967 PJSUA_UNLOCK();
968 return PJ_EINVAL;
969 }
970
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000971 s = pjmedia_vid_port_get_stream(w->vp_rend);
972 if (s == NULL) {
973 PJSUA_UNLOCK();
974 return PJ_EINVAL;
975 }
976
977 status = pjmedia_vid_dev_stream_set_cap(s,
978 PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION, &pos);
979
Benny Prijono9f468d12011-07-07 07:46:33 +0000980 PJSUA_UNLOCK();
981
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000982 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000983}
984
985/*
986 * Resize window.
987 */
988PJ_DEF(pj_status_t) pjsua_vid_win_set_size( pjsua_vid_win_id wid,
989 const pjmedia_rect_size *size)
990{
991 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000992 pjmedia_vid_dev_stream *s;
993 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000994
995 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && size, PJ_EINVAL);
996
997 PJSUA_LOCK();
998 w = &pjsua_var.win[wid];
999 if (w->vp_rend == NULL) {
1000 PJSUA_UNLOCK();
1001 return PJ_EINVAL;
1002 }
1003
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001004 s = pjmedia_vid_port_get_stream(w->vp_rend);
1005 if (s == NULL) {
1006 PJSUA_UNLOCK();
1007 return PJ_EINVAL;
1008 }
1009
1010 status = pjmedia_vid_dev_stream_set_cap(s,
1011 PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE, &size);
1012
Benny Prijono9f468d12011-07-07 07:46:33 +00001013 PJSUA_UNLOCK();
1014
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001015 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001016}
1017
1018
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001019static void call_get_vid_strm_info(pjsua_call *call,
1020 int *first_active,
1021 int *first_inactive,
1022 unsigned *active_cnt,
1023 unsigned *cnt)
1024{
1025 unsigned i, var_cnt = 0;
1026
1027 if (first_active && ++var_cnt)
1028 *first_active = -1;
1029 if (first_inactive && ++var_cnt)
1030 *first_inactive = -1;
1031 if (active_cnt && ++var_cnt)
1032 *active_cnt = 0;
1033 if (cnt && ++var_cnt)
1034 *cnt = 0;
1035
1036 for (i = 0; i < call->med_cnt && var_cnt; ++i) {
1037 if (call->media[i].type == PJMEDIA_TYPE_VIDEO) {
1038 if (call->media[i].dir != PJMEDIA_DIR_NONE)
1039 {
1040 if (first_active && *first_active == -1) {
1041 *first_active = i;
1042 --var_cnt;
1043 }
1044 if (active_cnt)
1045 ++(*active_cnt);
1046 } else if (first_inactive && *first_inactive == -1) {
1047 *first_inactive = i;
1048 --var_cnt;
1049 }
1050 if (cnt)
1051 ++(*cnt);
1052 }
1053 }
1054}
1055
1056
1057/* Send SDP reoffer. */
1058static pj_status_t call_reoffer_sdp(pjsua_call_id call_id,
1059 const pjmedia_sdp_session *sdp)
1060{
1061 pjsua_call *call;
1062 pjsip_tx_data *tdata;
1063 pjsip_dialog *dlg;
1064 pj_status_t status;
1065
1066 status = acquire_call("call_reoffer_sdp()", call_id, &call, &dlg);
1067 if (status != PJ_SUCCESS)
1068 return status;
1069
1070 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1071 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
1072 pjsip_dlg_dec_lock(dlg);
1073 return PJSIP_ESESSIONSTATE;
1074 }
1075
1076 /* Create re-INVITE with new offer */
1077 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1078 if (status != PJ_SUCCESS) {
1079 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
1080 pjsip_dlg_dec_lock(dlg);
1081 return status;
1082 }
1083
1084 /* Send the request */
1085 status = pjsip_inv_send_msg( call->inv, tdata);
1086 if (status != PJ_SUCCESS) {
1087 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
1088 pjsip_dlg_dec_lock(dlg);
1089 return status;
1090 }
1091
1092 pjsip_dlg_dec_lock(dlg);
1093
1094 return PJ_SUCCESS;
1095}
1096
1097
1098pj_status_t pjsua_call_media_init(pjsua_call_media *call_med,
1099 pjmedia_type type,
1100 const pjsua_transport_config *tcfg,
1101 int security_level,
1102 int *sip_err_code);
1103
1104
1105/* Add a new video stream into a call */
1106static pj_status_t call_add_video(pjsua_call *call,
1107 pjmedia_vid_dev_index cap_dev)
1108{
1109 pj_pool_t *pool = call->inv->pool_prov;
1110 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1111 pjsua_call_media *call_med;
1112 pjmedia_sdp_session *sdp;
1113 pjmedia_sdp_media *sdp_m;
1114 pjmedia_transport_info tpinfo;
1115 pjmedia_vid_dev_info vinfo;
1116 unsigned active_cnt;
1117 pj_status_t status;
1118
1119 /* Verify media slot availability */
1120 if (call->med_cnt == PJSUA_MAX_CALL_MEDIA)
1121 return PJ_ETOOMANY;
1122
1123 call_get_vid_strm_info(call, NULL, NULL, &active_cnt, NULL);
1124 if (active_cnt == acc_cfg->max_video_cnt)
1125 return PJ_ETOOMANY;
1126
1127 /* Verify the capture device */
1128 status = pjmedia_vid_dev_get_info(cap_dev, &vinfo);
1129 if (status != PJ_SUCCESS)
1130 return status;
1131
1132 if (vinfo.dir != PJMEDIA_DIR_CAPTURE)
1133 return PJ_EINVAL;
1134
1135 cap_dev = vinfo.id;
1136
1137 /* Get active local SDP */
1138 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
1139 if (status != PJ_SUCCESS)
1140 return status;
1141
1142 /* Initialize call media */
1143 call_med = &call->media[call->med_cnt++];
1144
1145 status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1146 &acc_cfg->rtp_cfg, call->secure_level,
1147 NULL);
1148 if (status != PJ_SUCCESS)
1149 goto on_error;
1150
1151 /* Override default capture device setting */
1152 call_med->strm.v.cap_dev = cap_dev;
1153
1154 /* Init transport media */
1155 status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1156 NULL, call_med->idx);
1157 if (status != PJ_SUCCESS)
1158 goto on_error;
1159
1160 call_med->tp_st = PJSUA_MED_TP_INIT;
1161
1162 /* Get transport address info */
1163 pjmedia_transport_info_init(&tpinfo);
1164 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1165
1166 /* Create SDP media line */
1167 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1168 &tpinfo.sock_info, 0, &sdp_m);
1169 if (status != PJ_SUCCESS)
1170 goto on_error;
1171
1172 sdp->media[sdp->media_count++] = sdp_m;
1173
1174 /* Update SDP media line by media transport */
1175 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1176 sdp, NULL, call_med->idx);
1177 if (status != PJ_SUCCESS)
1178 goto on_error;
1179
1180 status = call_reoffer_sdp(call->index, sdp);
1181 if (status != PJ_SUCCESS)
1182 goto on_error;
1183
1184 return PJ_SUCCESS;
1185
1186on_error:
1187 if (call_med->tp) {
1188 pjmedia_transport_close(call_med->tp);
1189 call_med->tp = call_med->tp_orig = NULL;
1190 }
1191
1192 return status;
1193}
1194
1195
1196/* Remove a video stream from a call */
1197static pj_status_t call_remove_video(pjsua_call *call,
1198 int med_idx)
1199{
1200 pjsua_call_media *call_med;
1201 pjmedia_sdp_session *sdp;
1202 pj_status_t status;
1203
1204 /* Verify and normalize media index */
1205 if (med_idx == -1) {
1206 int first_active;
1207
1208 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1209 if (first_active == -1)
1210 return PJ_ENOTFOUND;
1211
1212 med_idx = first_active;
1213 }
1214
1215 call_med = &call->media[med_idx];
1216
1217 /* Verify if the stream media type is video */
1218 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1219 return PJ_EINVAL;
1220
1221 /* Verify if the stream already disabled */
1222 if (call_med->dir != PJMEDIA_DIR_NONE)
1223 return PJ_SUCCESS;
1224
1225 /* Mark media transport to disabled */
1226 // Don't close this here, as SDP negotiation has not been
1227 // done and stream may be still active.
1228 call_med->tp_st = PJSUA_MED_TP_DISABLED;
1229
1230 /* Get active local SDP */
1231 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
1232 if (status != PJ_SUCCESS)
1233 return status;
1234
1235 pj_assert(med_idx < (int)sdp->media_count);
1236 sdp->media[med_idx]->desc.port = 0;
1237
1238 status = call_reoffer_sdp(call->index, sdp);
1239 if (status != PJ_SUCCESS)
1240 return status;
1241
1242 return PJ_SUCCESS;
1243}
1244
1245
1246/* Modify a video stream in a call */
1247static pj_status_t call_modify_video(pjsua_call *call,
1248 int med_idx,
1249 pjmedia_vid_dev_index cap_dev)
1250{
1251 pjsua_call_media *call_med;
1252 pjmedia_vid_dev_info info;
1253 pjsua_vid_win *w, *new_w = NULL;
1254 pjsua_vid_win_id wid, new_wid;
1255 pjmedia_port *media_port;
1256 pj_status_t status;
1257
1258 /* Verify and normalize media index */
1259 if (med_idx == -1) {
1260 int first_active;
1261
1262 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1263 if (first_active == -1)
1264 return PJ_ENOTFOUND;
1265
1266 med_idx = first_active;
1267 }
1268
1269 call_med = &call->media[med_idx];
1270
1271 /* Verify if the stream media type is video */
1272 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1273 return PJ_EINVAL;
1274
1275 /* Verify the capture device */
1276 status = pjmedia_vid_dev_get_info(cap_dev, &info);
1277 if (status != PJ_SUCCESS)
1278 return status;
1279
1280 if (info.dir != PJMEDIA_DIR_CAPTURE)
1281 return PJ_EINVAL;
1282
1283 cap_dev = info.id;
1284
1285 /* The specified capture device is being used already */
1286 if (call_med->strm.v.cap_dev == cap_dev)
1287 return PJ_SUCCESS;
1288
1289 /* == Apply the new capture device == */
1290
1291 wid = call_med->strm.v.cap_win_id;
1292 w = &pjsua_var.win[wid];
1293 pj_assert(w->type == PJSUA_WND_TYPE_PREVIEW && w->vp_cap);
1294
1295 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
1296 PJMEDIA_DIR_ENCODING, &media_port);
1297 if (status != PJ_SUCCESS)
1298 return status;
1299
1300 /* = Detach stream port from the old capture device = */
1301 status = pjmedia_vid_port_disconnect(w->vp_cap);
1302 if (status != PJ_SUCCESS)
1303 return status;
1304
1305 status = pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
1306 if (status != PJ_SUCCESS) {
1307 /* Connect back the old capturer */
1308 pjmedia_vid_port_connect(w->vp_cap, media_port, PJ_FALSE);
1309 return status;
1310 }
1311
1312 /* = Attach stream port to the new capture device = */
1313
1314 /* Create preview video window */
1315 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
1316 &media_port->info.fmt,
1317 call_med->strm.v.rdr_dev,
1318 cap_dev,
1319 PJ_FALSE,
1320 &new_wid);
1321 if (status != PJ_SUCCESS)
1322 goto on_error;
1323
1324 inc_vid_win(new_wid);
1325 new_w = &pjsua_var.win[new_wid];
1326
1327 /* Connect stream to capturer (via video window tee) */
1328 status = pjmedia_vid_tee_add_dst_port2(new_w->tee, 0, media_port);
1329 if (status != PJ_SUCCESS)
1330 goto on_error;
1331
1332 /* Connect capturer to tee */
1333 status = pjmedia_vid_port_connect(new_w->vp_cap, new_w->tee, PJ_FALSE);
1334 if (status != PJ_SUCCESS)
1335 return status;
1336
1337 /* Start renderer */
1338 status = pjmedia_vid_port_start(new_w->vp_rend);
1339 if (status != PJ_SUCCESS)
1340 goto on_error;
1341
1342 /* Start capturer */
1343 status = pjmedia_vid_port_start(new_w->vp_cap);
1344 if (status != PJ_SUCCESS)
1345 goto on_error;
1346
1347 /* Finally */
1348 call_med->strm.v.cap_dev = cap_dev;
1349 call_med->strm.v.cap_win_id = new_wid;
1350 dec_vid_win(wid);
1351
1352 return PJ_SUCCESS;
1353
1354on_error:
1355 if (new_w) {
1356 /* Disconnect media port from the new capturer */
1357 pjmedia_vid_tee_remove_dst_port(new_w->tee, media_port);
1358 /* Release the new capturer */
1359 dec_vid_win(new_wid);
1360 }
1361
1362 /* Revert back to the old capturer */
1363 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
1364 if (status != PJ_SUCCESS)
1365 return status;
1366
1367 status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
1368 if (status != PJ_SUCCESS)
1369 return status;
1370
1371 return status;
1372}
1373
1374
1375/* Start transmitting video stream in a call */
1376static pj_status_t call_start_tx_video(pjsua_call *call,
1377 int med_idx,
1378 pjmedia_vid_dev_index cap_dev)
1379{
1380 pjsua_call_media *call_med;
1381 pj_status_t status;
1382
1383 /* Verify and normalize media index */
1384 if (med_idx == -1) {
1385 int first_active;
1386
1387 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1388 if (first_active == -1)
1389 return PJ_ENOTFOUND;
1390
1391 med_idx = first_active;
1392 }
1393
1394 call_med = &call->media[med_idx];
1395
1396 /* Verify if the stream is transmitting video */
1397 if (call_med->type != PJMEDIA_TYPE_VIDEO ||
1398 (call_med->dir & PJMEDIA_DIR_ENCODING) == 0)
1399 {
1400 return PJ_EINVAL;
1401 }
1402
1403 /* Apply the new capture device */
1404 status = call_modify_video(call, med_idx, cap_dev);
1405 if (status != PJ_SUCCESS)
1406 return status;
1407
1408 /* Start stream in encoding direction */
1409 status = pjmedia_vid_stream_resume(call_med->strm.v.stream,
1410 PJMEDIA_DIR_ENCODING);
1411 if (status != PJ_SUCCESS)
1412 return status;
1413
1414 return PJ_SUCCESS;
1415}
1416
1417
1418/* Stop transmitting video stream in a call */
1419static pj_status_t call_stop_tx_video(pjsua_call *call,
1420 int med_idx)
1421{
1422 pjsua_call_media *call_med;
1423 pj_status_t status;
1424
1425 /* Verify and normalize media index */
1426 if (med_idx == -1) {
1427 int first_active;
1428
1429 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1430 if (first_active == -1)
1431 return PJ_ENOTFOUND;
1432
1433 med_idx = first_active;
1434 }
1435
1436 call_med = &call->media[med_idx];
1437
1438 /* Verify if the stream is transmitting video */
1439 if (call_med->type != PJMEDIA_TYPE_VIDEO ||
1440 (call_med->dir & PJMEDIA_DIR_ENCODING) == 0)
1441 {
1442 return PJ_EINVAL;
1443 }
1444
1445 /* Pause stream in encoding direction */
1446 status = pjmedia_vid_stream_pause( call_med->strm.v.stream,
1447 PJMEDIA_DIR_ENCODING);
1448 if (status != PJ_SUCCESS)
1449 return status;
1450
1451 return PJ_SUCCESS;
1452}
1453
1454
1455/*
1456 * Start, stop, and/or manipulate video transmission for the specified call.
1457 */
1458PJ_DEF(pj_status_t) pjsua_call_set_vid_strm (
1459 pjsua_call_id call_id,
1460 pjsua_call_vid_strm_op op,
1461 const pjsua_call_vid_strm_op_param *param)
1462{
1463 pjsua_call *call;
1464 pjsua_call_vid_strm_op_param param_;
1465 pj_status_t status;
1466
1467 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1468 PJ_EINVAL);
1469
1470 PJSUA_LOCK();
1471
1472 call = &pjsua_var.calls[call_id];
1473
1474 if (param) {
1475 param_ = *param;
1476 } else {
1477 param_.med_idx = -1;
1478 param_.cap_dev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
1479 }
1480
1481 /* Get real capture ID, if set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV */
1482 if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
1483 pjmedia_vid_dev_info info;
1484 pjmedia_vid_dev_get_info(param_.cap_dev, &info);
1485 param_.cap_dev = info.id;
1486 }
1487
1488 switch (op) {
1489 case PJSUA_CALL_VID_STRM_ADD:
1490 status = call_add_video(call, param_.cap_dev);
1491 break;
1492 case PJSUA_CALL_VID_STRM_REMOVE:
1493 status = call_remove_video(call, param_.med_idx);
1494 break;
1495 case PJSUA_CALL_VID_STRM_MODIFY:
1496 status = call_modify_video(call, param_.med_idx, param_.cap_dev);
1497 break;
1498 case PJSUA_CALL_VID_STRM_START_TRANSMIT:
1499 status = call_start_tx_video(call, param_.med_idx, param_.cap_dev);
1500 break;
1501 case PJSUA_CALL_VID_STRM_STOP_TRANSMIT:
1502 status = call_stop_tx_video(call, param_.med_idx);
1503 break;
1504 default:
1505 status = PJ_EINVALIDOP;
1506 break;
1507 }
1508
1509 PJSUA_UNLOCK();
1510
1511 return status;
1512}
1513
Benny Prijono9f468d12011-07-07 07:46:33 +00001514
1515#endif /* PJSUA_HAS_VIDEO */
1516