1
/* $Id: pjsua_vid.c 4750 2014-02-19 04:11:43Z bennylp $ */
3
* Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com)
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.
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.
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
19
#include <pjsua-lib/pjsua.h>
20
#include <pjsua-lib/pjsua_internal.h>
22
#if defined(PJSUA_MEDIA_HAS_PJMEDIA) && PJSUA_MEDIA_HAS_PJMEDIA != 0
24
#define THIS_FILE "pjsua_vid.c"
28
#define ENABLE_EVENT 1
29
#define VID_TEE_MAX_PORT (PJSUA_MAX_CALLS + 1)
31
#define PJSUA_SHOW_WINDOW 1
32
#define PJSUA_HIDE_WINDOW 0
35
static void free_vid_win(pjsua_vid_win_id wid);
37
/*****************************************************************************
38
* pjsua video subsystem.
40
pj_status_t pjsua_vid_subsys_init(void)
45
PJ_LOG(4,(THIS_FILE, "Initializing video subsystem.."));
48
status = pjmedia_video_format_mgr_create(pjsua_var.pool, 64, 0, NULL);
49
if (status != PJ_SUCCESS) {
50
PJ_PERROR(1,(THIS_FILE, status,
51
"Error creating PJMEDIA video format manager"));
55
status = pjmedia_converter_mgr_create(pjsua_var.pool, NULL);
56
if (status != PJ_SUCCESS) {
57
PJ_PERROR(1,(THIS_FILE, status,
58
"Error creating PJMEDIA converter manager"));
62
status = pjmedia_event_mgr_create(pjsua_var.pool, 0, NULL);
63
if (status != PJ_SUCCESS) {
64
PJ_PERROR(1,(THIS_FILE, status,
65
"Error creating PJMEDIA event manager"));
69
status = pjmedia_vid_codec_mgr_create(pjsua_var.pool, NULL);
70
if (status != PJ_SUCCESS) {
71
PJ_PERROR(1,(THIS_FILE, status,
72
"Error creating PJMEDIA video codec manager"));
76
#if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_FFMPEG_VID_CODEC
77
status = pjmedia_codec_ffmpeg_vid_init(NULL, &pjsua_var.cp.factory);
78
if (status != PJ_SUCCESS) {
79
PJ_PERROR(1,(THIS_FILE, status,
80
"Error initializing ffmpeg library"));
85
status = pjmedia_vid_dev_subsys_init(&pjsua_var.cp.factory);
86
if (status != PJ_SUCCESS) {
87
PJ_PERROR(1,(THIS_FILE, status,
88
"Error creating PJMEDIA video subsystem"));
92
for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
93
if (pjsua_var.win[i].pool == NULL) {
94
pjsua_var.win[i].pool = pjsua_pool_create("win%p", 512, 512);
95
if (pjsua_var.win[i].pool == NULL) {
110
pj_status_t pjsua_vid_subsys_start(void)
115
pj_status_t pjsua_vid_subsys_destroy(void)
119
PJ_LOG(4,(THIS_FILE, "Destroying video subsystem.."));
120
pj_log_push_indent();
122
for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
123
if (pjsua_var.win[i].pool) {
125
pj_pool_release(pjsua_var.win[i].pool);
126
pjsua_var.win[i].pool = NULL;
130
pjmedia_vid_dev_subsys_shutdown();
132
#if PJMEDIA_HAS_FFMPEG_VID_CODEC
133
pjmedia_codec_ffmpeg_vid_deinit();
136
if (pjmedia_vid_codec_mgr_instance())
137
pjmedia_vid_codec_mgr_destroy(NULL);
139
if (pjmedia_converter_mgr_instance())
140
pjmedia_converter_mgr_destroy(NULL);
142
if (pjmedia_event_mgr_instance())
143
pjmedia_event_mgr_destroy(NULL);
145
if (pjmedia_video_format_mgr_instance())
146
pjmedia_video_format_mgr_destroy(NULL);
152
PJ_DEF(const char*) pjsua_vid_win_type_name(pjsua_vid_win_type wt)
154
const char *win_type_names[] = {
160
return (wt < PJ_ARRAY_SIZE(win_type_names)) ? win_type_names[wt] : "??";
164
pjsua_call_vid_strm_op_param_default(pjsua_call_vid_strm_op_param *param)
166
pj_bzero(param, sizeof(*param));
168
param->dir = PJMEDIA_DIR_ENCODING_DECODING;
169
param->cap_dev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
172
PJ_DEF(void) pjsua_vid_preview_param_default(pjsua_vid_preview_param *p)
174
p->rend_id = PJMEDIA_VID_DEFAULT_RENDER_DEV;
180
/*****************************************************************************
185
* Get the number of video devices installed in the system.
187
PJ_DEF(unsigned) pjsua_vid_dev_count(void)
189
return pjmedia_vid_dev_count();
193
* Retrieve the video device info for the specified device index.
195
PJ_DEF(pj_status_t) pjsua_vid_dev_get_info(pjmedia_vid_dev_index id,
196
pjmedia_vid_dev_info *vdi)
198
return pjmedia_vid_dev_get_info(id, vdi);
202
* Enum all video devices installed in the system.
204
PJ_DEF(pj_status_t) pjsua_vid_enum_devs(pjmedia_vid_dev_info info[],
207
unsigned i, dev_count;
209
dev_count = pjmedia_vid_dev_count();
211
if (dev_count > *count) dev_count = *count;
213
for (i=0; i<dev_count; ++i) {
216
status = pjmedia_vid_dev_get_info(i, &info[i]);
217
if (status != PJ_SUCCESS)
227
/*****************************************************************************
231
static pj_status_t find_codecs_with_rtp_packing(
232
const pj_str_t *codec_id,
234
const pjmedia_vid_codec_info *p_info[])
236
const pjmedia_vid_codec_info *info[32];
237
unsigned i, j, count_ = PJ_ARRAY_SIZE(info);
240
status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, codec_id,
241
&count_, info, NULL);
242
if (status != PJ_SUCCESS)
245
for (i = 0, j = 0; i < count_ && j<*count; ++i) {
246
if ((info[i]->packings & PJMEDIA_VID_PACKING_PACKETS) == 0)
248
p_info[j++] = info[i];
255
* Enum all supported video codecs in the system.
257
PJ_DEF(pj_status_t) pjsua_vid_enum_codecs( pjsua_codec_info id[],
260
pjmedia_vid_codec_info info[32];
261
unsigned i, j, count, prio[32];
264
count = PJ_ARRAY_SIZE(info);
265
status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, info, prio);
266
if (status != PJ_SUCCESS) {
271
for (i=0, j=0; i<count && j<*p_count; ++i) {
272
if (info[i].packings & PJMEDIA_VID_PACKING_PACKETS) {
273
pj_bzero(&id[j], sizeof(pjsua_codec_info));
275
pjmedia_vid_codec_info_to_id(&info[i], id[j].buf_, sizeof(id[j].buf_));
276
id[j].codec_id = pj_str(id[j].buf_);
277
id[j].priority = (pj_uint8_t) prio[i];
279
if (id[j].codec_id.slen < sizeof(id[j].buf_)) {
280
id[j].desc.ptr = id[j].codec_id.ptr + id[j].codec_id.slen + 1;
281
pj_strncpy(&id[j].desc, &info[i].encoding_desc,
282
sizeof(id[j].buf_) - id[j].codec_id.slen - 1);
296
* Change video codec priority.
298
PJ_DEF(pj_status_t) pjsua_vid_codec_set_priority( const pj_str_t *codec_id,
299
pj_uint8_t priority )
301
const pj_str_t all = { NULL, 0 };
303
if (codec_id->slen==1 && *codec_id->ptr=='*')
306
return pjmedia_vid_codec_mgr_set_codec_priority(NULL, codec_id,
312
* Get video codec parameters.
314
PJ_DEF(pj_status_t) pjsua_vid_codec_get_param(
315
const pj_str_t *codec_id,
316
pjmedia_vid_codec_param *param)
318
const pjmedia_vid_codec_info *info[2];
322
status = find_codecs_with_rtp_packing(codec_id, &count, info);
323
if (status != PJ_SUCCESS)
327
return (count > 1? PJ_ETOOMANY : PJ_ENOTFOUND);
329
status = pjmedia_vid_codec_mgr_get_default_param(NULL, info[0], param);
335
* Set video codec parameters.
337
PJ_DEF(pj_status_t) pjsua_vid_codec_set_param(
338
const pj_str_t *codec_id,
339
const pjmedia_vid_codec_param *param)
341
const pjmedia_vid_codec_info *info[2];
345
status = find_codecs_with_rtp_packing(codec_id, &count, info);
346
if (status != PJ_SUCCESS)
350
return (count > 1? PJ_ETOOMANY : PJ_ENOTFOUND);
352
status = pjmedia_vid_codec_mgr_set_default_param(NULL, info[0], param);
357
/*****************************************************************************
361
static pjsua_vid_win_id vid_preview_get_win(pjmedia_vid_dev_index id,
362
pj_bool_t running_only)
364
pjsua_vid_win_id wid = PJSUA_INVALID_ID;
369
/* Get real capture ID, if set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV */
370
if (id == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
371
pjmedia_vid_dev_info info;
372
pjmedia_vid_dev_get_info(id, &info);
376
for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
377
pjsua_vid_win *w = &pjsua_var.win[i];
378
if (w->type == PJSUA_WND_TYPE_PREVIEW && w->preview_cap_id == id) {
384
if (wid != PJSUA_INVALID_ID && running_only) {
385
pjsua_vid_win *w = &pjsua_var.win[wid];
386
wid = w->preview_running ? wid : PJSUA_INVALID_ID;
395
* NOTE: internal function don't use this!!! Use vid_preview_get_win()
396
* instead. This is because this function will only return window ID
397
* if preview is currently running.
399
PJ_DEF(pjsua_vid_win_id) pjsua_vid_preview_get_win(pjmedia_vid_dev_index id)
401
return vid_preview_get_win(id, PJ_TRUE);
404
PJ_DEF(void) pjsua_vid_win_reset(pjsua_vid_win_id wid)
406
pjsua_vid_win *w = &pjsua_var.win[wid];
407
pj_pool_t *pool = w->pool;
409
pj_bzero(w, sizeof(*w));
410
if (pool) pj_pool_reset(pool);
413
w->preview_cap_id = PJMEDIA_VID_INVALID_DEV;
416
/* Allocate and initialize pjsua video window:
417
* - If the type is preview, video capture, tee, and render
418
* will be instantiated.
419
* - If the type is stream, only renderer will be created.
421
static pj_status_t create_vid_win(pjsua_vid_win_type type,
422
const pjmedia_format *fmt,
423
pjmedia_vid_dev_index rend_id,
424
pjmedia_vid_dev_index cap_id,
427
pjsua_vid_win_id *id)
429
pj_bool_t enable_native_preview;
430
pjsua_vid_win_id wid = PJSUA_INVALID_ID;
431
pjsua_vid_win *w = NULL;
432
pjmedia_vid_port_param vp_param;
437
enable_native_preview = pjsua_var.media_cfg.vid_preview_enable_native;
440
"Creating video window: type=%s, cap_id=%d, rend_id=%d",
441
pjsua_vid_win_type_name(type), cap_id, rend_id));
442
pj_log_push_indent();
444
/* If type is preview, check if it exists already */
445
if (type == PJSUA_WND_TYPE_PREVIEW) {
446
wid = vid_preview_get_win(cap_id, PJ_FALSE);
447
if (wid != PJSUA_INVALID_ID) {
449
/* Show/hide window */
450
pjmedia_vid_dev_stream *strm;
451
pj_bool_t hide = !show;
453
w = &pjsua_var.win[wid];
456
"Window already exists for cap_dev=%d, returning wid=%d",
461
strm = pjmedia_vid_port_get_stream(w->vp_cap);
463
strm = pjmedia_vid_port_get_stream(w->vp_rend);
467
status = pjmedia_vid_dev_stream_set_cap(
468
strm, PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE,
471
pjmedia_vid_dev_stream_set_cap(
472
strm, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS,
483
/* Allocate window */
484
for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
485
w = &pjsua_var.win[i];
486
if (w->type == PJSUA_WND_TYPE_NONE) {
492
if (i == PJSUA_MAX_VID_WINS) {
497
/* Initialize window */
498
pjmedia_vid_port_param_default(&vp_param);
500
if (w->type == PJSUA_WND_TYPE_PREVIEW) {
501
pjmedia_vid_dev_info vdi;
504
* Determine if the device supports native preview.
506
status = pjmedia_vid_dev_get_info(cap_id, &vdi);
507
if (status != PJ_SUCCESS)
510
if (enable_native_preview &&
511
(vdi.caps & PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW))
513
/* Device supports native preview! */
514
w->is_native = PJ_TRUE;
517
status = pjmedia_vid_dev_default_param(w->pool, cap_id,
519
if (status != PJ_SUCCESS)
523
vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
524
vp_param.vidparam.window_hide = !show;
525
vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS;
526
vp_param.vidparam.window_flags = wnd_flags;
529
/* Normalize capture ID, in case it was set to
530
* PJMEDIA_VID_DEFAULT_CAPTURE_DEV
532
cap_id = vp_param.vidparam.cap_id;
534
/* Assign preview capture device ID */
535
w->preview_cap_id = cap_id;
537
/* Create capture video port */
538
vp_param.active = PJ_TRUE;
539
vp_param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
541
vp_param.vidparam.fmt = *fmt;
543
status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_cap);
544
if (status != PJ_SUCCESS)
547
/* Update format info */
548
fmt_ = vp_param.vidparam.fmt;
551
/* Create video tee */
552
status = pjmedia_vid_tee_create(w->pool, fmt, VID_TEE_MAX_PORT,
554
if (status != PJ_SUCCESS)
557
/* Connect capturer to the video tee */
558
status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
559
if (status != PJ_SUCCESS)
562
/* If device supports native preview, enable it */
564
pjmedia_vid_dev_stream *cap_dev;
565
pj_bool_t enabled = PJ_TRUE;
567
cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
568
status = pjmedia_vid_dev_stream_set_cap(
569
cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
571
if (status != PJ_SUCCESS) {
572
PJ_PERROR(1,(THIS_FILE, status,
573
"Error activating native preview, falling back "
574
"to software preview.."));
575
w->is_native = PJ_FALSE;
580
/* Create renderer video port, only if it's not a native preview */
582
status = pjmedia_vid_dev_default_param(w->pool, rend_id,
584
if (status != PJ_SUCCESS)
587
vp_param.active = (w->type == PJSUA_WND_TYPE_STREAM);
588
vp_param.vidparam.dir = PJMEDIA_DIR_RENDER;
589
vp_param.vidparam.fmt = *fmt;
590
vp_param.vidparam.disp_size = fmt->det.vid.size;
591
vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
592
vp_param.vidparam.window_hide = !show;
593
vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS;
594
vp_param.vidparam.window_flags = wnd_flags;
596
status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_rend);
597
if (status != PJ_SUCCESS)
600
/* For preview window, connect capturer & renderer (via tee) */
601
if (w->type == PJSUA_WND_TYPE_PREVIEW) {
602
pjmedia_port *rend_port;
604
rend_port = pjmedia_vid_port_get_passive_port(w->vp_rend);
605
status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, rend_port);
606
if (status != PJ_SUCCESS)
611
"%s window id %d created for cap_dev=%d rend_dev=%d",
612
pjsua_vid_win_type_name(type), wid, cap_id, rend_id));
615
"Preview window id %d created for cap_dev %d, "
616
"using built-in preview!",
624
PJ_LOG(4,(THIS_FILE, "Window %d created", wid));
635
static void free_vid_win(pjsua_vid_win_id wid)
637
pjsua_vid_win *w = &pjsua_var.win[wid];
639
PJ_LOG(4,(THIS_FILE, "Window %d: destroying..", wid));
640
pj_log_push_indent();
643
pjmedia_event_unsubscribe(NULL, &call_media_on_event, NULL,
645
pjmedia_vid_port_stop(w->vp_cap);
646
pjmedia_vid_port_disconnect(w->vp_cap);
647
pjmedia_vid_port_destroy(w->vp_cap);
650
pjmedia_event_unsubscribe(NULL, &call_media_on_event, NULL,
652
pjmedia_vid_port_stop(w->vp_rend);
653
pjmedia_vid_port_destroy(w->vp_rend);
656
pjmedia_port_destroy(w->tee);
658
pjsua_vid_win_reset(wid);
664
static void inc_vid_win(pjsua_vid_win_id wid)
668
pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
670
w = &pjsua_var.win[wid];
671
pj_assert(w->type != PJSUA_WND_TYPE_NONE);
675
static void dec_vid_win(pjsua_vid_win_id wid)
679
pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
681
w = &pjsua_var.win[wid];
682
pj_assert(w->type != PJSUA_WND_TYPE_NONE);
683
if (--w->ref_cnt == 0)
687
/* Initialize video call media */
688
pj_status_t pjsua_vid_channel_init(pjsua_call_media *call_med)
690
pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
692
call_med->strm.v.rdr_dev = acc->cfg.vid_rend_dev;
693
call_med->strm.v.cap_dev = acc->cfg.vid_cap_dev;
694
if (call_med->strm.v.rdr_dev == PJMEDIA_VID_DEFAULT_RENDER_DEV) {
695
pjmedia_vid_dev_info info;
696
pjmedia_vid_dev_get_info(call_med->strm.v.rdr_dev, &info);
697
call_med->strm.v.rdr_dev = info.id;
699
if (call_med->strm.v.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
700
pjmedia_vid_dev_info info;
701
pjmedia_vid_dev_get_info(call_med->strm.v.cap_dev, &info);
702
call_med->strm.v.cap_dev = info.id;
708
/* Internal function: update video channel after SDP negotiation.
709
* Warning: do not use temporary/flip-flop pool, e.g: inv->pool_prov,
710
* for creating stream, etc, as after SDP negotiation and when
711
* the SDP media is not changed, the stream should remain running
712
* while the temporary/flip-flop pool may be released.
714
pj_status_t pjsua_vid_channel_update(pjsua_call_media *call_med,
716
pjmedia_vid_stream_info *si,
717
const pjmedia_sdp_session *local_sdp,
718
const pjmedia_sdp_session *remote_sdp)
720
pjsua_call *call = call_med->call;
721
pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
722
pjmedia_port *media_port;
725
PJ_UNUSED_ARG(tmp_pool);
726
PJ_UNUSED_ARG(local_sdp);
727
PJ_UNUSED_ARG(remote_sdp);
729
PJ_LOG(4,(THIS_FILE, "Video channel update.."));
730
pj_log_push_indent();
732
si->rtcp_sdes_bye_disabled = pjsua_var.media_cfg.no_rtcp_sdes_bye;;
734
/* Check if no media is active */
735
if (si->dir != PJMEDIA_DIR_NONE) {
736
/* Optionally, application may modify other stream settings here
737
* (such as jitter buffer parameters, codec ptime, etc.)
739
si->jb_init = pjsua_var.media_cfg.jb_init;
740
si->jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
741
si->jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
742
si->jb_max = pjsua_var.media_cfg.jb_max;
745
si->ssrc = call_med->ssrc;
747
/* Set RTP timestamp & sequence, normally these value are intialized
748
* automatically when stream session created, but for some cases (e.g:
749
* call reinvite, call update) timestamp and sequence need to be kept
752
si->rtp_ts = call_med->rtp_tx_ts;
753
si->rtp_seq = call_med->rtp_tx_seq;
754
si->rtp_seq_ts_set = call_med->rtp_tx_seq_ts_set;
756
/* Set rate control config from account setting */
757
si->rc_cfg = acc->cfg.vid_stream_rc_cfg;
759
#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
760
/* Enable/disable stream keep-alive and NAT hole punch. */
761
si->use_ka = pjsua_var.acc[call->acc_id].cfg.use_stream_ka;
764
/* Try to get shared format ID between the capture device and
765
* the encoder to avoid format conversion in the capture device.
767
if (si->dir & PJMEDIA_DIR_ENCODING) {
768
pjmedia_vid_dev_info dev_info;
769
pjmedia_vid_codec_info *codec_info = &si->codec_info;
772
status = pjmedia_vid_dev_get_info(call_med->strm.v.cap_dev,
774
if (status != PJ_SUCCESS)
777
/* Find matched format ID */
778
for (i = 0; i < codec_info->dec_fmt_id_cnt; ++i) {
779
for (j = 0; j < dev_info.fmt_cnt; ++j) {
780
if (codec_info->dec_fmt_id[i] ==
781
(pjmedia_format_id)dev_info.fmt[j].id)
783
/* Apply the matched format ID to the codec */
784
si->codec_param->dec_fmt.id =
785
codec_info->dec_fmt_id[i];
787
/* Force outer loop to break */
788
i = codec_info->dec_fmt_id_cnt;
795
/* Create session based on session info. */
796
status = pjmedia_vid_stream_create(pjsua_var.med_endpt, NULL, si,
798
&call_med->strm.v.stream);
799
if (status != PJ_SUCCESS)
803
status = pjmedia_vid_stream_start(call_med->strm.v.stream);
804
if (status != PJ_SUCCESS)
807
if (call_med->prev_state == PJSUA_CALL_MEDIA_NONE)
808
pjmedia_vid_stream_send_rtcp_sdes(call_med->strm.v.stream);
810
/* Setup decoding direction */
811
if (si->dir & PJMEDIA_DIR_DECODING)
813
pjsua_vid_win_id wid;
816
PJ_LOG(4,(THIS_FILE, "Setting up RX.."));
817
pj_log_push_indent();
819
status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
820
PJMEDIA_DIR_DECODING,
822
if (status != PJ_SUCCESS) {
827
/* Create stream video window */
828
status = create_vid_win(PJSUA_WND_TYPE_STREAM,
829
&media_port->info.fmt,
830
call_med->strm.v.rdr_dev,
831
//acc->cfg.vid_rend_dev,
833
acc->cfg.vid_in_auto_show,
834
acc->cfg.vid_wnd_flags,
836
if (status != PJ_SUCCESS) {
841
w = &pjsua_var.win[wid];
844
/* Register to video events */
845
pjmedia_event_subscribe(NULL, &call_media_on_event,
846
call_med, w->vp_rend);
849
/* Connect renderer to stream */
850
status = pjmedia_vid_port_connect(w->vp_rend, media_port,
852
if (status != PJ_SUCCESS) {
858
status = pjmedia_vid_port_start(w->vp_rend);
859
if (status != PJ_SUCCESS) {
866
call_med->strm.v.rdr_win_id = wid;
870
/* Setup encoding direction */
871
if (si->dir & PJMEDIA_DIR_ENCODING && !call->local_hold)
873
pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
875
pjsua_vid_win_id wid;
876
pj_bool_t just_created = PJ_FALSE;
878
PJ_LOG(4,(THIS_FILE, "Setting up TX.."));
879
pj_log_push_indent();
881
status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
882
PJMEDIA_DIR_ENCODING,
884
if (status != PJ_SUCCESS) {
889
/* Note: calling pjsua_vid_preview_get_win() even though
890
* create_vid_win() will automatically create the window
891
* if it doesn't exist, because create_vid_win() will modify
892
* existing window SHOW/HIDE value.
894
wid = vid_preview_get_win(call_med->strm.v.cap_dev, PJ_FALSE);
895
if (wid == PJSUA_INVALID_ID) {
896
/* Create preview video window */
897
status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
898
&media_port->info.fmt,
899
call_med->strm.v.rdr_dev,
900
call_med->strm.v.cap_dev,
901
//acc->cfg.vid_rend_dev,
902
//acc->cfg.vid_cap_dev,
904
acc->cfg.vid_wnd_flags,
906
if (status != PJ_SUCCESS) {
910
just_created = PJ_TRUE;
913
w = &pjsua_var.win[wid];
915
pjmedia_event_subscribe(NULL, &call_media_on_event,
916
call_med, w->vp_cap);
919
/* Connect stream to capturer (via video window tee) */
920
status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
921
if (status != PJ_SUCCESS) {
928
status = pjmedia_vid_port_start(w->vp_cap);
929
if (status != PJ_SUCCESS) {
937
call_med->strm.v.cap_win_id = wid;
943
if (!acc->cfg.vid_out_auto_transmit && call_med->strm.v.stream) {
944
status = pjmedia_vid_stream_pause(call_med->strm.v.stream,
945
PJMEDIA_DIR_ENCODING);
946
if (status != PJ_SUCCESS)
959
/* Internal function to stop video stream */
960
void pjsua_vid_stop_stream(pjsua_call_media *call_med)
962
pjmedia_vid_stream *strm = call_med->strm.v.stream;
963
pjmedia_rtcp_stat stat;
965
pj_assert(call_med->type == PJMEDIA_TYPE_VIDEO);
970
PJ_LOG(4,(THIS_FILE, "Stopping video stream.."));
971
pj_log_push_indent();
973
pjmedia_vid_stream_send_rtcp_bye(strm);
975
if (call_med->strm.v.cap_win_id != PJSUA_INVALID_ID) {
976
pjmedia_port *media_port;
977
pjsua_vid_win *w = &pjsua_var.win[call_med->strm.v.cap_win_id];
980
/* Stop the capture before detaching stream and unsubscribing event */
981
pjmedia_vid_port_stop(w->vp_cap);
983
/* Disconnect video stream from capture device */
984
status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
985
PJMEDIA_DIR_ENCODING,
987
if (status == PJ_SUCCESS) {
988
pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
991
/* Unsubscribe event */
992
pjmedia_event_unsubscribe(NULL, &call_media_on_event, call_med,
995
/* Re-start capture again, if it is used by other stream */
997
pjmedia_vid_port_start(w->vp_cap);
999
dec_vid_win(call_med->strm.v.cap_win_id);
1000
call_med->strm.v.cap_win_id = PJSUA_INVALID_ID;
1003
if (call_med->strm.v.rdr_win_id != PJSUA_INVALID_ID) {
1004
pjsua_vid_win *w = &pjsua_var.win[call_med->strm.v.rdr_win_id];
1006
/* Stop the render before unsubscribing event */
1007
pjmedia_vid_port_stop(w->vp_rend);
1008
pjmedia_event_unsubscribe(NULL, &call_media_on_event, call_med,
1011
dec_vid_win(call_med->strm.v.rdr_win_id);
1012
call_med->strm.v.rdr_win_id = PJSUA_INVALID_ID;
1015
if ((call_med->dir & PJMEDIA_DIR_ENCODING) &&
1016
(pjmedia_vid_stream_get_stat(strm, &stat) == PJ_SUCCESS))
1018
/* Save RTP timestamp & sequence, so when media session is
1019
* restarted, those values will be restored as the initial
1020
* RTP timestamp & sequence of the new media session. So in
1021
* the same call session, RTP timestamp and sequence are
1022
* guaranteed to be contigue.
1024
call_med->rtp_tx_seq_ts_set = 1 | (1 << 1);
1025
call_med->rtp_tx_seq = stat.rtp_tx_last_seq;
1026
call_med->rtp_tx_ts = stat.rtp_tx_last_ts;
1029
pjmedia_vid_stream_destroy(strm);
1030
call_med->strm.v.stream = NULL;
1032
pj_log_pop_indent();
1036
* Does it have built-in preview support.
1038
PJ_DEF(pj_bool_t) pjsua_vid_preview_has_native(pjmedia_vid_dev_index id)
1040
pjmedia_vid_dev_info vdi;
1042
return (pjmedia_vid_dev_get_info(id, &vdi)==PJ_SUCCESS) ?
1043
((vdi.caps & PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW)!=0) : PJ_FALSE;
1047
* Start video preview window for the specified capture device.
1049
PJ_DEF(pj_status_t) pjsua_vid_preview_start(pjmedia_vid_dev_index id,
1050
const pjsua_vid_preview_param *prm)
1052
pjsua_vid_win_id wid;
1054
pjmedia_vid_dev_index rend_id;
1055
pjsua_vid_preview_param default_param;
1059
pjsua_vid_preview_param_default(&default_param);
1060
prm = &default_param;
1063
PJ_LOG(4,(THIS_FILE, "Starting preview for cap_dev=%d, show=%d",
1065
pj_log_push_indent();
1069
rend_id = prm->rend_id;
1071
status = create_vid_win(PJSUA_WND_TYPE_PREVIEW, NULL, rend_id, id,
1072
prm->show, prm->wnd_flags, &wid);
1073
if (status != PJ_SUCCESS) {
1075
pj_log_pop_indent();
1079
w = &pjsua_var.win[wid];
1080
if (w->preview_running) {
1082
pj_log_pop_indent();
1086
/* Start renderer, unless it's native preview */
1087
if (w->is_native && !pjmedia_vid_port_is_running(w->vp_cap)) {
1088
pjmedia_vid_dev_stream *cap_dev;
1089
pj_bool_t enabled = PJ_TRUE;
1091
cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
1092
status = pjmedia_vid_dev_stream_set_cap(
1093
cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
1095
if (status != PJ_SUCCESS) {
1096
PJ_PERROR(1,(THIS_FILE, status,
1097
"Error activating native preview, falling back "
1098
"to software preview.."));
1099
w->is_native = PJ_FALSE;
1103
if (!w->is_native && !pjmedia_vid_port_is_running(w->vp_rend)) {
1104
status = pjmedia_vid_port_start(w->vp_rend);
1105
if (status != PJ_SUCCESS) {
1107
pj_log_pop_indent();
1112
/* Start capturer */
1113
if (!pjmedia_vid_port_is_running(w->vp_cap)) {
1114
status = pjmedia_vid_port_start(w->vp_cap);
1115
if (status != PJ_SUCCESS) {
1117
pj_log_pop_indent();
1123
w->preview_running = PJ_TRUE;
1126
pj_log_pop_indent();
1131
* Stop video preview.
1133
PJ_DEF(pj_status_t) pjsua_vid_preview_stop(pjmedia_vid_dev_index id)
1135
pjsua_vid_win_id wid = PJSUA_INVALID_ID;
1140
wid = pjsua_vid_preview_get_win(id);
1141
if (wid == PJSUA_INVALID_ID) {
1143
pj_log_pop_indent();
1144
return PJ_ENOTFOUND;
1147
PJ_LOG(4,(THIS_FILE, "Stopping preview for cap_dev=%d", id));
1148
pj_log_push_indent();
1150
w = &pjsua_var.win[wid];
1151
if (w->preview_running) {
1153
pjmedia_vid_dev_stream *cap_dev;
1154
pj_bool_t enabled = PJ_FALSE;
1156
cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
1157
status = pjmedia_vid_dev_stream_set_cap(
1158
cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
1161
status = pjmedia_vid_port_stop(w->vp_rend);
1164
if (status != PJ_SUCCESS) {
1165
PJ_PERROR(1,(THIS_FILE, status, "Error stopping %spreview",
1166
(w->is_native ? "native " : "")));
1168
pj_log_pop_indent();
1173
w->preview_running = PJ_FALSE;
1177
pj_log_pop_indent();
1183
/*****************************************************************************
1189
* Enumerates all video windows.
1191
PJ_DEF(pj_status_t) pjsua_vid_enum_wins( pjsua_vid_win_id wids[],
1198
for (i=0; i<PJSUA_MAX_VID_WINS && cnt <*count; ++i) {
1199
pjsua_vid_win *w = &pjsua_var.win[i];
1200
if (w->type != PJSUA_WND_TYPE_NONE)
1213
PJ_DEF(pj_status_t) pjsua_vid_win_get_info( pjsua_vid_win_id wid,
1214
pjsua_vid_win_info *wi)
1217
pjmedia_vid_dev_stream *s;
1218
pjmedia_vid_dev_param vparam;
1221
PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && wi, PJ_EINVAL);
1223
pj_bzero(wi, sizeof(*wi));
1226
w = &pjsua_var.win[wid];
1228
wi->is_native = w->is_native;
1231
pjmedia_vid_dev_stream *cap_strm;
1232
pjmedia_vid_dev_cap cap = PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
1234
cap_strm = pjmedia_vid_port_get_stream(w->vp_cap);
1238
status = pjmedia_vid_dev_stream_get_cap(cap_strm, cap, &wi->hwnd);
1245
if (w->vp_rend == NULL) {
1250
s = pjmedia_vid_port_get_stream(w->vp_rend);
1256
status = pjmedia_vid_dev_stream_get_param(s, &vparam);
1257
if (status != PJ_SUCCESS) {
1262
wi->rdr_dev = vparam.rend_id;
1263
wi->hwnd = vparam.window;
1264
wi->show = !vparam.window_hide;
1265
wi->pos = vparam.window_pos;
1266
wi->size = vparam.disp_size;
1274
* Show or hide window.
1276
PJ_DEF(pj_status_t) pjsua_vid_win_set_show( pjsua_vid_win_id wid,
1280
pjmedia_vid_dev_stream *s;
1284
PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
1287
w = &pjsua_var.win[wid];
1288
if (w->vp_rend == NULL) {
1294
s = pjmedia_vid_port_get_stream(w->vp_rend);
1300
/* Make sure that renderer gets started before shown up */
1301
if (show && !pjmedia_vid_port_is_running(w->vp_rend))
1302
status = pjmedia_vid_port_start(w->vp_rend);
1305
status = pjmedia_vid_dev_stream_set_cap(s,
1306
PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE, &hide);
1314
* Set video window position.
1316
PJ_DEF(pj_status_t) pjsua_vid_win_set_pos( pjsua_vid_win_id wid,
1317
const pjmedia_coord *pos)
1320
pjmedia_vid_dev_stream *s;
1323
PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && pos, PJ_EINVAL);
1326
w = &pjsua_var.win[wid];
1327
if (w->vp_rend == NULL) {
1333
s = pjmedia_vid_port_get_stream(w->vp_rend);
1339
status = pjmedia_vid_dev_stream_set_cap(s,
1340
PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION, pos);
1350
PJ_DEF(pj_status_t) pjsua_vid_win_set_size( pjsua_vid_win_id wid,
1351
const pjmedia_rect_size *size)
1354
pjmedia_vid_dev_stream *s;
1357
PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && size, PJ_EINVAL);
1360
w = &pjsua_var.win[wid];
1361
if (w->vp_rend == NULL) {
1367
s = pjmedia_vid_port_get_stream(w->vp_rend);
1373
status = pjmedia_vid_dev_stream_set_cap(s,
1374
PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE, size);
1382
* Set video orientation.
1384
PJ_DEF(pj_status_t) pjsua_vid_win_rotate( pjsua_vid_win_id wid,
1388
pjmedia_vid_dev_stream *s;
1389
pjmedia_orient orient;
1392
PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
1393
PJ_ASSERT_RETURN((angle % 90) == 0, PJ_EINVAL);
1395
/* Normalize angle, so it must be 0, 90, 180, or 270. */
1400
/* Convert angle to pjmedia_orient */
1406
orient = PJMEDIA_ORIENT_ROTATE_90DEG;
1409
orient = PJMEDIA_ORIENT_ROTATE_180DEG;
1412
orient = PJMEDIA_ORIENT_ROTATE_270DEG;
1415
pj_assert(!"Angle must have been validated");
1420
w = &pjsua_var.win[wid];
1421
if (w->vp_rend == NULL) {
1427
s = pjmedia_vid_port_get_stream(w->vp_rend);
1433
status = pjmedia_vid_dev_stream_set_cap(s,
1434
PJMEDIA_VID_DEV_CAP_ORIENTATION, &orient);
1442
static void call_get_vid_strm_info(pjsua_call *call,
1444
int *first_inactive,
1445
unsigned *active_cnt,
1448
unsigned i, var_cnt = 0;
1450
if (first_active && ++var_cnt)
1452
if (first_inactive && ++var_cnt)
1453
*first_inactive = -1;
1454
if (active_cnt && ++var_cnt)
1456
if (cnt && ++var_cnt)
1459
for (i = 0; i < call->med_cnt && var_cnt; ++i) {
1460
if (call->media[i].type == PJMEDIA_TYPE_VIDEO) {
1461
if (call->media[i].dir != PJMEDIA_DIR_NONE)
1463
if (first_active && *first_active == -1) {
1469
} else if (first_inactive && *first_inactive == -1) {
1470
*first_inactive = i;
1480
/* Send SDP reoffer. */
1481
static pj_status_t call_reoffer_sdp(pjsua_call_id call_id,
1482
const pjmedia_sdp_session *sdp)
1485
pjsip_tx_data *tdata;
1489
status = acquire_call("call_reoffer_sdp()", call_id, &call, &dlg);
1490
if (status != PJ_SUCCESS)
1493
if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1494
PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
1495
pjsip_dlg_dec_lock(dlg);
1496
return PJSIP_ESESSIONSTATE;
1499
/* Create re-INVITE with new offer */
1500
status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1501
if (status != PJ_SUCCESS) {
1502
pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
1503
pjsip_dlg_dec_lock(dlg);
1507
/* Send the request */
1508
status = pjsip_inv_send_msg( call->inv, tdata);
1509
if (status != PJ_SUCCESS) {
1510
pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
1511
pjsip_dlg_dec_lock(dlg);
1515
pjsip_dlg_dec_lock(dlg);
1520
/* Add a new video stream into a call */
1521
static pj_status_t call_add_video(pjsua_call *call,
1522
pjmedia_vid_dev_index cap_dev,
1525
pj_pool_t *pool = call->inv->pool_prov;
1526
pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1527
pjsua_call_media *call_med;
1528
const pjmedia_sdp_session *current_sdp;
1529
pjmedia_sdp_session *sdp;
1530
pjmedia_sdp_media *sdp_m;
1531
pjmedia_transport_info tpinfo;
1534
/* Verify media slot availability */
1535
if (call->med_cnt == PJSUA_MAX_CALL_MEDIA)
1538
if (pjsua_call_media_is_changing(call)) {
1539
PJ_LOG(1,(THIS_FILE, "Unable to add video" ERR_MEDIA_CHANGING));
1540
return PJ_EINVALIDOP;
1543
/* Get active local SDP and clone it */
1544
status = pjmedia_sdp_neg_get_active_local(call->inv->neg, ¤t_sdp);
1545
if (status != PJ_SUCCESS)
1548
sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, current_sdp);
1550
/* Clean up provisional media before using it */
1551
pjsua_media_prov_clean_up(call->index);
1553
/* Update provisional media from call media */
1554
call->med_prov_cnt = call->med_cnt;
1555
pj_memcpy(call->media_prov, call->media,
1556
sizeof(call->media[0]) * call->med_cnt);
1558
/* Initialize call media */
1559
call_med = &call->media_prov[call->med_prov_cnt++];
1560
status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1561
&acc_cfg->rtp_cfg, call->secure_level,
1562
NULL, PJ_FALSE, NULL);
1563
if (status != PJ_SUCCESS)
1566
/* Override default capture device setting */
1567
call_med->strm.v.cap_dev = cap_dev;
1569
/* Init transport media */
1570
status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1571
NULL, call_med->idx);
1572
if (status != PJ_SUCCESS)
1575
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_INIT);
1577
/* Get transport address info */
1578
pjmedia_transport_info_init(&tpinfo);
1579
pjmedia_transport_get_info(call_med->tp, &tpinfo);
1581
/* Create SDP media line */
1582
status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1583
&tpinfo.sock_info, 0, &sdp_m);
1584
if (status != PJ_SUCCESS)
1587
sdp->media[sdp->media_count++] = sdp_m;
1589
/* Update media direction, if it is not 'sendrecv' */
1590
if (dir != PJMEDIA_DIR_ENCODING_DECODING) {
1591
pjmedia_sdp_attr *a;
1593
/* Remove sendrecv direction attribute, if any */
1594
pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
1596
if (dir == PJMEDIA_DIR_ENCODING)
1597
a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
1598
else if (dir == PJMEDIA_DIR_DECODING)
1599
a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
1601
a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
1603
pjmedia_sdp_media_add_attr(sdp_m, a);
1606
/* Update SDP media line by media transport */
1607
status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1608
sdp, NULL, call_med->idx);
1609
if (status != PJ_SUCCESS)
1612
status = call_reoffer_sdp(call->index, sdp);
1613
if (status != PJ_SUCCESS)
1616
call->opt.vid_cnt++;
1622
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
1623
pjmedia_transport_close(call_med->tp);
1624
call_med->tp = call_med->tp_orig = NULL;
1631
/* Modify a video stream from a call, i.e: update direction,
1634
static pj_status_t call_modify_video(pjsua_call *call,
1639
pjsua_call_media *call_med;
1640
const pjmedia_sdp_session *current_sdp;
1641
pjmedia_sdp_session *sdp;
1644
if (pjsua_call_media_is_changing(call)) {
1645
PJ_LOG(1,(THIS_FILE, "Unable to modify video" ERR_MEDIA_CHANGING));
1646
return PJ_EINVALIDOP;
1649
/* Verify and normalize media index */
1650
if (med_idx == -1) {
1653
call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1654
if (first_active == -1)
1655
return PJ_ENOTFOUND;
1657
med_idx = first_active;
1660
/* Clean up provisional media before using it */
1661
pjsua_media_prov_clean_up(call->index);
1663
/* Update provisional media from call media */
1664
call->med_prov_cnt = call->med_cnt;
1665
pj_memcpy(call->media_prov, call->media,
1666
sizeof(call->media[0]) * call->med_cnt);
1668
call_med = &call->media_prov[med_idx];
1670
/* Verify if the stream media type is video */
1671
if (call_med->type != PJMEDIA_TYPE_VIDEO)
1674
/* Verify if the stream dir is not changed */
1675
if ((!remove && call_med->dir == dir) ||
1676
( remove && (call_med->tp_st == PJSUA_MED_TP_DISABLED ||
1677
call_med->tp == NULL)))
1682
/* Get active local SDP and clone it */
1683
status = pjmedia_sdp_neg_get_active_local(call->inv->neg, ¤t_sdp);
1684
if (status != PJ_SUCCESS)
1687
sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, current_sdp);
1689
pj_assert(med_idx < (int)sdp->media_count);
1692
pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1693
pj_pool_t *pool = call->inv->pool_prov;
1694
pjmedia_sdp_media *sdp_m;
1696
/* Enabling video */
1697
if (call_med->dir == PJMEDIA_DIR_NONE) {
1698
unsigned i, vid_cnt = 0;
1700
/* Check if vid_cnt in call option needs to be increased */
1701
for (i = 0; i < call->med_cnt; ++i) {
1702
if (call->media[i].type == PJMEDIA_TYPE_VIDEO &&
1703
call->media[i].dir != PJMEDIA_DIR_NONE)
1708
if (call->opt.vid_cnt <= vid_cnt)
1709
call->opt.vid_cnt++;
1712
status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1713
&acc_cfg->rtp_cfg, call->secure_level,
1714
NULL, PJ_FALSE, NULL);
1715
if (status != PJ_SUCCESS)
1718
/* Init transport media */
1719
if (call_med->tp && call_med->tp_st == PJSUA_MED_TP_IDLE) {
1720
status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1721
NULL, call_med->idx);
1722
if (status != PJ_SUCCESS)
1726
sdp_m = sdp->media[med_idx];
1728
/* Create new SDP media line if the stream is disabled */
1729
if (sdp->media[med_idx]->desc.port == 0) {
1730
pjmedia_transport_info tpinfo;
1732
/* Get transport address info */
1733
pjmedia_transport_info_init(&tpinfo);
1734
pjmedia_transport_get_info(call_med->tp, &tpinfo);
1736
status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1737
&tpinfo.sock_info, 0, &sdp_m);
1738
if (status != PJ_SUCCESS)
1743
pjmedia_sdp_attr *a;
1745
/* Remove any direction attributes */
1746
pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
1747
pjmedia_sdp_media_remove_all_attr(sdp_m, "sendonly");
1748
pjmedia_sdp_media_remove_all_attr(sdp_m, "recvonly");
1749
pjmedia_sdp_media_remove_all_attr(sdp_m, "inactive");
1751
/* Update media direction */
1752
if (dir == PJMEDIA_DIR_ENCODING_DECODING)
1753
a = pjmedia_sdp_attr_create(pool, "sendrecv", NULL);
1754
else if (dir == PJMEDIA_DIR_ENCODING)
1755
a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
1756
else if (dir == PJMEDIA_DIR_DECODING)
1757
a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
1759
a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
1761
pjmedia_sdp_media_add_attr(sdp_m, a);
1764
sdp->media[med_idx] = sdp_m;
1766
if (call_med->dir == PJMEDIA_DIR_NONE) {
1767
/* Update SDP media line by media transport */
1768
status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1769
sdp, NULL, call_med->idx);
1770
if (status != PJ_SUCCESS)
1775
if (status != PJ_SUCCESS) {
1776
pjsua_media_prov_clean_up(call->index);
1782
pj_pool_t *pool = call->inv->pool_prov;
1784
/* Mark media transport to disabled */
1785
// Don't close this here, as SDP negotiation has not been
1786
// done and stream may be still active.
1787
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED);
1789
/* Deactivate the stream */
1790
pjmedia_sdp_media_deactivate(pool, sdp->media[med_idx]);
1792
call->opt.vid_cnt--;
1795
status = call_reoffer_sdp(call->index, sdp);
1796
if (status != PJ_SUCCESS)
1803
/* Change capture device of a video stream in a call */
1804
static pj_status_t call_change_cap_dev(pjsua_call *call,
1806
pjmedia_vid_dev_index cap_dev)
1808
pjsua_call_media *call_med;
1809
pjmedia_vid_dev_stream *old_dev;
1810
pjmedia_vid_dev_switch_param switch_prm;
1811
pjmedia_vid_dev_info info;
1812
pjsua_vid_win *w, *new_w = NULL;
1813
pjsua_vid_win_id wid, new_wid;
1814
pjmedia_port *media_port;
1817
/* Verify and normalize media index */
1818
if (med_idx == -1) {
1821
call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1822
if (first_active == -1)
1823
return PJ_ENOTFOUND;
1825
med_idx = first_active;
1828
call_med = &call->media[med_idx];
1830
/* Verify if the stream media type is video */
1831
if (call_med->type != PJMEDIA_TYPE_VIDEO)
1834
/* Verify the capture device */
1835
status = pjmedia_vid_dev_get_info(cap_dev, &info);
1836
if (status != PJ_SUCCESS || info.dir != PJMEDIA_DIR_CAPTURE)
1839
/* The specified capture device is being used already */
1840
if (call_med->strm.v.cap_dev == cap_dev)
1843
/* == Apply the new capture device == */
1845
wid = call_med->strm.v.cap_win_id;
1846
w = &pjsua_var.win[wid];
1847
pj_assert(w->type == PJSUA_WND_TYPE_PREVIEW && w->vp_cap);
1849
/* If the old device supports fast switching, then that's excellent! */
1850
old_dev = pjmedia_vid_port_get_stream(w->vp_cap);
1851
pjmedia_vid_dev_switch_param_default(&switch_prm);
1852
switch_prm.target_id = cap_dev;
1853
status = pjmedia_vid_dev_stream_set_cap(old_dev,
1854
PJMEDIA_VID_DEV_CAP_SWITCH,
1856
if (status == PJ_SUCCESS) {
1857
w->preview_cap_id = cap_dev;
1858
call_med->strm.v.cap_dev = cap_dev;
1862
/* No it doesn't support fast switching. Do slow switching then.. */
1863
status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
1864
PJMEDIA_DIR_ENCODING, &media_port);
1865
if (status != PJ_SUCCESS)
1868
pjmedia_event_unsubscribe(NULL, &call_media_on_event, call_med,
1871
/* temporarily disconnect while we operate on the tee. */
1872
pjmedia_vid_port_disconnect(w->vp_cap);
1874
/* = Detach stream port from the old capture device's tee = */
1875
status = pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
1876
if (status != PJ_SUCCESS) {
1877
/* Something wrong, assume that media_port has been removed
1880
PJ_PERROR(4,(THIS_FILE, status,
1881
"Warning: call %d: unable to remove video from tee",
1885
/* Reconnect again immediately. We're done with w->tee */
1886
pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
1888
/* = Attach stream port to the new capture device = */
1890
/* Note: calling pjsua_vid_preview_get_win() even though
1891
* create_vid_win() will automatically create the window
1892
* if it doesn't exist, because create_vid_win() will modify
1893
* existing window SHOW/HIDE value.
1895
new_wid = vid_preview_get_win(cap_dev, PJ_FALSE);
1896
if (new_wid == PJSUA_INVALID_ID) {
1897
pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
1899
/* Create preview video window */
1900
status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
1901
&media_port->info.fmt,
1902
call_med->strm.v.rdr_dev,
1905
acc->cfg.vid_wnd_flags,
1907
if (status != PJ_SUCCESS)
1911
inc_vid_win(new_wid);
1912
new_w = &pjsua_var.win[new_wid];
1914
/* Connect stream to capturer (via video window tee) */
1915
status = pjmedia_vid_tee_add_dst_port2(new_w->tee, 0, media_port);
1916
if (status != PJ_SUCCESS)
1920
/* Start renderer */
1921
status = pjmedia_vid_port_start(new_w->vp_rend);
1922
if (status != PJ_SUCCESS)
1927
pjmedia_event_subscribe(NULL, &call_media_on_event,
1928
call_med, new_w->vp_cap);
1931
/* Start capturer */
1932
if (!pjmedia_vid_port_is_running(new_w->vp_cap)) {
1933
status = pjmedia_vid_port_start(new_w->vp_cap);
1934
if (status != PJ_SUCCESS)
1939
call_med->strm.v.cap_dev = cap_dev;
1940
call_med->strm.v.cap_win_id = new_wid;
1946
PJ_PERROR(4,(THIS_FILE, status,
1947
"Call %d: error changing capture device to %d",
1948
call->index, cap_dev));
1951
/* Unsubscribe, just in case */
1952
pjmedia_event_unsubscribe(NULL, &call_media_on_event, call_med,
1954
/* Disconnect media port from the new capturer */
1955
pjmedia_vid_tee_remove_dst_port(new_w->tee, media_port);
1956
/* Release the new capturer */
1957
dec_vid_win(new_wid);
1960
/* Revert back to the old capturer */
1961
pjmedia_vid_port_disconnect(w->vp_cap);
1962
status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
1963
pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
1964
if (status != PJ_SUCCESS)
1969
pjmedia_event_subscribe(NULL, &call_media_on_event,
1970
call_med, w->vp_cap);
1977
/* Start/stop transmitting video stream in a call */
1978
static pj_status_t call_set_tx_video(pjsua_call *call,
1982
pjsua_call_media *call_med;
1985
/* Verify and normalize media index */
1986
if (med_idx == -1) {
1989
call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1990
if (first_active == -1)
1991
return PJ_ENOTFOUND;
1993
med_idx = first_active;
1996
call_med = &call->media[med_idx];
1998
/* Verify if the stream is transmitting video */
1999
if (call_med->type != PJMEDIA_TYPE_VIDEO ||
2000
(enable && (call_med->dir & PJMEDIA_DIR_ENCODING) == 0))
2006
/* Start stream in encoding direction */
2007
status = pjmedia_vid_stream_resume(call_med->strm.v.stream,
2008
PJMEDIA_DIR_ENCODING);
2010
/* Pause stream in encoding direction */
2011
status = pjmedia_vid_stream_pause( call_med->strm.v.stream,
2012
PJMEDIA_DIR_ENCODING);
2019
static pj_status_t call_send_vid_keyframe(pjsua_call *call,
2022
pjsua_call_media *call_med;
2024
/* Verify and normalize media index */
2025
if (med_idx == -1) {
2028
call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
2029
if (first_active == -1)
2030
return PJ_ENOTFOUND;
2032
med_idx = first_active;
2035
call_med = &call->media[med_idx];
2037
/* Verify media type and stream instance. */
2038
if (call_med->type != PJMEDIA_TYPE_VIDEO || !call_med->strm.v.stream)
2041
return pjmedia_vid_stream_send_keyframe(call_med->strm.v.stream);
2046
* Start, stop, and/or manipulate video transmission for the specified call.
2048
PJ_DEF(pj_status_t) pjsua_call_set_vid_strm (
2049
pjsua_call_id call_id,
2050
pjsua_call_vid_strm_op op,
2051
const pjsua_call_vid_strm_op_param *param)
2054
pjsip_dialog *dlg = NULL;
2055
pjsua_call_vid_strm_op_param param_;
2058
PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2060
PJ_ASSERT_RETURN(op != PJSUA_CALL_VID_STRM_NO_OP, PJ_EINVAL);
2062
PJ_LOG(4,(THIS_FILE, "Call %d: set video stream, op=%d",
2064
pj_log_push_indent();
2066
status = acquire_call("pjsua_call_set_vid_strm()", call_id, &call, &dlg);
2067
if (status != PJ_SUCCESS)
2073
pjsua_call_vid_strm_op_param_default(¶m_);
2076
/* If set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
2077
* account default video capture device.
2079
if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
2080
pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
2081
param_.cap_dev = acc_cfg->vid_cap_dev;
2083
/* If the account default video capture device is
2084
* PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
2085
* global default video capture device.
2087
if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
2088
pjmedia_vid_dev_info info;
2089
pjmedia_vid_dev_get_info(param_.cap_dev, &info);
2090
pj_assert(info.dir == PJMEDIA_DIR_CAPTURE);
2091
param_.cap_dev = info.id;
2096
case PJSUA_CALL_VID_STRM_ADD:
2097
status = call_add_video(call, param_.cap_dev, param_.dir);
2099
case PJSUA_CALL_VID_STRM_REMOVE:
2100
status = call_modify_video(call, param_.med_idx, PJMEDIA_DIR_NONE,
2103
case PJSUA_CALL_VID_STRM_CHANGE_DIR:
2104
status = call_modify_video(call, param_.med_idx, param_.dir, PJ_FALSE);
2106
case PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV:
2107
status = call_change_cap_dev(call, param_.med_idx, param_.cap_dev);
2109
case PJSUA_CALL_VID_STRM_START_TRANSMIT:
2110
status = call_set_tx_video(call, param_.med_idx, PJ_TRUE);
2112
case PJSUA_CALL_VID_STRM_STOP_TRANSMIT:
2113
status = call_set_tx_video(call, param_.med_idx, PJ_FALSE);
2115
case PJSUA_CALL_VID_STRM_SEND_KEYFRAME:
2116
status = call_send_vid_keyframe(call, param_.med_idx);
2119
status = PJ_EINVALIDOP;
2124
if (dlg) pjsip_dlg_dec_lock(dlg);
2125
pj_log_pop_indent();
2131
* Get the media stream index of the default video stream in the call.
2133
PJ_DEF(int) pjsua_call_get_vid_stream_idx(pjsua_call_id call_id)
2136
int first_active, first_inactive;
2138
PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2142
call = &pjsua_var.calls[call_id];
2143
call_get_vid_strm_info(call, &first_active, &first_inactive, NULL, NULL);
2146
if (first_active == -1)
2147
return first_inactive;
2149
return first_active;
2154
* Determine if video stream for the specified call is currently running
2155
* for the specified direction.
2157
PJ_DEF(pj_bool_t) pjsua_call_vid_stream_is_running( pjsua_call_id call_id,
2162
pjsua_call_media *call_med;
2164
PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2167
/* Verify and normalize media index */
2168
if (med_idx == -1) {
2169
med_idx = pjsua_call_get_vid_stream_idx(call_id);
2172
call = &pjsua_var.calls[call_id];
2173
PJ_ASSERT_RETURN(med_idx >= 0 && med_idx < (int)call->med_cnt, PJ_EINVAL);
2175
call_med = &call->media[med_idx];
2177
/* Verify if the stream is transmitting video */
2178
if (call_med->type != PJMEDIA_TYPE_VIDEO || (call_med->dir & dir) == 0 ||
2179
!call_med->strm.v.stream)
2184
return pjmedia_vid_stream_is_running(call_med->strm.v.stream, dir);
2187
#endif /* PJSUA_HAS_VIDEO */
2189
#endif /* PJSUA_MEDIA_HAS_PJMEDIA */