blob: 169b86beb15647a3ff6e280d72f77a2419dbc406 [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
634 w = &pjsua_var.win[wid];
635
636 /* Connect renderer to stream */
637 status = pjmedia_vid_port_connect(w->vp_rend, media_port,
638 PJ_FALSE);
639 if (status != PJ_SUCCESS)
640 return status;
641
642 /* Start renderer */
643 status = pjmedia_vid_port_start(w->vp_rend);
644 if (status != PJ_SUCCESS)
645 return status;
646
647 /* Done */
648 inc_vid_win(wid);
649 call_med->strm.v.rdr_win_id = wid;
650 }
651
652 /* Setup encoding direction */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000653 if (si->dir & PJMEDIA_DIR_ENCODING && !call->local_hold)
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000654 {
655 pjsua_vid_win *w;
656 pjsua_vid_win_id wid;
657
658 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
659 PJMEDIA_DIR_ENCODING,
660 &media_port);
661 if (status != PJ_SUCCESS)
662 return status;
663
664 /* Create preview video window */
665 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
666 &media_port->info.fmt,
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000667 call_med->strm.v.rdr_dev,
668 call_med->strm.v.cap_dev,
669 //acc->cfg.vid_rend_dev,
670 //acc->cfg.vid_cap_dev,
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000671 PJ_FALSE,
672 &wid);
673 if (status != PJ_SUCCESS)
674 return status;
675
676 w = &pjsua_var.win[wid];
677
678 /* Connect stream to capturer (via video window tee) */
679 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
680 if (status != PJ_SUCCESS)
681 return status;
682
683 /* Start renderer */
684 status = pjmedia_vid_port_start(w->vp_rend);
685 if (status != PJ_SUCCESS)
686 return status;
687
688 /* Start capturer */
689 status = pjmedia_vid_port_start(w->vp_cap);
690 if (status != PJ_SUCCESS)
691 return status;
692
693 /* Done */
694 inc_vid_win(wid);
695 call_med->strm.v.cap_win_id = wid;
696 }
697
698 /* Call media direction */
699 call_med->dir = si->dir;
700
701 /* Call media state */
702 if (call->local_hold)
703 call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
704 else if (call_med->dir == PJMEDIA_DIR_DECODING)
705 call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
706 else
707 call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
708 }
709
710 /* Print info. */
711 {
712 char info[80];
713 int info_len = 0;
714 int len;
715 const char *dir;
716
717 switch (si->dir) {
718 case PJMEDIA_DIR_NONE:
719 dir = "inactive";
720 break;
721 case PJMEDIA_DIR_ENCODING:
722 dir = "sendonly";
723 break;
724 case PJMEDIA_DIR_DECODING:
725 dir = "recvonly";
726 break;
727 case PJMEDIA_DIR_ENCODING_DECODING:
728 dir = "sendrecv";
729 break;
730 default:
731 dir = "unknown";
732 break;
733 }
734 len = pj_ansi_sprintf( info+info_len,
735 ", stream #%d: %.*s (%s)", strm_idx,
736 (int)si->codec_info.encoding_name.slen,
737 si->codec_info.encoding_name.ptr,
738 dir);
739 if (len > 0)
740 info_len += len;
741 PJ_LOG(4,(THIS_FILE,"Media updates%s", info));
742 }
743
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +0000744 if (!acc->cfg.vid_out_auto_transmit) {
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000745 status = pjmedia_vid_stream_pause(call_med->strm.v.stream,
746 PJMEDIA_DIR_ENCODING);
747 if (status != PJ_SUCCESS)
748 return status;
749 }
750
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000751 return PJ_SUCCESS;
752}
753
754
755/* Internal function to stop video stream */
756void stop_video_stream(pjsua_call_media *call_med)
757{
758 pjmedia_vid_stream *strm = call_med->strm.v.stream;
759 pjmedia_rtcp_stat stat;
760
761 pj_assert(call_med->type == PJMEDIA_TYPE_VIDEO);
762
763 if (!strm)
764 return;
765
766 if (call_med->strm.v.cap_win_id != PJSUA_INVALID_ID) {
767 pjmedia_port *media_port;
768 pjsua_vid_win *w =
769 &pjsua_var.win[call_med->strm.v.cap_win_id];
770
771 pjmedia_vid_stream_get_port(call_med->strm.v.stream,
772 PJMEDIA_DIR_ENCODING,
773 &media_port);
774 pj_assert(media_port);
775
776 pjmedia_vid_port_stop(w->vp_cap);
777 pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
778 pjmedia_vid_port_start(w->vp_cap);
779
780 dec_vid_win(call_med->strm.v.cap_win_id);
781 }
782
783 if (call_med->strm.v.rdr_win_id != PJSUA_INVALID_ID) {
784 dec_vid_win(call_med->strm.v.rdr_win_id);
785 }
786
787 if ((call_med->dir & PJMEDIA_DIR_ENCODING) &&
788 (pjmedia_vid_stream_get_stat(strm, &stat) == PJ_SUCCESS))
789 {
790 /* Save RTP timestamp & sequence, so when media session is
791 * restarted, those values will be restored as the initial
792 * RTP timestamp & sequence of the new media session. So in
793 * the same call session, RTP timestamp and sequence are
794 * guaranteed to be contigue.
795 */
796 call_med->rtp_tx_seq_ts_set = 1 | (1 << 1);
797 call_med->rtp_tx_seq = stat.rtp_tx_last_seq;
798 call_med->rtp_tx_ts = stat.rtp_tx_last_ts;
799 }
800
801 pjmedia_vid_stream_destroy(strm);
802 call_med->strm.v.stream = NULL;
803}
804
805
Benny Prijono9f468d12011-07-07 07:46:33 +0000806/*
807 * Start video preview window for the specified capture device.
808 */
809PJ_DEF(pj_status_t) pjsua_vid_preview_start(pjmedia_vid_dev_index id,
810 pjsua_vid_preview_param *prm)
811{
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000812 pjsua_vid_win_id wid;
813 pjsua_vid_win *w;
814 pjmedia_vid_dev_index rend_id;
Benny Prijono9f468d12011-07-07 07:46:33 +0000815 pj_status_t status;
816
817 PJSUA_LOCK();
818
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000819 if (prm) {
820 rend_id = prm->rend_id;
821 } else {
822 rend_id = PJMEDIA_VID_DEFAULT_RENDER_DEV;
Benny Prijono9f468d12011-07-07 07:46:33 +0000823 }
824
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000825 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW, NULL, rend_id, id,
826 PJ_TRUE, &wid);
827 if (status != PJ_SUCCESS) {
Benny Prijono9f468d12011-07-07 07:46:33 +0000828 PJSUA_UNLOCK();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000829 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000830 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000831
Benny Prijono9f468d12011-07-07 07:46:33 +0000832 w = &pjsua_var.win[wid];
833
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000834 /* Start capturer */
835 status = pjmedia_vid_port_start(w->vp_rend);
836 if (status != PJ_SUCCESS) {
837 PJSUA_UNLOCK();
838 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000839 }
840
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000841 /* Start renderer */
Benny Prijono9f468d12011-07-07 07:46:33 +0000842 status = pjmedia_vid_port_start(w->vp_cap);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000843 if (status != PJ_SUCCESS) {
844 PJSUA_UNLOCK();
845 return status;
846 }
Benny Prijono9f468d12011-07-07 07:46:33 +0000847
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000848 inc_vid_win(wid);
849
Benny Prijono9f468d12011-07-07 07:46:33 +0000850 PJSUA_UNLOCK();
851 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +0000852}
853
854/*
855 * Stop video preview.
856 */
857PJ_DEF(pj_status_t) pjsua_vid_preview_stop(pjmedia_vid_dev_index id)
858{
859 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
Benny Prijono9f468d12011-07-07 07:46:33 +0000860
861 PJSUA_LOCK();
862 wid = pjsua_vid_preview_get_win(id);
863 if (wid == PJSUA_INVALID_ID) {
864 PJSUA_UNLOCK();
865 return PJ_ENOTFOUND;
866 }
867
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000868 dec_vid_win(wid);
Benny Prijono9f468d12011-07-07 07:46:33 +0000869
870 PJSUA_UNLOCK();
871
872 return PJ_SUCCESS;
873}
874
875
876/*****************************************************************************
877 * Window
878 */
879
880/*
881 * Get window info.
882 */
883PJ_DEF(pj_status_t) pjsua_vid_win_get_info( pjsua_vid_win_id wid,
884 pjsua_vid_win_info *wi)
885{
886 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000887 pjmedia_vid_dev_stream *s;
888 pjmedia_vid_param vparam;
889 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000890
891 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && wi, PJ_EINVAL);
892
893 PJSUA_LOCK();
894 w = &pjsua_var.win[wid];
895 if (w->vp_rend == NULL) {
896 PJSUA_UNLOCK();
897 return PJ_EINVAL;
898 }
899
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000900 s = pjmedia_vid_port_get_stream(w->vp_rend);
901 if (s == NULL) {
902 PJSUA_UNLOCK();
903 return PJ_EINVAL;
904 }
905
906 status = pjmedia_vid_dev_stream_get_param(s, &vparam);
907 if (status != PJ_SUCCESS) {
908 PJSUA_UNLOCK();
909 return status;
910 }
911
912 wi->show = !vparam.window_hide;
913 wi->pos = vparam.window_pos;
914 wi->size = vparam.disp_size;
915
Benny Prijono9f468d12011-07-07 07:46:33 +0000916 PJSUA_UNLOCK();
917
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000918 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +0000919}
920
921/*
922 * Show or hide window.
923 */
924PJ_DEF(pj_status_t) pjsua_vid_win_set_show( pjsua_vid_win_id wid,
925 pj_bool_t show)
926{
927 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000928 pjmedia_vid_dev_stream *s;
Nanang Izzuddinc16590a2011-07-14 02:14:25 +0000929 pj_bool_t hide;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000930 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000931
932 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
933
934 PJSUA_LOCK();
935 w = &pjsua_var.win[wid];
936 if (w->vp_rend == NULL) {
937 PJSUA_UNLOCK();
938 return PJ_EINVAL;
939 }
940
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000941 s = pjmedia_vid_port_get_stream(w->vp_rend);
942 if (s == NULL) {
943 PJSUA_UNLOCK();
944 return PJ_EINVAL;
945 }
946
Nanang Izzuddinc16590a2011-07-14 02:14:25 +0000947 hide = !show;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000948 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinc16590a2011-07-14 02:14:25 +0000949 PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE, &hide);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000950
Benny Prijono9f468d12011-07-07 07:46:33 +0000951 PJSUA_UNLOCK();
952
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000953 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000954}
955
956/*
957 * Set video window position.
958 */
959PJ_DEF(pj_status_t) pjsua_vid_win_set_pos( pjsua_vid_win_id wid,
960 const pjmedia_coord *pos)
961{
962 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000963 pjmedia_vid_dev_stream *s;
964 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000965
966 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && pos, PJ_EINVAL);
967
968 PJSUA_LOCK();
969 w = &pjsua_var.win[wid];
970 if (w->vp_rend == NULL) {
971 PJSUA_UNLOCK();
972 return PJ_EINVAL;
973 }
974
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000975 s = pjmedia_vid_port_get_stream(w->vp_rend);
976 if (s == NULL) {
977 PJSUA_UNLOCK();
978 return PJ_EINVAL;
979 }
980
981 status = pjmedia_vid_dev_stream_set_cap(s,
982 PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION, &pos);
983
Benny Prijono9f468d12011-07-07 07:46:33 +0000984 PJSUA_UNLOCK();
985
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000986 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000987}
988
989/*
990 * Resize window.
991 */
992PJ_DEF(pj_status_t) pjsua_vid_win_set_size( pjsua_vid_win_id wid,
993 const pjmedia_rect_size *size)
994{
995 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000996 pjmedia_vid_dev_stream *s;
997 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000998
999 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && size, PJ_EINVAL);
1000
1001 PJSUA_LOCK();
1002 w = &pjsua_var.win[wid];
1003 if (w->vp_rend == NULL) {
1004 PJSUA_UNLOCK();
1005 return PJ_EINVAL;
1006 }
1007
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001008 s = pjmedia_vid_port_get_stream(w->vp_rend);
1009 if (s == NULL) {
1010 PJSUA_UNLOCK();
1011 return PJ_EINVAL;
1012 }
1013
1014 status = pjmedia_vid_dev_stream_set_cap(s,
1015 PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE, &size);
1016
Benny Prijono9f468d12011-07-07 07:46:33 +00001017 PJSUA_UNLOCK();
1018
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001019 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001020}
1021
1022
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001023static void call_get_vid_strm_info(pjsua_call *call,
1024 int *first_active,
1025 int *first_inactive,
1026 unsigned *active_cnt,
1027 unsigned *cnt)
1028{
1029 unsigned i, var_cnt = 0;
1030
1031 if (first_active && ++var_cnt)
1032 *first_active = -1;
1033 if (first_inactive && ++var_cnt)
1034 *first_inactive = -1;
1035 if (active_cnt && ++var_cnt)
1036 *active_cnt = 0;
1037 if (cnt && ++var_cnt)
1038 *cnt = 0;
1039
1040 for (i = 0; i < call->med_cnt && var_cnt; ++i) {
1041 if (call->media[i].type == PJMEDIA_TYPE_VIDEO) {
1042 if (call->media[i].dir != PJMEDIA_DIR_NONE)
1043 {
1044 if (first_active && *first_active == -1) {
1045 *first_active = i;
1046 --var_cnt;
1047 }
1048 if (active_cnt)
1049 ++(*active_cnt);
1050 } else if (first_inactive && *first_inactive == -1) {
1051 *first_inactive = i;
1052 --var_cnt;
1053 }
1054 if (cnt)
1055 ++(*cnt);
1056 }
1057 }
1058}
1059
1060
1061/* Send SDP reoffer. */
1062static pj_status_t call_reoffer_sdp(pjsua_call_id call_id,
1063 const pjmedia_sdp_session *sdp)
1064{
1065 pjsua_call *call;
1066 pjsip_tx_data *tdata;
1067 pjsip_dialog *dlg;
1068 pj_status_t status;
1069
1070 status = acquire_call("call_reoffer_sdp()", call_id, &call, &dlg);
1071 if (status != PJ_SUCCESS)
1072 return status;
1073
1074 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1075 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
1076 pjsip_dlg_dec_lock(dlg);
1077 return PJSIP_ESESSIONSTATE;
1078 }
1079
1080 /* Create re-INVITE with new offer */
1081 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1082 if (status != PJ_SUCCESS) {
1083 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
1084 pjsip_dlg_dec_lock(dlg);
1085 return status;
1086 }
1087
1088 /* Send the request */
1089 status = pjsip_inv_send_msg( call->inv, tdata);
1090 if (status != PJ_SUCCESS) {
1091 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
1092 pjsip_dlg_dec_lock(dlg);
1093 return status;
1094 }
1095
1096 pjsip_dlg_dec_lock(dlg);
1097
1098 return PJ_SUCCESS;
1099}
1100
1101
1102pj_status_t pjsua_call_media_init(pjsua_call_media *call_med,
1103 pjmedia_type type,
1104 const pjsua_transport_config *tcfg,
1105 int security_level,
1106 int *sip_err_code);
1107
1108
1109/* Add a new video stream into a call */
1110static pj_status_t call_add_video(pjsua_call *call,
1111 pjmedia_vid_dev_index cap_dev)
1112{
1113 pj_pool_t *pool = call->inv->pool_prov;
1114 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1115 pjsua_call_media *call_med;
1116 pjmedia_sdp_session *sdp;
1117 pjmedia_sdp_media *sdp_m;
1118 pjmedia_transport_info tpinfo;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001119 unsigned active_cnt;
1120 pj_status_t status;
1121
1122 /* Verify media slot availability */
1123 if (call->med_cnt == PJSUA_MAX_CALL_MEDIA)
1124 return PJ_ETOOMANY;
1125
1126 call_get_vid_strm_info(call, NULL, NULL, &active_cnt, NULL);
1127 if (active_cnt == acc_cfg->max_video_cnt)
1128 return PJ_ETOOMANY;
1129
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001130 /* Get active local SDP */
1131 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
1132 if (status != PJ_SUCCESS)
1133 return status;
1134
1135 /* Initialize call media */
1136 call_med = &call->media[call->med_cnt++];
1137
1138 status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1139 &acc_cfg->rtp_cfg, call->secure_level,
1140 NULL);
1141 if (status != PJ_SUCCESS)
1142 goto on_error;
1143
1144 /* Override default capture device setting */
1145 call_med->strm.v.cap_dev = cap_dev;
1146
1147 /* Init transport media */
1148 status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1149 NULL, call_med->idx);
1150 if (status != PJ_SUCCESS)
1151 goto on_error;
1152
1153 call_med->tp_st = PJSUA_MED_TP_INIT;
1154
1155 /* Get transport address info */
1156 pjmedia_transport_info_init(&tpinfo);
1157 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1158
1159 /* Create SDP media line */
1160 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1161 &tpinfo.sock_info, 0, &sdp_m);
1162 if (status != PJ_SUCCESS)
1163 goto on_error;
1164
1165 sdp->media[sdp->media_count++] = sdp_m;
1166
1167 /* Update SDP media line by media transport */
1168 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1169 sdp, NULL, call_med->idx);
1170 if (status != PJ_SUCCESS)
1171 goto on_error;
1172
1173 status = call_reoffer_sdp(call->index, sdp);
1174 if (status != PJ_SUCCESS)
1175 goto on_error;
1176
1177 return PJ_SUCCESS;
1178
1179on_error:
1180 if (call_med->tp) {
1181 pjmedia_transport_close(call_med->tp);
1182 call_med->tp = call_med->tp_orig = NULL;
1183 }
1184
1185 return status;
1186}
1187
1188
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001189/* Modify a video stream from a call, i.e: enable/disable */
1190static pj_status_t call_modify_video(pjsua_call *call,
1191 int med_idx,
1192 pj_bool_t enable)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001193{
1194 pjsua_call_media *call_med;
1195 pjmedia_sdp_session *sdp;
1196 pj_status_t status;
1197
1198 /* Verify and normalize media index */
1199 if (med_idx == -1) {
1200 int first_active;
1201
1202 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1203 if (first_active == -1)
1204 return PJ_ENOTFOUND;
1205
1206 med_idx = first_active;
1207 }
1208
1209 call_med = &call->media[med_idx];
1210
1211 /* Verify if the stream media type is video */
1212 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1213 return PJ_EINVAL;
1214
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001215 /* Verify if the stream already enabled/disabled */
1216 if (( enable && call_med->dir != PJMEDIA_DIR_NONE) ||
1217 (!enable && call_med->dir == PJMEDIA_DIR_NONE))
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001218 return PJ_SUCCESS;
1219
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001220 /* Get active local SDP */
1221 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
1222 if (status != PJ_SUCCESS)
1223 return status;
1224
1225 pj_assert(med_idx < (int)sdp->media_count);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001226
1227 if (enable) {
1228 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1229 pj_pool_t *pool = call->inv->pool_prov;
1230 pjmedia_sdp_media *sdp_m;
1231 pjmedia_transport_info tpinfo;
1232
1233 status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1234 &acc_cfg->rtp_cfg, call->secure_level,
1235 NULL);
1236 if (status != PJ_SUCCESS)
1237 goto on_error;
1238
1239 /* Init transport media */
1240 status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1241 NULL, call_med->idx);
1242 if (status != PJ_SUCCESS)
1243 goto on_error;
1244
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001245 /* Get transport address info */
1246 pjmedia_transport_info_init(&tpinfo);
1247 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1248
1249 /* Create SDP media line */
1250 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1251 &tpinfo.sock_info, 0, &sdp_m);
1252 if (status != PJ_SUCCESS)
1253 goto on_error;
1254
1255 sdp->media[med_idx] = sdp_m;
1256
1257 /* Update SDP media line by media transport */
1258 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1259 sdp, NULL, call_med->idx);
1260 if (status != PJ_SUCCESS)
1261 goto on_error;
1262
1263on_error:
1264 if (status != PJ_SUCCESS) {
1265 if (call_med->tp) {
1266 pjmedia_transport_close(call_med->tp);
1267 call_med->tp = call_med->tp_orig = NULL;
1268 }
1269 return status;
1270 }
1271 } else {
1272 /* Mark media transport to disabled */
1273 // Don't close this here, as SDP negotiation has not been
1274 // done and stream may be still active.
1275 call_med->tp_st = PJSUA_MED_TP_DISABLED;
1276
1277 /* Disable the stream in SDP by setting port to 0 */
1278 sdp->media[med_idx]->desc.port = 0;
1279 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001280
1281 status = call_reoffer_sdp(call->index, sdp);
1282 if (status != PJ_SUCCESS)
1283 return status;
1284
1285 return PJ_SUCCESS;
1286}
1287
1288
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001289/* Change capture device of a video stream in a call */
1290static pj_status_t call_change_cap_dev(pjsua_call *call,
1291 int med_idx,
1292 pjmedia_vid_dev_index cap_dev)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001293{
1294 pjsua_call_media *call_med;
1295 pjmedia_vid_dev_info info;
1296 pjsua_vid_win *w, *new_w = NULL;
1297 pjsua_vid_win_id wid, new_wid;
1298 pjmedia_port *media_port;
1299 pj_status_t status;
1300
1301 /* Verify and normalize media index */
1302 if (med_idx == -1) {
1303 int first_active;
1304
1305 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1306 if (first_active == -1)
1307 return PJ_ENOTFOUND;
1308
1309 med_idx = first_active;
1310 }
1311
1312 call_med = &call->media[med_idx];
1313
1314 /* Verify if the stream media type is video */
1315 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1316 return PJ_EINVAL;
1317
1318 /* Verify the capture device */
1319 status = pjmedia_vid_dev_get_info(cap_dev, &info);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001320 if (status != PJ_SUCCESS || info.dir != PJMEDIA_DIR_CAPTURE)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001321 return PJ_EINVAL;
1322
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001323 /* The specified capture device is being used already */
1324 if (call_med->strm.v.cap_dev == cap_dev)
1325 return PJ_SUCCESS;
1326
1327 /* == Apply the new capture device == */
1328
1329 wid = call_med->strm.v.cap_win_id;
1330 w = &pjsua_var.win[wid];
1331 pj_assert(w->type == PJSUA_WND_TYPE_PREVIEW && w->vp_cap);
1332
1333 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
1334 PJMEDIA_DIR_ENCODING, &media_port);
1335 if (status != PJ_SUCCESS)
1336 return status;
1337
1338 /* = Detach stream port from the old capture device = */
1339 status = pjmedia_vid_port_disconnect(w->vp_cap);
1340 if (status != PJ_SUCCESS)
1341 return status;
1342
1343 status = pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
1344 if (status != PJ_SUCCESS) {
1345 /* Connect back the old capturer */
1346 pjmedia_vid_port_connect(w->vp_cap, media_port, PJ_FALSE);
1347 return status;
1348 }
1349
1350 /* = Attach stream port to the new capture device = */
1351
1352 /* Create preview video window */
1353 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
1354 &media_port->info.fmt,
1355 call_med->strm.v.rdr_dev,
1356 cap_dev,
1357 PJ_FALSE,
1358 &new_wid);
1359 if (status != PJ_SUCCESS)
1360 goto on_error;
1361
1362 inc_vid_win(new_wid);
1363 new_w = &pjsua_var.win[new_wid];
1364
1365 /* Connect stream to capturer (via video window tee) */
1366 status = pjmedia_vid_tee_add_dst_port2(new_w->tee, 0, media_port);
1367 if (status != PJ_SUCCESS)
1368 goto on_error;
1369
1370 /* Connect capturer to tee */
1371 status = pjmedia_vid_port_connect(new_w->vp_cap, new_w->tee, PJ_FALSE);
1372 if (status != PJ_SUCCESS)
1373 return status;
1374
1375 /* Start renderer */
1376 status = pjmedia_vid_port_start(new_w->vp_rend);
1377 if (status != PJ_SUCCESS)
1378 goto on_error;
1379
1380 /* Start capturer */
1381 status = pjmedia_vid_port_start(new_w->vp_cap);
1382 if (status != PJ_SUCCESS)
1383 goto on_error;
1384
1385 /* Finally */
1386 call_med->strm.v.cap_dev = cap_dev;
1387 call_med->strm.v.cap_win_id = new_wid;
1388 dec_vid_win(wid);
1389
1390 return PJ_SUCCESS;
1391
1392on_error:
1393 if (new_w) {
1394 /* Disconnect media port from the new capturer */
1395 pjmedia_vid_tee_remove_dst_port(new_w->tee, media_port);
1396 /* Release the new capturer */
1397 dec_vid_win(new_wid);
1398 }
1399
1400 /* Revert back to the old capturer */
1401 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
1402 if (status != PJ_SUCCESS)
1403 return status;
1404
1405 status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
1406 if (status != PJ_SUCCESS)
1407 return status;
1408
1409 return status;
1410}
1411
1412
1413/* Start transmitting video stream in a call */
1414static pj_status_t call_start_tx_video(pjsua_call *call,
1415 int med_idx,
1416 pjmedia_vid_dev_index cap_dev)
1417{
1418 pjsua_call_media *call_med;
1419 pj_status_t status;
1420
1421 /* Verify and normalize media index */
1422 if (med_idx == -1) {
1423 int first_active;
1424
1425 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1426 if (first_active == -1)
1427 return PJ_ENOTFOUND;
1428
1429 med_idx = first_active;
1430 }
1431
1432 call_med = &call->media[med_idx];
1433
1434 /* Verify if the stream is transmitting video */
1435 if (call_med->type != PJMEDIA_TYPE_VIDEO ||
1436 (call_med->dir & PJMEDIA_DIR_ENCODING) == 0)
1437 {
1438 return PJ_EINVAL;
1439 }
1440
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001441 /* Apply the capture device, it may be changed! */
1442 status = call_change_cap_dev(call, med_idx, cap_dev);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001443 if (status != PJ_SUCCESS)
1444 return status;
1445
1446 /* Start stream in encoding direction */
1447 status = pjmedia_vid_stream_resume(call_med->strm.v.stream,
1448 PJMEDIA_DIR_ENCODING);
1449 if (status != PJ_SUCCESS)
1450 return status;
1451
1452 return PJ_SUCCESS;
1453}
1454
1455
1456/* Stop transmitting video stream in a call */
1457static pj_status_t call_stop_tx_video(pjsua_call *call,
1458 int med_idx)
1459{
1460 pjsua_call_media *call_med;
1461 pj_status_t status;
1462
1463 /* Verify and normalize media index */
1464 if (med_idx == -1) {
1465 int first_active;
1466
1467 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1468 if (first_active == -1)
1469 return PJ_ENOTFOUND;
1470
1471 med_idx = first_active;
1472 }
1473
1474 call_med = &call->media[med_idx];
1475
1476 /* Verify if the stream is transmitting video */
1477 if (call_med->type != PJMEDIA_TYPE_VIDEO ||
1478 (call_med->dir & PJMEDIA_DIR_ENCODING) == 0)
1479 {
1480 return PJ_EINVAL;
1481 }
1482
1483 /* Pause stream in encoding direction */
1484 status = pjmedia_vid_stream_pause( call_med->strm.v.stream,
1485 PJMEDIA_DIR_ENCODING);
1486 if (status != PJ_SUCCESS)
1487 return status;
1488
1489 return PJ_SUCCESS;
1490}
1491
1492
1493/*
1494 * Start, stop, and/or manipulate video transmission for the specified call.
1495 */
1496PJ_DEF(pj_status_t) pjsua_call_set_vid_strm (
1497 pjsua_call_id call_id,
1498 pjsua_call_vid_strm_op op,
1499 const pjsua_call_vid_strm_op_param *param)
1500{
1501 pjsua_call *call;
1502 pjsua_call_vid_strm_op_param param_;
1503 pj_status_t status;
1504
1505 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1506 PJ_EINVAL);
1507
1508 PJSUA_LOCK();
1509
1510 call = &pjsua_var.calls[call_id];
1511
1512 if (param) {
1513 param_ = *param;
1514 } else {
1515 param_.med_idx = -1;
1516 param_.cap_dev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
1517 }
1518
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001519 /* If set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
1520 * account default video capture device.
1521 */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001522 if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001523 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1524 param_.cap_dev = acc_cfg->vid_cap_dev;
1525
1526 /* If the account default video capture device is
1527 * PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
1528 * global default video capture device.
1529 */
1530 if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
1531 pjmedia_vid_dev_info info;
1532 pjmedia_vid_dev_get_info(param_.cap_dev, &info);
1533 pj_assert(info.dir == PJMEDIA_DIR_CAPTURE);
1534 param_.cap_dev = info.id;
1535 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001536 }
1537
1538 switch (op) {
1539 case PJSUA_CALL_VID_STRM_ADD:
1540 status = call_add_video(call, param_.cap_dev);
1541 break;
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001542 case PJSUA_CALL_VID_STRM_ENABLE:
1543 status = call_modify_video(call, param_.med_idx, PJ_TRUE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001544 break;
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001545 case PJSUA_CALL_VID_STRM_DISABLE:
1546 status = call_modify_video(call, param_.med_idx, PJ_FALSE);
1547 break;
1548 case PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV:
1549 status = call_change_cap_dev(call, param_.med_idx, param_.cap_dev);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001550 break;
1551 case PJSUA_CALL_VID_STRM_START_TRANSMIT:
1552 status = call_start_tx_video(call, param_.med_idx, param_.cap_dev);
1553 break;
1554 case PJSUA_CALL_VID_STRM_STOP_TRANSMIT:
1555 status = call_stop_tx_video(call, param_.med_idx);
1556 break;
1557 default:
1558 status = PJ_EINVALIDOP;
1559 break;
1560 }
1561
1562 PJSUA_UNLOCK();
1563
1564 return status;
1565}
1566
Benny Prijono9f468d12011-07-07 07:46:33 +00001567
1568#endif /* PJSUA_HAS_VIDEO */
1569