summaryrefslogtreecommitdiff
path: root/pjsip-apps/src/pjsua/pjsua_app.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjsip-apps/src/pjsua/pjsua_app.c')
-rw-r--r--pjsip-apps/src/pjsua/pjsua_app.c636
1 files changed, 570 insertions, 66 deletions
diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c
index 81b1f2a4..e311c32a 100644
--- a/pjsip-apps/src/pjsua/pjsua_app.c
+++ b/pjsip-apps/src/pjsua/pjsua_app.c
@@ -124,6 +124,7 @@ static struct app_config
int ring_cnt;
pjmedia_port *ring_port;
+ int vcapture_dev, vrender_dev;
} app_config;
@@ -136,7 +137,13 @@ static const char *stdout_refresh_text = "STDOUT_REFRESH";
static pj_bool_t stdout_refresh_quit = PJ_FALSE;
static pj_str_t uri_arg;
-static char some_buf[1024 * 3];
+#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
+# define SOME_BUF_SIZE (1024 * 10)
+#else
+# define SOME_BUF_SIZE (1024 * 3)
+#endif
+
+static char some_buf[SOME_BUF_SIZE];
#ifdef STEREO_DEMO
static void stereo_demo();
@@ -194,6 +201,7 @@ static void usage(void)
puts (" --color Use colorful logging (default yes on Win32)");
puts (" --no-color Disable colorful logging");
puts (" --light-bg Use dark colors for light background (default is dark bg)");
+ puts (" --no-stderr Disable stderr");
puts ("");
puts ("SIP Account options:");
@@ -267,7 +275,7 @@ static void usage(void)
puts (" --tls-srv-name Specify TLS server name for multihosting server");
puts ("");
- puts ("Media Options:");
+ puts ("Audio Options:");
puts (" --add-codec=name Manually add codec (default is to enable all)");
puts (" --dis-codec=name Disable codec (can be specified multiple times)");
puts (" --clock-rate=N Override conference bridge clock rate");
@@ -301,6 +309,15 @@ static void usage(void)
puts (" Specify N=0 for instant close when unused.");
puts (" --no-tones Disable audible tones");
puts (" --jb-max-size Specify jitter buffer maximum size, in frames (default=-1)");
+ puts (" --extra-audio Add one more audio stream");
+
+#if PJSUA_HAS_VIDEO
+ puts ("");
+ puts ("Video Options:");
+ puts (" --video Enable video");
+ puts (" --vcapture-dev=id Video capture device ID (default=-1)");
+ puts (" --vrender-dev=id Video render device ID (default=-1)");
+#endif
puts ("");
puts ("Media Transport Options:");
@@ -376,6 +393,9 @@ static void default_config(struct app_config *cfg)
for (i=0; i<PJ_ARRAY_SIZE(cfg->buddy_cfg); ++i)
pjsua_buddy_config_default(&cfg->buddy_cfg[i]);
+
+ cfg->vcapture_dev = PJSUA_INVALID_ID;
+ cfg->vrender_dev = PJSUA_INVALID_ID;
}
@@ -509,7 +529,7 @@ static pj_status_t parse_args(int argc, char *argv[],
int c;
int option_index;
enum { OPT_CONFIG_FILE=127, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL,
- OPT_LOG_APPEND, OPT_COLOR, OPT_NO_COLOR, OPT_LIGHT_BG,
+ OPT_LOG_APPEND, OPT_COLOR, OPT_NO_COLOR, OPT_LIGHT_BG, OPT_NO_STDERR,
OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO, OPT_SND_AUTO_CLOSE,
OPT_LOCAL_PORT, OPT_IP_ADDR, OPT_PROXY, OPT_OUTBOUND_PROXY,
OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT,
@@ -541,7 +561,9 @@ static pj_status_t parse_args(int argc, char *argv[],
#endif
OPT_AUTO_UPDATE_NAT,OPT_USE_COMPACT_FORM,OPT_DIS_CODEC,
OPT_NO_FORCE_LR,
- OPT_TIMER, OPT_TIMER_SE, OPT_TIMER_MIN_SE
+ OPT_TIMER, OPT_TIMER_SE, OPT_TIMER_MIN_SE,
+ OPT_VIDEO, OPT_EXTRA_AUDIO,
+ OPT_VCAPTURE_DEV, OPT_VRENDER_DEV,
};
struct pj_getopt_option long_options[] = {
{ "config-file",1, 0, OPT_CONFIG_FILE},
@@ -552,6 +574,7 @@ static pj_status_t parse_args(int argc, char *argv[],
{ "color", 0, 0, OPT_COLOR},
{ "no-color", 0, 0, OPT_NO_COLOR},
{ "light-bg", 0, 0, OPT_LIGHT_BG},
+ { "no-stderr", 0, 0, OPT_NO_STDERR},
{ "help", 0, 0, OPT_HELP},
{ "version", 0, 0, OPT_VERSION},
{ "clock-rate", 1, 0, OPT_CLOCK_RATE},
@@ -660,6 +683,10 @@ static pj_status_t parse_args(int argc, char *argv[],
{ "timer-se", 1, 0, OPT_TIMER_SE},
{ "timer-min-se", 1, 0, OPT_TIMER_MIN_SE},
{ "outb-rid", 1, 0, OPT_OUTB_RID},
+ { "video", 0, 0, OPT_VIDEO},
+ { "extra-audio",0, 0, OPT_EXTRA_AUDIO},
+ { "vcapture-dev", 1, 0, OPT_VCAPTURE_DEV},
+ { "vrender-dev", 1, 0, OPT_VRENDER_DEV},
{ NULL, 0, 0, 0}
};
pj_status_t status;
@@ -752,6 +779,10 @@ static pj_status_t parse_args(int argc, char *argv[],
pj_log_set_color(77, 0);
break;
+ case OPT_NO_STDERR:
+ freopen("/dev/null", "w", stderr);
+ break;
+
case OPT_HELP:
usage();
return PJ_EINVAL;
@@ -1417,6 +1448,26 @@ static pj_status_t parse_args(int argc, char *argv[],
cfg->udp_cfg.qos_params.flags = PJ_QOS_PARAM_HAS_DSCP;
cfg->udp_cfg.qos_params.dscp_val = 0x18;
break;
+ case OPT_VIDEO:
+ ++cur_acc->max_video_cnt;
+ cur_acc->vid_in_auto_show = PJ_TRUE;
+ cur_acc->vid_out_auto_transmit = PJ_TRUE;
+ PJ_TODO(implement_pjsua_option_for_vid_auto_show_and_transmit);
+ break;
+ case OPT_EXTRA_AUDIO:
+ ++cur_acc->max_audio_cnt;
+ break;
+
+ case OPT_VCAPTURE_DEV:
+ cfg->vcapture_dev = atoi(pj_optarg);
+ cur_acc->vid_cap_dev = cfg->vcapture_dev;
+ break;
+
+ case OPT_VRENDER_DEV:
+ cfg->vrender_dev = atoi(pj_optarg);
+ cur_acc->vid_rend_dev = cfg->vrender_dev;
+ break;
+
default:
PJ_LOG(1,(THIS_FILE,
"Argument \"%s\" is not valid. Use --help to see help",
@@ -1661,6 +1712,14 @@ static void write_account_settings(int acc_index, pj_str_t *result)
/* MWI */
if (acc_cfg->mwi_enabled)
pj_strcat2(result, "--mwi\n");
+
+ /* Video & extra audio */
+ for (i=0; i<acc_cfg->max_video_cnt; ++i) {
+ pj_strcat2(result, "--video\n");
+ }
+ for (i=1; i<acc_cfg->max_audio_cnt; ++i) {
+ pj_strcat2(result, "--extra-audio\n");
+ }
}
@@ -1985,6 +2044,14 @@ static int write_settings(const struct app_config *config,
pj_strcat2(&cfg, line);
}
+ if (config->vcapture_dev != PJSUA_INVALID_ID) {
+ pj_ansi_sprintf(line, "--vcapture-dev %d\n", config->vcapture_dev);
+ pj_strcat2(&cfg, line);
+ }
+ if (config->vrender_dev != PJSUA_INVALID_ID) {
+ pj_ansi_sprintf(line, "--vrender-dev %d\n", config->vrender_dev);
+ pj_strcat2(&cfg, line);
+ }
/* ptime */
if (config->media_cfg.ptime) {
@@ -2584,45 +2651,60 @@ static void on_call_tsx_state(pjsua_call_id call_id,
}
}
-
-/*
- * Callback on media state changed event.
- * The action may connect the call to sound device, to file, or
- * to loop the call.
- */
-static void on_call_media_state(pjsua_call_id call_id)
+/* General processing for media state. "mi" is the media index */
+static void on_call_generic_media_state(pjsua_call_info *ci, unsigned mi,
+ pj_bool_t *has_error)
{
- pjsua_call_info call_info;
+ const char *status_name[] = {
+ "None",
+ "Active",
+ "Local hold",
+ "Remote hold",
+ "Error"
+ };
- pjsua_call_get_info(call_id, &call_info);
+ pj_assert(ci->media[mi].status <= PJ_ARRAY_SIZE(status_name));
+ pj_assert(PJSUA_CALL_MEDIA_ERROR == 4);
+
+ PJ_LOG(4,(THIS_FILE, "Call %d media %d [type=%s], status is %s",
+ ci->id, mi, pjmedia_type_name(ci->media[mi].type),
+ status_name[ci->media[mi].status]));
+}
+/* Process audio media state. "mi" is the media index. */
+static void on_call_audio_state(pjsua_call_info *ci, unsigned mi,
+ pj_bool_t *has_error)
+{
/* Stop ringback */
- ring_stop(call_id);
+ ring_stop(ci->id);
/* Connect ports appropriately when media status is ACTIVE or REMOTE HOLD,
* otherwise we should NOT connect the ports.
*/
- if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE ||
- call_info.media_status == PJSUA_CALL_MEDIA_REMOTE_HOLD)
+ if (ci->media[mi].status == PJSUA_CALL_MEDIA_ACTIVE ||
+ ci->media[mi].status == PJSUA_CALL_MEDIA_REMOTE_HOLD)
{
pj_bool_t connect_sound = PJ_TRUE;
+ pjsua_conf_port_id call_conf_slot;
+
+ call_conf_slot = ci->media[mi].stream.aud.conf_slot;
/* Loopback sound, if desired */
if (app_config.auto_loop) {
- pjsua_conf_connect(call_info.conf_slot, call_info.conf_slot);
+ pjsua_conf_connect(call_conf_slot, call_conf_slot);
connect_sound = PJ_FALSE;
}
/* Automatically record conversation, if desired */
if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
- pjsua_conf_connect(call_info.conf_slot, app_config.rec_port);
+ pjsua_conf_connect(call_conf_slot, app_config.rec_port);
}
/* Stream a file, if desired */
if ((app_config.auto_play || app_config.auto_play_hangup) &&
app_config.wav_port != PJSUA_INVALID_ID)
{
- pjsua_conf_connect(app_config.wav_port, call_info.conf_slot);
+ pjsua_conf_connect(app_config.wav_port, call_conf_slot);
connect_sound = PJ_FALSE;
}
@@ -2638,16 +2720,16 @@ static void on_call_media_state(pjsua_call_id call_id)
pjsua_enum_calls(call_ids, &call_cnt);
for (i=0; i<call_cnt; ++i) {
- if (call_ids[i] == call_id)
+ if (call_ids[i] == ci->id)
continue;
if (!pjsua_call_has_media(call_ids[i]))
continue;
- pjsua_conf_connect(call_info.conf_slot,
+ pjsua_conf_connect(call_conf_slot,
pjsua_call_get_conf_port(call_ids[i]));
pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]),
- call_info.conf_slot);
+ call_conf_slot);
/* Automatically record conversation, if desired */
if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
@@ -2663,52 +2745,53 @@ static void on_call_media_state(pjsua_call_id call_id)
/* Otherwise connect to sound device */
if (connect_sound) {
- pjsua_conf_connect(call_info.conf_slot, 0);
- pjsua_conf_connect(0, call_info.conf_slot);
+ pjsua_conf_connect(call_conf_slot, 0);
+ pjsua_conf_connect(0, call_conf_slot);
/* Automatically record conversation, if desired */
if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
- pjsua_conf_connect(call_info.conf_slot, app_config.rec_port);
+ pjsua_conf_connect(call_conf_slot, app_config.rec_port);
pjsua_conf_connect(0, app_config.rec_port);
}
}
}
+}
- /* Handle media status */
- switch (call_info.media_status) {
- case PJSUA_CALL_MEDIA_ACTIVE:
- PJ_LOG(3,(THIS_FILE, "Media for call %d is active", call_id));
- break;
+/* Process video media state. "mi" is the media index. */
+static void on_call_video_state(pjsua_call_info *ci, unsigned mi,
+ pj_bool_t *has_error)
+{
+}
- case PJSUA_CALL_MEDIA_LOCAL_HOLD:
- PJ_LOG(3,(THIS_FILE, "Media for call %d is suspended (hold) by local",
- call_id));
- break;
+/*
+ * Callback on media state changed event.
+ * The action may connect the call to sound device, to file, or
+ * to loop the call.
+ */
+static void on_call_media_state(pjsua_call_id call_id)
+{
+ pjsua_call_info call_info;
+ unsigned mi;
+ pj_bool_t has_error = PJ_FALSE;
- case PJSUA_CALL_MEDIA_REMOTE_HOLD:
- PJ_LOG(3,(THIS_FILE,
- "Media for call %d is suspended (hold) by remote",
- call_id));
- break;
+ pjsua_call_get_info(call_id, &call_info);
- case PJSUA_CALL_MEDIA_ERROR:
- PJ_LOG(3,(THIS_FILE,
- "Media has reported error, disconnecting call"));
- {
- pj_str_t reason = pj_str("ICE negotiation failed");
- pjsua_call_hangup(call_id, 500, &reason, NULL);
- }
- break;
+ for (mi=0; mi<call_info.media_cnt; ++mi) {
+ on_call_generic_media_state(&call_info, mi, &has_error);
- case PJSUA_CALL_MEDIA_NONE:
- PJ_LOG(3,(THIS_FILE,
- "Media for call %d is inactive",
- call_id));
- break;
+ switch (call_info.media[mi].type) {
+ case PJMEDIA_TYPE_AUDIO:
+ on_call_audio_state(&call_info, mi, &has_error);
+ break;
+ case PJMEDIA_TYPE_VIDEO:
+ on_call_video_state(&call_info, mi, &has_error);
+ break;
+ }
+ }
- default:
- pj_assert(!"Unhandled media status");
- break;
+ if (has_error) {
+ pj_str_t reason = pj_str("Media failed");
+ pjsua_call_hangup(call_id, 500, &reason, NULL);
}
}
@@ -3052,6 +3135,25 @@ static void on_ice_transport_error(int index, pj_ice_strans_op op,
}
/*
+ * Notification on sound device operation.
+ */
+static pj_status_t on_snd_dev_operation(int operation)
+{
+ PJ_LOG(3,(THIS_FILE, "Turning sound device %s", (operation? "ON":"OFF")));
+ return PJ_SUCCESS;
+}
+
+/* Callback on media events */
+static void on_call_media_event(pjsua_call_id call_id,
+ unsigned med_idx,
+ pjmedia_event *event)
+{
+ char event_name[5];
+ PJ_LOG(4,(THIS_FILE, "Event %s",
+ pjmedia_fourcc_name(event->type, event_name)));
+}
+
+/*
* Print buddy list.
*/
static void print_buddy_list(void)
@@ -3193,8 +3295,12 @@ static void keystroke_help(void)
puts("| * Send DTMF with INFO | cc Connect port | dd Dump detailed |");
puts("| dq Dump curr. call quality | cd Disconnect port | dc Dump config |");
puts("| | V Adjust audio Volume | f Save config |");
- puts("| S Send arbitrary REQUEST | Cp Codec priorities | f Save config |");
- puts("+------------------------------+--------------------------+-------------------+");
+ puts("| S Send arbitrary REQUEST | Cp Codec priorities | |");
+ puts("+-----------------------------------------------------------------------------+");
+#if PJSUA_HAS_VIDEO
+ puts("| Video: \"vid help\" for more info |");
+ puts("+-----------------------------------------------------------------------------+");
+#endif
puts("| q QUIT L ReLoad sleep MS echo [0|1|txt] n: detect NAT type |");
puts("+=============================================================================+");
@@ -3210,6 +3316,31 @@ static void keystroke_help(void)
}
}
+/* Help screen for video */
+static void vid_show_help(void)
+{
+#if PJSUA_HAS_VIDEO
+ puts("+=============================================================================+");
+ puts("| Video commands: |");
+ puts("| |");
+ puts("| vid help Show this help screen |");
+ puts("| vid call rx on|off N Enable/disable video rx for stream N in curr call |");
+ puts("| vid call tx on|off N Enable/disable video tx for stream N in curr call |");
+ puts("| vid call add Add video stream for current call |");
+ puts("| vid call enable/disable N Enable/disable stream #N in current call |");
+ puts("| vid call set-cap N ID Set capture dev ID for stream #N in current call |");
+ puts("| vid dev list List all video devices |");
+ puts("| vid dev refresh Refresh video device list |");
+ puts("| vid dev prev on|off ID Enable/disable preview for specified device ID |");
+ puts("| vid codec list List video codecs |");
+ puts("| vid codec prio PT PRIO Set codec with pt PT priority to PRIO |");
+ puts("| vid win list List all active video windows |");
+ puts("| vid win show|hide ID Show/hide the specified video window ID |");
+ puts("| vid win move ID X Y Move window ID to position X,Y |");
+ puts("| vid win resize ID w h Resize window ID to the specified width, height |");
+ puts("+=============================================================================+");
+#endif
+}
/*
* Input simple string
@@ -3481,17 +3612,30 @@ static void manage_codec_prio(void)
int new_prio;
pj_status_t status;
- printf("List of codecs:\n");
-
+ printf("List of audio codecs:\n");
pjsua_enum_codecs(c, &count);
for (i=0; i<count; ++i) {
printf(" %d\t%.*s\n", c[i].priority, (int)c[i].codec_id.slen,
c[i].codec_id.ptr);
}
+#if PJSUA_HAS_VIDEO
puts("");
- puts("Enter codec id and its new priority "
- "(e.g. \"speex/16000 200\"), empty to cancel:");
+ printf("List of video codecs:\n");
+ pjsua_vid_enum_codecs(c, &count);
+ for (i=0; i<count; ++i) {
+ printf(" %d\t%.*s%s%.*s\n", c[i].priority,
+ (int)c[i].codec_id.slen,
+ c[i].codec_id.ptr,
+ c[i].desc.slen? " - ":"",
+ (int)c[i].desc.slen,
+ c[i].desc.ptr);
+ }
+#endif
+
+ puts("");
+ puts("Enter codec id and its new priority (e.g. \"speex/16000 200\", ""\"H263 200\"),");
+ puts("or empty to cancel.");
printf("Codec name (\"*\" for all) and priority: ");
if (fgets(input, sizeof(input), stdin) == NULL)
@@ -3517,11 +3661,274 @@ static void manage_codec_prio(void)
status = pjsua_codec_set_priority(pj_cstr(&id, codec),
(pj_uint8_t)new_prio);
+#if PJSUA_HAS_VIDEO
+ if (status != PJ_SUCCESS) {
+ status = pjsua_vid_codec_set_priority(pj_cstr(&id, codec),
+ (pj_uint8_t)new_prio);
+ }
+#endif
if (status != PJ_SUCCESS)
pjsua_perror(THIS_FILE, "Error setting codec priority", status);
}
+#if PJSUA_HAS_VIDEO
+static void vid_print_dev(int id, const pjmedia_vid_dev_info *vdi,
+ const char *title)
+{
+ char capnames[120];
+ char formats[120];
+ const char *dirname;
+ unsigned i;
+
+ if (vdi->dir == PJMEDIA_DIR_CAPTURE_RENDER) {
+ dirname = "capture, render";
+ } else if (vdi->dir == PJMEDIA_DIR_CAPTURE) {
+ dirname = "capture";
+ } else {
+ dirname = "render";
+ }
+
+
+ capnames[0] = '\0';
+ for (i=0; i<sizeof(int)*8 && (1 << i) < PJMEDIA_VID_DEV_CAP_MAX; ++i) {
+ if (vdi->caps & (1 << i)) {
+ const char *capname = pjmedia_vid_dev_cap_name(1 << i, NULL);
+ if (capname) {
+ if (*capnames)
+ strcat(capnames, ", ");
+ strncat(capnames, capname,
+ sizeof(capnames)-strlen(capnames)-1);
+ }
+ }
+ }
+
+ formats[0] = '\0';
+ for (i=0; i<vdi->fmt_cnt; ++i) {
+ const pjmedia_video_format_info *vfi =
+ pjmedia_get_video_format_info(NULL, vdi->fmt[i].id);
+ if (vfi) {
+ if (*formats)
+ strcat(formats, ", ");
+ strncat(formats, vfi->name, sizeof(formats)-strlen(formats)-1);
+ }
+ }
+
+ PJ_LOG(3,(THIS_FILE, "%3d %s [%s][%s] %s", id, vdi->name, vdi->driver,
+ dirname, title));
+ PJ_LOG(3,(THIS_FILE, " Supported capabilities: %s", capnames));
+ PJ_LOG(3,(THIS_FILE, " Supported formats: %s", formats));
+}
+
+static void vid_list_devs(void)
+{
+ unsigned i, count;
+ pjmedia_vid_dev_info vdi;
+ pj_status_t status;
+
+ PJ_LOG(3,(THIS_FILE, "Video device list:"));
+ count = pjsua_vid_dev_count();
+ if (count == 0) {
+ PJ_LOG(3,(THIS_FILE, " - no device detected -"));
+ return;
+ } else {
+ PJ_LOG(3,(THIS_FILE, "%d device(s) detected:", count));
+ }
+
+ status = pjsua_vid_dev_get_info(PJMEDIA_VID_DEFAULT_RENDER_DEV, &vdi);
+ if (status == PJ_SUCCESS)
+ vid_print_dev(PJMEDIA_VID_DEFAULT_RENDER_DEV, &vdi,
+ "(default renderer device)");
+
+ status = pjsua_vid_dev_get_info(PJMEDIA_VID_DEFAULT_CAPTURE_DEV, &vdi);
+ if (status == PJ_SUCCESS)
+ vid_print_dev(PJMEDIA_VID_DEFAULT_CAPTURE_DEV, &vdi,
+ "(default capture device)");
+
+ for (i=0; i<count; ++i) {
+ status = pjsua_vid_dev_get_info(i, &vdi);
+ if (status == PJ_SUCCESS)
+ vid_print_dev(i, &vdi, "");
+ }
+}
+
+static void vid_handle_menu(char *menuin)
+{
+ char *argv[8];
+ int argc = 0;
+
+ /* Tokenize */
+ argv[argc] = strtok(menuin, " \t\r\n");
+ while (argv[argc] && *argv[argc]) {
+ argc++;
+ argv[argc] = strtok(NULL, " \t\r\n");
+ }
+
+ if (strcmp(argv[1], "help")==0 || argc == 1) {
+ vid_show_help();
+ } else if (strcmp(argv[1], "call")==0) {
+ pjsua_call_vid_strm_op_param param;
+
+ if (argc == 5 && strcmp(argv[2], "rx")==0) {
+ pjsua_stream_info si;
+ pj_bool_t on = (strcmp(argv[3], "on") == 0);
+
+ param.med_idx = atoi(argv[4]);
+ if (pjsua_call_get_stream_info(current_call, param.med_idx, &si) ||
+ si.type != PJMEDIA_TYPE_VIDEO)
+ {
+ PJ_PERROR(1,(THIS_FILE, PJ_EINVAL, "Invalid stream"));
+ return;
+ }
+
+ if (on) param.dir = (si.info.vid.dir | PJMEDIA_DIR_DECODING);
+ else param.dir = (si.info.vid.dir & PJMEDIA_DIR_ENCODING);
+
+ pjsua_call_set_vid_strm(current_call, PJSUA_CALL_VID_STRM_CHANGE_DIR, &param);
+ }
+ else if (argc == 5 && strcmp(argv[2], "tx")==0) {
+ pj_bool_t on = (strcmp(argv[3], "on") == 0);
+ pjsua_call_vid_strm_op op = on? PJSUA_CALL_VID_STRM_START_TRANSMIT :
+ PJSUA_CALL_VID_STRM_STOP_TRANSMIT;
+
+ param.med_idx = atoi(argv[4]);
+
+ pjsua_call_set_vid_strm(current_call, op, &param);
+ }
+ else if (argc == 3 && strcmp(argv[2], "add")==0) {
+ pjsua_call_set_vid_strm(current_call, PJSUA_CALL_VID_STRM_ADD, NULL);
+ }
+ else if (argc >= 3 &&
+ (strcmp(argv[2], "disable")==0 || strcmp(argv[2], "enable")==0))
+ {
+ pj_bool_t enable = (strcmp(argv[2], "enable") == 0);
+ pjsua_call_vid_strm_op op = enable? PJSUA_CALL_VID_STRM_CHANGE_DIR :
+ PJSUA_CALL_VID_STRM_REMOVE;
+
+ param.med_idx = argc >= 4? atoi(argv[3]) : -1;
+ param.dir = PJMEDIA_DIR_ENCODING_DECODING;
+ pjsua_call_set_vid_strm(current_call, op, &param);
+ }
+ else if (argc >= 3 && strcmp(argv[2], "set-cap")==0) {
+ param.med_idx = argc >= 4? atoi(argv[3]) : -1;
+ param.cap_dev = argc >= 5? atoi(argv[4]) : PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
+ pjsua_call_set_vid_strm(current_call, PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV, &param);
+ } else
+ goto on_error;
+ } else if (argc >= 3 && strcmp(argv[1], "dev")==0) {
+ if (strcmp(argv[2], "list")==0) {
+ vid_list_devs();
+ } else if (strcmp(argv[2], "refresh")==0) {
+ pjmedia_vid_dev_refresh();
+ } else if (strcmp(argv[2], "prev")==0) {
+ if (argc != 5) {
+ goto on_error;
+ } else {
+ pj_bool_t on = (strcmp(argv[3], "on") == 0);
+ int dev_id = atoi(argv[4]);
+ if (on) {
+ pjsua_vid_preview_start(dev_id, NULL);
+ } else {
+ pjsua_vid_preview_stop(dev_id);
+ }
+ }
+ } else
+ goto on_error;
+ } else if (strcmp(argv[1], "win")==0) {
+ if (argc==3 && strcmp(argv[2], "list")==0) {
+ pjsua_vid_win_id wids[PJSUA_MAX_VID_WINS];
+ unsigned i, cnt = PJ_ARRAY_SIZE(wids);
+
+ pjsua_vid_enum_wins(wids, &cnt);
+
+ PJ_LOG(3,(THIS_FILE, "Found %d video windows:", cnt));
+ PJ_LOG(3,(THIS_FILE, "WID show pos size"));
+ PJ_LOG(3,(THIS_FILE, "------------------------------"));
+ for (i = 0; i < cnt; ++i) {
+ pjsua_vid_win_info wi;
+ pjsua_vid_win_get_info(wids[i], &wi);
+ PJ_LOG(3,(THIS_FILE, "%3d %c (%d,%d) %dx%d",
+ wids[i], (wi.show?'Y':'N'), wi.pos.x, wi.pos.y,
+ wi.size.w, wi.size.h));
+ }
+ } else if (argc==4 && (strcmp(argv[2], "show")==0 ||
+ strcmp(argv[2], "hide")==0))
+ {
+ pj_bool_t show = (strcmp(argv[2], "show")==0);
+ pjsua_vid_win_id wid = atoi(argv[3]);
+ pjsua_vid_win_set_show(wid, show);
+ } else if (argc==6 && strcmp(argv[2], "move")==0) {
+ pjsua_vid_win_id wid = atoi(argv[3]);
+ pjmedia_coord pos;
+
+ pos.x = atoi(argv[4]);
+ pos.y = atoi(argv[5]);
+ pjsua_vid_win_set_pos(wid, &pos);
+ } else if (argc==6 && strcmp(argv[2], "resize")==0) {
+ pjsua_vid_win_id wid = atoi(argv[3]);
+ pjmedia_rect_size size;
+
+ size.w = atoi(argv[4]);
+ size.h = atoi(argv[5]);
+ pjsua_vid_win_set_size(wid, &size);
+ } else
+ goto on_error;
+ } else if (strcmp(argv[1], "codec")==0) {
+ pjmedia_vid_codec_info ci[PJMEDIA_CODEC_MGR_MAX_CODECS];
+ unsigned prio[PJMEDIA_CODEC_MGR_MAX_CODECS];
+ unsigned count = PJMEDIA_CODEC_MGR_MAX_CODECS;
+ pj_status_t status;
+
+ if (argc==3 && strcmp(argv[2], "list")==0) {
+ status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, ci, prio);
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(1,(THIS_FILE, status, "Error enumerating codecs"));
+ } else {
+ unsigned i;
+ PJ_LOG(3,(THIS_FILE, "Found %d video codecs:", count));
+ PJ_LOG(3,(THIS_FILE, " PT Prio Name"));
+ PJ_LOG(3,(THIS_FILE, "-------------------------"));
+ for (i=0; i<count; ++i) {
+ PJ_LOG(3,(THIS_FILE, "% 3d % 3d %.*s", ci[i].pt, prio[i],
+ (int)ci[i].encoding_name.slen,
+ ci[i].encoding_name.ptr));
+ }
+ }
+ } else if (argc==5 && strcmp(argv[2], "prio")==0) {
+ int pt = atoi(argv[3]);
+ int prio = atoi(argv[4]);
+ const pjmedia_vid_codec_info *pci;
+
+ status = pjmedia_vid_codec_mgr_get_codec_info(NULL, pt, &pci);
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(1,(THIS_FILE, status, "Unable to find codec"));
+ } else {
+ char codec_id[40];
+ if (pjmedia_vid_codec_info_to_id(pci, codec_id,
+ sizeof(codec_id)) == NULL)
+ {
+ PJ_PERROR(1,(THIS_FILE, status, "Unable to get codec id"));
+ } else {
+ pj_str_t cid = pj_str(codec_id);
+ status = pjsua_vid_codec_set_priority(&cid,
+ (pj_uint8_t)prio);
+ }
+ }
+
+ } else
+ goto on_error;
+ } else
+ goto on_error;
+
+ return;
+
+on_error:
+ PJ_LOG(1,(THIS_FILE, "Invalid command, use 'vid help'"));
+}
+
+#endif /* PJSUA_HAS_VIDEO */
+
+
/*
* Main "user interface" loop.
*/
@@ -3892,6 +4299,8 @@ void console_app_main(const pj_str_t *uri_to_call)
acc_cfg.cred_info[0].data_type = 0;
acc_cfg.cred_info[0].data = pj_str(passwd);
+ acc_cfg.rtp_cfg = app_config.rtp_cfg;
+
status = pjsua_acc_add(&acc_cfg, PJ_TRUE, NULL);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Error adding new account", status);
@@ -3949,11 +4358,17 @@ void console_app_main(const pj_str_t *uri_to_call)
break;
case 'v':
- /*
- * Send re-INVITE (to release hold, etc).
- */
+#if PJSUA_HAS_VIDEO
+ if (menuin[1]=='i' && menuin[2]=='d' && menuin[3]==' ') {
+
+ vid_handle_menu(menuin);
+
+ } else
+#endif
if (current_call != -1) {
-
+ /*
+ * re-INVITE
+ */
pjsua_call_reinvite(current_call, PJ_TRUE, NULL);
} else {
@@ -4511,6 +4926,59 @@ on_exit:
;
}
+/*
+ * A simple registrar, invoked by default_mod_on_rx_request()
+ */
+static void simple_registrar(pjsip_rx_data *rdata)
+{
+ pjsip_tx_data *tdata;
+ const pjsip_expires_hdr *exp;
+ const pjsip_hdr *h;
+ unsigned cnt = 0;
+ pjsip_generic_string_hdr *srv;
+ pj_status_t status;
+
+ status = pjsip_endpt_create_response(pjsua_get_pjsip_endpt(),
+ rdata, 200, NULL, &tdata);
+ if (status != PJ_SUCCESS)
+ return;
+
+ exp = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
+
+ h = rdata->msg_info.msg->hdr.next;
+ while (h != &rdata->msg_info.msg->hdr) {
+ if (h->type == PJSIP_H_CONTACT) {
+ const pjsip_contact_hdr *c = (const pjsip_contact_hdr*)h;
+ int e = c->expires;
+
+ if (e < 0) {
+ if (exp)
+ e = exp->ivalue;
+ else
+ e = 3600;
+ }
+
+ if (e > 0) {
+ pjsip_contact_hdr *nc = pjsip_hdr_clone(tdata->pool, h);
+ nc->expires = e;
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)nc);
+ ++cnt;
+ }
+ }
+ h = h->next;
+ }
+
+ srv = pjsip_generic_string_hdr_create(tdata->pool, NULL, NULL);
+ srv->name = pj_str("Server");
+ srv->hvalue = pj_str("pjsua simple registrar");
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)srv);
+
+ pjsip_endpt_send_response2(pjsua_get_pjsip_endpt(),
+ rdata, tdata, NULL, NULL);
+}
+
+
+
/*****************************************************************************
* A simple module to handle otherwise unhandled request. We will register
* this with the lowest priority.
@@ -4524,10 +4992,18 @@ static pj_bool_t default_mod_on_rx_request(pjsip_rx_data *rdata)
pj_status_t status;
/* Don't respond to ACK! */
- if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
+ if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
&pjsip_ack_method) == 0)
return PJ_TRUE;
+ /* Simple registrar */
+ if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
+ &pjsip_register_method) == 0)
+ {
+ simple_registrar(rdata);
+ return PJ_TRUE;
+ }
+
/* Create basic response. */
if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
&pjsip_notify_method) == 0)
@@ -4651,6 +5127,8 @@ pj_status_t app_init(int argc, char *argv[])
app_config.cfg.cb.on_mwi_info = &on_mwi_info;
app_config.cfg.cb.on_transport_state = &on_transport_state;
app_config.cfg.cb.on_ice_transport_error = &on_ice_transport_error;
+ app_config.cfg.cb.on_snd_dev_operation = &on_snd_dev_operation;
+ app_config.cfg.cb.on_call_media_event = &on_call_media_event;
app_config.log_cfg.cb = log_cb;
/* Set sound device latency */
@@ -4929,6 +5407,7 @@ pj_status_t app_init(int argc, char *argv[])
/* Add accounts */
for (i=0; i<app_config.acc_cnt; ++i) {
+ app_config.acc_cfg[i].rtp_cfg = app_config.rtp_cfg;
status = pjsua_acc_add(&app_config.acc_cfg[i], PJ_TRUE, NULL);
if (status != PJ_SUCCESS)
goto on_error;
@@ -4945,12 +5424,19 @@ pj_status_t app_init(int argc, char *argv[])
/* Optionally disable some codec */
for (i=0; i<app_config.codec_dis_cnt; ++i) {
pjsua_codec_set_priority(&app_config.codec_dis[i],PJMEDIA_CODEC_PRIO_DISABLED);
+#if PJSUA_HAS_VIDEO
+ pjsua_vid_codec_set_priority(&app_config.codec_dis[i],PJMEDIA_CODEC_PRIO_DISABLED);
+#endif
}
/* Optionally set codec orders */
for (i=0; i<app_config.codec_cnt; ++i) {
pjsua_codec_set_priority(&app_config.codec_arg[i],
(pj_uint8_t)(PJMEDIA_CODEC_PRIO_NORMAL+i+9));
+#if PJSUA_HAS_VIDEO
+ pjsua_vid_codec_set_priority(&app_config.codec_arg[i],
+ (pj_uint8_t)(PJMEDIA_CODEC_PRIO_NORMAL+i+9));
+#endif
}
/* Add RTP transports */
@@ -4960,8 +5446,10 @@ pj_status_t app_init(int argc, char *argv[])
#else
if (app_config.ipv6)
status = create_ipv6_media_transports();
+ #if DISABLED_FOR_TICKET_1185
else
status = pjsua_media_transports_create(&app_config.rtp_cfg);
+ #endif
#endif
if (status != PJ_SUCCESS)
goto on_error;
@@ -4984,6 +5472,18 @@ pj_status_t app_init(int argc, char *argv[])
goto on_error;
}
+#if PJSUA_HAS_VIDEO
+ if (app_config.vcapture_dev != PJSUA_INVALID_ID ||
+ app_config.vrender_dev != PJSUA_INVALID_ID)
+ {
+ //status = pjsua_vid_set_dev(app_config.vcapture_dev,
+ // app_config.vrender_dev);
+ PJ_TODO(vid_implement_pjsua_vid_set_dev);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ }
+#endif
+
return PJ_SUCCESS;
on_error:
@@ -5296,6 +5796,10 @@ static pj_status_t create_ipv6_media_transports(void)
}
}
+#if DISABLED_FOR_TICKET_1185
return pjsua_media_transports_attach(tp, i, PJ_TRUE);
+#else
+ return PJ_ENOTSUP;
+#endif
}