From 2ba3536e2d318130242c35ed053aaae7f771b261 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Wed, 7 Dec 2011 10:43:28 +0000 Subject: Re #1234: Initial version of keyframe request/response via SIP INFO. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3901 74dad513-b988-da41-8d7b-12977e46ad98 --- pjsip/include/pjsua-lib/pjsua.h | 53 ++++++++++++++++++++++- pjsip/include/pjsua-lib/pjsua_internal.h | 4 ++ pjsip/src/pjsua-lib/pjsua_call.c | 53 ++++++++++++++++++++--- pjsip/src/pjsua-lib/pjsua_media.c | 74 +++++++++++++++++++++++++++++++- pjsip/src/pjsua-lib/pjsua_vid.c | 29 +++++++++++++ 5 files changed, 206 insertions(+), 7 deletions(-) (limited to 'pjsip') diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 31fbfca1..b043f266 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -331,6 +331,17 @@ typedef struct pjsua_msg_data pjsua_msg_data; # define PJSUA_HAS_VIDEO PJMEDIA_HAS_VIDEO #endif + +/** + * Interval between two keyframe requests, in milliseconds. + * + * Default: 500 ms + */ +#ifndef PJSUA_VID_REQ_KEYFRAME_INTERVAL +# define PJSUA_VID_REQ_KEYFRAME_INTERVAL 500 +#endif + + /** * This enumeration represents pjsua state. */ @@ -3370,18 +3381,46 @@ typedef enum pjsua_call_media_status } pjsua_call_media_status; +/** + * Enumeration of video keyframe request methods. Keyframe request is + * triggered by decoder, usually when the incoming video stream cannot + * be decoded properly due to missing video keyframe. + */ +typedef enum pjsua_vid_req_keyframe_method +{ + /** + * Requesting keyframe via SIP INFO message. Note that incoming keyframe + * request via SIP INFO will always be handled even if this flag is unset. + */ + PJSUA_VID_REQ_KEYFRAME_SIP_INFO = 1, + + /** + * Requesting keyframe via Picture Loss Indication of RTCP feedback. + * This is currently not supported. + */ + PJSUA_VID_REQ_KEYFRAME_RTCP_PLI = 2 + +} pjsua_vid_req_keyframe_method; + + /** * Call settings. */ typedef struct pjsua_call_setting { /** - * Bitmask of pjsua_call_flag constants. + * Bitmask of #pjsua_call_flag constants. * * Default: 0 */ unsigned flag; + /** + * This flag controls what methods to request keyframe are allowed on + * the call. Value is bitmask of #pjsua_vid_req_keyframe_method. + */ + unsigned req_keyframe_method; + /** * Number of simultaneous active audio streams for this call. Setting * this to zero will disable audio in this call. @@ -3649,6 +3688,13 @@ typedef enum pjsua_call_vid_strm_op */ PJSUA_CALL_VID_STRM_STOP_TRANSMIT, + /** + * Send keyframe in the video stream. This will force the stream to + * generate and send video keyframe as soon as possible. No + * re-INVITE/UPDATE is to be transmitted to remote with this operation. + */ + PJSUA_CALL_VID_STRM_SEND_KEYFRAME + } pjsua_call_vid_strm_op; @@ -4664,6 +4710,11 @@ PJ_DECL(void) pjsua_pres_dump(pj_bool_t verbose); extern const pjsip_method pjsip_message_method; +/** + * The INFO method (defined in pjsua_call.c) + */ +extern const pjsip_method pjsip_info_method; + /** * Send instant messaging outside dialog, using the specified account for diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h index 5c16370a..31447ddf 100644 --- a/pjsip/include/pjsua-lib/pjsua_internal.h +++ b/pjsip/include/pjsua-lib/pjsua_internal.h @@ -84,6 +84,7 @@ struct pjsua_call_media (used to update ICE default address) */ pjmedia_srtp_use rem_srtp_use; /**< Remote's SRTP usage policy. */ + pj_timestamp last_req_keyframe;/**< Last TX keyframe request. */ pjsua_med_tp_state_cb med_init_cb;/**< Media transport initialization callback. */ @@ -477,6 +478,9 @@ typedef struct pjsua_im_data void *user_data; } pjsua_im_data; +pj_status_t pjsua_media_apply_xml_control(pjsua_call_id call_id, + const pj_str_t *xml_st); + /** * Duplicate IM data. diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index 2447b80c..9c1c0500 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -34,6 +34,17 @@ */ #define LOCK_CODEC_MAX_RETRY 5 + +/* + * The INFO method. + */ +const pjsip_method pjsip_info_method = +{ + PJSIP_OTHER_METHOD, + { "INFO", 4 } +}; + + /* This callback receives notification from invite session when the * session state has changed. */ @@ -500,11 +511,8 @@ PJ_DEF(void) pjsua_call_setting_default(pjsua_call_setting *opt) #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) opt->video_cnt = 1; - //{ - // unsigned i; - // for (i = 0; i < PJ_ARRAY_SIZE(opt->vid_cap_dev); ++i) - // opt->vid_cap_dev[i] = PJMEDIA_VID_DEFAULT_CAPTURE_DEV; - //} + opt->req_keyframe_method = PJSUA_VID_REQ_KEYFRAME_SIP_INFO | + PJSUA_VID_REQ_KEYFRAME_RTCP_PLI; #endif } @@ -4190,6 +4198,41 @@ static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv, PJ_LOG(3,(THIS_FILE, "Error putting call %d on hold (reason=%d)", call->index, tsx->status_code)); } + } else if (tsx->role==PJSIP_ROLE_UAS && + tsx->state==PJSIP_TSX_STATE_TRYING && + pjsip_method_cmp(&tsx->method, &pjsip_info_method)==0) + { + /* + * Incoming INFO request for media control. + */ + const pj_str_t STR_APPLICATION = { "application", 11}; + const pj_str_t STR_MEDIA_CONTROL_XML = { "media_control+xml", 17 }; + pjsip_rx_data *rdata = e->body.tsx_state.src.rdata; + pjsip_msg_body *body = rdata->msg_info.msg->body; + + if (body && body->len && + pj_stricmp(&body->content_type.type, &STR_APPLICATION)==0 && + pj_stricmp(&body->content_type.subtype, &STR_MEDIA_CONTROL_XML)==0) + { + pjsip_tx_data *tdata; + pj_str_t control_st; + pj_status_t status; + + /* Apply and answer the INFO request */ + pj_strset(&control_st, (char*)body->data, body->len); + status = pjsua_media_apply_xml_control(call->index, &control_st); + if (status == PJ_SUCCESS) { + status = pjsip_endpt_create_response(tsx->endpt, rdata, + 200, NULL, &tdata); + if (status == PJ_SUCCESS) + status = pjsip_tsx_send_msg(tsx, tdata); + } else { + status = pjsip_endpt_create_response(tsx->endpt, rdata, + 400, NULL, &tdata); + if (status == PJ_SUCCESS) + status = pjsip_tsx_send_msg(tsx, tdata); + } + } } on_return: diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index e938247f..4a413e37 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -1258,13 +1258,55 @@ pj_status_t call_media_on_event(pjmedia_event *event, { pjsua_call_media *call_med = (pjsua_call_media*)user_data; pjsua_call *call = call_med->call; + pj_status_t status = PJ_SUCCESS; + + switch(event->type) { + case PJMEDIA_EVENT_KEYFRAME_MISSING: + if (call->opt.req_keyframe_method & PJSUA_VID_REQ_KEYFRAME_SIP_INFO) + { + pj_timestamp now; + + pj_get_timestamp(&now); + if (pj_elapsed_msec(&call_med->last_req_keyframe, &now) >= + PJSUA_VID_REQ_KEYFRAME_INTERVAL) + { + pjsua_msg_data msg_data; + const pj_str_t SIP_INFO = {"INFO", 4}; + const char *BODY_TYPE = "application/media_control+xml"; + const char *BODY = + "" + "" + "" + ""; + + PJ_LOG(4,(THIS_FILE, + "Sending video keyframe request via SIP INFO")); + + pjsua_msg_data_init(&msg_data); + pj_cstr(&msg_data.content_type, BODY_TYPE); + pj_cstr(&msg_data.msg_body, BODY); + status = pjsua_call_send_request(call->index, &SIP_INFO, + &msg_data); + if (status != PJ_SUCCESS) { + pj_perror(3, THIS_FILE, status, + "Failed requesting keyframe via SIP INFO"); + } else { + call_med->last_req_keyframe = now; + } + } + } + break; + + default: + break; + } if (pjsua_var.ua_cfg.cb.on_call_media_event && call) { (*pjsua_var.ua_cfg.cb.on_call_media_event)(call->index, call_med->idx, event); } - return PJ_SUCCESS; + return status; } /* Set media transport state and notify the application via the callback. */ @@ -4187,3 +4229,33 @@ PJ_DEF(pj_status_t) pjsua_codec_set_param( const pj_str_t *codec_id, } +pj_status_t pjsua_media_apply_xml_control(pjsua_call_id call_id, + const pj_str_t *xml_st) +{ + pjsua_call *call = &pjsua_var.calls[call_id]; + const pj_str_t PICT_FAST_UPDATE = {"picture_fast_update", 19}; + +#if PJMEDIA_HAS_VIDEO + if (pj_strstr(xml_st, &PICT_FAST_UPDATE)) { + unsigned i; + + PJ_LOG(4,(THIS_FILE, "Received keyframe request via SIP INFO")); + + for (i = 0; i < call->med_cnt; ++i) { + pjsua_call_media *cm = &call->media[i]; + if (cm->type != PJMEDIA_TYPE_VIDEO || !cm->strm.v.stream) + continue; + + pjmedia_vid_stream_send_keyframe(cm->strm.v.stream); + } + + return PJ_SUCCESS; + } +#endif + + /* Just to avoid compiler warning of unused var */ + PJ_UNUSED_ARG(xml_st); + + return PJ_ENOTSUP; +} + diff --git a/pjsip/src/pjsua-lib/pjsua_vid.c b/pjsip/src/pjsua-lib/pjsua_vid.c index 4e9017fa..b8768c00 100644 --- a/pjsip/src/pjsua-lib/pjsua_vid.c +++ b/pjsip/src/pjsua-lib/pjsua_vid.c @@ -2000,6 +2000,32 @@ static pj_status_t call_set_tx_video(pjsua_call *call, } +static pj_status_t call_send_vid_keyframe(pjsua_call *call, + int med_idx) +{ + pjsua_call_media *call_med; + + /* 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 media type and stream instance. */ + if (call_med->type != PJMEDIA_TYPE_VIDEO || !call_med->strm.v.stream) + return PJ_EINVAL; + + return pjmedia_vid_stream_send_keyframe(call_med->strm.v.stream); +} + + /* * Start, stop, and/or manipulate video transmission for the specified call. */ @@ -2069,6 +2095,9 @@ PJ_DEF(pj_status_t) pjsua_call_set_vid_strm ( case PJSUA_CALL_VID_STRM_STOP_TRANSMIT: status = call_set_tx_video(call, param_.med_idx, PJ_FALSE); break; + case PJSUA_CALL_VID_STRM_SEND_KEYFRAME: + status = call_send_vid_keyframe(call, param_.med_idx); + break; default: status = PJ_EINVALIDOP; break; -- cgit v1.2.3