summaryrefslogtreecommitdiff
path: root/pjsip
diff options
context:
space:
mode:
authorNanang Izzuddin <nanang@teluu.com>2011-12-07 10:43:28 +0000
committerNanang Izzuddin <nanang@teluu.com>2011-12-07 10:43:28 +0000
commit2ba3536e2d318130242c35ed053aaae7f771b261 (patch)
tree564da2c0e0a5b2b2fef7a50342286727eb825662 /pjsip
parent3a0786774a23558b8da85fd261b2858995c2c999 (diff)
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
Diffstat (limited to 'pjsip')
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h53
-rw-r--r--pjsip/include/pjsua-lib/pjsua_internal.h4
-rw-r--r--pjsip/src/pjsua-lib/pjsua_call.c53
-rw-r--r--pjsip/src/pjsua-lib/pjsua_media.c74
-rw-r--r--pjsip/src/pjsua-lib/pjsua_vid.c29
5 files changed, 206 insertions, 7 deletions
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.
*/
@@ -3371,18 +3382,46 @@ typedef enum 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 =
+ "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
+ "<media_control><vc_primitive><to_encoder>"
+ "<picture_fast_update/>"
+ "</to_encoder></vc_primitive></media_control>";
+
+ 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;