diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c index 978d35064..2a63dde62 100644 --- a/pjsip-apps/src/pjsua/pjsua_app.c +++ b/pjsip-apps/src/pjsua/pjsua_app.c @@ -1451,7 +1451,7 @@ static pj_status_t parse_args(int argc, char *argv[], ++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_transmit); + PJ_TODO(implement_pjsua_option_for_vid_auto_show_and_transmit); break; case OPT_EXTRA_AUDIO: ++cur_acc->max_audio_cnt; @@ -3315,6 +3315,8 @@ static void vid_show_help(void) puts("| vid help Show this help screen |"); puts("| vid call rx on|off Enable/disable incoming video for current call |"); puts("| vid call tx on|off Enable/disable video tx for current call |"); + puts("| vid call add Add video stream for current call |"); + puts("| vid call remove [idx] Remove video stream #idx for 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 |"); @@ -3753,9 +3755,19 @@ static void vid_handle_menu(char *menuin) 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; pj_bool_t tx = (strcmp(argv[2], "tx") == 0); - pj_bool_t on = (strcmp(argv[3], "on") == 0); + if (tx) { + pj_bool_t on = (strcmp(argv[3], "on") == 0); + } + if (strcmp(argv[2], "add")==0) { + pjsua_call_set_vid_strm(current_call, PJSUA_CALL_VID_STRM_ADD, NULL); + } + if (strcmp(argv[2], "remove")==0) { + param.med_idx = argc >= 4? atoi(argv[3]) : -1; + pjsua_call_set_vid_strm(current_call, PJSUA_CALL_VID_STRM_REMOVE, ¶m); + } PJ_TODO(vid_enable_disable_video_on_call); PJ_LOG(1,(THIS_FILE, "Not implemented")); } else if (strcmp(argv[1], "dev")==0) { diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index e3da7a17e..06e5430a8 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -369,6 +369,69 @@ typedef enum pjsua_state } pjsua_state; +/** + * This enumeration represents video stream operation on a call. + * See also #pjsua_call_vid_strm_op_param for further info. + */ +typedef enum pjsua_call_vid_strm_op +{ + /** + * Add a new video stream. + */ + PJSUA_CALL_VID_STRM_ADD, + + /** + * Remove an existing video stream. + */ + PJSUA_CALL_VID_STRM_REMOVE, + + /** + * Modify an existing video stream, such as changing the capture device. + */ + PJSUA_CALL_VID_STRM_MODIFY, + + /** + * Start transmitting video stream. + */ + PJSUA_CALL_VID_STRM_START_TRANSMIT, + + /** + * Stop transmitting video stream. + */ + PJSUA_CALL_VID_STRM_STOP_TRANSMIT, + +} pjsua_call_vid_strm_op; + + +/** + * Parameters for video stream operation on a call. + */ +typedef struct pjsua_call_vid_strm_op_param +{ + /** + * Specify the media stream index. This can be set to -1 to denote + * the default video stream in the call, which is the first active + * video stream or any first video stream if none is active. + * + * This field is valid for all video stream operations, except + * PJSUA_CALL_VID_STRM_ADD. + */ + int med_idx; + + /** + * Specify the video capture device ID. This can be set to + * PJMEDIA_VID_DEFAULT_CAPTURE_DEV to specify the default capture + * device as configured in the account. + * + * This field is valid for the following video stream operations: + * PJSUA_CALL_VID_STRM_ADD, PJSUA_CALL_VID_STRM_MODIFY, and + * PJSUA_CALL_VID_STRM_START_TRANSMIT. + */ + pjmedia_vid_dev_index cap_dev; + +} pjsua_call_vid_strm_op_param; + + /** * Logging configuration, which can be (optionally) specified when calling * #pjsua_init(). Application must call #pjsua_logging_config_default() to @@ -3680,43 +3743,24 @@ PJ_DECL(pj_status_t) pjsua_call_dump(pjsua_call_id call_id, */ PJ_DECL(int) pjsua_call_get_vid_stream_idx(pjsua_call_id call_id); + /** - * Start, stop, and/or manipulate video transmission for the specified - * call. This would trigger a re-INVITE or UPDATE to be sent for the - * call. This function may add, remove, or modify existing video media - * stream, depending on the media index specified (the \a med_idx argument). - * - * To add a new or edit existing video stream (for transmission), specify - * a valid video capture device ID or PJMEDIA_VID_DEFAULT_CAPTURE_DEV in - * the \a cap_dev argument. If \a med_idx is set to default stream (-1), - * then the function will modify existing video stream if one exists, or - * add a new one if it doesn't. If \a med_idx is set to a specific stream - * index, the function will modify that video stream. Otherwise if \a med_idx - * is set to value larger than the current media count, a new video stream - * will be added to the call. - * - * To remove an existing video stream, specify PJMEDIA_VID_INVALID_DEV in - * \a cap_dev argument. If \a med_idx is set to default stream (-1), this - * will remove the default/first video stream in the call, otherwise - * application can put a specific value to request removal of that particular - * video stream. + * Add, remove, modify, and/or manipulate video media stream for the + * specified call. This may trigger a re-INVITE or UPDATE to be sent + * for the call. * * @param call_id Call identification. - * @param med_idx The media stream index. Currently the value MUST - * be -1 to denote the default video stream in the - * call. - * @param cap_dev To add or modify existing video media stream, - * specify PJMEDIA_VID_DEFAULT_CAPTURE_DEV to use - * the default capture device as configured in the - * account, or specify a specific capture device ID. - * To disable an existing video stream, specify - * PJMEDIA_VID_INVALID_DEV for this parameter. + * @param op The video stream operation to be performed, + * possible values are #pjsua_call_vid_strm_op. + * @param param The parameters for the video stream operation. * * @return PJ_SUCCESS on success or the appropriate error. */ -PJ_DECL(pj_status_t) pjsua_call_set_vid_out(pjsua_call_id call_id, - int med_idx, - pjmedia_vid_dev_index cap_dev); +PJ_DECL(pj_status_t) pjsua_call_set_vid_strm ( + pjsua_call_id call_id, + pjsua_call_vid_strm_op op, + const pjsua_call_vid_strm_op_param *param); + /** * Get media stream info for the specified media index. diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h index 84b6b1f34..4dd9e9dca 100644 --- a/pjsip/include/pjsua-lib/pjsua_internal.h +++ b/pjsip/include/pjsua-lib/pjsua_internal.h @@ -74,6 +74,8 @@ typedef struct pjsua_call_media pjmedia_vid_stream *stream; /**< The video stream. */ pjsua_vid_win_id cap_win_id;/**< The video capture window */ pjsua_vid_win_id rdr_win_id;/**< The video render window */ + pjmedia_vid_dev_index cap_dev; /**< The video capture device */ + pjmedia_vid_dev_index rdr_dev; /**< The video-in render device */ } v; } strm; diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index 1d54a49cf..b40cb7e4c 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -1313,11 +1313,11 @@ static void sort_media(const pjmedia_sdp_session *sdp, } /* Initialize the media line */ -static pj_status_t pjsua_call_media_init(pjsua_call_media *call_med, - pjmedia_type type, - const pjsua_transport_config *tcfg, - int security_level, - int *sip_err_code) +pj_status_t pjsua_call_media_init(pjsua_call_media *call_med, + pjmedia_type type, + const pjsua_transport_config *tcfg, + int security_level, + int *sip_err_code) { pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id]; pj_status_t status; @@ -1342,6 +1342,22 @@ static pj_status_t pjsua_call_media_init(pjsua_call_media *call_med, } call_med->tp_st = PJSUA_MED_TP_IDLE; + + /* While in initial call, set default video devices */ + if (type == PJMEDIA_TYPE_VIDEO) { + call_med->strm.v.rdr_dev = acc->cfg.vid_rend_dev; + call_med->strm.v.cap_dev = acc->cfg.vid_cap_dev; + if (call_med->strm.v.rdr_dev == PJMEDIA_VID_DEFAULT_RENDER_DEV) { + pjmedia_vid_dev_info info; + pjmedia_vid_dev_get_info(call_med->strm.v.rdr_dev, &info); + call_med->strm.v.rdr_dev = info.id; + } + if (call_med->strm.v.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) { + pjmedia_vid_dev_info info; + pjmedia_vid_dev_get_info(call_med->strm.v.cap_dev, &info); + call_med->strm.v.cap_dev = info.id; + } + } } else if (call_med->tp_st == PJSUA_MED_TP_DISABLED) { /* Media is being reenabled. */ call_med->tp_st = PJSUA_MED_TP_INIT; diff --git a/pjsip/src/pjsua-lib/pjsua_vid.c b/pjsip/src/pjsua-lib/pjsua_vid.c index b8db3cada..65a5998f8 100644 --- a/pjsip/src/pjsua-lib/pjsua_vid.c +++ b/pjsip/src/pjsua-lib/pjsua_vid.c @@ -607,7 +607,8 @@ pj_status_t video_channel_update(pjsua_call_media *call_med, return status; /* Setup decoding direction */ - if (si->dir & PJMEDIA_DIR_DECODING) { + if (si->dir & PJMEDIA_DIR_DECODING) + { pjsua_vid_win_id wid; pjsua_vid_win *w; @@ -620,7 +621,8 @@ pj_status_t video_channel_update(pjsua_call_media *call_med, /* Create stream video window */ status = create_vid_win(PJSUA_WND_TYPE_STREAM, &media_port->info.fmt, - acc->cfg.vid_rend_dev, + call_med->strm.v.rdr_dev, + //acc->cfg.vid_rend_dev, PJSUA_INVALID_ID, acc->cfg.vid_in_auto_show, &wid); @@ -646,8 +648,7 @@ pj_status_t video_channel_update(pjsua_call_media *call_med, } /* Setup encoding direction */ - if (si->dir & PJMEDIA_DIR_ENCODING && !call->local_hold && - acc->cfg.vid_out_auto_transmit) + if (si->dir & PJMEDIA_DIR_ENCODING && !call->local_hold) { pjsua_vid_win *w; pjsua_vid_win_id wid; @@ -661,8 +662,10 @@ pj_status_t video_channel_update(pjsua_call_media *call_med, /* Create preview video window */ status = create_vid_win(PJSUA_WND_TYPE_PREVIEW, &media_port->info.fmt, - acc->cfg.vid_rend_dev, - acc->cfg.vid_cap_dev, + call_med->strm.v.rdr_dev, + call_med->strm.v.cap_dev, + //acc->cfg.vid_rend_dev, + //acc->cfg.vid_cap_dev, PJ_FALSE, &wid); if (status != PJ_SUCCESS) @@ -736,6 +739,13 @@ pj_status_t video_channel_update(pjsua_call_media *call_med, PJ_LOG(4,(THIS_FILE,"Media updates%s", info)); } + if (acc->cfg.vid_out_auto_transmit) { + status = pjmedia_vid_stream_pause(call_med->strm.v.stream, + PJMEDIA_DIR_ENCODING); + if (status != PJ_SUCCESS) + return status; + } + return PJ_SUCCESS; } @@ -1006,6 +1016,501 @@ PJ_DEF(pj_status_t) pjsua_vid_win_set_size( pjsua_vid_win_id wid, } +static void call_get_vid_strm_info(pjsua_call *call, + int *first_active, + int *first_inactive, + unsigned *active_cnt, + unsigned *cnt) +{ + unsigned i, var_cnt = 0; + + if (first_active && ++var_cnt) + *first_active = -1; + if (first_inactive && ++var_cnt) + *first_inactive = -1; + if (active_cnt && ++var_cnt) + *active_cnt = 0; + if (cnt && ++var_cnt) + *cnt = 0; + + for (i = 0; i < call->med_cnt && var_cnt; ++i) { + if (call->media[i].type == PJMEDIA_TYPE_VIDEO) { + if (call->media[i].dir != PJMEDIA_DIR_NONE) + { + if (first_active && *first_active == -1) { + *first_active = i; + --var_cnt; + } + if (active_cnt) + ++(*active_cnt); + } else if (first_inactive && *first_inactive == -1) { + *first_inactive = i; + --var_cnt; + } + if (cnt) + ++(*cnt); + } + } +} + + +/* Send SDP reoffer. */ +static pj_status_t call_reoffer_sdp(pjsua_call_id call_id, + const pjmedia_sdp_session *sdp) +{ + pjsua_call *call; + pjsip_tx_data *tdata; + pjsip_dialog *dlg; + pj_status_t status; + + status = acquire_call("call_reoffer_sdp()", call_id, &call, &dlg); + if (status != PJ_SUCCESS) + return status; + + if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) { + PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed")); + pjsip_dlg_dec_lock(dlg); + return PJSIP_ESESSIONSTATE; + } + + /* Create re-INVITE with new offer */ + status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status); + pjsip_dlg_dec_lock(dlg); + return status; + } + + /* Send the request */ + status = pjsip_inv_send_msg( call->inv, tdata); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status); + pjsip_dlg_dec_lock(dlg); + return status; + } + + pjsip_dlg_dec_lock(dlg); + + return PJ_SUCCESS; +} + + +pj_status_t pjsua_call_media_init(pjsua_call_media *call_med, + pjmedia_type type, + const pjsua_transport_config *tcfg, + int security_level, + int *sip_err_code); + + +/* Add a new video stream into a call */ +static pj_status_t call_add_video(pjsua_call *call, + pjmedia_vid_dev_index cap_dev) +{ + pj_pool_t *pool = call->inv->pool_prov; + pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg; + pjsua_call_media *call_med; + pjmedia_sdp_session *sdp; + pjmedia_sdp_media *sdp_m; + pjmedia_transport_info tpinfo; + pjmedia_vid_dev_info vinfo; + unsigned active_cnt; + pj_status_t status; + + /* Verify media slot availability */ + if (call->med_cnt == PJSUA_MAX_CALL_MEDIA) + return PJ_ETOOMANY; + + call_get_vid_strm_info(call, NULL, NULL, &active_cnt, NULL); + if (active_cnt == acc_cfg->max_video_cnt) + return PJ_ETOOMANY; + + /* Verify the capture device */ + status = pjmedia_vid_dev_get_info(cap_dev, &vinfo); + if (status != PJ_SUCCESS) + return status; + + if (vinfo.dir != PJMEDIA_DIR_CAPTURE) + return PJ_EINVAL; + + cap_dev = vinfo.id; + + /* Get active local SDP */ + status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp); + if (status != PJ_SUCCESS) + return status; + + /* Initialize call media */ + call_med = &call->media[call->med_cnt++]; + + status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO, + &acc_cfg->rtp_cfg, call->secure_level, + NULL); + if (status != PJ_SUCCESS) + goto on_error; + + /* Override default capture device setting */ + call_med->strm.v.cap_dev = cap_dev; + + /* Init transport media */ + status = pjmedia_transport_media_create(call_med->tp, pool, 0, + NULL, call_med->idx); + if (status != PJ_SUCCESS) + goto on_error; + + call_med->tp_st = PJSUA_MED_TP_INIT; + + /* Get transport address info */ + pjmedia_transport_info_init(&tpinfo); + pjmedia_transport_get_info(call_med->tp, &tpinfo); + + /* Create SDP media line */ + status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool, + &tpinfo.sock_info, 0, &sdp_m); + if (status != PJ_SUCCESS) + goto on_error; + + sdp->media[sdp->media_count++] = sdp_m; + + /* Update SDP media line by media transport */ + status = pjmedia_transport_encode_sdp(call_med->tp, pool, + sdp, NULL, call_med->idx); + if (status != PJ_SUCCESS) + goto on_error; + + status = call_reoffer_sdp(call->index, sdp); + if (status != PJ_SUCCESS) + goto on_error; + + return PJ_SUCCESS; + +on_error: + if (call_med->tp) { + pjmedia_transport_close(call_med->tp); + call_med->tp = call_med->tp_orig = NULL; + } + + return status; +} + + +/* Remove a video stream from a call */ +static pj_status_t call_remove_video(pjsua_call *call, + int med_idx) +{ + pjsua_call_media *call_med; + pjmedia_sdp_session *sdp; + pj_status_t status; + + /* Verify and normalize media index */ + if (med_idx == -1) { + int first_active; + + call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL); + if (first_active == -1) + return PJ_ENOTFOUND; + + med_idx = first_active; + } + + call_med = &call->media[med_idx]; + + /* Verify if the stream media type is video */ + if (call_med->type != PJMEDIA_TYPE_VIDEO) + return PJ_EINVAL; + + /* Verify if the stream already disabled */ + if (call_med->dir != PJMEDIA_DIR_NONE) + return PJ_SUCCESS; + + /* Mark media transport to disabled */ + // Don't close this here, as SDP negotiation has not been + // done and stream may be still active. + call_med->tp_st = PJSUA_MED_TP_DISABLED; + + /* Get active local SDP */ + status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp); + if (status != PJ_SUCCESS) + return status; + + pj_assert(med_idx < (int)sdp->media_count); + sdp->media[med_idx]->desc.port = 0; + + status = call_reoffer_sdp(call->index, sdp); + if (status != PJ_SUCCESS) + return status; + + return PJ_SUCCESS; +} + + +/* Modify a video stream in a call */ +static pj_status_t call_modify_video(pjsua_call *call, + int med_idx, + pjmedia_vid_dev_index cap_dev) +{ + pjsua_call_media *call_med; + pjmedia_vid_dev_info info; + pjsua_vid_win *w, *new_w = NULL; + pjsua_vid_win_id wid, new_wid; + pjmedia_port *media_port; + pj_status_t status; + + /* Verify and normalize media index */ + if (med_idx == -1) { + int first_active; + + call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL); + if (first_active == -1) + return PJ_ENOTFOUND; + + med_idx = first_active; + } + + call_med = &call->media[med_idx]; + + /* Verify if the stream media type is video */ + if (call_med->type != PJMEDIA_TYPE_VIDEO) + return PJ_EINVAL; + + /* Verify the capture device */ + status = pjmedia_vid_dev_get_info(cap_dev, &info); + if (status != PJ_SUCCESS) + return status; + + if (info.dir != PJMEDIA_DIR_CAPTURE) + return PJ_EINVAL; + + cap_dev = info.id; + + /* The specified capture device is being used already */ + if (call_med->strm.v.cap_dev == cap_dev) + return PJ_SUCCESS; + + /* == Apply the new capture device == */ + + wid = call_med->strm.v.cap_win_id; + w = &pjsua_var.win[wid]; + pj_assert(w->type == PJSUA_WND_TYPE_PREVIEW && w->vp_cap); + + status = pjmedia_vid_stream_get_port(call_med->strm.v.stream, + PJMEDIA_DIR_ENCODING, &media_port); + if (status != PJ_SUCCESS) + return status; + + /* = Detach stream port from the old capture device = */ + status = pjmedia_vid_port_disconnect(w->vp_cap); + if (status != PJ_SUCCESS) + return status; + + status = pjmedia_vid_tee_remove_dst_port(w->tee, media_port); + if (status != PJ_SUCCESS) { + /* Connect back the old capturer */ + pjmedia_vid_port_connect(w->vp_cap, media_port, PJ_FALSE); + return status; + } + + /* = Attach stream port to the new capture device = */ + + /* Create preview video window */ + status = create_vid_win(PJSUA_WND_TYPE_PREVIEW, + &media_port->info.fmt, + call_med->strm.v.rdr_dev, + cap_dev, + PJ_FALSE, + &new_wid); + if (status != PJ_SUCCESS) + goto on_error; + + inc_vid_win(new_wid); + new_w = &pjsua_var.win[new_wid]; + + /* Connect stream to capturer (via video window tee) */ + status = pjmedia_vid_tee_add_dst_port2(new_w->tee, 0, media_port); + if (status != PJ_SUCCESS) + goto on_error; + + /* Connect capturer to tee */ + status = pjmedia_vid_port_connect(new_w->vp_cap, new_w->tee, PJ_FALSE); + if (status != PJ_SUCCESS) + return status; + + /* Start renderer */ + status = pjmedia_vid_port_start(new_w->vp_rend); + if (status != PJ_SUCCESS) + goto on_error; + + /* Start capturer */ + status = pjmedia_vid_port_start(new_w->vp_cap); + if (status != PJ_SUCCESS) + goto on_error; + + /* Finally */ + call_med->strm.v.cap_dev = cap_dev; + call_med->strm.v.cap_win_id = new_wid; + dec_vid_win(wid); + + return PJ_SUCCESS; + +on_error: + if (new_w) { + /* Disconnect media port from the new capturer */ + pjmedia_vid_tee_remove_dst_port(new_w->tee, media_port); + /* Release the new capturer */ + dec_vid_win(new_wid); + } + + /* Revert back to the old capturer */ + status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port); + if (status != PJ_SUCCESS) + return status; + + status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE); + if (status != PJ_SUCCESS) + return status; + + return status; +} + + +/* Start transmitting video stream in a call */ +static pj_status_t call_start_tx_video(pjsua_call *call, + int med_idx, + pjmedia_vid_dev_index cap_dev) +{ + pjsua_call_media *call_med; + pj_status_t status; + + /* Verify and normalize media index */ + if (med_idx == -1) { + int first_active; + + call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL); + if (first_active == -1) + return PJ_ENOTFOUND; + + med_idx = first_active; + } + + call_med = &call->media[med_idx]; + + /* Verify if the stream is transmitting video */ + if (call_med->type != PJMEDIA_TYPE_VIDEO || + (call_med->dir & PJMEDIA_DIR_ENCODING) == 0) + { + return PJ_EINVAL; + } + + /* Apply the new capture device */ + status = call_modify_video(call, med_idx, cap_dev); + if (status != PJ_SUCCESS) + return status; + + /* Start stream in encoding direction */ + status = pjmedia_vid_stream_resume(call_med->strm.v.stream, + PJMEDIA_DIR_ENCODING); + if (status != PJ_SUCCESS) + return status; + + return PJ_SUCCESS; +} + + +/* Stop transmitting video stream in a call */ +static pj_status_t call_stop_tx_video(pjsua_call *call, + int med_idx) +{ + pjsua_call_media *call_med; + pj_status_t status; + + /* Verify and normalize media index */ + if (med_idx == -1) { + int first_active; + + call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL); + if (first_active == -1) + return PJ_ENOTFOUND; + + med_idx = first_active; + } + + call_med = &call->media[med_idx]; + + /* Verify if the stream is transmitting video */ + if (call_med->type != PJMEDIA_TYPE_VIDEO || + (call_med->dir & PJMEDIA_DIR_ENCODING) == 0) + { + return PJ_EINVAL; + } + + /* Pause stream in encoding direction */ + status = pjmedia_vid_stream_pause( call_med->strm.v.stream, + PJMEDIA_DIR_ENCODING); + if (status != PJ_SUCCESS) + return status; + + return PJ_SUCCESS; +} + + +/* + * Start, stop, and/or manipulate video transmission for the specified call. + */ +PJ_DEF(pj_status_t) pjsua_call_set_vid_strm ( + pjsua_call_id call_id, + pjsua_call_vid_strm_op op, + const pjsua_call_vid_strm_op_param *param) +{ + pjsua_call *call; + pjsua_call_vid_strm_op_param param_; + pj_status_t status; + + PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, + PJ_EINVAL); + + PJSUA_LOCK(); + + call = &pjsua_var.calls[call_id]; + + if (param) { + param_ = *param; + } else { + param_.med_idx = -1; + param_.cap_dev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV; + } + + /* Get real capture ID, if set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV */ + if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) { + pjmedia_vid_dev_info info; + pjmedia_vid_dev_get_info(param_.cap_dev, &info); + param_.cap_dev = info.id; + } + + switch (op) { + case PJSUA_CALL_VID_STRM_ADD: + status = call_add_video(call, param_.cap_dev); + break; + case PJSUA_CALL_VID_STRM_REMOVE: + status = call_remove_video(call, param_.med_idx); + break; + case PJSUA_CALL_VID_STRM_MODIFY: + status = call_modify_video(call, param_.med_idx, param_.cap_dev); + break; + case PJSUA_CALL_VID_STRM_START_TRANSMIT: + status = call_start_tx_video(call, param_.med_idx, param_.cap_dev); + break; + case PJSUA_CALL_VID_STRM_STOP_TRANSMIT: + status = call_stop_tx_video(call, param_.med_idx); + break; + default: + status = PJ_EINVALIDOP; + break; + } + + PJSUA_UNLOCK(); + + return status; +} + #endif /* PJSUA_HAS_VIDEO */