Skip to content

Commit

Permalink
Add support of abs-capture-time extension to Streaming plugin (#3291)
Browse files Browse the repository at this point in the history
  • Loading branch information
IbrayevRamil committed Jan 15, 2024
1 parent 9525ef6 commit 2fa046b
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 15 deletions.
7 changes: 7 additions & 0 deletions conf/janus.plugin.streaming.jcfg.sample.in
Expand Up @@ -83,6 +83,13 @@
# assuming the browser supports the RTP extension in the first place.
# playoutdelay_ext = true
#
# To allow mountpoints to negotiate the abs-capture-time RTP extension,
# you can set the 'abscapturetime_src_ext_id' property to value in range 1..14 inclusive: this way, any
# subscriber can receive the abs-capture-time of incoming RTP streams,
# assuming the browser supports the RTP extension in the first place.
# Incoming RTP stream should provide abs-capture-time exactly in the same header id.
# abscapturetime_src_ext_id = 1
#
# The following options are only valid for the 'rtsp' type:
# url = RTSP stream URL (only for restreaming RTSP)
# rtsp_user = RTSP authorization username (only if type=rtsp)
Expand Down
18 changes: 8 additions & 10 deletions src/ice.c
Expand Up @@ -4103,20 +4103,18 @@ static void janus_ice_rtp_extension_update(janus_ice_handle *handle, janus_ice_p
if(packet->extensions.abs_capture_ts > 0 && handle->pc->abs_capture_time_ext_id > 0) {
uint64_t abs64 = htonll(packet->extensions.abs_capture_ts);
if(!use_2byte) {
*index = (handle->pc->abs_capture_time_ext_id << 4) + 15;
*index = (handle->pc->abs_capture_time_ext_id << 4) + 7;
memcpy(index+1, &abs64, 8);
memset(index+9, 0, 8);
index += 17;
extlen += 17;
extbufsize -= 17;
index += 9;
extlen += 9;
extbufsize -= 9;
} else {
*index = handle->pc->abs_capture_time_ext_id;
*(index+1) = 16;
*(index+1) = 8;
memcpy(index+2, &abs64, 8);
memset(index+8, 0, 8);
index += 18;
extlen += 18;
extbufsize -= 18;
index += 10;
extlen += 10;
extbufsize -= 10;
}
}
/* Calculate the whole length */
Expand Down
90 changes: 85 additions & 5 deletions src/plugins/janus_streaming.c
Expand Up @@ -144,6 +144,13 @@ subscriber can customize the playout delay of incoming video streams,
assuming the browser supports the RTP extension in the first place.
playoutdelay_ext = true
To allow mountpoints to negotiate the abs-capture-time RTP extension,
you can set the 'abscapturetime_src_ext_id' property to value in range 1..14 inclusive: this way, any
subscriber can receive the abs-capture-time of incoming RTP streams,
assuming the browser supports the RTP extension in the first place.
Incoming RTP stream should provide abs-capture-time exactly in the same header id.
abscapturetime_src_ext_id = 1
The following options are only valid for the 'rtsp' type:
url = RTSP stream URL
rtsp_user = RTSP authorization username, if needed
Expand Down Expand Up @@ -1050,7 +1057,8 @@ static struct janus_json_parameter rtp_parameters[] = {
{"srtpsuite", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
{"srtpcrypto", JSON_STRING, 0},
{"e2ee", JANUS_JSON_BOOL, 0},
{"playoutdelay_ext", JANUS_JSON_BOOL, 0}
{"playoutdelay_ext", JANUS_JSON_BOOL, 0},
{"abscapturetime_src_ext_id", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
};
static struct janus_json_parameter live_parameters[] = {
{"filename", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
Expand Down Expand Up @@ -1340,6 +1348,8 @@ typedef struct janus_streaming_rtp_source {
gboolean e2ee;
/* Whether the playout-delay extension should be negotiated or not for new subscribers */
gboolean playoutdelay_ext;
/* Extension header id in RTP source with abs-capture-time */
int abscapturetime_src_ext_id;
} janus_streaming_rtp_source;

typedef enum janus_streaming_media {
Expand Down Expand Up @@ -1488,7 +1498,8 @@ janus_streaming_rtp_source_stream *janus_streaming_create_rtp_source_stream(
gboolean textdata, gboolean buffermsg);
janus_streaming_mountpoint *janus_streaming_create_rtp_source(
uint64_t id, char *id_str, char *name, char *desc, char *metadata,
GList *media, int srtpsuite, char *srtpcrypto, int threads, int rtp_collision, gboolean e2ee, gboolean playoutdelay_ext);
GList *media, int srtpsuite, char *srtpcrypto, int threads, int rtp_collision,
gboolean e2ee, gboolean playoutdelay_ext, int abscapturetime_src_ext_id);
/* Helper to create a file/ondemand live source */
janus_streaming_mountpoint *janus_streaming_create_file_source(
uint64_t id, char *id_str, char *name, char *desc, char *metadata, char *filename, gboolean live,
Expand Down Expand Up @@ -1547,6 +1558,8 @@ typedef struct janus_streaming_session {
gboolean e2ee;
/* Whether the playout-delay extension should be negotiated */
gboolean playoutdelay_ext;
/* Extension header id in RTP source with abs-capture-time */
int abscapturetime_src_ext_id;
janus_mutex mutex;
volatile gint dataready;
volatile gint stopping;
Expand Down Expand Up @@ -2094,6 +2107,7 @@ int janus_streaming_init(janus_callbacks *callback, const char *config_path) {
janus_config_item *scrypto = janus_config_get(config, cat, janus_config_type_item, "srtpcrypto");
janus_config_item *e2ee = janus_config_get(config, cat, janus_config_type_item, "e2ee");
janus_config_item *pd = janus_config_get(config, cat, janus_config_type_item, "playoutdelay_ext");
janus_config_item *abscaptime_src_id = janus_config_get(config, cat, janus_config_type_item, "abscapturetime_src_ext_id");
gboolean is_private = priv && priv->value && janus_is_true(priv->value);
if(ssuite && ssuite->value && atoi(ssuite->value) != 32 && atoi(ssuite->value) != 80) {
JANUS_LOG(LOG_ERR, "Can't add 'rtp' mountpoint '%s', invalid SRTP suite...\n", cat->name);
Expand All @@ -2110,6 +2124,15 @@ int janus_streaming_init(janus_callbacks *callback, const char *config_path) {
cl = cl->next;
continue;
}
int abscaptime_src_id_int = 0;
if(abscaptime_src_id && abscaptime_src_id->value) {
abscaptime_src_id_int = atoi(abscaptime_src_id->value);
if(abscaptime_src_id_int < 0 || abscaptime_src_id_int > 14) {
JANUS_LOG(LOG_ERR, "Can't add 'rtp' mountpoint '%s', invalid abscaptime_src_id configuration...\n", cat->name);
cl = cl->next;
continue;
}
}
/* How are we adding media? */
if(media != NULL) {
/* We're using the new media-based configuration, iterate on all media objects */
Expand Down Expand Up @@ -2501,7 +2524,8 @@ int janus_streaming_init(janus_callbacks *callback, const char *config_path) {
(threads && threads->value) ? atoi(threads->value) : 0,
(rtpcollision && rtpcollision->value) ? atoi(rtpcollision->value) : 0,
(e2ee && e2ee->value) ? janus_is_true(e2ee->value) : FALSE,
(pd && pd->value) ? janus_is_true(pd->value) : FALSE)) == NULL) {
(pd && pd->value) ? janus_is_true(pd->value) : FALSE,
abscaptime_src_id_int)) == NULL) {
JANUS_LOG(LOG_ERR, "Error creating 'rtp' mountpoint '%s'...\n", cat->name);
cl = cl->next;
continue;
Expand Down Expand Up @@ -2997,6 +3021,8 @@ json_t *janus_streaming_query_session(janus_plugin_session *handle) {
json_object_set_new(pd, "max-delay", json_integer(s->max_delay));
json_object_set_new(info, "playout-delay", pd);
}
if(session->abscapturetime_src_ext_id > 0)
json_object_set_new(info, "abs-capture-time-src-ext-id", json_integer(session->abscapturetime_src_ext_id));
json_array_append_new(media, info);
temp = temp->next;
}
Expand Down Expand Up @@ -3394,6 +3420,16 @@ static json_t *janus_streaming_process_synchronous_request(janus_streaming_sessi
json_t *scrypto = json_object_get(root, "srtpcrypto");
json_t *e2ee = json_object_get(root, "e2ee");
json_t *pd = json_object_get(root, "playoutdelay_ext");
json_t *abscaptime_src_id = json_object_get(root, "abscapturetime_src_ext_id");
if(abscaptime_src_id && (json_integer_value(abscaptime_src_id) < 1 || json_integer_value(abscaptime_src_id) > 14)) {
JANUS_LOG(LOG_ERR, "Invalid element (abscaptime_src_id must be an integer between 1 and 14)\n");
error_code = JANUS_STREAMING_ERROR_INVALID_ELEMENT;
g_snprintf(error_cause, 512, "Invalid element (abscaptime_src_id must be an integer between 1 and 14)");
janus_mutex_lock(&mountpoints_mutex);
g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
janus_mutex_unlock(&mountpoints_mutex);
goto prepare_response;
}
if(ssuite && json_integer_value(ssuite) != 32 && json_integer_value(ssuite) != 80) {
JANUS_LOG(LOG_ERR, "Can't add 'rtp' stream, invalid SRTP suite...\n");
error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
Expand Down Expand Up @@ -3818,7 +3854,8 @@ static json_t *janus_streaming_process_synchronous_request(janus_streaming_sessi
threads ? json_integer_value(threads) : 0,
rtpcollision ? json_integer_value(rtpcollision) : 0,
e2ee ? json_is_true(e2ee) : FALSE,
pd ? json_is_true(pd) : FALSE);
pd ? json_is_true(pd) : FALSE,
abscaptime_src_id ? json_integer_value(abscaptime_src_id) : 0);
janus_mutex_lock(&mountpoints_mutex);
g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
janus_mutex_unlock(&mountpoints_mutex);
Expand Down Expand Up @@ -4176,6 +4213,10 @@ static json_t *janus_streaming_process_synchronous_request(janus_streaming_sessi
janus_config_add(config, c, janus_config_item_create("e2ee", "true"));
if(source->playoutdelay_ext)
janus_config_add(config, c, janus_config_item_create("playoutdelay_ext", "true"));
if(source->abscapturetime_src_ext_id > 0) {
g_snprintf(value, BUFSIZ, "%d", source->abscapturetime_src_ext_id);
janus_config_add(config, c, janus_config_item_create("abscapturetime_src_ext_id", value));
}
/* Iterate on all media streams */
janus_config_array *media = janus_config_array_create("media");
janus_config_add(config, c, media);
Expand Down Expand Up @@ -4575,6 +4616,10 @@ static json_t *janus_streaming_process_synchronous_request(janus_streaming_sessi
janus_config_add(config, c, janus_config_item_create("e2ee", "true"));
if(source->playoutdelay_ext)
janus_config_add(config, c, janus_config_item_create("playoutdelay_ext", "true"));
if(source->abscapturetime_src_ext_id > 0) {
g_snprintf(value, BUFSIZ, "%d", source->abscapturetime_src_ext_id);
janus_config_add(config, c, janus_config_item_create("abscapturetime_src_ext_id", value));
}
/* Iterate on all media streams */
janus_config_array *media = janus_config_array_create("media");
janus_config_add(config, c, media);
Expand Down Expand Up @@ -6132,6 +6177,8 @@ static void *janus_streaming_handler(void *data) {
session->e2ee = source->e2ee;
/* Also check if we have to offer the playout-delay extension */
session->playoutdelay_ext = source->playoutdelay_ext;
/* Also check if we have to offer the abs-capture-time extension */
session->abscapturetime_src_ext_id = source->abscapturetime_src_ext_id;
}
janus_refcount_increase(&session->ref);
done:
Expand All @@ -6153,6 +6200,8 @@ static void *janus_streaming_handler(void *data) {
JANUS_SDP_OA_DIRECTION, JANUS_SDP_SENDONLY,
JANUS_SDP_OA_EXTENSION, JANUS_RTP_EXTMAP_MID, janus_rtp_extension_id(JANUS_RTP_EXTMAP_MID),
JANUS_SDP_OA_EXTENSION, JANUS_RTP_EXTMAP_ABS_SEND_TIME, janus_rtp_extension_id(JANUS_RTP_EXTMAP_ABS_SEND_TIME),
JANUS_SDP_OA_EXTENSION, JANUS_RTP_EXTMAP_ABS_CAPTURE_TIME,
(session->abscapturetime_src_ext_id > 0 ? janus_rtp_extension_id(JANUS_RTP_EXTMAP_ABS_CAPTURE_TIME) : 0),
JANUS_SDP_OA_EXTENSION, JANUS_RTP_EXTMAP_PLAYOUT_DELAY,
(session->playoutdelay_ext ? janus_rtp_extension_id(JANUS_RTP_EXTMAP_PLAYOUT_DELAY) : 0),
JANUS_SDP_OA_DONE);
Expand All @@ -6176,6 +6225,8 @@ static void *janus_streaming_handler(void *data) {
JANUS_SDP_OA_DIRECTION, JANUS_SDP_SENDONLY,
JANUS_SDP_OA_EXTENSION, JANUS_RTP_EXTMAP_MID, janus_rtp_extension_id(JANUS_RTP_EXTMAP_MID),
JANUS_SDP_OA_EXTENSION, JANUS_RTP_EXTMAP_ABS_SEND_TIME, janus_rtp_extension_id(JANUS_RTP_EXTMAP_ABS_SEND_TIME),
JANUS_SDP_OA_EXTENSION, JANUS_RTP_EXTMAP_ABS_CAPTURE_TIME,
(session->abscapturetime_src_ext_id > 0 ? janus_rtp_extension_id(JANUS_RTP_EXTMAP_ABS_CAPTURE_TIME) : 0),
JANUS_SDP_OA_EXTENSION, JANUS_RTP_EXTMAP_PLAYOUT_DELAY,
(session->playoutdelay_ext ? janus_rtp_extension_id(JANUS_RTP_EXTMAP_PLAYOUT_DELAY) : 0),
JANUS_SDP_OA_DONE);
Expand All @@ -6192,6 +6243,8 @@ static void *janus_streaming_handler(void *data) {
JANUS_SDP_OA_DIRECTION, JANUS_SDP_SENDONLY,
JANUS_SDP_OA_EXTENSION, JANUS_RTP_EXTMAP_MID, janus_rtp_extension_id(JANUS_RTP_EXTMAP_MID),
JANUS_SDP_OA_EXTENSION, JANUS_RTP_EXTMAP_ABS_SEND_TIME, janus_rtp_extension_id(JANUS_RTP_EXTMAP_ABS_SEND_TIME),
JANUS_SDP_OA_EXTENSION, JANUS_RTP_EXTMAP_ABS_CAPTURE_TIME,
(session->abscapturetime_src_ext_id > 0 ? janus_rtp_extension_id(JANUS_RTP_EXTMAP_ABS_CAPTURE_TIME) : 0),
JANUS_SDP_OA_EXTENSION, JANUS_RTP_EXTMAP_PLAYOUT_DELAY,
(session->playoutdelay_ext ? janus_rtp_extension_id(JANUS_RTP_EXTMAP_PLAYOUT_DELAY) : 0),
JANUS_SDP_OA_DONE);
Expand Down Expand Up @@ -6386,6 +6439,7 @@ static void *janus_streaming_handler(void *data) {
JANUS_SDP_OA_DIRECTION, JANUS_SDP_SENDONLY,
JANUS_SDP_OA_ACCEPT_EXTMAP, JANUS_RTP_EXTMAP_MID,
JANUS_SDP_OA_ACCEPT_EXTMAP, JANUS_RTP_EXTMAP_ABS_SEND_TIME,
JANUS_SDP_OA_ACCEPT_EXTMAP, JANUS_RTP_EXTMAP_ABS_CAPTURE_TIME,
JANUS_SDP_OA_ACCEPT_EXTMAP, JANUS_RTP_EXTMAP_PLAYOUT_DELAY,
JANUS_SDP_OA_DONE);
/* Done */
Expand Down Expand Up @@ -6461,6 +6515,8 @@ static void *janus_streaming_handler(void *data) {
session->e2ee = source->e2ee;
/* Also check if we have to offer the playout-delay extension */
session->playoutdelay_ext = source->playoutdelay_ext;
/* Also check if we have to offer the abs-capture-time extension */
session->abscapturetime_src_ext_id = source->abscapturetime_src_ext_id;
/* Accept the m-line */
janus_sdp_generate_answer_mline(parsed_sdp, answer, m,
JANUS_SDP_OA_MLINE, m->type,
Expand All @@ -6470,6 +6526,7 @@ static void *janus_streaming_handler(void *data) {
JANUS_SDP_OA_DIRECTION, JANUS_SDP_SENDONLY,
JANUS_SDP_OA_ACCEPT_EXTMAP, JANUS_RTP_EXTMAP_MID,
JANUS_SDP_OA_ACCEPT_EXTMAP, JANUS_RTP_EXTMAP_ABS_SEND_TIME,
JANUS_SDP_OA_ACCEPT_EXTMAP, JANUS_RTP_EXTMAP_ABS_CAPTURE_TIME,
JANUS_SDP_OA_ACCEPT_EXTMAP, JANUS_RTP_EXTMAP_PLAYOUT_DELAY,
JANUS_SDP_OA_DONE);
/* Done */
Expand Down Expand Up @@ -7547,7 +7604,8 @@ janus_streaming_rtp_source_stream *janus_streaming_create_rtp_source_stream(

janus_streaming_mountpoint *janus_streaming_create_rtp_source(
uint64_t id, char *id_str, char *name, char *desc, char *metadata,
GList *media, int srtpsuite, char *srtpcrypto, int threads, int rtp_collision, gboolean e2ee, gboolean playoutdelay_ext) {
GList *media, int srtpsuite, char *srtpcrypto, int threads, int rtp_collision,
gboolean e2ee, gboolean playoutdelay_ext, int abscapturetime_src_ext_id) {
char id_num[30];
if(!string_ids) {
g_snprintf(id_num, sizeof(id_num), "%"SCNu64, id);
Expand Down Expand Up @@ -7673,6 +7731,7 @@ janus_streaming_mountpoint *janus_streaming_create_rtp_source(
live_rtp_source->rtp_collision = rtp_collision;
live_rtp_source->e2ee = e2ee;
live_rtp_source->playoutdelay_ext = playoutdelay_ext;
live_rtp_source->abscapturetime_src_ext_id = abscapturetime_src_ext_id;
live_rtp->source = live_rtp_source;
live_rtp->source_destroy = (GDestroyNotify) janus_streaming_rtp_source_free;
live_rtp->viewers = NULL;
Expand Down Expand Up @@ -10192,6 +10251,13 @@ static void janus_streaming_relay_rtp_packet(gpointer data, gpointer user_data)
rtp.extensions.min_delay = s->min_delay;
rtp.extensions.max_delay = s->max_delay;
}
if(session->abscapturetime_src_ext_id > 0) {
uint64_t abs_ts = 0;
if(janus_rtp_header_extension_parse_abs_capture_time((char *)packet->data, packet->length,
session->abscapturetime_src_ext_id, &abs_ts) == 0) {
rtp.extensions.abs_capture_ts = abs_ts;
}
}
if(gateway != NULL)
gateway->relay_rtp(session->handle, &rtp);
if(override_mark_bit && !has_marker_bit) {
Expand Down Expand Up @@ -10267,6 +10333,13 @@ static void janus_streaming_relay_rtp_packet(gpointer data, gpointer user_data)
rtp.extensions.min_delay = s->min_delay;
rtp.extensions.max_delay = s->max_delay;
}
if(session->abscapturetime_src_ext_id > 0) {
uint64_t abs_ts = 0;
if(janus_rtp_header_extension_parse_abs_capture_time((char *)packet->data, packet->length,
session->abscapturetime_src_ext_id, &abs_ts) == 0) {
rtp.extensions.abs_capture_ts = abs_ts;
}
}
if(gateway != NULL)
gateway->relay_rtp(session->handle, &rtp);
/* Restore the timestamp and sequence number to what the publisher set them to */
Expand All @@ -10288,6 +10361,13 @@ static void janus_streaming_relay_rtp_packet(gpointer data, gpointer user_data)
rtp.extensions.min_delay = s->min_delay;
rtp.extensions.max_delay = s->max_delay;
}
if(session->abscapturetime_src_ext_id > 0) {
uint64_t abs_ts = 0;
if(janus_rtp_header_extension_parse_abs_capture_time((char *)packet->data, packet->length,
session->abscapturetime_src_ext_id, &abs_ts) == 0) {
rtp.extensions.abs_capture_ts = abs_ts;
}
}
if(gateway != NULL)
gateway->relay_rtp(session->handle, &rtp);
/* Restore the timestamp and sequence number to what the video source set them to */
Expand Down
2 changes: 2 additions & 0 deletions src/rtp.c
Expand Up @@ -504,6 +504,8 @@ int janus_rtp_extension_id(const char *type) {
return 14;
else if(!strcasecmp(type, JANUS_RTP_EXTMAP_ABS_SEND_TIME))
return 2;
else if(!strcasecmp(type, JANUS_RTP_EXTMAP_ABS_CAPTURE_TIME))
return 7;
else if(!strcasecmp(type, JANUS_RTP_EXTMAP_VIDEO_ORIENTATION))
return 13;
else if(!strcasecmp(type, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC))
Expand Down

0 comments on commit 2fa046b

Please sign in to comment.