diff options
Diffstat (limited to 'pjsip-apps/src/pjsua/pjsua_app.c')
-rw-r--r-- | pjsip-apps/src/pjsua/pjsua_app.c | 636 |
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, ¶m); + } + 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, ¶m); + } + 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, ¶m); + } + 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, ¶m); + } 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 } |