blob: 72580f9ffb831dd3a486ee8ac661415e28e00651 [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
Benny Prijonoe212bc12011-08-15 09:38:42 +0000110PJ_DEF(void)
111pjsua_call_vid_strm_op_param_default(pjsua_call_vid_strm_op_param *param)
112{
113 pj_bzero(param, sizeof(*param));
114 param->med_idx = -1;
115 param->dir = PJMEDIA_DIR_ENCODING_DECODING;
116 param->cap_dev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
117}
Benny Prijono9f468d12011-07-07 07:46:33 +0000118
119/*****************************************************************************
120 * Devices.
121 */
122
123/*
124 * Get the number of video devices installed in the system.
125 */
126PJ_DEF(unsigned) pjsua_vid_dev_count(void)
127{
128 return pjmedia_vid_dev_count();
129}
130
131/*
132 * Retrieve the video device info for the specified device index.
133 */
134PJ_DEF(pj_status_t) pjsua_vid_dev_get_info(pjmedia_vid_dev_index id,
135 pjmedia_vid_dev_info *vdi)
136{
137 return pjmedia_vid_dev_get_info(id, vdi);
138}
139
140/*
141 * Enum all video devices installed in the system.
142 */
143PJ_DEF(pj_status_t) pjsua_vid_enum_devs(pjmedia_vid_dev_info info[],
144 unsigned *count)
145{
146 unsigned i, dev_count;
147
148 dev_count = pjmedia_vid_dev_count();
149
150 if (dev_count > *count) dev_count = *count;
151
152 for (i=0; i<dev_count; ++i) {
153 pj_status_t status;
154
155 status = pjmedia_vid_dev_get_info(i, &info[i]);
156 if (status != PJ_SUCCESS)
157 return status;
158 }
159
160 *count = dev_count;
161
162 return PJ_SUCCESS;
163}
164
165
166/*****************************************************************************
167 * Codecs.
168 */
169
170/*
171 * Enum all supported video codecs in the system.
172 */
173PJ_DEF(pj_status_t) pjsua_vid_enum_codecs( pjsua_codec_info id[],
174 unsigned *p_count )
175{
176 pjmedia_vid_codec_info info[32];
177 unsigned i, j, count, prio[32];
178 pj_status_t status;
179
180 count = PJ_ARRAY_SIZE(info);
181 status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, info, prio);
182 if (status != PJ_SUCCESS) {
183 *p_count = 0;
184 return status;
185 }
186
187 for (i=0, j=0; i<count && j<*p_count; ++i) {
188 if (info[i].has_rtp_pack) {
189 pj_bzero(&id[j], sizeof(pjsua_codec_info));
190
191 pjmedia_vid_codec_info_to_id(&info[i], id[j].buf_, sizeof(id[j].buf_));
192 id[j].codec_id = pj_str(id[j].buf_);
193 id[j].priority = (pj_uint8_t) prio[i];
194
195 if (id[j].codec_id.slen < sizeof(id[j].buf_)) {
196 id[j].desc.ptr = id[j].codec_id.ptr + id[j].codec_id.slen + 1;
197 pj_strncpy(&id[j].desc, &info[i].encoding_desc,
198 sizeof(id[j].buf_) - id[j].codec_id.slen - 1);
199 }
200
201 ++j;
202 }
203 }
204
205 *p_count = j;
206
207 return PJ_SUCCESS;
208}
209
210
211/*
212 * Change video codec priority.
213 */
214PJ_DEF(pj_status_t) pjsua_vid_codec_set_priority( const pj_str_t *codec_id,
215 pj_uint8_t priority )
216{
217 const pj_str_t all = { NULL, 0 };
218
219 if (codec_id->slen==1 && *codec_id->ptr=='*')
220 codec_id = &all;
221
222 return pjmedia_vid_codec_mgr_set_codec_priority(NULL, codec_id,
223 priority);
224}
225
226
227/*
228 * Get video codec parameters.
229 */
230PJ_DEF(pj_status_t) pjsua_vid_codec_get_param(
231 const pj_str_t *codec_id,
232 pjmedia_vid_codec_param *param)
233{
234 const pj_str_t all = { NULL, 0 };
235 const pjmedia_vid_codec_info *info;
236 unsigned count = 1;
237 pj_status_t status;
238
239 if (codec_id->slen==1 && *codec_id->ptr=='*')
240 codec_id = &all;
241
242 status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, codec_id,
243 &count, &info, NULL);
244 if (status != PJ_SUCCESS)
245 return status;
246
247 if (count != 1)
248 return (count > 1? PJ_ETOOMANY : PJ_ENOTFOUND);
249
250 status = pjmedia_vid_codec_mgr_get_default_param(NULL, info, param);
251 return status;
252}
253
254
255/*
256 * Set video codec parameters.
257 */
258PJ_DEF(pj_status_t) pjsua_vid_codec_set_param(
259 const pj_str_t *codec_id,
260 const pjmedia_vid_codec_param *param)
261{
262 const pjmedia_vid_codec_info *info[2];
263 unsigned count = 2;
264 pj_status_t status;
265
266 status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, codec_id,
267 &count, info, NULL);
268 if (status != PJ_SUCCESS)
269 return status;
270
271 /* Codec ID should be specific */
272 if (count > 1) {
273 pj_assert(!"Codec ID is not specific");
274 return PJ_ETOOMANY;
275 }
276
277 status = pjmedia_vid_codec_mgr_set_default_param(NULL, pjsua_var.pool,
278 info[0], param);
279 return status;
280}
281
282
283/*****************************************************************************
284 * Preview
285 */
286
287/*
288 * Get the preview window handle associated with the capture device, if any.
289 */
290PJ_DEF(pjsua_vid_win_id) pjsua_vid_preview_get_win(pjmedia_vid_dev_index id)
291{
292 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
293 unsigned i;
294
295 PJSUA_LOCK();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000296
297 /* Get real capture ID, if set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV */
298 if (id == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
299 pjmedia_vid_dev_info info;
300 pjmedia_vid_dev_get_info(id, &info);
301 id = info.id;
302 }
303
Benny Prijono9f468d12011-07-07 07:46:33 +0000304 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
305 pjsua_vid_win *w = &pjsua_var.win[i];
306 if (w->type == PJSUA_WND_TYPE_PREVIEW && w->preview_cap_id == id) {
307 wid = i;
308 break;
309 }
310 }
311 PJSUA_UNLOCK();
312
313 return wid;
314}
315
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000316
317/* Allocate and initialize pjsua video window:
318 * - If the type is preview, video capture, tee, and render
319 * will be instantiated.
320 * - If the type is stream, only renderer will be created.
321 */
322static pj_status_t create_vid_win(pjsua_vid_win_type type,
323 const pjmedia_format *fmt,
324 pjmedia_vid_dev_index rend_id,
325 pjmedia_vid_dev_index cap_id,
326 pj_bool_t show,
327 pjsua_vid_win_id *id)
Benny Prijono9f468d12011-07-07 07:46:33 +0000328{
329 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000330 pjsua_vid_win *w = NULL;
331 pjmedia_vid_port_param vp_param;
332 pjmedia_format fmt_;
333 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000334 unsigned i;
335
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000336 /* If type is preview, check if it exists already */
337 if (type == PJSUA_WND_TYPE_PREVIEW) {
338 wid = pjsua_vid_preview_get_win(cap_id);
339 if (wid != PJSUA_INVALID_ID) {
340 /* Yes, it exists */
341
342 /* Show window if requested */
343 if (show) {
344 pjmedia_vid_dev_stream *rdr;
Nanang Izzuddinc16590a2011-07-14 02:14:25 +0000345 pj_bool_t hide = PJ_FALSE;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000346
347 rdr = pjmedia_vid_port_get_stream(pjsua_var.win[wid].vp_rend);
348 pj_assert(rdr);
349 status = pjmedia_vid_dev_stream_set_cap(
350 rdr,
351 PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE,
Nanang Izzuddinc16590a2011-07-14 02:14:25 +0000352 &hide);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000353 }
354
355 /* Done */
356 *id = wid;
357 return PJ_SUCCESS;
358 }
359 }
360
361 /* Allocate window */
Benny Prijono9f468d12011-07-07 07:46:33 +0000362 for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000363 w = &pjsua_var.win[i];
Benny Prijono9f468d12011-07-07 07:46:33 +0000364 if (w->type == PJSUA_WND_TYPE_NONE) {
365 wid = i;
366 w->type = type;
367 break;
368 }
369 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000370 if (i == PJSUA_MAX_VID_WINS)
371 return PJ_ETOOMANY;
Benny Prijono9f468d12011-07-07 07:46:33 +0000372
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000373 /* Initialize window */
374 pjmedia_vid_port_param_default(&vp_param);
375
376 if (w->type == PJSUA_WND_TYPE_PREVIEW) {
377 status = pjmedia_vid_dev_default_param(w->pool, cap_id,
378 &vp_param.vidparam);
379 if (status != PJ_SUCCESS)
380 goto on_error;
381
382 /* Normalize capture ID, in case it was set to
383 * PJMEDIA_VID_DEFAULT_CAPTURE_DEV
384 */
385 cap_id = vp_param.vidparam.cap_id;
386
387 /* Assign preview capture device ID */
388 w->preview_cap_id = cap_id;
389
390 /* Create capture video port */
391 vp_param.active = PJ_TRUE;
392 vp_param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
393 if (fmt)
394 vp_param.vidparam.fmt = *fmt;
395
396 status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_cap);
397 if (status != PJ_SUCCESS)
398 goto on_error;
399
400 /* Update format info */
401 fmt_ = vp_param.vidparam.fmt;
402 fmt = &fmt_;
403
404 /* Create video tee */
405 status = pjmedia_vid_tee_create(w->pool, fmt, 2, &w->tee);
406 if (status != PJ_SUCCESS)
407 goto on_error;
408 }
409
410 /* Create renderer video port */
411 status = pjmedia_vid_dev_default_param(w->pool, rend_id,
412 &vp_param.vidparam);
413 if (status != PJ_SUCCESS)
414 goto on_error;
415
416 vp_param.active = (w->type == PJSUA_WND_TYPE_STREAM);
417 vp_param.vidparam.dir = PJMEDIA_DIR_RENDER;
418 vp_param.vidparam.fmt = *fmt;
419 vp_param.vidparam.disp_size = fmt->det.vid.size;
420 vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
421 vp_param.vidparam.window_hide = !show;
422
423 status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_rend);
424 if (status != PJ_SUCCESS)
425 goto on_error;
426
427 /* For preview window, connect capturer & renderer (via tee) */
428 if (w->type == PJSUA_WND_TYPE_PREVIEW) {
429 pjmedia_port *rend_port;
430
431 status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
432 if (status != PJ_SUCCESS)
433 goto on_error;
434
435 rend_port = pjmedia_vid_port_get_passive_port(w->vp_rend);
436 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, rend_port);
437 if (status != PJ_SUCCESS)
438 goto on_error;
439 }
440
441 /* Done */
442 *id = wid;
443
444 return PJ_SUCCESS;
445
446on_error:
447 free_vid_win(wid);
448 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000449}
450
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000451
Benny Prijono9f468d12011-07-07 07:46:33 +0000452static void free_vid_win(pjsua_vid_win_id wid)
453{
454 pjsua_vid_win *w = &pjsua_var.win[wid];
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000455
Benny Prijono9f468d12011-07-07 07:46:33 +0000456 if (w->vp_cap) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000457 pjmedia_vid_port_stop(w->vp_cap);
458 pjmedia_vid_port_disconnect(w->vp_cap);
Benny Prijono9f468d12011-07-07 07:46:33 +0000459 pjmedia_vid_port_destroy(w->vp_cap);
460 }
461 if (w->vp_rend) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000462 pjmedia_vid_port_stop(w->vp_rend);
Benny Prijono9f468d12011-07-07 07:46:33 +0000463 pjmedia_vid_port_destroy(w->vp_rend);
464 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000465 if (w->tee) {
466 pjmedia_port_destroy(w->tee);
467 }
Benny Prijono9f468d12011-07-07 07:46:33 +0000468 pjsua_vid_win_reset(wid);
469}
470
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000471
472static void inc_vid_win(pjsua_vid_win_id wid)
473{
474 pjsua_vid_win *w;
475
476 pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
477
478 w = &pjsua_var.win[wid];
479 pj_assert(w->type != PJSUA_WND_TYPE_NONE);
480 ++w->ref_cnt;
481}
482
483static void dec_vid_win(pjsua_vid_win_id wid)
484{
485 pjsua_vid_win *w;
486
487 pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
488
489 w = &pjsua_var.win[wid];
490 pj_assert(w->type != PJSUA_WND_TYPE_NONE);
491 if (--w->ref_cnt == 0)
492 free_vid_win(wid);
493}
494
495
496/* Internal function: update video channel after SDP negotiation */
497pj_status_t video_channel_update(pjsua_call_media *call_med,
498 pj_pool_t *tmp_pool,
499 const pjmedia_sdp_session *local_sdp,
500 const pjmedia_sdp_session *remote_sdp)
501{
502 pjsua_call *call = call_med->call;
503 pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
504 pjmedia_vid_stream_info the_si, *si = &the_si;
505 pjmedia_port *media_port;
506 unsigned strm_idx = call_med->idx;
507 pj_status_t status;
508
509 status = pjmedia_vid_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt,
510 local_sdp, remote_sdp, strm_idx);
511 if (status != PJ_SUCCESS)
512 return status;
513
514 /* Check if no media is active */
515 if (si->dir == PJMEDIA_DIR_NONE) {
516 /* Call media state */
517 call_med->state = PJSUA_CALL_MEDIA_NONE;
518
519 /* Call media direction */
520 call_med->dir = PJMEDIA_DIR_NONE;
521
522 } else {
523 pjmedia_transport_info tp_info;
524
525 /* Start/restart media transport */
526 status = pjmedia_transport_media_start(call_med->tp,
527 tmp_pool, local_sdp,
528 remote_sdp, strm_idx);
529 if (status != PJ_SUCCESS)
530 return status;
531
532 call_med->tp_st = PJSUA_MED_TP_RUNNING;
533
534 /* Get remote SRTP usage policy */
535 pjmedia_transport_info_init(&tp_info);
536 pjmedia_transport_get_info(call_med->tp, &tp_info);
537 if (tp_info.specific_info_cnt > 0) {
538 unsigned i;
539 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
540 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
541 {
542 pjmedia_srtp_info *srtp_info =
543 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
544
545 call_med->rem_srtp_use = srtp_info->peer_use;
546 break;
547 }
548 }
549 }
550
551 /* Optionally, application may modify other stream settings here
552 * (such as jitter buffer parameters, codec ptime, etc.)
553 */
554 si->jb_init = pjsua_var.media_cfg.jb_init;
555 si->jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
556 si->jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
557 si->jb_max = pjsua_var.media_cfg.jb_max;
558
559 /* Set SSRC */
560 si->ssrc = call_med->ssrc;
561
562 /* Set RTP timestamp & sequence, normally these value are intialized
563 * automatically when stream session created, but for some cases (e.g:
564 * call reinvite, call update) timestamp and sequence need to be kept
565 * contigue.
566 */
567 si->rtp_ts = call_med->rtp_tx_ts;
568 si->rtp_seq = call_med->rtp_tx_seq;
569 si->rtp_seq_ts_set = call_med->rtp_tx_seq_ts_set;
570
571#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
572 /* Enable/disable stream keep-alive and NAT hole punch. */
573 si->use_ka = pjsua_var.acc[call->acc_id].cfg.use_stream_ka;
574#endif
575
576 /* Try to get shared format ID between the capture device and
577 * the encoder to avoid format conversion in the capture device.
578 */
579 if (si->dir & PJMEDIA_DIR_ENCODING) {
580 pjmedia_vid_dev_info dev_info;
581 pjmedia_vid_codec_info *codec_info = &si->codec_info;
582 unsigned i, j;
583
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +0000584 status = pjmedia_vid_dev_get_info(call_med->strm.v.cap_dev,
585 &dev_info);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000586 if (status != PJ_SUCCESS)
587 return status;
588
589 /* Find matched format ID */
590 for (i = 0; i < codec_info->dec_fmt_id_cnt; ++i) {
591 for (j = 0; j < dev_info.fmt_cnt; ++j) {
592 if (codec_info->dec_fmt_id[i] ==
593 (pjmedia_format_id)dev_info.fmt[j].id)
594 {
595 /* Apply the matched format ID to the codec */
596 si->codec_param->dec_fmt.id =
597 codec_info->dec_fmt_id[i];
598
599 /* Force outer loop to break */
600 i = codec_info->dec_fmt_id_cnt;
601 break;
602 }
603 }
604 }
605 }
606
607 /* Create session based on session info. */
608 status = pjmedia_vid_stream_create(pjsua_var.med_endpt, NULL, si,
609 call_med->tp, NULL,
610 &call_med->strm.v.stream);
611 if (status != PJ_SUCCESS)
612 return status;
613
614 /* Start stream */
615 status = pjmedia_vid_stream_start(call_med->strm.v.stream);
616 if (status != PJ_SUCCESS)
617 return status;
618
619 /* Setup decoding direction */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000620 if (si->dir & PJMEDIA_DIR_DECODING)
621 {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000622 pjsua_vid_win_id wid;
623 pjsua_vid_win *w;
624
625 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
626 PJMEDIA_DIR_DECODING,
627 &media_port);
628 if (status != PJ_SUCCESS)
629 return status;
630
631 /* Create stream video window */
632 status = create_vid_win(PJSUA_WND_TYPE_STREAM,
633 &media_port->info.fmt,
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000634 call_med->strm.v.rdr_dev,
635 //acc->cfg.vid_rend_dev,
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000636 PJSUA_INVALID_ID,
637 acc->cfg.vid_in_auto_show,
638 &wid);
639 if (status != PJ_SUCCESS)
640 return status;
641
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000642 w = &pjsua_var.win[wid];
643
Benny Prijonoee0ba182011-07-15 06:18:29 +0000644 /* Register to video events */
645 pjmedia_event_subscribe(
646 pjmedia_vid_port_get_event_publisher(w->vp_rend),
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000647 &call_med->esub_rend);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000648
649 /* Connect renderer to stream */
650 status = pjmedia_vid_port_connect(w->vp_rend, media_port,
651 PJ_FALSE);
652 if (status != PJ_SUCCESS)
653 return status;
654
655 /* Start renderer */
656 status = pjmedia_vid_port_start(w->vp_rend);
657 if (status != PJ_SUCCESS)
658 return status;
659
660 /* Done */
661 inc_vid_win(wid);
662 call_med->strm.v.rdr_win_id = wid;
663 }
664
665 /* Setup encoding direction */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000666 if (si->dir & PJMEDIA_DIR_ENCODING && !call->local_hold)
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000667 {
668 pjsua_vid_win *w;
669 pjsua_vid_win_id wid;
670
671 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
672 PJMEDIA_DIR_ENCODING,
673 &media_port);
674 if (status != PJ_SUCCESS)
675 return status;
676
677 /* Create preview video window */
678 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
679 &media_port->info.fmt,
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000680 call_med->strm.v.rdr_dev,
681 call_med->strm.v.cap_dev,
682 //acc->cfg.vid_rend_dev,
683 //acc->cfg.vid_cap_dev,
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000684 PJ_FALSE,
685 &wid);
686 if (status != PJ_SUCCESS)
687 return status;
688
689 w = &pjsua_var.win[wid];
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000690
691 pjmedia_event_subscribe(
692 pjmedia_vid_port_get_event_publisher(w->vp_cap),
693 &call_med->esub_cap);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000694
695 /* Connect stream to capturer (via video window tee) */
696 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
697 if (status != PJ_SUCCESS)
698 return status;
699
700 /* Start renderer */
701 status = pjmedia_vid_port_start(w->vp_rend);
702 if (status != PJ_SUCCESS)
703 return status;
704
705 /* Start capturer */
706 status = pjmedia_vid_port_start(w->vp_cap);
707 if (status != PJ_SUCCESS)
708 return status;
709
710 /* Done */
711 inc_vid_win(wid);
712 call_med->strm.v.cap_win_id = wid;
713 }
714
715 /* Call media direction */
716 call_med->dir = si->dir;
717
718 /* Call media state */
719 if (call->local_hold)
720 call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
721 else if (call_med->dir == PJMEDIA_DIR_DECODING)
722 call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
723 else
724 call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
725 }
726
727 /* Print info. */
728 {
729 char info[80];
730 int info_len = 0;
731 int len;
732 const char *dir;
733
734 switch (si->dir) {
735 case PJMEDIA_DIR_NONE:
736 dir = "inactive";
737 break;
738 case PJMEDIA_DIR_ENCODING:
739 dir = "sendonly";
740 break;
741 case PJMEDIA_DIR_DECODING:
742 dir = "recvonly";
743 break;
744 case PJMEDIA_DIR_ENCODING_DECODING:
745 dir = "sendrecv";
746 break;
747 default:
748 dir = "unknown";
749 break;
750 }
751 len = pj_ansi_sprintf( info+info_len,
752 ", stream #%d: %.*s (%s)", strm_idx,
753 (int)si->codec_info.encoding_name.slen,
754 si->codec_info.encoding_name.ptr,
755 dir);
756 if (len > 0)
757 info_len += len;
758 PJ_LOG(4,(THIS_FILE,"Media updates%s", info));
759 }
760
Sauw Mingc7bc3aa2011-07-15 07:22:49 +0000761 if (!acc->cfg.vid_out_auto_transmit && call_med->strm.v.stream) {
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +0000762 status = pjmedia_vid_stream_pause(call_med->strm.v.stream,
763 PJMEDIA_DIR_ENCODING);
764 if (status != PJ_SUCCESS)
765 return status;
766 }
767
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000768 return PJ_SUCCESS;
769}
770
771
772/* Internal function to stop video stream */
773void stop_video_stream(pjsua_call_media *call_med)
774{
775 pjmedia_vid_stream *strm = call_med->strm.v.stream;
776 pjmedia_rtcp_stat stat;
777
778 pj_assert(call_med->type == PJMEDIA_TYPE_VIDEO);
779
780 if (!strm)
781 return;
782
Benny Prijono1fe04ee2011-07-15 07:27:05 +0000783 /* Unsubscribe events */
784 pjmedia_event_unsubscribe(&call_med->esub_rend);
785 pjmedia_event_unsubscribe(&call_med->esub_cap);
786
Nanang Izzuddin98085612011-07-15 07:41:02 +0000787 if (call_med->dir & PJMEDIA_DIR_ENCODING &&
788 call_med->strm.v.cap_win_id != PJSUA_INVALID_ID)
789 {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000790 pjmedia_port *media_port;
791 pjsua_vid_win *w =
792 &pjsua_var.win[call_med->strm.v.cap_win_id];
793
794 pjmedia_vid_stream_get_port(call_med->strm.v.stream,
795 PJMEDIA_DIR_ENCODING,
796 &media_port);
797 pj_assert(media_port);
798
799 pjmedia_vid_port_stop(w->vp_cap);
800 pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
801 pjmedia_vid_port_start(w->vp_cap);
802
803 dec_vid_win(call_med->strm.v.cap_win_id);
804 }
805
Nanang Izzuddin98085612011-07-15 07:41:02 +0000806 if (call_med->dir & PJMEDIA_DIR_DECODING &&
807 call_med->strm.v.rdr_win_id != PJSUA_INVALID_ID)
808 {
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000809 dec_vid_win(call_med->strm.v.rdr_win_id);
810 }
811
812 if ((call_med->dir & PJMEDIA_DIR_ENCODING) &&
813 (pjmedia_vid_stream_get_stat(strm, &stat) == PJ_SUCCESS))
814 {
815 /* Save RTP timestamp & sequence, so when media session is
816 * restarted, those values will be restored as the initial
817 * RTP timestamp & sequence of the new media session. So in
818 * the same call session, RTP timestamp and sequence are
819 * guaranteed to be contigue.
820 */
821 call_med->rtp_tx_seq_ts_set = 1 | (1 << 1);
822 call_med->rtp_tx_seq = stat.rtp_tx_last_seq;
823 call_med->rtp_tx_ts = stat.rtp_tx_last_ts;
824 }
825
826 pjmedia_vid_stream_destroy(strm);
827 call_med->strm.v.stream = NULL;
828}
829
830
Benny Prijono9f468d12011-07-07 07:46:33 +0000831/*
832 * Start video preview window for the specified capture device.
833 */
834PJ_DEF(pj_status_t) pjsua_vid_preview_start(pjmedia_vid_dev_index id,
835 pjsua_vid_preview_param *prm)
836{
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000837 pjsua_vid_win_id wid;
838 pjsua_vid_win *w;
839 pjmedia_vid_dev_index rend_id;
Benny Prijono9f468d12011-07-07 07:46:33 +0000840 pj_status_t status;
841
842 PJSUA_LOCK();
843
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000844 if (prm) {
845 rend_id = prm->rend_id;
846 } else {
847 rend_id = PJMEDIA_VID_DEFAULT_RENDER_DEV;
Benny Prijono9f468d12011-07-07 07:46:33 +0000848 }
849
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000850 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW, NULL, rend_id, id,
851 PJ_TRUE, &wid);
852 if (status != PJ_SUCCESS) {
Benny Prijono9f468d12011-07-07 07:46:33 +0000853 PJSUA_UNLOCK();
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000854 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000855 }
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000856
Benny Prijono9f468d12011-07-07 07:46:33 +0000857 w = &pjsua_var.win[wid];
858
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000859 /* Start capturer */
860 status = pjmedia_vid_port_start(w->vp_rend);
861 if (status != PJ_SUCCESS) {
862 PJSUA_UNLOCK();
863 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000864 }
865
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000866 /* Start renderer */
Benny Prijono9f468d12011-07-07 07:46:33 +0000867 status = pjmedia_vid_port_start(w->vp_cap);
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000868 if (status != PJ_SUCCESS) {
869 PJSUA_UNLOCK();
870 return status;
871 }
Benny Prijono9f468d12011-07-07 07:46:33 +0000872
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000873 inc_vid_win(wid);
874
Benny Prijono9f468d12011-07-07 07:46:33 +0000875 PJSUA_UNLOCK();
876 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +0000877}
878
879/*
880 * Stop video preview.
881 */
882PJ_DEF(pj_status_t) pjsua_vid_preview_stop(pjmedia_vid_dev_index id)
883{
884 pjsua_vid_win_id wid = PJSUA_INVALID_ID;
Benny Prijono9f468d12011-07-07 07:46:33 +0000885
886 PJSUA_LOCK();
887 wid = pjsua_vid_preview_get_win(id);
888 if (wid == PJSUA_INVALID_ID) {
889 PJSUA_UNLOCK();
890 return PJ_ENOTFOUND;
891 }
892
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000893 dec_vid_win(wid);
Benny Prijono9f468d12011-07-07 07:46:33 +0000894
895 PJSUA_UNLOCK();
896
897 return PJ_SUCCESS;
898}
899
900
901/*****************************************************************************
902 * Window
903 */
904
Nanang Izzuddinf3638022011-07-14 03:47:04 +0000905
906/*
907 * Enumerates all video windows.
908 */
909PJ_DEF(pj_status_t) pjsua_vid_enum_wins( pjsua_vid_win_id wids[],
910 unsigned *count)
911{
912 unsigned i, cnt;
913
914 cnt = 0;
915
916 for (i=0; i<PJSUA_MAX_VID_WINS && cnt <*count; ++i) {
917 pjsua_vid_win *w = &pjsua_var.win[i];
918 if (w->type != PJSUA_WND_TYPE_NONE)
919 wids[cnt++] = i;
920 }
921
922 *count = cnt;
923
924 return PJ_SUCCESS;
925}
926
927
Benny Prijono9f468d12011-07-07 07:46:33 +0000928/*
929 * Get window info.
930 */
931PJ_DEF(pj_status_t) pjsua_vid_win_get_info( pjsua_vid_win_id wid,
932 pjsua_vid_win_info *wi)
933{
934 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000935 pjmedia_vid_dev_stream *s;
Sauw Ming5291a2d2011-07-15 07:52:44 +0000936 pjmedia_vid_dev_param vparam;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000937 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000938
939 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && wi, PJ_EINVAL);
940
941 PJSUA_LOCK();
942 w = &pjsua_var.win[wid];
943 if (w->vp_rend == NULL) {
944 PJSUA_UNLOCK();
945 return PJ_EINVAL;
946 }
947
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000948 s = pjmedia_vid_port_get_stream(w->vp_rend);
949 if (s == NULL) {
950 PJSUA_UNLOCK();
951 return PJ_EINVAL;
952 }
953
954 status = pjmedia_vid_dev_stream_get_param(s, &vparam);
955 if (status != PJ_SUCCESS) {
956 PJSUA_UNLOCK();
957 return status;
958 }
959
Nanang Izzuddindb9b0022011-07-26 08:17:25 +0000960 wi->rdr_dev = vparam.rend_id;
Nanang Izzuddin6e2fcc32011-07-22 04:49:36 +0000961 wi->hwnd = vparam.window;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000962 wi->show = !vparam.window_hide;
963 wi->pos = vparam.window_pos;
964 wi->size = vparam.disp_size;
965
Benny Prijono9f468d12011-07-07 07:46:33 +0000966 PJSUA_UNLOCK();
967
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000968 return PJ_SUCCESS;
Benny Prijono9f468d12011-07-07 07:46:33 +0000969}
970
971/*
972 * Show or hide window.
973 */
974PJ_DEF(pj_status_t) pjsua_vid_win_set_show( pjsua_vid_win_id wid,
975 pj_bool_t show)
976{
977 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000978 pjmedia_vid_dev_stream *s;
Nanang Izzuddinc16590a2011-07-14 02:14:25 +0000979 pj_bool_t hide;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000980 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +0000981
982 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
983
984 PJSUA_LOCK();
985 w = &pjsua_var.win[wid];
986 if (w->vp_rend == NULL) {
987 PJSUA_UNLOCK();
988 return PJ_EINVAL;
989 }
990
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000991 s = pjmedia_vid_port_get_stream(w->vp_rend);
992 if (s == NULL) {
993 PJSUA_UNLOCK();
994 return PJ_EINVAL;
995 }
996
Nanang Izzuddinc16590a2011-07-14 02:14:25 +0000997 hide = !show;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000998 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinc16590a2011-07-14 02:14:25 +0000999 PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE, &hide);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001000
Benny Prijono9f468d12011-07-07 07:46:33 +00001001 PJSUA_UNLOCK();
1002
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001003 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001004}
1005
1006/*
1007 * Set video window position.
1008 */
1009PJ_DEF(pj_status_t) pjsua_vid_win_set_pos( pjsua_vid_win_id wid,
1010 const pjmedia_coord *pos)
1011{
1012 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001013 pjmedia_vid_dev_stream *s;
1014 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001015
1016 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && pos, PJ_EINVAL);
1017
1018 PJSUA_LOCK();
1019 w = &pjsua_var.win[wid];
1020 if (w->vp_rend == NULL) {
1021 PJSUA_UNLOCK();
1022 return PJ_EINVAL;
1023 }
1024
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001025 s = pjmedia_vid_port_get_stream(w->vp_rend);
1026 if (s == NULL) {
1027 PJSUA_UNLOCK();
1028 return PJ_EINVAL;
1029 }
1030
1031 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001032 PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION, pos);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001033
Benny Prijono9f468d12011-07-07 07:46:33 +00001034 PJSUA_UNLOCK();
1035
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001036 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001037}
1038
1039/*
1040 * Resize window.
1041 */
1042PJ_DEF(pj_status_t) pjsua_vid_win_set_size( pjsua_vid_win_id wid,
1043 const pjmedia_rect_size *size)
1044{
1045 pjsua_vid_win *w;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001046 pjmedia_vid_dev_stream *s;
1047 pj_status_t status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001048
1049 PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && size, PJ_EINVAL);
1050
1051 PJSUA_LOCK();
1052 w = &pjsua_var.win[wid];
1053 if (w->vp_rend == NULL) {
1054 PJSUA_UNLOCK();
1055 return PJ_EINVAL;
1056 }
1057
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001058 s = pjmedia_vid_port_get_stream(w->vp_rend);
1059 if (s == NULL) {
1060 PJSUA_UNLOCK();
1061 return PJ_EINVAL;
1062 }
1063
1064 status = pjmedia_vid_dev_stream_set_cap(s,
Nanang Izzuddinf3638022011-07-14 03:47:04 +00001065 PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE, size);
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001066
Benny Prijono9f468d12011-07-07 07:46:33 +00001067 PJSUA_UNLOCK();
1068
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001069 return status;
Benny Prijono9f468d12011-07-07 07:46:33 +00001070}
1071
1072
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001073static void call_get_vid_strm_info(pjsua_call *call,
1074 int *first_active,
1075 int *first_inactive,
1076 unsigned *active_cnt,
1077 unsigned *cnt)
1078{
1079 unsigned i, var_cnt = 0;
1080
1081 if (first_active && ++var_cnt)
1082 *first_active = -1;
1083 if (first_inactive && ++var_cnt)
1084 *first_inactive = -1;
1085 if (active_cnt && ++var_cnt)
1086 *active_cnt = 0;
1087 if (cnt && ++var_cnt)
1088 *cnt = 0;
1089
1090 for (i = 0; i < call->med_cnt && var_cnt; ++i) {
1091 if (call->media[i].type == PJMEDIA_TYPE_VIDEO) {
1092 if (call->media[i].dir != PJMEDIA_DIR_NONE)
1093 {
1094 if (first_active && *first_active == -1) {
1095 *first_active = i;
1096 --var_cnt;
1097 }
1098 if (active_cnt)
1099 ++(*active_cnt);
1100 } else if (first_inactive && *first_inactive == -1) {
1101 *first_inactive = i;
1102 --var_cnt;
1103 }
1104 if (cnt)
1105 ++(*cnt);
1106 }
1107 }
1108}
1109
1110
1111/* Send SDP reoffer. */
1112static pj_status_t call_reoffer_sdp(pjsua_call_id call_id,
1113 const pjmedia_sdp_session *sdp)
1114{
1115 pjsua_call *call;
1116 pjsip_tx_data *tdata;
1117 pjsip_dialog *dlg;
1118 pj_status_t status;
1119
1120 status = acquire_call("call_reoffer_sdp()", call_id, &call, &dlg);
1121 if (status != PJ_SUCCESS)
1122 return status;
1123
1124 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1125 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
1126 pjsip_dlg_dec_lock(dlg);
1127 return PJSIP_ESESSIONSTATE;
1128 }
1129
1130 /* Create re-INVITE with new offer */
1131 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1132 if (status != PJ_SUCCESS) {
1133 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
1134 pjsip_dlg_dec_lock(dlg);
1135 return status;
1136 }
1137
1138 /* Send the request */
1139 status = pjsip_inv_send_msg( call->inv, tdata);
1140 if (status != PJ_SUCCESS) {
1141 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
1142 pjsip_dlg_dec_lock(dlg);
1143 return status;
1144 }
1145
1146 pjsip_dlg_dec_lock(dlg);
1147
1148 return PJ_SUCCESS;
1149}
1150
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001151/* Add a new video stream into a call */
1152static pj_status_t call_add_video(pjsua_call *call,
Nanang Izzuddin98085612011-07-15 07:41:02 +00001153 pjmedia_vid_dev_index cap_dev,
1154 pjmedia_dir dir)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001155{
1156 pj_pool_t *pool = call->inv->pool_prov;
1157 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1158 pjsua_call_media *call_med;
1159 pjmedia_sdp_session *sdp;
1160 pjmedia_sdp_media *sdp_m;
1161 pjmedia_transport_info tpinfo;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001162 unsigned active_cnt;
1163 pj_status_t status;
1164
1165 /* Verify media slot availability */
1166 if (call->med_cnt == PJSUA_MAX_CALL_MEDIA)
1167 return PJ_ETOOMANY;
1168
1169 call_get_vid_strm_info(call, NULL, NULL, &active_cnt, NULL);
1170 if (active_cnt == acc_cfg->max_video_cnt)
1171 return PJ_ETOOMANY;
1172
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001173 /* Get active local SDP */
1174 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
1175 if (status != PJ_SUCCESS)
1176 return status;
1177
1178 /* Initialize call media */
1179 call_med = &call->media[call->med_cnt++];
1180
1181 status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1182 &acc_cfg->rtp_cfg, call->secure_level,
1183 NULL);
1184 if (status != PJ_SUCCESS)
1185 goto on_error;
1186
1187 /* Override default capture device setting */
1188 call_med->strm.v.cap_dev = cap_dev;
1189
1190 /* Init transport media */
1191 status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1192 NULL, call_med->idx);
1193 if (status != PJ_SUCCESS)
1194 goto on_error;
1195
1196 call_med->tp_st = PJSUA_MED_TP_INIT;
1197
1198 /* Get transport address info */
1199 pjmedia_transport_info_init(&tpinfo);
1200 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1201
1202 /* Create SDP media line */
1203 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1204 &tpinfo.sock_info, 0, &sdp_m);
1205 if (status != PJ_SUCCESS)
1206 goto on_error;
1207
1208 sdp->media[sdp->media_count++] = sdp_m;
1209
Nanang Izzuddin98085612011-07-15 07:41:02 +00001210 /* Update media direction, if it is not 'sendrecv' */
1211 if (dir != PJMEDIA_DIR_ENCODING_DECODING) {
1212 pjmedia_sdp_attr *a;
1213
1214 /* Remove sendrecv direction attribute, if any */
1215 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
1216
1217 if (dir == PJMEDIA_DIR_ENCODING)
1218 a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
1219 else if (dir == PJMEDIA_DIR_DECODING)
1220 a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
1221 else
1222 a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
1223
1224 pjmedia_sdp_media_add_attr(sdp_m, a);
1225 }
1226
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001227 /* Update SDP media line by media transport */
1228 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1229 sdp, NULL, call_med->idx);
1230 if (status != PJ_SUCCESS)
1231 goto on_error;
1232
1233 status = call_reoffer_sdp(call->index, sdp);
1234 if (status != PJ_SUCCESS)
1235 goto on_error;
1236
1237 return PJ_SUCCESS;
1238
1239on_error:
1240 if (call_med->tp) {
1241 pjmedia_transport_close(call_med->tp);
1242 call_med->tp = call_med->tp_orig = NULL;
1243 }
1244
1245 return status;
1246}
1247
1248
Nanang Izzuddin98085612011-07-15 07:41:02 +00001249/* Modify a video stream from a call, i.e: update direction,
1250 * remove/disable.
1251 */
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001252static pj_status_t call_modify_video(pjsua_call *call,
1253 int med_idx,
Nanang Izzuddin98085612011-07-15 07:41:02 +00001254 pjmedia_dir dir,
1255 pj_bool_t remove)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001256{
1257 pjsua_call_media *call_med;
1258 pjmedia_sdp_session *sdp;
1259 pj_status_t status;
1260
1261 /* Verify and normalize media index */
1262 if (med_idx == -1) {
1263 int first_active;
1264
1265 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1266 if (first_active == -1)
1267 return PJ_ENOTFOUND;
1268
1269 med_idx = first_active;
1270 }
1271
1272 call_med = &call->media[med_idx];
1273
1274 /* Verify if the stream media type is video */
1275 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1276 return PJ_EINVAL;
1277
Nanang Izzuddin98085612011-07-15 07:41:02 +00001278 /* Verify if the stream dir is not changed */
1279 if ((!remove && call_med->dir == dir) ||
1280 ( remove && (call_med->tp_st == PJSUA_MED_TP_DISABLED ||
1281 call_med->tp == NULL)))
1282 {
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001283 return PJ_SUCCESS;
Nanang Izzuddin98085612011-07-15 07:41:02 +00001284 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001285
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001286 /* Get active local SDP */
1287 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
1288 if (status != PJ_SUCCESS)
1289 return status;
1290
1291 pj_assert(med_idx < (int)sdp->media_count);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001292
Nanang Izzuddin98085612011-07-15 07:41:02 +00001293 if (!remove) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001294 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1295 pj_pool_t *pool = call->inv->pool_prov;
1296 pjmedia_sdp_media *sdp_m;
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001297
1298 status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1299 &acc_cfg->rtp_cfg, call->secure_level,
1300 NULL);
1301 if (status != PJ_SUCCESS)
1302 goto on_error;
1303
1304 /* Init transport media */
Nanang Izzuddin98085612011-07-15 07:41:02 +00001305 if (call_med->tp && call_med->tp_st == PJSUA_MED_TP_IDLE) {
1306 status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1307 NULL, call_med->idx);
1308 if (status != PJ_SUCCESS)
1309 goto on_error;
1310 }
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001311
Nanang Izzuddin98085612011-07-15 07:41:02 +00001312 sdp_m = sdp->media[med_idx];
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001313
Nanang Izzuddin98085612011-07-15 07:41:02 +00001314 /* Create new SDP media line if the stream is disabled */
1315 if (sdp->media[med_idx]->desc.port == 0) {
1316 pjmedia_transport_info tpinfo;
1317
1318 /* Get transport address info */
1319 pjmedia_transport_info_init(&tpinfo);
1320 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1321
1322 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1323 &tpinfo.sock_info, 0, &sdp_m);
1324 if (status != PJ_SUCCESS)
1325 goto on_error;
1326 }
1327
1328 {
1329 pjmedia_sdp_attr *a;
1330
1331 /* Remove any direction attributes */
1332 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
1333 pjmedia_sdp_media_remove_all_attr(sdp_m, "sendonly");
1334 pjmedia_sdp_media_remove_all_attr(sdp_m, "recvonly");
1335 pjmedia_sdp_media_remove_all_attr(sdp_m, "inactive");
1336
1337 /* Update media direction */
1338 if (dir == PJMEDIA_DIR_ENCODING_DECODING)
1339 a = pjmedia_sdp_attr_create(pool, "sendrecv", NULL);
1340 else if (dir == PJMEDIA_DIR_ENCODING)
1341 a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
1342 else if (dir == PJMEDIA_DIR_DECODING)
1343 a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
1344 else
1345 a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
1346
1347 pjmedia_sdp_media_add_attr(sdp_m, a);
1348 }
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001349
1350 sdp->media[med_idx] = sdp_m;
1351
1352 /* Update SDP media line by media transport */
1353 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1354 sdp, NULL, call_med->idx);
1355 if (status != PJ_SUCCESS)
1356 goto on_error;
1357
1358on_error:
1359 if (status != PJ_SUCCESS) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001360 return status;
1361 }
Nanang Izzuddin98085612011-07-15 07:41:02 +00001362
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001363 } else {
Nanang Izzuddin98085612011-07-15 07:41:02 +00001364
1365 pj_pool_t *pool = call->inv->pool_prov;
1366
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001367 /* Mark media transport to disabled */
1368 // Don't close this here, as SDP negotiation has not been
1369 // done and stream may be still active.
1370 call_med->tp_st = PJSUA_MED_TP_DISABLED;
1371
Nanang Izzuddin98085612011-07-15 07:41:02 +00001372 /* Deactivate the stream */
1373 pjmedia_sdp_media_deactivate(pool, sdp->media[med_idx]);
1374
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001375 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001376
1377 status = call_reoffer_sdp(call->index, sdp);
1378 if (status != PJ_SUCCESS)
1379 return status;
1380
1381 return PJ_SUCCESS;
1382}
1383
1384
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001385/* Change capture device of a video stream in a call */
1386static pj_status_t call_change_cap_dev(pjsua_call *call,
1387 int med_idx,
1388 pjmedia_vid_dev_index cap_dev)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001389{
1390 pjsua_call_media *call_med;
1391 pjmedia_vid_dev_info info;
1392 pjsua_vid_win *w, *new_w = NULL;
1393 pjsua_vid_win_id wid, new_wid;
1394 pjmedia_port *media_port;
1395 pj_status_t status;
1396
1397 /* Verify and normalize media index */
1398 if (med_idx == -1) {
1399 int first_active;
1400
1401 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1402 if (first_active == -1)
1403 return PJ_ENOTFOUND;
1404
1405 med_idx = first_active;
1406 }
1407
1408 call_med = &call->media[med_idx];
1409
1410 /* Verify if the stream media type is video */
1411 if (call_med->type != PJMEDIA_TYPE_VIDEO)
1412 return PJ_EINVAL;
1413
1414 /* Verify the capture device */
1415 status = pjmedia_vid_dev_get_info(cap_dev, &info);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001416 if (status != PJ_SUCCESS || info.dir != PJMEDIA_DIR_CAPTURE)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001417 return PJ_EINVAL;
1418
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001419 /* The specified capture device is being used already */
1420 if (call_med->strm.v.cap_dev == cap_dev)
1421 return PJ_SUCCESS;
1422
1423 /* == Apply the new capture device == */
1424
1425 wid = call_med->strm.v.cap_win_id;
1426 w = &pjsua_var.win[wid];
1427 pj_assert(w->type == PJSUA_WND_TYPE_PREVIEW && w->vp_cap);
1428
1429 status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
1430 PJMEDIA_DIR_ENCODING, &media_port);
1431 if (status != PJ_SUCCESS)
1432 return status;
Benny Prijono1fe04ee2011-07-15 07:27:05 +00001433
1434 pjmedia_event_unsubscribe(&call_med->esub_cap);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001435
1436 /* = Detach stream port from the old capture device = */
1437 status = pjmedia_vid_port_disconnect(w->vp_cap);
1438 if (status != PJ_SUCCESS)
1439 return status;
1440
1441 status = pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
1442 if (status != PJ_SUCCESS) {
1443 /* Connect back the old capturer */
1444 pjmedia_vid_port_connect(w->vp_cap, media_port, PJ_FALSE);
1445 return status;
1446 }
1447
1448 /* = Attach stream port to the new capture device = */
1449
1450 /* Create preview video window */
1451 status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
1452 &media_port->info.fmt,
1453 call_med->strm.v.rdr_dev,
1454 cap_dev,
1455 PJ_FALSE,
1456 &new_wid);
1457 if (status != PJ_SUCCESS)
1458 goto on_error;
1459
1460 inc_vid_win(new_wid);
1461 new_w = &pjsua_var.win[new_wid];
1462
1463 /* Connect stream to capturer (via video window tee) */
1464 status = pjmedia_vid_tee_add_dst_port2(new_w->tee, 0, media_port);
1465 if (status != PJ_SUCCESS)
1466 goto on_error;
1467
1468 /* Connect capturer to tee */
1469 status = pjmedia_vid_port_connect(new_w->vp_cap, new_w->tee, PJ_FALSE);
1470 if (status != PJ_SUCCESS)
1471 return status;
1472
Benny Prijono1fe04ee2011-07-15 07:27:05 +00001473 pjmedia_event_subscribe(
1474 pjmedia_vid_port_get_event_publisher(w->vp_rend),
1475 &call_med->esub_cap);
1476
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001477 /* Start renderer */
1478 status = pjmedia_vid_port_start(new_w->vp_rend);
1479 if (status != PJ_SUCCESS)
1480 goto on_error;
1481
1482 /* Start capturer */
1483 status = pjmedia_vid_port_start(new_w->vp_cap);
1484 if (status != PJ_SUCCESS)
1485 goto on_error;
1486
1487 /* Finally */
1488 call_med->strm.v.cap_dev = cap_dev;
1489 call_med->strm.v.cap_win_id = new_wid;
1490 dec_vid_win(wid);
1491
1492 return PJ_SUCCESS;
1493
1494on_error:
1495 if (new_w) {
1496 /* Disconnect media port from the new capturer */
1497 pjmedia_vid_tee_remove_dst_port(new_w->tee, media_port);
1498 /* Release the new capturer */
1499 dec_vid_win(new_wid);
1500 }
1501
1502 /* Revert back to the old capturer */
1503 status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
1504 if (status != PJ_SUCCESS)
1505 return status;
1506
1507 status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
1508 if (status != PJ_SUCCESS)
1509 return status;
1510
1511 return status;
1512}
1513
1514
Nanang Izzuddin98085612011-07-15 07:41:02 +00001515/* Start/stop transmitting video stream in a call */
1516static pj_status_t call_set_tx_video(pjsua_call *call,
1517 int med_idx,
1518 pj_bool_t enable)
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001519{
1520 pjsua_call_media *call_med;
1521 pj_status_t status;
1522
1523 /* Verify and normalize media index */
1524 if (med_idx == -1) {
1525 int first_active;
1526
1527 call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1528 if (first_active == -1)
1529 return PJ_ENOTFOUND;
1530
1531 med_idx = first_active;
1532 }
1533
1534 call_med = &call->media[med_idx];
1535
1536 /* Verify if the stream is transmitting video */
1537 if (call_med->type != PJMEDIA_TYPE_VIDEO ||
Nanang Izzuddin98085612011-07-15 07:41:02 +00001538 (enable && (call_med->dir & PJMEDIA_DIR_ENCODING) == 0))
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001539 {
1540 return PJ_EINVAL;
1541 }
1542
Nanang Izzuddin98085612011-07-15 07:41:02 +00001543 if (enable) {
1544 /* Start stream in encoding direction */
1545 status = pjmedia_vid_stream_resume(call_med->strm.v.stream,
1546 PJMEDIA_DIR_ENCODING);
1547 } else {
1548 /* Pause stream in encoding direction */
1549 status = pjmedia_vid_stream_pause( call_med->strm.v.stream,
1550 PJMEDIA_DIR_ENCODING);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001551 }
1552
Nanang Izzuddin98085612011-07-15 07:41:02 +00001553 return status;
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001554}
1555
1556
1557/*
1558 * Start, stop, and/or manipulate video transmission for the specified call.
1559 */
1560PJ_DEF(pj_status_t) pjsua_call_set_vid_strm (
1561 pjsua_call_id call_id,
1562 pjsua_call_vid_strm_op op,
1563 const pjsua_call_vid_strm_op_param *param)
1564{
1565 pjsua_call *call;
1566 pjsua_call_vid_strm_op_param param_;
1567 pj_status_t status;
1568
1569 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1570 PJ_EINVAL);
Benny Prijonoe212bc12011-08-15 09:38:42 +00001571 PJ_ASSERT_RETURN(op != PJSUA_CALL_VID_STRM_NO_OP, PJ_EINVAL);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001572
1573 PJSUA_LOCK();
1574
1575 call = &pjsua_var.calls[call_id];
1576
1577 if (param) {
1578 param_ = *param;
1579 } else {
Benny Prijonoe212bc12011-08-15 09:38:42 +00001580 pjsua_call_vid_strm_op_param_default(&param_);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001581 }
1582
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001583 /* If set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
1584 * account default video capture device.
1585 */
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001586 if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001587 pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1588 param_.cap_dev = acc_cfg->vid_cap_dev;
1589
1590 /* If the account default video capture device is
1591 * PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
1592 * global default video capture device.
1593 */
1594 if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
1595 pjmedia_vid_dev_info info;
1596 pjmedia_vid_dev_get_info(param_.cap_dev, &info);
1597 pj_assert(info.dir == PJMEDIA_DIR_CAPTURE);
1598 param_.cap_dev = info.id;
1599 }
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001600 }
1601
1602 switch (op) {
1603 case PJSUA_CALL_VID_STRM_ADD:
Nanang Izzuddin98085612011-07-15 07:41:02 +00001604 status = call_add_video(call, param_.cap_dev, param_.dir);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001605 break;
Nanang Izzuddin98085612011-07-15 07:41:02 +00001606 case PJSUA_CALL_VID_STRM_REMOVE:
1607 status = call_modify_video(call, param_.med_idx, PJMEDIA_DIR_NONE,
1608 PJ_TRUE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001609 break;
Nanang Izzuddin98085612011-07-15 07:41:02 +00001610 case PJSUA_CALL_VID_STRM_CHANGE_DIR:
1611 status = call_modify_video(call, param_.med_idx, param_.dir, PJ_FALSE);
Nanang Izzuddin7ad53c32011-07-13 13:31:08 +00001612 break;
1613 case PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV:
1614 status = call_change_cap_dev(call, param_.med_idx, param_.cap_dev);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001615 break;
1616 case PJSUA_CALL_VID_STRM_START_TRANSMIT:
Nanang Izzuddin98085612011-07-15 07:41:02 +00001617 status = call_set_tx_video(call, param_.med_idx, PJ_TRUE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001618 break;
1619 case PJSUA_CALL_VID_STRM_STOP_TRANSMIT:
Nanang Izzuddin98085612011-07-15 07:41:02 +00001620 status = call_set_tx_video(call, param_.med_idx, PJ_FALSE);
Nanang Izzuddinb4d4dad2011-07-13 08:51:10 +00001621 break;
1622 default:
1623 status = PJ_EINVALIDOP;
1624 break;
1625 }
1626
1627 PJSUA_UNLOCK();
1628
1629 return status;
1630}
1631
Benny Prijono9f468d12011-07-07 07:46:33 +00001632
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001633/*
1634 * Get the media stream index of the default video stream in the call.
1635 */
1636PJ_DEF(int) pjsua_call_get_vid_stream_idx(pjsua_call_id call_id)
1637{
1638 pjsua_call *call;
1639 int first_active, first_inactive;
1640
1641 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1642 PJ_EINVAL);
1643
1644 PJSUA_LOCK();
1645 call = &pjsua_var.calls[call_id];
1646 call_get_vid_strm_info(call, &first_active, &first_inactive, NULL, NULL);
1647 PJSUA_UNLOCK();
1648
1649 if (first_active == -1)
1650 return first_inactive;
1651
1652 return first_active;
1653}
1654
1655
Benny Prijono9f468d12011-07-07 07:46:33 +00001656#endif /* PJSUA_HAS_VIDEO */
1657