1
/* $Id: pjsua_vid.c 4071 2012-04-24 05:40:32Z nanang $ */
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
pj_status_t pjsua_vid_channel_update(pjsua_call_media *call_med,
711
pjmedia_vid_stream_info *si,
712
const pjmedia_sdp_session *local_sdp,
713
const pjmedia_sdp_session *remote_sdp)
715
pjsua_call *call = call_med->call;
716
pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
717
pjmedia_port *media_port;
720
PJ_UNUSED_ARG(tmp_pool);
721
PJ_UNUSED_ARG(local_sdp);
722
PJ_UNUSED_ARG(remote_sdp);
724
PJ_LOG(4,(THIS_FILE, "Video channel update.."));
725
pj_log_push_indent();
727
si->rtcp_sdes_bye_disabled = PJ_TRUE;
729
/* Check if no media is active */
730
if (si->dir != PJMEDIA_DIR_NONE) {
731
/* Optionally, application may modify other stream settings here
732
* (such as jitter buffer parameters, codec ptime, etc.)
734
si->jb_init = pjsua_var.media_cfg.jb_init;
735
si->jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
736
si->jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
737
si->jb_max = pjsua_var.media_cfg.jb_max;
740
si->ssrc = call_med->ssrc;
742
/* Set RTP timestamp & sequence, normally these value are intialized
743
* automatically when stream session created, but for some cases (e.g:
744
* call reinvite, call update) timestamp and sequence need to be kept
747
si->rtp_ts = call_med->rtp_tx_ts;
748
si->rtp_seq = call_med->rtp_tx_seq;
749
si->rtp_seq_ts_set = call_med->rtp_tx_seq_ts_set;
751
/* Set rate control config from account setting */
752
si->rc_cfg = acc->cfg.vid_stream_rc_cfg;
754
#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
755
/* Enable/disable stream keep-alive and NAT hole punch. */
756
si->use_ka = pjsua_var.acc[call->acc_id].cfg.use_stream_ka;
759
/* Try to get shared format ID between the capture device and
760
* the encoder to avoid format conversion in the capture device.
762
if (si->dir & PJMEDIA_DIR_ENCODING) {
763
pjmedia_vid_dev_info dev_info;
764
pjmedia_vid_codec_info *codec_info = &si->codec_info;
767
status = pjmedia_vid_dev_get_info(call_med->strm.v.cap_dev,
769
if (status != PJ_SUCCESS)
772
/* Find matched format ID */
773
for (i = 0; i < codec_info->dec_fmt_id_cnt; ++i) {
774
for (j = 0; j < dev_info.fmt_cnt; ++j) {
775
if (codec_info->dec_fmt_id[i] ==
776
(pjmedia_format_id)dev_info.fmt[j].id)
778
/* Apply the matched format ID to the codec */
779
si->codec_param->dec_fmt.id =
780
codec_info->dec_fmt_id[i];
782
/* Force outer loop to break */
783
i = codec_info->dec_fmt_id_cnt;
790
/* Create session based on session info. */
791
status = pjmedia_vid_stream_create(pjsua_var.med_endpt, NULL, si,
793
&call_med->strm.v.stream);
794
if (status != PJ_SUCCESS)
798
status = pjmedia_vid_stream_start(call_med->strm.v.stream);
799
if (status != PJ_SUCCESS)
802
/* Setup decoding direction */
803
if (si->dir & PJMEDIA_DIR_DECODING)
805
pjsua_vid_win_id wid;
808
PJ_LOG(4,(THIS_FILE, "Setting up RX.."));
809
pj_log_push_indent();
811
status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
812
PJMEDIA_DIR_DECODING,
814
if (status != PJ_SUCCESS) {
819
/* Create stream video window */
820
status = create_vid_win(PJSUA_WND_TYPE_STREAM,
821
&media_port->info.fmt,
822
call_med->strm.v.rdr_dev,
823
//acc->cfg.vid_rend_dev,
825
acc->cfg.vid_in_auto_show,
826
acc->cfg.vid_wnd_flags,
828
if (status != PJ_SUCCESS) {
833
w = &pjsua_var.win[wid];
836
/* Register to video events */
837
pjmedia_event_subscribe(NULL, &call_media_on_event,
838
call_med, w->vp_rend);
841
/* Connect renderer to stream */
842
status = pjmedia_vid_port_connect(w->vp_rend, media_port,
844
if (status != PJ_SUCCESS) {
850
status = pjmedia_vid_port_start(w->vp_rend);
851
if (status != PJ_SUCCESS) {
858
call_med->strm.v.rdr_win_id = wid;
862
/* Setup encoding direction */
863
if (si->dir & PJMEDIA_DIR_ENCODING && !call->local_hold)
865
pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
867
pjsua_vid_win_id wid;
868
pj_bool_t just_created = PJ_FALSE;
870
PJ_LOG(4,(THIS_FILE, "Setting up TX.."));
871
pj_log_push_indent();
873
status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
874
PJMEDIA_DIR_ENCODING,
876
if (status != PJ_SUCCESS) {
881
/* Note: calling pjsua_vid_preview_get_win() even though
882
* create_vid_win() will automatically create the window
883
* if it doesn't exist, because create_vid_win() will modify
884
* existing window SHOW/HIDE value.
886
wid = vid_preview_get_win(call_med->strm.v.cap_dev, PJ_FALSE);
887
if (wid == PJSUA_INVALID_ID) {
888
/* Create preview video window */
889
status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
890
&media_port->info.fmt,
891
call_med->strm.v.rdr_dev,
892
call_med->strm.v.cap_dev,
893
//acc->cfg.vid_rend_dev,
894
//acc->cfg.vid_cap_dev,
896
acc->cfg.vid_wnd_flags,
898
if (status != PJ_SUCCESS) {
902
just_created = PJ_TRUE;
905
w = &pjsua_var.win[wid];
907
pjmedia_event_subscribe(NULL, &call_media_on_event,
908
call_med, w->vp_cap);
911
/* Connect stream to capturer (via video window tee) */
912
status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
913
if (status != PJ_SUCCESS) {
920
status = pjmedia_vid_port_start(w->vp_cap);
921
if (status != PJ_SUCCESS) {
929
call_med->strm.v.cap_win_id = wid;
935
if (!acc->cfg.vid_out_auto_transmit && call_med->strm.v.stream) {
936
status = pjmedia_vid_stream_pause(call_med->strm.v.stream,
937
PJMEDIA_DIR_ENCODING);
938
if (status != PJ_SUCCESS)
951
/* Internal function to stop video stream */
952
void pjsua_vid_stop_stream(pjsua_call_media *call_med)
954
pjmedia_vid_stream *strm = call_med->strm.v.stream;
955
pjmedia_rtcp_stat stat;
957
pj_assert(call_med->type == PJMEDIA_TYPE_VIDEO);
962
PJ_LOG(4,(THIS_FILE, "Stopping video stream.."));
963
pj_log_push_indent();
965
if (call_med->strm.v.cap_win_id != PJSUA_INVALID_ID) {
966
pjmedia_port *media_port;
967
pjsua_vid_win *w = &pjsua_var.win[call_med->strm.v.cap_win_id];
970
/* Stop the capture before detaching stream and unsubscribing event */
971
pjmedia_vid_port_stop(w->vp_cap);
973
/* Disconnect video stream from capture device */
974
status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
975
PJMEDIA_DIR_ENCODING,
977
if (status == PJ_SUCCESS) {
978
pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
981
/* Unsubscribe event */
982
pjmedia_event_unsubscribe(NULL, &call_media_on_event, call_med,
985
/* Re-start capture again, if it is used by other stream */
987
pjmedia_vid_port_start(w->vp_cap);
989
dec_vid_win(call_med->strm.v.cap_win_id);
990
call_med->strm.v.cap_win_id = PJSUA_INVALID_ID;
993
if (call_med->strm.v.rdr_win_id != PJSUA_INVALID_ID) {
994
pjsua_vid_win *w = &pjsua_var.win[call_med->strm.v.rdr_win_id];
996
/* Stop the render before unsubscribing event */
997
pjmedia_vid_port_stop(w->vp_rend);
998
pjmedia_event_unsubscribe(NULL, &call_media_on_event, call_med,
1001
dec_vid_win(call_med->strm.v.rdr_win_id);
1002
call_med->strm.v.rdr_win_id = PJSUA_INVALID_ID;
1005
if ((call_med->dir & PJMEDIA_DIR_ENCODING) &&
1006
(pjmedia_vid_stream_get_stat(strm, &stat) == PJ_SUCCESS))
1008
/* Save RTP timestamp & sequence, so when media session is
1009
* restarted, those values will be restored as the initial
1010
* RTP timestamp & sequence of the new media session. So in
1011
* the same call session, RTP timestamp and sequence are
1012
* guaranteed to be contigue.
1014
call_med->rtp_tx_seq_ts_set = 1 | (1 << 1);
1015
call_med->rtp_tx_seq = stat.rtp_tx_last_seq;
1016
call_med->rtp_tx_ts = stat.rtp_tx_last_ts;
1019
pjmedia_vid_stream_destroy(strm);
1020
call_med->strm.v.stream = NULL;
1022
pj_log_pop_indent();
1026
* Does it have built-in preview support.
1028
PJ_DEF(pj_bool_t) pjsua_vid_preview_has_native(pjmedia_vid_dev_index id)
1030
pjmedia_vid_dev_info vdi;
1032
return (pjmedia_vid_dev_get_info(id, &vdi)==PJ_SUCCESS) ?
1033
((vdi.caps & PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW)!=0) : PJ_FALSE;
1037
* Start video preview window for the specified capture device.
1039
PJ_DEF(pj_status_t) pjsua_vid_preview_start(pjmedia_vid_dev_index id,
1040
const pjsua_vid_preview_param *prm)
1042
pjsua_vid_win_id wid;
1044
pjmedia_vid_dev_index rend_id;
1045
pjsua_vid_preview_param default_param;
1049
pjsua_vid_preview_param_default(&default_param);
1050
prm = &default_param;
1053
PJ_LOG(4,(THIS_FILE, "Starting preview for cap_dev=%d, show=%d",
1055
pj_log_push_indent();
1059
rend_id = prm->rend_id;
1061
status = create_vid_win(PJSUA_WND_TYPE_PREVIEW, NULL, rend_id, id,
1062
prm->show, prm->wnd_flags, &wid);
1063
if (status != PJ_SUCCESS) {
1065
pj_log_pop_indent();
1069
w = &pjsua_var.win[wid];
1070
if (w->preview_running) {
1072
pj_log_pop_indent();
1076
/* Start renderer, unless it's native preview */
1077
if (w->is_native && !pjmedia_vid_port_is_running(w->vp_cap)) {
1078
pjmedia_vid_dev_stream *cap_dev;
1079
pj_bool_t enabled = PJ_TRUE;
1081
cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
1082
status = pjmedia_vid_dev_stream_set_cap(
1083
cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
1085
if (status != PJ_SUCCESS) {
1086
PJ_PERROR(1,(THIS_FILE, status,
1087
"Error activating native preview, falling back "
1088
"to software preview.."));
1089
w->is_native = PJ_FALSE;
1093
if (!w->is_native && !pjmedia_vid_port_is_running(w->vp_rend)) {
1094
status = pjmedia_vid_port_start(w->vp_rend);
1095
if (status != PJ_SUCCESS) {
1097
pj_log_pop_indent();
1102
/* Start capturer */
1103
if (!pjmedia_vid_port_is_running(w->vp_cap)) {
1104
status = pjmedia_vid_port_start(w->vp_cap);
1105
if (status != PJ_SUCCESS) {
1107
pj_log_pop_indent();
1113
w->preview_running = PJ_TRUE;
1116
pj_log_pop_indent();
1121
* Stop video preview.
1123
PJ_DEF(pj_status_t) pjsua_vid_preview_stop(pjmedia_vid_dev_index id)
1125
pjsua_vid_win_id wid = PJSUA_INVALID_ID;
1130
wid = pjsua_vid_preview_get_win(id);
1131
if (wid == PJSUA_INVALID_ID) {
1133
pj_log_pop_indent();
1134
return PJ_ENOTFOUND;
1137
PJ_LOG(4,(THIS_FILE, "Stopping preview for cap_dev=%d", id));
1138
pj_log_push_indent();
1140
w = &pjsua_var.win[wid];
1141
if (w->preview_running) {
1143
pjmedia_vid_dev_stream *cap_dev;
1144
pj_bool_t enabled = PJ_FALSE;
1146
cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
1147
status = pjmedia_vid_dev_stream_set_cap(
1148
cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
1151
status = pjmedia_vid_port_stop(w->vp_rend);
1154
if (status != PJ_SUCCESS) {
1155
PJ_PERROR(1,(THIS_FILE, status, "Error stopping %spreview",
1156
(w->is_native ? "native " : "")));
1158
pj_log_pop_indent();
1163
w->preview_running = PJ_FALSE;
1167
pj_log_pop_indent();
1173
/*****************************************************************************
1179
* Enumerates all video windows.
1181
PJ_DEF(pj_status_t) pjsua_vid_enum_wins( pjsua_vid_win_id wids[],
1188
for (i=0; i<PJSUA_MAX_VID_WINS && cnt <*count; ++i) {
1189
pjsua_vid_win *w = &pjsua_var.win[i];
1190
if (w->type != PJSUA_WND_TYPE_NONE)
1203
PJ_DEF(pj_status_t) pjsua_vid_win_get_info( pjsua_vid_win_id wid,
1204
pjsua_vid_win_info *wi)
1207
pjmedia_vid_dev_stream *s;
1208
pjmedia_vid_dev_param vparam;
1211
PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && wi, PJ_EINVAL);
1213
pj_bzero(wi, sizeof(*wi));
1216
w = &pjsua_var.win[wid];
1218
wi->is_native = w->is_native;
1221
pjmedia_vid_dev_stream *cap_strm;
1222
pjmedia_vid_dev_cap cap = PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
1224
cap_strm = pjmedia_vid_port_get_stream(w->vp_cap);
1228
status = pjmedia_vid_dev_stream_get_cap(cap_strm, cap, &wi->hwnd);
1235
if (w->vp_rend == NULL) {
1240
s = pjmedia_vid_port_get_stream(w->vp_rend);
1246
status = pjmedia_vid_dev_stream_get_param(s, &vparam);
1247
if (status != PJ_SUCCESS) {
1252
wi->rdr_dev = vparam.rend_id;
1253
wi->hwnd = vparam.window;
1254
wi->show = !vparam.window_hide;
1255
wi->pos = vparam.window_pos;
1256
wi->size = vparam.disp_size;
1264
* Show or hide window.
1266
PJ_DEF(pj_status_t) pjsua_vid_win_set_show( pjsua_vid_win_id wid,
1270
pjmedia_vid_dev_stream *s;
1274
PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
1277
w = &pjsua_var.win[wid];
1278
if (w->vp_rend == NULL) {
1284
s = pjmedia_vid_port_get_stream(w->vp_rend);
1290
/* Make sure that renderer gets started before shown up */
1291
if (show && !pjmedia_vid_port_is_running(w->vp_rend))
1292
status = pjmedia_vid_port_start(w->vp_rend);
1295
status = pjmedia_vid_dev_stream_set_cap(s,
1296
PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE, &hide);
1304
* Set video window position.
1306
PJ_DEF(pj_status_t) pjsua_vid_win_set_pos( pjsua_vid_win_id wid,
1307
const pjmedia_coord *pos)
1310
pjmedia_vid_dev_stream *s;
1313
PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && pos, PJ_EINVAL);
1316
w = &pjsua_var.win[wid];
1317
if (w->vp_rend == NULL) {
1323
s = pjmedia_vid_port_get_stream(w->vp_rend);
1329
status = pjmedia_vid_dev_stream_set_cap(s,
1330
PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION, pos);
1340
PJ_DEF(pj_status_t) pjsua_vid_win_set_size( pjsua_vid_win_id wid,
1341
const pjmedia_rect_size *size)
1344
pjmedia_vid_dev_stream *s;
1347
PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && size, PJ_EINVAL);
1350
w = &pjsua_var.win[wid];
1351
if (w->vp_rend == NULL) {
1357
s = pjmedia_vid_port_get_stream(w->vp_rend);
1363
status = pjmedia_vid_dev_stream_set_cap(s,
1364
PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE, size);
1372
* Set video orientation.
1374
PJ_DEF(pj_status_t) pjsua_vid_win_rotate( pjsua_vid_win_id wid,
1378
pjmedia_vid_dev_stream *s;
1379
pjmedia_orient orient;
1382
PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
1383
PJ_ASSERT_RETURN((angle % 90) == 0, PJ_EINVAL);
1385
/* Normalize angle, so it must be 0, 90, 180, or 270. */
1390
/* Convert angle to pjmedia_orient */
1396
orient = PJMEDIA_ORIENT_ROTATE_90DEG;
1399
orient = PJMEDIA_ORIENT_ROTATE_180DEG;
1402
orient = PJMEDIA_ORIENT_ROTATE_270DEG;
1405
pj_assert(!"Angle must have been validated");
1410
w = &pjsua_var.win[wid];
1411
if (w->vp_rend == NULL) {
1417
s = pjmedia_vid_port_get_stream(w->vp_rend);
1423
status = pjmedia_vid_dev_stream_set_cap(s,
1424
PJMEDIA_VID_DEV_CAP_ORIENTATION, &orient);
1432
static void call_get_vid_strm_info(pjsua_call *call,
1434
int *first_inactive,
1435
unsigned *active_cnt,
1438
unsigned i, var_cnt = 0;
1440
if (first_active && ++var_cnt)
1442
if (first_inactive && ++var_cnt)
1443
*first_inactive = -1;
1444
if (active_cnt && ++var_cnt)
1446
if (cnt && ++var_cnt)
1449
for (i = 0; i < call->med_cnt && var_cnt; ++i) {
1450
if (call->media[i].type == PJMEDIA_TYPE_VIDEO) {
1451
if (call->media[i].dir != PJMEDIA_DIR_NONE)
1453
if (first_active && *first_active == -1) {
1459
} else if (first_inactive && *first_inactive == -1) {
1460
*first_inactive = i;
1470
/* Send SDP reoffer. */
1471
static pj_status_t call_reoffer_sdp(pjsua_call_id call_id,
1472
const pjmedia_sdp_session *sdp)
1475
pjsip_tx_data *tdata;
1479
status = acquire_call("call_reoffer_sdp()", call_id, &call, &dlg);
1480
if (status != PJ_SUCCESS)
1483
if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1484
PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
1485
pjsip_dlg_dec_lock(dlg);
1486
return PJSIP_ESESSIONSTATE;
1489
/* Create re-INVITE with new offer */
1490
status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1491
if (status != PJ_SUCCESS) {
1492
pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
1493
pjsip_dlg_dec_lock(dlg);
1497
/* Send the request */
1498
status = pjsip_inv_send_msg( call->inv, tdata);
1499
if (status != PJ_SUCCESS) {
1500
pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
1501
pjsip_dlg_dec_lock(dlg);
1505
pjsip_dlg_dec_lock(dlg);
1510
/* Add a new video stream into a call */
1511
static pj_status_t call_add_video(pjsua_call *call,
1512
pjmedia_vid_dev_index cap_dev,
1515
pj_pool_t *pool = call->inv->pool_prov;
1516
pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1517
pjsua_call_media *call_med;
1518
const pjmedia_sdp_session *current_sdp;
1519
pjmedia_sdp_session *sdp;
1520
pjmedia_sdp_media *sdp_m;
1521
pjmedia_transport_info tpinfo;
1524
/* Verify media slot availability */
1525
if (call->med_cnt == PJSUA_MAX_CALL_MEDIA)
1528
/* Get active local SDP and clone it */
1529
status = pjmedia_sdp_neg_get_active_local(call->inv->neg, ¤t_sdp);
1530
if (status != PJ_SUCCESS)
1533
sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, current_sdp);
1535
/* Clean up provisional media before using it */
1536
pjsua_media_prov_clean_up(call->index);
1538
/* Update provisional media from call media */
1539
call->med_prov_cnt = call->med_cnt;
1540
pj_memcpy(call->media_prov, call->media,
1541
sizeof(call->media[0]) * call->med_cnt);
1543
/* Initialize call media */
1544
call_med = &call->media_prov[call->med_prov_cnt++];
1545
status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1546
&acc_cfg->rtp_cfg, call->secure_level,
1547
NULL, PJ_FALSE, NULL);
1548
if (status != PJ_SUCCESS)
1551
/* Override default capture device setting */
1552
call_med->strm.v.cap_dev = cap_dev;
1554
/* Init transport media */
1555
status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1556
NULL, call_med->idx);
1557
if (status != PJ_SUCCESS)
1560
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_INIT);
1562
/* Get transport address info */
1563
pjmedia_transport_info_init(&tpinfo);
1564
pjmedia_transport_get_info(call_med->tp, &tpinfo);
1566
/* Create SDP media line */
1567
status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1568
&tpinfo.sock_info, 0, &sdp_m);
1569
if (status != PJ_SUCCESS)
1572
sdp->media[sdp->media_count++] = sdp_m;
1574
/* Update media direction, if it is not 'sendrecv' */
1575
if (dir != PJMEDIA_DIR_ENCODING_DECODING) {
1576
pjmedia_sdp_attr *a;
1578
/* Remove sendrecv direction attribute, if any */
1579
pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
1581
if (dir == PJMEDIA_DIR_ENCODING)
1582
a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
1583
else if (dir == PJMEDIA_DIR_DECODING)
1584
a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
1586
a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
1588
pjmedia_sdp_media_add_attr(sdp_m, a);
1591
/* Update SDP media line by media transport */
1592
status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1593
sdp, NULL, call_med->idx);
1594
if (status != PJ_SUCCESS)
1597
status = call_reoffer_sdp(call->index, sdp);
1598
if (status != PJ_SUCCESS)
1601
call->opt.vid_cnt++;
1607
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
1608
pjmedia_transport_close(call_med->tp);
1609
call_med->tp = call_med->tp_orig = NULL;
1616
/* Modify a video stream from a call, i.e: update direction,
1619
static pj_status_t call_modify_video(pjsua_call *call,
1624
pjsua_call_media *call_med;
1625
const pjmedia_sdp_session *current_sdp;
1626
pjmedia_sdp_session *sdp;
1629
/* Verify and normalize media index */
1630
if (med_idx == -1) {
1633
call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1634
if (first_active == -1)
1635
return PJ_ENOTFOUND;
1637
med_idx = first_active;
1640
/* Clean up provisional media before using it */
1641
pjsua_media_prov_clean_up(call->index);
1643
/* Update provisional media from call media */
1644
call->med_prov_cnt = call->med_cnt;
1645
pj_memcpy(call->media_prov, call->media,
1646
sizeof(call->media[0]) * call->med_cnt);
1648
call_med = &call->media_prov[med_idx];
1650
/* Verify if the stream media type is video */
1651
if (call_med->type != PJMEDIA_TYPE_VIDEO)
1654
/* Verify if the stream dir is not changed */
1655
if ((!remove && call_med->dir == dir) ||
1656
( remove && (call_med->tp_st == PJSUA_MED_TP_DISABLED ||
1657
call_med->tp == NULL)))
1662
/* Get active local SDP and clone it */
1663
status = pjmedia_sdp_neg_get_active_local(call->inv->neg, ¤t_sdp);
1664
if (status != PJ_SUCCESS)
1667
sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, current_sdp);
1669
pj_assert(med_idx < (int)sdp->media_count);
1672
pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
1673
pj_pool_t *pool = call->inv->pool_prov;
1674
pjmedia_sdp_media *sdp_m;
1676
/* Enabling video */
1677
if (call_med->dir == PJMEDIA_DIR_NONE) {
1678
unsigned i, vid_cnt = 0;
1680
/* Check if vid_cnt in call option needs to be increased */
1681
for (i = 0; i < call->med_cnt; ++i) {
1682
if (call->media[i].type == PJMEDIA_TYPE_VIDEO &&
1683
call->media[i].dir != PJMEDIA_DIR_NONE)
1688
if (call->opt.vid_cnt <= vid_cnt)
1689
call->opt.vid_cnt++;
1692
status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
1693
&acc_cfg->rtp_cfg, call->secure_level,
1694
NULL, PJ_FALSE, NULL);
1695
if (status != PJ_SUCCESS)
1698
/* Init transport media */
1699
if (call_med->tp && call_med->tp_st == PJSUA_MED_TP_IDLE) {
1700
status = pjmedia_transport_media_create(call_med->tp, pool, 0,
1701
NULL, call_med->idx);
1702
if (status != PJ_SUCCESS)
1706
sdp_m = sdp->media[med_idx];
1708
/* Create new SDP media line if the stream is disabled */
1709
if (sdp->media[med_idx]->desc.port == 0) {
1710
pjmedia_transport_info tpinfo;
1712
/* Get transport address info */
1713
pjmedia_transport_info_init(&tpinfo);
1714
pjmedia_transport_get_info(call_med->tp, &tpinfo);
1716
status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1717
&tpinfo.sock_info, 0, &sdp_m);
1718
if (status != PJ_SUCCESS)
1723
pjmedia_sdp_attr *a;
1725
/* Remove any direction attributes */
1726
pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
1727
pjmedia_sdp_media_remove_all_attr(sdp_m, "sendonly");
1728
pjmedia_sdp_media_remove_all_attr(sdp_m, "recvonly");
1729
pjmedia_sdp_media_remove_all_attr(sdp_m, "inactive");
1731
/* Update media direction */
1732
if (dir == PJMEDIA_DIR_ENCODING_DECODING)
1733
a = pjmedia_sdp_attr_create(pool, "sendrecv", NULL);
1734
else if (dir == PJMEDIA_DIR_ENCODING)
1735
a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
1736
else if (dir == PJMEDIA_DIR_DECODING)
1737
a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
1739
a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
1741
pjmedia_sdp_media_add_attr(sdp_m, a);
1744
sdp->media[med_idx] = sdp_m;
1746
/* Update SDP media line by media transport */
1747
status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1748
sdp, NULL, call_med->idx);
1749
if (status != PJ_SUCCESS)
1753
if (status != PJ_SUCCESS) {
1754
pjsua_media_prov_clean_up(call->index);
1760
pj_pool_t *pool = call->inv->pool_prov;
1762
/* Mark media transport to disabled */
1763
// Don't close this here, as SDP negotiation has not been
1764
// done and stream may be still active.
1765
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED);
1767
/* Deactivate the stream */
1768
pjmedia_sdp_media_deactivate(pool, sdp->media[med_idx]);
1770
call->opt.vid_cnt--;
1773
status = call_reoffer_sdp(call->index, sdp);
1774
if (status != PJ_SUCCESS)
1781
/* Change capture device of a video stream in a call */
1782
static pj_status_t call_change_cap_dev(pjsua_call *call,
1784
pjmedia_vid_dev_index cap_dev)
1786
pjsua_call_media *call_med;
1787
pjmedia_vid_dev_stream *old_dev;
1788
pjmedia_vid_dev_switch_param switch_prm;
1789
pjmedia_vid_dev_info info;
1790
pjsua_vid_win *w, *new_w = NULL;
1791
pjsua_vid_win_id wid, new_wid;
1792
pjmedia_port *media_port;
1795
/* Verify and normalize media index */
1796
if (med_idx == -1) {
1799
call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1800
if (first_active == -1)
1801
return PJ_ENOTFOUND;
1803
med_idx = first_active;
1806
call_med = &call->media[med_idx];
1808
/* Verify if the stream media type is video */
1809
if (call_med->type != PJMEDIA_TYPE_VIDEO)
1812
/* Verify the capture device */
1813
status = pjmedia_vid_dev_get_info(cap_dev, &info);
1814
if (status != PJ_SUCCESS || info.dir != PJMEDIA_DIR_CAPTURE)
1817
/* The specified capture device is being used already */
1818
if (call_med->strm.v.cap_dev == cap_dev)
1821
/* == Apply the new capture device == */
1823
wid = call_med->strm.v.cap_win_id;
1824
w = &pjsua_var.win[wid];
1825
pj_assert(w->type == PJSUA_WND_TYPE_PREVIEW && w->vp_cap);
1827
/* If the old device supports fast switching, then that's excellent! */
1828
old_dev = pjmedia_vid_port_get_stream(w->vp_cap);
1829
pjmedia_vid_dev_switch_param_default(&switch_prm);
1830
switch_prm.target_id = cap_dev;
1831
status = pjmedia_vid_dev_stream_set_cap(old_dev,
1832
PJMEDIA_VID_DEV_CAP_SWITCH,
1834
if (status == PJ_SUCCESS) {
1835
w->preview_cap_id = cap_dev;
1836
call_med->strm.v.cap_dev = cap_dev;
1840
/* No it doesn't support fast switching. Do slow switching then.. */
1841
status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
1842
PJMEDIA_DIR_ENCODING, &media_port);
1843
if (status != PJ_SUCCESS)
1846
pjmedia_event_unsubscribe(NULL, &call_media_on_event, call_med,
1849
/* temporarily disconnect while we operate on the tee. */
1850
pjmedia_vid_port_disconnect(w->vp_cap);
1852
/* = Detach stream port from the old capture device's tee = */
1853
status = pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
1854
if (status != PJ_SUCCESS) {
1855
/* Something wrong, assume that media_port has been removed
1858
PJ_PERROR(4,(THIS_FILE, status,
1859
"Warning: call %d: unable to remove video from tee",
1863
/* Reconnect again immediately. We're done with w->tee */
1864
pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
1866
/* = Attach stream port to the new capture device = */
1868
/* Note: calling pjsua_vid_preview_get_win() even though
1869
* create_vid_win() will automatically create the window
1870
* if it doesn't exist, because create_vid_win() will modify
1871
* existing window SHOW/HIDE value.
1873
new_wid = vid_preview_get_win(cap_dev, PJ_FALSE);
1874
if (new_wid == PJSUA_INVALID_ID) {
1875
pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
1877
/* Create preview video window */
1878
status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
1879
&media_port->info.fmt,
1880
call_med->strm.v.rdr_dev,
1883
acc->cfg.vid_wnd_flags,
1885
if (status != PJ_SUCCESS)
1889
inc_vid_win(new_wid);
1890
new_w = &pjsua_var.win[new_wid];
1892
/* Connect stream to capturer (via video window tee) */
1893
status = pjmedia_vid_tee_add_dst_port2(new_w->tee, 0, media_port);
1894
if (status != PJ_SUCCESS)
1898
/* Start renderer */
1899
status = pjmedia_vid_port_start(new_w->vp_rend);
1900
if (status != PJ_SUCCESS)
1905
pjmedia_event_subscribe(NULL, &call_media_on_event,
1906
call_med, new_w->vp_cap);
1909
/* Start capturer */
1910
if (!pjmedia_vid_port_is_running(new_w->vp_cap)) {
1911
status = pjmedia_vid_port_start(new_w->vp_cap);
1912
if (status != PJ_SUCCESS)
1917
call_med->strm.v.cap_dev = cap_dev;
1918
call_med->strm.v.cap_win_id = new_wid;
1924
PJ_PERROR(4,(THIS_FILE, status,
1925
"Call %d: error changing capture device to %d",
1926
call->index, cap_dev));
1929
/* Unsubscribe, just in case */
1930
pjmedia_event_unsubscribe(NULL, &call_media_on_event, call_med,
1932
/* Disconnect media port from the new capturer */
1933
pjmedia_vid_tee_remove_dst_port(new_w->tee, media_port);
1934
/* Release the new capturer */
1935
dec_vid_win(new_wid);
1938
/* Revert back to the old capturer */
1939
pjmedia_vid_port_disconnect(w->vp_cap);
1940
status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
1941
pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
1942
if (status != PJ_SUCCESS)
1947
pjmedia_event_subscribe(NULL, &call_media_on_event,
1948
call_med, w->vp_cap);
1955
/* Start/stop transmitting video stream in a call */
1956
static pj_status_t call_set_tx_video(pjsua_call *call,
1960
pjsua_call_media *call_med;
1963
/* Verify and normalize media index */
1964
if (med_idx == -1) {
1967
call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
1968
if (first_active == -1)
1969
return PJ_ENOTFOUND;
1971
med_idx = first_active;
1974
call_med = &call->media[med_idx];
1976
/* Verify if the stream is transmitting video */
1977
if (call_med->type != PJMEDIA_TYPE_VIDEO ||
1978
(enable && (call_med->dir & PJMEDIA_DIR_ENCODING) == 0))
1984
/* Start stream in encoding direction */
1985
status = pjmedia_vid_stream_resume(call_med->strm.v.stream,
1986
PJMEDIA_DIR_ENCODING);
1988
/* Pause stream in encoding direction */
1989
status = pjmedia_vid_stream_pause( call_med->strm.v.stream,
1990
PJMEDIA_DIR_ENCODING);
1997
static pj_status_t call_send_vid_keyframe(pjsua_call *call,
2000
pjsua_call_media *call_med;
2002
/* Verify and normalize media index */
2003
if (med_idx == -1) {
2006
call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
2007
if (first_active == -1)
2008
return PJ_ENOTFOUND;
2010
med_idx = first_active;
2013
call_med = &call->media[med_idx];
2015
/* Verify media type and stream instance. */
2016
if (call_med->type != PJMEDIA_TYPE_VIDEO || !call_med->strm.v.stream)
2019
return pjmedia_vid_stream_send_keyframe(call_med->strm.v.stream);
2024
* Start, stop, and/or manipulate video transmission for the specified call.
2026
PJ_DEF(pj_status_t) pjsua_call_set_vid_strm (
2027
pjsua_call_id call_id,
2028
pjsua_call_vid_strm_op op,
2029
const pjsua_call_vid_strm_op_param *param)
2032
pjsua_call_vid_strm_op_param param_;
2035
PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2037
PJ_ASSERT_RETURN(op != PJSUA_CALL_VID_STRM_NO_OP, PJ_EINVAL);
2039
PJ_LOG(4,(THIS_FILE, "Call %d: set video stream, op=%d",
2041
pj_log_push_indent();
2045
call = &pjsua_var.calls[call_id];
2050
pjsua_call_vid_strm_op_param_default(¶m_);
2053
/* If set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
2054
* account default video capture device.
2056
if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
2057
pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
2058
param_.cap_dev = acc_cfg->vid_cap_dev;
2060
/* If the account default video capture device is
2061
* PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
2062
* global default video capture device.
2064
if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
2065
pjmedia_vid_dev_info info;
2066
pjmedia_vid_dev_get_info(param_.cap_dev, &info);
2067
pj_assert(info.dir == PJMEDIA_DIR_CAPTURE);
2068
param_.cap_dev = info.id;
2073
case PJSUA_CALL_VID_STRM_ADD:
2074
status = call_add_video(call, param_.cap_dev, param_.dir);
2076
case PJSUA_CALL_VID_STRM_REMOVE:
2077
status = call_modify_video(call, param_.med_idx, PJMEDIA_DIR_NONE,
2080
case PJSUA_CALL_VID_STRM_CHANGE_DIR:
2081
status = call_modify_video(call, param_.med_idx, param_.dir, PJ_FALSE);
2083
case PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV:
2084
status = call_change_cap_dev(call, param_.med_idx, param_.cap_dev);
2086
case PJSUA_CALL_VID_STRM_START_TRANSMIT:
2087
status = call_set_tx_video(call, param_.med_idx, PJ_TRUE);
2089
case PJSUA_CALL_VID_STRM_STOP_TRANSMIT:
2090
status = call_set_tx_video(call, param_.med_idx, PJ_FALSE);
2092
case PJSUA_CALL_VID_STRM_SEND_KEYFRAME:
2093
status = call_send_vid_keyframe(call, param_.med_idx);
2096
status = PJ_EINVALIDOP;
2101
pj_log_pop_indent();
2108
* Get the media stream index of the default video stream in the call.
2110
PJ_DEF(int) pjsua_call_get_vid_stream_idx(pjsua_call_id call_id)
2113
int first_active, first_inactive;
2115
PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2119
call = &pjsua_var.calls[call_id];
2120
call_get_vid_strm_info(call, &first_active, &first_inactive, NULL, NULL);
2123
if (first_active == -1)
2124
return first_inactive;
2126
return first_active;
2131
* Determine if video stream for the specified call is currently running
2132
* for the specified direction.
2134
PJ_DEF(pj_bool_t) pjsua_call_vid_stream_is_running( pjsua_call_id call_id,
2139
pjsua_call_media *call_med;
2141
PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2144
/* Verify and normalize media index */
2145
if (med_idx == -1) {
2146
med_idx = pjsua_call_get_vid_stream_idx(call_id);
2149
call = &pjsua_var.calls[call_id];
2150
PJ_ASSERT_RETURN(med_idx >= 0 && med_idx < (int)call->med_cnt, PJ_EINVAL);
2152
call_med = &call->media[med_idx];
2154
/* Verify if the stream is transmitting video */
2155
if (call_med->type != PJMEDIA_TYPE_VIDEO || (call_med->dir & dir) == 0 ||
2156
!call_med->strm.v.stream)
2161
return pjmedia_vid_stream_is_running(call_med->strm.v.stream, dir);
2164
#endif /* PJSUA_HAS_VIDEO */
2166
#endif /* PJSUA_MEDIA_HAS_PJMEDIA */