summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--addons/chan_ooh323.c4
-rw-r--r--addons/ooh323c/src/context.c1
-rw-r--r--addons/ooh323c/src/memheap.c11
-rw-r--r--addons/ooh323c/src/ooCalls.c3
-rw-r--r--addons/ooh323c/src/ooCapability.c8
-rw-r--r--addons/ooh323c/src/ooGkClient.c3
-rw-r--r--addons/ooh323c/src/ooh245.c2
-rw-r--r--addons/ooh323c/src/ooq931.c6
-rw-r--r--apps/app_confbridge.c7
-rw-r--r--apps/confbridge/conf_config_parser.c84
-rw-r--r--apps/confbridge/include/confbridge.h3
-rw-r--r--bridges/bridge_softmix.c250
-rw-r--r--bridges/bridge_softmix/include/bridge_softmix_internal.h14
-rw-r--r--channels/chan_vpb.cc125
-rw-r--r--configs/samples/confbridge.conf.sample4
-rwxr-xr-xcontrib/scripts/refcounter.py23
-rwxr-xr-xcontrib/scripts/spandspflow2pcap.py2
-rwxr-xr-xcontrib/scripts/voicemailpwcheck.py10
-rw-r--r--doc/.gitignore1
-rw-r--r--include/asterisk/bridge.h33
-rw-r--r--include/asterisk/res_pjsip.h22
-rw-r--r--include/asterisk/stasis_bridges.h4
-rw-r--r--include/asterisk/utils.h13
-rw-r--r--main/bridge.c13
-rw-r--r--main/stasis_bridges.c3
-rw-r--r--res/res_pjsip/location.c5
-rw-r--r--res/res_pjsip/pjsip_configuration.c2
-rw-r--r--res/res_pjsip_outbound_registration.c2
-rw-r--r--res/res_pjsip_pubsub.c45
-rw-r--r--res/res_pjsip_registrar.c34
-rw-r--r--rest-api-templates/api.wiki.mustache2
-rw-r--r--rest-api-templates/ari_resource.h.mustache6
-rw-r--r--rest-api-templates/asterisk_processor.py8
-rwxr-xr-xrest-api-templates/make_ari_stubs.py5
-rw-r--r--rest-api-templates/res_ari_resource.c.mustache6
-rw-r--r--rest-api-templates/swagger_model.py19
-rw-r--r--rest-api-templates/transform.py9
-rw-r--r--utils/Makefile3
39 files changed, 629 insertions, 168 deletions
diff --git a/Makefile b/Makefile
index 2c10dc497..1cd96650f 100644
--- a/Makefile
+++ b/Makefile
@@ -511,7 +511,7 @@ else
@echo "<!DOCTYPE docs SYSTEM \"appdocsxml.dtd\">" >> $@
@echo "<?xml-stylesheet type=\"text/xsl\" href=\"appdocsxml.xslt\"?>" >> $@
@echo "<docs xmlns:xi=\"http://www.w3.org/2001/XInclude\">" >> $@
- @for x in $(MOD_SUBDIRS); do \
+ @for x in $(filter-out third-party,$(MOD_SUBDIRS)); do \
printf "$$x " ; \
for i in `find $$x -name '*.c'`; do \
$(PYTHON) build_tools/get_documentation.py < $$i >> $@ ; \
diff --git a/addons/chan_ooh323.c b/addons/chan_ooh323.c
index ffdbf6721..24cc65c0f 100644
--- a/addons/chan_ooh323.c
+++ b/addons/chan_ooh323.c
@@ -5053,9 +5053,7 @@ struct ast_frame *ooh323_rtp_read(struct ast_channel *ast, struct ooh323_pvt *p)
ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", ast_channel_name(p->owner),target_context);
}
p->faxdetected = 1;
- if (dfr) {
- ast_frfree(dfr);
- }
+ ast_frfree(dfr);
return &ast_null_frame;
}
}
diff --git a/addons/ooh323c/src/context.c b/addons/ooh323c/src/context.c
index bc3db4387..c1e2003a2 100644
--- a/addons/ooh323c/src/context.c
+++ b/addons/ooh323c/src/context.c
@@ -164,6 +164,7 @@ OOCTXT* newContext ()
/* ASN1CRTFREE0 (pctxt); */
ast_free(pctxt);
pctxt = 0;
+ return (pctxt);
}
pctxt->flags |= ASN1DYNCTXT;
}
diff --git a/addons/ooh323c/src/memheap.c b/addons/ooh323c/src/memheap.c
index 4bcbd7a3d..4020261bb 100644
--- a/addons/ooh323c/src/memheap.c
+++ b/addons/ooh323c/src/memheap.c
@@ -623,7 +623,7 @@ void memHeapFreePtr (void** ppvMemHeap, void* mem_p)
}
}
}
- if (!ISLAST (pElem) && ISFREE (GETNEXT (pElem))) {
+ if (pElem && !ISLAST (pElem) && ISFREE (GETNEXT (pElem))) {
OSMemElemDescr* nextelem_p = GETNEXT (pElem);
/* +1 because the OSMemElemDescr has size ONE unit (8 bytes) */
@@ -638,7 +638,7 @@ void memHeapFreePtr (void** ppvMemHeap, void* mem_p)
}
/* correct the prevOff field of next element */
- if (!ISLAST (pElem)) {
+ if (pElem && !ISLAST (pElem)) {
OSMemElemDescr* nextelem_p = GETNEXT (pElem);
pElem_prevOff (nextelem_p) = QOFFSETOF (nextelem_p, pElem);
}
@@ -686,7 +686,7 @@ static void initNewFreeElement (OSMemBlk* pMemBlk,
}
pNextElem = GETNEXT (pNewElem);
- if (ISFREE (pNextElem)) {
+ if (pNextElem && ISFREE (pNextElem)) {
/* if the next elem is free, then unite them together */
@@ -820,7 +820,7 @@ void* memHeapRealloc (void** ppvMemHeap, void* mem_p, int nbytes_)
/* look for free element after pElem */
pNextElem = GETNEXT (pElem);
- if (ISFREE (pNextElem)) {
+ if (pNextElem && ISFREE (pNextElem)) {
/* +1 'cos sizeof (OSMemElemDescr) == 1 unit */
sumSize += pElem_nunits (pNextElem) + 1;
freeMem++;
@@ -1062,7 +1062,7 @@ void memHeapAddRef (void** ppvMemHeap)
void memHeapRelease (void** ppvMemHeap)
{
OSMemHeap** ppMemHeap = (OSMemHeap**)ppvMemHeap;
- OSMemHeap* pMemHeap = *ppMemHeap;
+ OSMemHeap* pMemHeap;
if (ppMemHeap != 0 && *ppMemHeap != 0 && --(*ppMemHeap)->refCnt == 0) {
OSMemLink* pMemLink, *pMemLink2;
@@ -1080,6 +1080,7 @@ void memHeapRelease (void** ppvMemHeap)
}
if ((*ppMemHeap)->flags & RT_MH_FREEHEAPDESC) {
+ pMemHeap = *ppMemHeap;
ast_mutex_destroy(&pMemHeap->pLock);
ast_free(*ppMemHeap);
}
diff --git a/addons/ooh323c/src/ooCalls.c b/addons/ooh323c/src/ooCalls.c
index 3097c6d28..15ab3258f 100644
--- a/addons/ooh323c/src/ooCalls.c
+++ b/addons/ooh323c/src/ooCalls.c
@@ -805,8 +805,7 @@ int ooAddMediaInfo(OOH323CallData *call, OOMediaInfo mediaInfo)
if(!call)
{
- OOTRACEERR3("Error:Invalid 'call' param for ooAddMediaInfo.(%s, %s)\n",
- call->callType, call->callToken);
+ OOTRACEERR1("Error:Invalid 'call' param for ooAddMediaInfo.\n");
return OO_FAILED;
}
newMediaInfo = (OOMediaInfo*) memAlloc(call->pctxt, sizeof(OOMediaInfo));
diff --git a/addons/ooh323c/src/ooCapability.c b/addons/ooh323c/src/ooCapability.c
index 731478346..0796c46bf 100644
--- a/addons/ooh323c/src/ooCapability.c
+++ b/addons/ooh323c/src/ooCapability.c
@@ -62,8 +62,6 @@ int ooCapabilityEnableDTMFCISCO
/*Dynamic RTP payload type range is from 96 - 127 */
if(dynamicRTPPayloadType >= 96 && dynamicRTPPayloadType <= 127)
gcDynamicRTPPayloadType = dynamicRTPPayloadType;
- else
- call->dtmfcodec = dynamicRTPPayloadType;
}
else{
call->dtmfmode |= OO_CAP_DTMF_CISCO;
@@ -623,8 +621,7 @@ int ooCapabilityAddT38Capability
else pctxt = call->pctxt;
epCap = (ooH323EpCapability*)memAllocZ(pctxt, sizeof(ooH323EpCapability));
- params = (OOCapParams*) memAlloc(pctxt, sizeof(OOCapParams));
- memset(params, 0 , sizeof(OOCapParams));
+ params = (OOCapParams*) memAllocZ(pctxt, sizeof(OOCapParams));
if(!epCap || !params)
{
OOTRACEERR1("ERROR: Memory - ooCapabilityAddT38Capability - "
@@ -808,8 +805,7 @@ void* ooCapabilityCreateDTMFCapability(int cap, int dtmfcodec, OOCTXT *pctxt)
}
memset(pATECap, 0, sizeof(H245AudioTelephonyEventCapability));
pATECap->dynamicRTPPayloadType = dtmfcodec;
- events = (char*)memAlloc(pctxt, strlen("0-16")+1);
- memset(events, 0, strlen("0-16")+1);
+ events = (char*)memAllocZ(pctxt, strlen("0-16")+1);
if(!events)
{
OOTRACEERR1("Error:Memory - ooCapabilityCreateDTMFCapability - events\n");
diff --git a/addons/ooh323c/src/ooGkClient.c b/addons/ooh323c/src/ooGkClient.c
index a307f4eef..0168ee7de 100644
--- a/addons/ooh323c/src/ooGkClient.c
+++ b/addons/ooh323c/src/ooGkClient.c
@@ -2332,9 +2332,8 @@ int ooGkClientSendIRR
pIRR->m.perCallInfoPresent = TRUE;
perCallInfo =
- (H225InfoRequestResponse_perCallInfo_element *)memAlloc(pctxt,
+ (H225InfoRequestResponse_perCallInfo_element *)memAllocZ(pctxt,
sizeof(H225InfoRequestResponse_perCallInfo_element));
- memset(perCallInfo, 0, sizeof(H225InfoRequestResponse_perCallInfo_element));
if(!perCallInfo)
{
diff --git a/addons/ooh323c/src/ooh245.c b/addons/ooh323c/src/ooh245.c
index adff91790..fe8ff28e0 100644
--- a/addons/ooh323c/src/ooh245.c
+++ b/addons/ooh323c/src/ooh245.c
@@ -356,7 +356,6 @@ int ooSendTermCapMsg(OOH323CallData *call)
/* pctxt = &gH323ep.msgctxt; */
pctxt = call->msgctxt;
ph245msg->msgType = OOTerminalCapabilitySet;
- memset(request, 0, sizeof(H245RequestMessage));
if(request == NULL)
{
OOTRACEERR3("ERROR: No memory allocated for request message (%s, %s)\n",
@@ -364,6 +363,7 @@ int ooSendTermCapMsg(OOH323CallData *call)
return OO_FAILED;
}
+ memset(request, 0, sizeof(H245RequestMessage));
request->t = T_H245RequestMessage_terminalCapabilitySet;
request->u.terminalCapabilitySet = (H245TerminalCapabilitySet*)
memAlloc(pctxt, sizeof(H245TerminalCapabilitySet));
diff --git a/addons/ooh323c/src/ooq931.c b/addons/ooh323c/src/ooq931.c
index 1ca361c2c..01a8e4aaf 100644
--- a/addons/ooh323c/src/ooq931.c
+++ b/addons/ooh323c/src/ooq931.c
@@ -2439,8 +2439,10 @@ int ooH323HandleCallFwdRequest(OOH323CallData *call)
alias = call->pCallFwdData->aliases;
while(alias)
{
- pNewAlias = (ooAliases*) memAlloc(pctxt, sizeof(ooAliases));
- pNewAlias->value = (char*) memAlloc(pctxt, strlen(alias->value)+1);
+ pNewAlias = (ooAliases*) memAllocZ(pctxt, sizeof(ooAliases));
+ if (pNewAlias) {
+ pNewAlias->value = (char*) memAllocZ(pctxt, strlen(alias->value)+1);
+ }
if(!pNewAlias || !pNewAlias->value)
{
OOTRACEERR3("Error:Memory - ooH323HandleCallFwdRequest - "
diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c
index d8407d857..25cf2758f 100644
--- a/apps/app_confbridge.c
+++ b/apps/app_confbridge.c
@@ -1544,6 +1544,13 @@ static struct confbridge_conference *join_conference_bridge(const char *conferen
ast_bridge_set_sfu_video_mode(conference->bridge);
ast_bridge_set_video_update_discard(conference->bridge, conference->b_profile.video_update_discard);
ast_bridge_set_remb_send_interval(conference->bridge, conference->b_profile.remb_send_interval);
+ if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE)) {
+ ast_brige_set_remb_behavior(conference->bridge, AST_BRIDGE_VIDEO_SFU_REMB_AVERAGE);
+ } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_LOWEST)) {
+ ast_brige_set_remb_behavior(conference->bridge, AST_BRIDGE_VIDEO_SFU_REMB_LOWEST);
+ } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST)) {
+ ast_brige_set_remb_behavior(conference->bridge, AST_BRIDGE_VIDEO_SFU_REMB_HIGHEST);
+ }
}
/* Link it into the conference bridges container */
diff --git a/apps/confbridge/conf_config_parser.c b/apps/confbridge/conf_config_parser.c
index f9d74831c..873831911 100644
--- a/apps/confbridge/conf_config_parser.c
+++ b/apps/confbridge/conf_config_parser.c
@@ -470,6 +470,27 @@
better quality for all receivers.
</para></description>
</configOption>
+ <configOption name="remb_behavior" default="average">
+ <synopsis>Sets how REMB reports are generated from multiple sources</synopsis>
+ <description><para>
+ Sets how REMB reports are combined from multiple sources to form one. A REMB report
+ consists of information about the receiver estimated maximum bitrate. As a source
+ stream may be forwarded to multiple receivers the reports must be combined into
+ a single one which is sent to the sender.</para>
+ <enumlist>
+ <enum name="average">
+ <para>The average of all estimated maximum bitrates is taken and sent
+ to the sender.</para>
+ </enum>
+ <enum name="lowest">
+ <para>The lowest estimated maximum bitrate is forwarded to the sender.</para>
+ </enum>
+ <enum name="highest">
+ <para>The highest estimated maximum bitrate is forwarded to the sender.</para>
+ </enum>
+ </enumlist>
+ </description>
+ </configOption>
<configOption name="template">
<synopsis>When using the CONFBRIDGE dialplan function, use a bridge profile as a template for creating a new temporary profile</synopsis>
</configOption>
@@ -1652,8 +1673,10 @@ static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e,
ast_cli(a->fd,"Registration context: %s\n", b_profile.regcontext);
switch (b_profile.flags
- & (BRIDGE_OPT_VIDEO_SRC_LAST_MARKED | BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
- | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
+ & (BRIDGE_OPT_VIDEO_SRC_LAST_MARKED |
+ BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED |
+ BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER |
+ BRIDGE_OPT_VIDEO_SRC_SFU)) {
case BRIDGE_OPT_VIDEO_SRC_LAST_MARKED:
ast_cli(a->fd, "Video Mode: last_marked\n");
break;
@@ -1663,6 +1686,9 @@ static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e,
case BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER:
ast_cli(a->fd, "Video Mode: follow_talker\n");
break;
+ case BRIDGE_OPT_VIDEO_SRC_SFU:
+ ast_cli(a->fd, "Video Mode: sfu\n");
+ break;
case 0:
ast_cli(a->fd, "Video Mode: no video\n");
break;
@@ -1675,6 +1701,23 @@ static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e,
ast_cli(a->fd,"Video Update Discard: %u\n", b_profile.video_update_discard);
ast_cli(a->fd,"REMB Send Interval: %u\n", b_profile.remb_send_interval);
+ switch (b_profile.flags
+ & (BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE | BRIDGE_OPT_REMB_BEHAVIOR_LOWEST
+ | BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST)) {
+ case BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE:
+ ast_cli(a->fd, "REMB Behavior: average\n");
+ break;
+ case BRIDGE_OPT_REMB_BEHAVIOR_LOWEST:
+ ast_cli(a->fd, "REMB Behavior: lowest\n");
+ break;
+ case BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST:
+ ast_cli(a->fd, "REMB Behavior: highest\n");
+ break;
+ default:
+ ast_assert(0);
+ break;
+ }
+
ast_cli(a->fd,"sound_only_person: %s\n", conf_get_sound(CONF_SOUND_ONLY_PERSON, b_profile.sounds));
ast_cli(a->fd,"sound_only_one: %s\n", conf_get_sound(CONF_SOUND_ONLY_ONE, b_profile.sounds));
ast_cli(a->fd,"sound_has_joined: %s\n", conf_get_sound(CONF_SOUND_HAS_JOINED, b_profile.sounds));
@@ -1992,12 +2035,6 @@ static int video_mode_handler(const struct aco_option *opt, struct ast_variable
| BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER
| BRIDGE_OPT_VIDEO_SRC_SFU,
BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER);
- } else if (!strcasecmp(var->value, "none")) {
- ast_clear_flag(b_profile,
- BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
- | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
- | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER
- | BRIDGE_OPT_VIDEO_SRC_SFU);
} else if (!strcasecmp(var->value, "sfu")) {
ast_set_flags_to(b_profile,
BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
@@ -2005,6 +2042,36 @@ static int video_mode_handler(const struct aco_option *opt, struct ast_variable
| BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER
| BRIDGE_OPT_VIDEO_SRC_SFU,
BRIDGE_OPT_VIDEO_SRC_SFU);
+ } else if (!strcasecmp(var->value, "none")) {
+ ast_clear_flag(b_profile,
+ BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
+ | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
+ | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER
+ | BRIDGE_OPT_VIDEO_SRC_SFU);
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
+static int remb_behavior_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+ struct bridge_profile *b_profile = obj;
+
+ if (strcasecmp(var->name, "remb_behavior")) {
+ return -1;
+ }
+
+ ast_clear_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE |
+ BRIDGE_OPT_REMB_BEHAVIOR_LOWEST |
+ BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST);
+
+ if (!strcasecmp(var->value, "average")) {
+ ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE);
+ } else if (!strcasecmp(var->value, "lowest")) {
+ ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_LOWEST);
+ } else if (!strcasecmp(var->value, "highest")) {
+ ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST);
} else {
return -1;
}
@@ -2245,6 +2312,7 @@ int conf_load_config(void)
aco_option_register_custom(&cfg_info, "sound_", ACO_PREFIX, bridge_types, NULL, sound_option_handler, 0);
aco_option_register(&cfg_info, "video_update_discard", ACO_EXACT, bridge_types, "2000", OPT_UINT_T, 0, FLDSET(struct bridge_profile, video_update_discard));
aco_option_register(&cfg_info, "remb_send_interval", ACO_EXACT, bridge_types, "0", OPT_UINT_T, 0, FLDSET(struct bridge_profile, remb_send_interval));
+ aco_option_register_custom(&cfg_info, "remb_behavior", ACO_EXACT, bridge_types, "average", remb_behavior_handler, 0);
/* This option should only be used with the CONFBRIDGE dialplan function */
aco_option_register_custom(&cfg_info, "template", ACO_EXACT, bridge_types, NULL, bridge_template_handler, 0);
diff --git a/apps/confbridge/include/confbridge.h b/apps/confbridge/include/confbridge.h
index c2f8f9a58..0a0a5713f 100644
--- a/apps/confbridge/include/confbridge.h
+++ b/apps/confbridge/include/confbridge.h
@@ -76,6 +76,9 @@ enum bridge_profile_flags {
BRIDGE_OPT_RECORD_FILE_TIMESTAMP = (1 << 5), /*!< Set if the record file should have a timestamp appended */
BRIDGE_OPT_BINAURAL_ACTIVE = (1 << 6), /*!< Set if binaural convolution is activated */
BRIDGE_OPT_VIDEO_SRC_SFU = (1 << 7), /*!< Selective forwarding unit */
+ BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE = (1 << 8), /*!< The average of all REMB reports is sent to the sender */
+ BRIDGE_OPT_REMB_BEHAVIOR_LOWEST = (1 << 9), /*!< The lowest estimated maximum bitrate is sent to the sender */
+ BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST = (1 << 10), /*!< The highest estimated maximum bitrate is sent to the sender */
};
enum conf_menu_action_id {
diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c
index 16e1fb897..ed88b7cd5 100644
--- a/bridges/bridge_softmix.c
+++ b/bridges/bridge_softmix.c
@@ -69,6 +69,15 @@
#define SOFTBRIDGE_VIDEO_DEST_LEN strlen(SOFTBRIDGE_VIDEO_DEST_PREFIX)
#define SOFTBRIDGE_VIDEO_DEST_SEPARATOR '_'
+struct softmix_remb_collector {
+ /*! The frame which will be given to each source stream */
+ struct ast_frame frame;
+ /*! The REMB to send to the source which is collecting REMB reports */
+ struct ast_rtp_rtcp_feedback feedback;
+ /*! The maximum bitrate */
+ unsigned int bitrate;
+};
+
struct softmix_stats {
/*! Each index represents a sample rate used above the internal rate. */
unsigned int sample_rates[16];
@@ -768,6 +777,10 @@ static void softmix_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_ch
ast_stream_topology_free(sc->topology);
+ ao2_cleanup(sc->remb_collector);
+
+ AST_VECTOR_FREE(&sc->video_sources);
+
/* Drop mutex lock */
ast_mutex_destroy(&sc->lock);
@@ -1160,6 +1173,39 @@ static int softmix_bridge_write_control(struct ast_bridge *bridge, struct ast_br
/*!
* \internal
+ * \brief Determine what to do with an RTCP frame.
+ * \since 15.4.0
+ *
+ * \param bridge Which bridge is getting the frame
+ * \param bridge_channel Which channel is writing the frame.
+ * \param frame What is being written.
+ */
+static void softmix_bridge_write_rtcp(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
+{
+ struct ast_rtp_rtcp_feedback *feedback = frame->data.ptr;
+ struct softmix_channel *sc = bridge_channel->tech_pvt;
+
+ /* We only care about REMB reports right now. In the future we may be able to use sender or
+ * receiver reports to further tweak things, but not yet.
+ */
+ if (frame->subclass.integer != AST_RTP_RTCP_PSFB || feedback->fmt != AST_RTP_RTCP_FMT_REMB ||
+ bridge->softmix.video_mode.mode != AST_BRIDGE_VIDEO_MODE_SFU ||
+ !bridge->softmix.video_mode.mode_data.sfu_data.remb_send_interval) {
+ return;
+ }
+
+ /* REMB is the total estimated maximum bitrate across all streams within the session, so we store
+ * only the latest report and use it everywhere.
+ */
+ ast_mutex_lock(&sc->lock);
+ sc->remb = feedback->remb;
+ ast_mutex_unlock(&sc->lock);
+
+ return;
+}
+
+/*!
+ * \internal
* \brief Determine what to do with a frame written into the bridge.
* \since 12.0.0
*
@@ -1204,6 +1250,9 @@ static int softmix_bridge_write(struct ast_bridge *bridge, struct ast_bridge_cha
case AST_FRAME_CONTROL:
res = softmix_bridge_write_control(bridge, bridge_channel, frame);
break;
+ case AST_FRAME_RTCP:
+ softmix_bridge_write_rtcp(bridge, bridge_channel, frame);
+ break;
case AST_FRAME_BRIDGE_ACTION:
res = ast_bridge_queue_everyone_else(bridge, bridge_channel, frame);
break;
@@ -1219,6 +1268,108 @@ static int softmix_bridge_write(struct ast_bridge *bridge, struct ast_bridge_cha
return res;
}
+static void remb_collect_report(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel,
+ struct softmix_bridge_data *softmix_data, struct softmix_channel *sc)
+{
+ int i;
+ unsigned int bitrate;
+
+ /* If there are no video sources that we are a receiver of then we have noone to
+ * report REMB to.
+ */
+ if (!AST_VECTOR_SIZE(&sc->video_sources)) {
+ return;
+ }
+
+ /* We evenly divide the available maximum bitrate across the video sources
+ * to this receiver so each source gets an equal slice.
+ */
+ bitrate = (sc->remb.br_mantissa << sc->remb.br_exp) / AST_VECTOR_SIZE(&sc->video_sources);
+
+ /* If this receiver has no bitrate yet ignore it */
+ if (!bitrate) {
+ return;
+ }
+
+ for (i = 0; i < AST_VECTOR_SIZE(&sc->video_sources); ++i) {
+ struct softmix_remb_collector *collector;
+
+ /* The collector will always exist if a video source is in our list */
+ collector = AST_VECTOR_GET(&softmix_data->remb_collectors, AST_VECTOR_GET(&sc->video_sources, i));
+
+ if (!collector->bitrate) {
+ collector->bitrate = bitrate;
+ continue;
+ }
+
+ switch (bridge->softmix.video_mode.mode_data.sfu_data.remb_behavior) {
+ case AST_BRIDGE_VIDEO_SFU_REMB_AVERAGE:
+ collector->bitrate = (collector->bitrate + bitrate) / 2;
+ break;
+ case AST_BRIDGE_VIDEO_SFU_REMB_LOWEST:
+ if (bitrate < collector->bitrate) {
+ collector->bitrate = bitrate;
+ }
+ break;
+ case AST_BRIDGE_VIDEO_SFU_REMB_HIGHEST:
+ if (bitrate > collector->bitrate) {
+ collector->bitrate = bitrate;
+ }
+ break;
+ }
+ }
+
+ /* After the report is integrated we reset this to 0 in case they stop producing
+ * REMB reports.
+ */
+ sc->remb.br_mantissa = 0;
+ sc->remb.br_exp = 0;
+}
+
+static void remb_send_report(struct ast_bridge_channel *bridge_channel, struct softmix_channel *sc)
+{
+ int i;
+
+ if (!sc->remb_collector) {
+ return;
+ }
+
+ /* We always do this calculation as even when the bitrate is zero the browser
+ * still prefers it to be accurate instead of lying.
+ */
+ sc->remb_collector->feedback.remb.br_mantissa = sc->remb_collector->bitrate;
+ sc->remb_collector->feedback.remb.br_exp = 0;
+
+ /* The mantissa only has 18 bits available, so while it exceeds them we bump
+ * up the exp.
+ */
+ while (sc->remb_collector->feedback.remb.br_mantissa > 0x3ffff) {
+ sc->remb_collector->feedback.remb.br_mantissa = sc->remb_collector->feedback.remb.br_mantissa >> 1;
+ sc->remb_collector->feedback.remb.br_exp++;
+ }
+
+ for (i = 0; i < AST_VECTOR_SIZE(&bridge_channel->stream_map.to_bridge); ++i) {
+ int bridge_num = AST_VECTOR_GET(&bridge_channel->stream_map.to_bridge, i);
+
+ /* If this stream is not being provided to the bridge there can be no receivers of it
+ * so therefore no REMB reports.
+ */
+ if (bridge_num == -1) {
+ continue;
+ }
+
+ /* We need to update the frame with this stream, or else it won't be
+ * properly routed. We don't use the actual channel stream identifier as
+ * the bridging core will do the translation from bridge stream identifier to
+ * channel stream identifier.
+ */
+ sc->remb_collector->frame.stream_num = bridge_num;
+ ast_bridge_channel_queue_frame(bridge_channel, &sc->remb_collector->frame);
+ }
+
+ sc->remb_collector->bitrate = 0;
+}
+
static void gather_softmix_stats(struct softmix_stats *stats,
const struct softmix_bridge_data *softmix_data,
struct ast_bridge_channel *bridge_channel)
@@ -1440,6 +1591,7 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
struct ast_format *cur_slin = ast_format_cache_get_slin_by_rate(softmix_data->internal_rate);
unsigned int softmix_samples = SOFTMIX_SAMPLES(softmix_data->internal_rate, softmix_data->internal_mixing_interval);
unsigned int softmix_datalen = SOFTMIX_DATALEN(softmix_data->internal_rate, softmix_data->internal_mixing_interval);
+ int remb_update = 0;
if (softmix_datalen > MAX_DATALEN) {
/* This should NEVER happen, but if it does we need to know about it. Almost
@@ -1478,6 +1630,14 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
check_binaural_position_change(bridge, softmix_data);
#endif
+ /* If we need to do a REMB update to all video sources then do so */
+ if (bridge->softmix.video_mode.mode == AST_BRIDGE_VIDEO_MODE_SFU &&
+ bridge->softmix.video_mode.mode_data.sfu_data.remb_send_interval &&
+ ast_tvdiff_ms(ast_tvnow(), softmix_data->last_remb_update) > bridge->softmix.video_mode.mode_data.sfu_data.remb_send_interval) {
+ remb_update = 1;
+ softmix_data->last_remb_update = ast_tvnow();
+ }
+
/* Go through pulling audio from each factory that has it available */
AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
struct softmix_channel *sc = bridge_channel->tech_pvt;
@@ -1512,6 +1672,9 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
#endif
mixing_array.used_entries++;
}
+ if (remb_update) {
+ remb_collect_report(bridge, bridge_channel, softmix_data, sc);
+ }
ast_mutex_unlock(&sc->lock);
}
@@ -1562,6 +1725,10 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
/* A frame is now ready for the channel. */
ast_bridge_channel_queue_frame(bridge_channel, &sc->write_frame);
+
+ if (remb_update) {
+ remb_send_report(bridge_channel, sc);
+ }
}
update_all_rates = 0;
@@ -1688,6 +1855,8 @@ static void softmix_bridge_data_destroy(struct softmix_bridge_data *softmix_data
}
ast_mutex_destroy(&softmix_data->lock);
ast_cond_destroy(&softmix_data->cond);
+ AST_VECTOR_RESET(&softmix_data->remb_collectors, ao2_cleanup);
+ AST_VECTOR_FREE(&softmix_data->remb_collectors);
ast_free(softmix_data);
}
@@ -1718,6 +1887,8 @@ static int softmix_bridge_create(struct ast_bridge *bridge)
softmix_data->internal_mixing_interval);
#endif
+ AST_VECTOR_INIT(&softmix_data->remb_collectors, 0);
+
bridge->tech_pvt = softmix_data;
/* Start the mixing thread. */
@@ -1814,7 +1985,10 @@ static void map_source_to_destinations(const char *source_stream_name, const cha
stream = ast_stream_topology_get_stream(topology, i);
if (is_video_dest(stream, source_channel_name, source_stream_name)) {
+ struct softmix_channel *sc = participant->tech_pvt;
+
AST_VECTOR_REPLACE(&participant->stream_map.to_channel, bridge_stream_position, i);
+ AST_VECTOR_APPEND(&sc->video_sources, bridge_stream_position);
break;
}
}
@@ -1824,6 +1998,58 @@ static void map_source_to_destinations(const char *source_stream_name, const cha
}
/*!
+ * \brief Allocate a REMB collector
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+static struct softmix_remb_collector *remb_collector_alloc(void)
+{
+ struct softmix_remb_collector *collector;
+
+ collector = ao2_alloc_options(sizeof(*collector), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (!collector) {
+ return NULL;
+ }
+
+ collector->frame.frametype = AST_FRAME_RTCP;
+ collector->frame.subclass.integer = AST_RTP_RTCP_PSFB;
+ collector->feedback.fmt = AST_RTP_RTCP_FMT_REMB;
+ collector->frame.data.ptr = &collector->feedback;
+ collector->frame.datalen = sizeof(collector->feedback);
+
+ return collector;
+}
+
+/*!
+ * \brief Setup REMB collection for a particular bridge stream and channel.
+ *
+ * \param bridge The bridge
+ * \param bridge_channel Channel that is collecting REMB information
+ * \param bridge_stream_position The slot in the bridge where source video comes from
+ */
+static void remb_enable_collection(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel,
+ size_t bridge_stream_position)
+{
+ struct softmix_channel *sc = bridge_channel->tech_pvt;
+ struct softmix_bridge_data *softmix_data = bridge->tech_pvt;
+
+ if (!sc->remb_collector) {
+ sc->remb_collector = remb_collector_alloc();
+ if (!sc->remb_collector) {
+ /* This is not fatal. Things will still continue to work but we won't
+ * produce a REMB report to the sender.
+ */
+ return;
+ }
+ }
+
+ if (AST_VECTOR_REPLACE(&softmix_data->remb_collectors, bridge_stream_position, ao2_bump(sc->remb_collector))) {
+ ao2_ref(sc->remb_collector, -1);
+ }
+}
+
+/*!
* \brief stream_topology_changed callback
*
* For most video modes, nothing beyond the ordinary is required.
@@ -1835,9 +2061,12 @@ static void map_source_to_destinations(const char *source_stream_name, const cha
*/
static void softmix_bridge_stream_topology_changed(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
{
+ struct softmix_bridge_data *softmix_data = bridge->tech_pvt;
+ struct softmix_channel *sc;
struct ast_bridge_channel *participant;
struct ast_vector_int media_types;
int nths[AST_MEDIA_TYPE_END] = {0};
+ int idx;
switch (bridge->softmix.video_mode.mode) {
case AST_BRIDGE_VIDEO_MODE_NONE:
@@ -1852,11 +2081,25 @@ static void softmix_bridge_stream_topology_changed(struct ast_bridge *bridge, st
AST_VECTOR_INIT(&media_types, AST_MEDIA_TYPE_END);
+ /* The bridge stream identifiers may change, so reset the mapping for them.
+ * When channels end up getting added back in they'll reuse their existing
+ * collector and won't need to allocate a new one (unless they were just added).
+ */
+ for (idx = 0; idx < AST_VECTOR_SIZE(&softmix_data->remb_collectors); ++idx) {
+ ao2_cleanup(AST_VECTOR_GET(&softmix_data->remb_collectors, idx));
+ AST_VECTOR_REPLACE(&softmix_data->remb_collectors, idx, NULL);
+ }
+
/* First traversal: re-initialize all of the participants' stream maps */
AST_LIST_TRAVERSE(&bridge->channels, participant, entry) {
ast_bridge_channel_lock(participant);
+
AST_VECTOR_RESET(&participant->stream_map.to_channel, AST_VECTOR_ELEM_CLEANUP_NOOP);
AST_VECTOR_RESET(&participant->stream_map.to_bridge, AST_VECTOR_ELEM_CLEANUP_NOOP);
+
+ sc = participant->tech_pvt;
+ AST_VECTOR_RESET(&sc->video_sources, AST_VECTOR_ELEM_CLEANUP_NOOP);
+
ast_bridge_channel_unlock(participant);
}
@@ -1897,7 +2140,12 @@ static void softmix_bridge_stream_topology_changed(struct ast_bridge *bridge, st
if (is_video_source(stream)) {
AST_VECTOR_APPEND(&media_types, AST_MEDIA_TYPE_VIDEO);
AST_VECTOR_REPLACE(&participant->stream_map.to_bridge, i, AST_VECTOR_SIZE(&media_types) - 1);
- AST_VECTOR_REPLACE(&participant->stream_map.to_channel, AST_VECTOR_SIZE(&media_types) - 1, -1);
+ /*
+ * There are cases where we need to bidirectionally send frames, such as for REMB reports
+ * so we also map back to the channel.
+ */
+ AST_VECTOR_REPLACE(&participant->stream_map.to_channel, AST_VECTOR_SIZE(&media_types) - 1, i);
+ remb_enable_collection(bridge, participant, AST_VECTOR_SIZE(&media_types) - 1);
/*
* Unlock the channel and participant to prevent
* potential deadlock in map_source_to_destinations().
diff --git a/bridges/bridge_softmix/include/bridge_softmix_internal.h b/bridges/bridge_softmix/include/bridge_softmix_internal.h
index f842acb5e..3aa90915d 100644
--- a/bridges/bridge_softmix/include/bridge_softmix_internal.h
+++ b/bridges/bridge_softmix/include/bridge_softmix_internal.h
@@ -50,6 +50,8 @@
#include "asterisk/astobj2.h"
#include "asterisk/timing.h"
#include "asterisk/translate.h"
+#include "asterisk/rtp_engine.h"
+#include "asterisk/vector.h"
#ifdef BINAURAL_RENDERING
#include <fftw3.h>
@@ -124,6 +126,8 @@ struct video_follow_talker_data {
int energy_average;
};
+struct softmix_remb_collector;
+
/*! \brief Structure which contains per-channel mixing information */
struct softmix_channel {
/*! Lock to protect this structure */
@@ -169,6 +173,12 @@ struct softmix_channel {
struct video_follow_talker_data video_talker;
/*! The ideal stream topology for the channel */
struct ast_stream_topology *topology;
+ /*! The latest REMB report from this participant */
+ struct ast_rtp_rtcp_feedback_remb remb;
+ /*! The REMB collector for this channel, collects REMB from all video receivers */
+ struct softmix_remb_collector *remb_collector;
+ /*! The bridge streams which are feeding us video sources */
+ AST_VECTOR(, int) video_sources;
};
struct softmix_bridge_data {
@@ -202,6 +212,10 @@ struct softmix_bridge_data {
unsigned int binaural_init;
/*! The last time a video update was sent into the bridge */
struct timeval last_video_update;
+ /*! The last time a REMB frame was sent to each source of video */
+ struct timeval last_remb_update;
+ /*! Per-bridge stream REMB collectors, which flow back to video source */
+ AST_VECTOR(, struct softmix_remb_collector *) remb_collectors;
};
struct softmix_mixing_array {
diff --git a/channels/chan_vpb.cc b/channels/chan_vpb.cc
index 1736cc6b2..7fdb9edb7 100644
--- a/channels/chan_vpb.cc
+++ b/channels/chan_vpb.cc
@@ -108,7 +108,6 @@ extern "C" {
#endif
/**/
-static const char desc[] = "VoiceTronix V6PCI/V12PCI/V4PCI API Support";
static const char tdesc[] = "Standard VoiceTronix API Driver";
static const char config[] = "vpb.conf";
@@ -360,71 +359,71 @@ static int vpb_indicate(struct ast_channel *ast, int condition, const void *data
static int vpb_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
static struct ast_channel_tech vpb_tech = {
- type: "vpb",
- description: tdesc,
- capabilities: NULL,
- properties: 0,
- requester: vpb_request,
- requester_with_stream_topology: NULL,
- devicestate: NULL,
- presencestate: NULL,
- send_digit_begin: vpb_digit_begin,
- send_digit_end: vpb_digit_end,
- call: vpb_call,
- hangup: vpb_hangup,
- answer: vpb_answer,
- read: vpb_read,
- read_stream: NULL,
- write: vpb_write,
- write_stream: NULL,
- send_text: NULL,
- send_image: NULL,
- send_html: NULL,
- exception: NULL,
- early_bridge: NULL,
- indicate: vpb_indicate,
- fixup: vpb_fixup,
- setoption: NULL,
- queryoption: NULL,
- transfer: NULL,
- write_video: NULL,
- write_text: NULL,
- func_channel_read: NULL,
- func_channel_write: NULL,
+ .type = "vpb",
+ .description = tdesc,
+ .capabilities = NULL,
+ .properties = 0,
+ .requester = vpb_request,
+ .requester_with_stream_topology = NULL,
+ .devicestate = NULL,
+ .presencestate = NULL,
+ .send_digit_begin = vpb_digit_begin,
+ .send_digit_end = vpb_digit_end,
+ .call = vpb_call,
+ .hangup = vpb_hangup,
+ .answer = vpb_answer,
+ .read = vpb_read,
+ .read_stream = NULL,
+ .write = vpb_write,
+ .write_stream = NULL,
+ .send_text = NULL,
+ .send_image = NULL,
+ .send_html = NULL,
+ .exception = NULL,
+ .early_bridge = NULL,
+ .indicate = vpb_indicate,
+ .fixup = vpb_fixup,
+ .setoption = NULL,
+ .queryoption = NULL,
+ .transfer = NULL,
+ .write_video = NULL,
+ .write_text = NULL,
+ .func_channel_read = NULL,
+ .func_channel_write = NULL,
};
static struct ast_channel_tech vpb_tech_indicate = {
- type: "vpb",
- description: tdesc,
- capabilities: NULL,
- properties: 0,
- requester: vpb_request,
- requester_with_stream_topology: NULL,
- devicestate: NULL,
- presencestate: NULL,
- send_digit_begin: vpb_digit_begin,
- send_digit_end: vpb_digit_end,
- call: vpb_call,
- hangup: vpb_hangup,
- answer: vpb_answer,
- read: vpb_read,
- read_stream: NULL,
- write: vpb_write,
- write_stream: NULL,
- send_text: NULL,
- send_image: NULL,
- send_html: NULL,
- exception: NULL,
- early_bridge: NULL,
- indicate: NULL,
- fixup: vpb_fixup,
- setoption: NULL,
- queryoption: NULL,
- transfer: NULL,
- write_video: NULL,
- write_text: NULL,
- func_channel_read: NULL,
- func_channel_write: NULL,
+ .type = "vpb",
+ .description = tdesc,
+ .capabilities = NULL,
+ .properties = 0,
+ .requester = vpb_request,
+ .requester_with_stream_topology = NULL,
+ .devicestate = NULL,
+ .presencestate = NULL,
+ .send_digit_begin = vpb_digit_begin,
+ .send_digit_end = vpb_digit_end,
+ .call = vpb_call,
+ .hangup = vpb_hangup,
+ .answer = vpb_answer,
+ .read = vpb_read,
+ .read_stream = NULL,
+ .write = vpb_write,
+ .write_stream = NULL,
+ .send_text = NULL,
+ .send_image = NULL,
+ .send_html = NULL,
+ .exception = NULL,
+ .early_bridge = NULL,
+ .indicate = NULL,
+ .fixup = vpb_fixup,
+ .setoption = NULL,
+ .queryoption = NULL,
+ .transfer = NULL,
+ .write_video = NULL,
+ .write_text = NULL,
+ .func_channel_read = NULL,
+ .func_channel_write = NULL,
};
#if defined(VPB_NATIVE_BRIDGING)
diff --git a/configs/samples/confbridge.conf.sample b/configs/samples/confbridge.conf.sample
index 4028593d2..8b276cdb8 100644
--- a/configs/samples/confbridge.conf.sample
+++ b/configs/samples/confbridge.conf.sample
@@ -239,6 +239,10 @@ type=bridge
; A REMB frame contains receiver estimated maximum bitrate information. By creating a combined
; frame and sending it to the sources of video the sender can be influenced on what bitrate
; they choose allowing a better experience for the receivers. This defaults to 0, or disabled.
+;remb_behavior=average ; How the combined REMB report for an SFU video bridge is constructed. If set to "average" then
+ ; the estimated maximum bitrate of each receiver is used to construct an average bitrate. If
+ ; set to "lowest" the lowest maximum bitrate is forwarded to the sender. If set to "highest"
+ ; the highest maximum bitrate is forwarded to the sender. This defaults to "average".
; All sounds in the conference are customizable using the bridge profile options below.
; Simply state the option followed by the filename or full path of the filename after
diff --git a/contrib/scripts/refcounter.py b/contrib/scripts/refcounter.py
index 1f4b37517..de3cda051 100755
--- a/contrib/scripts/refcounter.py
+++ b/contrib/scripts/refcounter.py
@@ -18,6 +18,7 @@
Matt Jordan <mjordan@digium.com>
"""
+from __future__ import print_function
import sys
import os
@@ -35,8 +36,8 @@ def parse_line(line):
"""
tokens = line.strip().split(',', 7)
if len(tokens) < 8:
- print "ERROR: ref debug line '%s' contains fewer tokens than " \
- "expected: %d" % (line.strip(), len(tokens))
+ print("ERROR: ref debug line '%s' contains fewer tokens than "
+ "expected: %d" % (line.strip(), len(tokens)))
return None
processed_line = {'addr': tokens[0],
@@ -142,7 +143,7 @@ def process_file(options):
del current_objects[obj]
if options.leaks:
- for key, lines in current_objects.iteritems():
+ for (key, lines) in current_objects.items():
leaked_objects.append((key, lines))
return (finished_objects, invalid_objects, leaked_objects, skewed_objects)
@@ -156,13 +157,13 @@ def print_objects(objects, prefix=""):
this object
"""
- print "======== %s Objects ========" % prefix
- print "\n"
+ print("======== %s Objects ========" % prefix)
+ print("\n")
for obj in objects:
- print "==== %s Object %s history ====" % (prefix, obj[0])
+ print("==== %s Object %s history ====" % (prefix, obj[0]))
for line in obj[1]['log']:
- print line
- print "\n"
+ print(line)
+ print("\n")
def main(argv=None):
@@ -198,11 +199,11 @@ def main(argv=None):
if not options.invalid and not options.leaks and not options.normal \
and not options.skewed:
- print >>sys.stderr, "All options disabled"
+ print("All options disabled", file=sys.stderr)
return -1
if not os.path.isfile(options.filepath):
- print >>sys.stderr, "File not found: %s" % options.filepath
+ print("File not found: %s" % options.filepath, file=sys.stderr)
return -1
try:
@@ -227,7 +228,7 @@ def main(argv=None):
print_objects(finished_objects, "Finalized")
except (KeyboardInterrupt, SystemExit, IOError):
- print >>sys.stderr, "File processing cancelled"
+ print("File processing cancelled", file=sys.stderr)
return -1
return ret_code
diff --git a/contrib/scripts/spandspflow2pcap.py b/contrib/scripts/spandspflow2pcap.py
index a6546b693..7c403f105 100755
--- a/contrib/scripts/spandspflow2pcap.py
+++ b/contrib/scripts/spandspflow2pcap.py
@@ -119,7 +119,7 @@ class FaxPcap(object):
else:
self.date += timedelta(microseconds=9000)
- print seqno, '\t', self.date + self.dateoff
+ print(seqno, '\t', self.date + self.dateoff)
# Make packet.
packet, prev_data = self.data2packet(self.date + self.dateoff,
diff --git a/contrib/scripts/voicemailpwcheck.py b/contrib/scripts/voicemailpwcheck.py
index d7a66d4b9..452255c35 100755
--- a/contrib/scripts/voicemailpwcheck.py
+++ b/contrib/scripts/voicemailpwcheck.py
@@ -46,20 +46,20 @@ mailbox, context, old_pw, new_pw = sys.argv[1:5]
# Enforce a password length of at least 6 characters
if len(new_pw) < REQUIRED_LENGTH:
- print "INVALID: Password is too short (%d) - must be at least %d" % \
- (len(new_pw), REQUIRED_LENGTH)
+ print("INVALID: Password is too short (%d) - must be at least %d" % \
+ (len(new_pw), REQUIRED_LENGTH))
sys.exit(0)
for regex, error in REGEX_BLACKLIST:
if re.search(regex, new_pw):
- print "INVALID: %s" % error
+ print("INVALID: %s" % error)
sys.exit(0)
for pw in PW_BLACKLIST:
if new_pw.find(pw) != -1:
- print "INVALID: %s is forbidden in a password" % pw
+ print("INVALID: %s is forbidden in a password" % pw)
sys.exit(0)
-print "VALID"
+print("VALID")
sys.exit(0)
diff --git a/doc/.gitignore b/doc/.gitignore
index 3461c58c5..49bfe4293 100644
--- a/doc/.gitignore
+++ b/doc/.gitignore
@@ -1,4 +1,5 @@
core-en_US.xml
+full-en_US.xml
rest-api
api
asterisk-ng-doxygen
diff --git a/include/asterisk/bridge.h b/include/asterisk/bridge.h
index c96cefb60..3584085af 100644
--- a/include/asterisk/bridge.h
+++ b/include/asterisk/bridge.h
@@ -126,6 +126,24 @@ struct ast_bridge_video_talker_src_data {
struct ast_channel *chan_old_vsrc;
};
+/*! \brief REMB report behaviors */
+enum ast_bridge_video_sfu_remb_behavior {
+ /*! The average of all reports is sent to the sender */
+ AST_BRIDGE_VIDEO_SFU_REMB_AVERAGE = 0,
+ /*! The lowest reported bitrate is forwarded to the sender */
+ AST_BRIDGE_VIDEO_SFU_REMB_LOWEST,
+ /*! The highest reported bitrate is forwarded to the sender */
+ AST_BRIDGE_VIDEO_SFU_REMB_HIGHEST,
+};
+
+/*! \brief This is used for selective forwarding unit configuration */
+struct ast_bridge_video_sfu_data {
+ /*! The interval at which a REMB report is generated and sent */
+ unsigned int remb_send_interval;
+ /*! How the combined REMB report is generated */
+ enum ast_bridge_video_sfu_remb_behavior remb_behavior;
+};
+
/*! \brief Data structure that defines a video source mode */
struct ast_bridge_video_mode {
enum ast_bridge_video_mode_type mode;
@@ -133,9 +151,10 @@ struct ast_bridge_video_mode {
union {
struct ast_bridge_video_single_src_data single_src_data;
struct ast_bridge_video_talker_src_data talker_src_data;
+ struct ast_bridge_video_sfu_data sfu_data;
} mode_data;
+ /*! The minimum interval between video updates */
unsigned int video_update_discard;
- unsigned int remb_send_interval;
};
/*!
@@ -917,10 +936,22 @@ void ast_bridge_set_video_update_discard(struct ast_bridge *bridge, unsigned int
*
* \param bridge Bridge to set the REMB send interval on
* \param remb_send_interval The REMB send interval
+ *
+ * \note This can only be called when the bridge has been set to the SFU video mode.
*/
void ast_bridge_set_remb_send_interval(struct ast_bridge *bridge, unsigned int remb_send_interval);
/*!
+ * \brief Set the REMB report generation behavior on a bridge
+ *
+ * \param bridge Bridge to set the REMB behavior on
+ * \param behavior How REMB reports are generated
+ *
+ * \note This can only be called when the bridge has been set to the SFU video mode.
+ */
+void ast_brige_set_remb_behavior(struct ast_bridge *bridge, enum ast_bridge_video_sfu_remb_behavior behavior);
+
+/*!
* \brief Update information about talker energy for talker src video mode.
*/
void ast_bridge_update_talker_src_video_mode(struct ast_bridge *bridge, struct ast_channel *chan, int talker_energy, int is_keyfame);
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index 092bb8420..d2ae39baf 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -258,6 +258,14 @@ struct ast_sip_contact {
AST_STRING_FIELD(user_agent);
/*! The name of the aor this contact belongs to */
AST_STRING_FIELD(aor);
+ /*! Asterisk Server name */
+ AST_STRING_FIELD(reg_server);
+ /*! IP-address of the Via header in REGISTER request */
+ AST_STRING_FIELD(via_addr);
+ /*! Content of the Call-ID header in REGISTER request */
+ AST_STRING_FIELD(call_id);
+ /*! The name of the endpoint that added the contact */
+ AST_STRING_FIELD(endpoint_name);
);
/*! Absolute time that this contact is no longer valid after */
struct timeval expiration_time;
@@ -269,16 +277,8 @@ struct ast_sip_contact {
double qualify_timeout;
/*! Endpoint that added the contact, only available in observers */
struct ast_sip_endpoint *endpoint;
- /*! Asterisk Server name */
- AST_STRING_FIELD_EXTENDED(reg_server);
- /*! IP-address of the Via header in REGISTER request */
- AST_STRING_FIELD_EXTENDED(via_addr);
- /* Port of the Via header in REGISTER request */
+ /*! Port of the Via header in REGISTER request */
int via_port;
- /*! Content of the Call-ID header in REGISTER request */
- AST_STRING_FIELD_EXTENDED(call_id);
- /*! The name of the endpoint that added the contact */
- AST_STRING_FIELD_EXTENDED(endpoint_name);
/*! If true delete the contact on Asterisk restart/boot */
int prune_on_boot;
};
@@ -751,6 +751,8 @@ struct ast_sip_endpoint {
AST_STRING_FIELD(message_context);
/*! Accountcode to auto-set on channels */
AST_STRING_FIELD(accountcode);
+ /*! If set, we'll push incoming MWI NOTIFYs to stasis using this mailbox */
+ AST_STRING_FIELD(incoming_mwi_mailbox);
);
/*! Configuration for extensions */
struct ast_sip_endpoint_extensions extensions;
@@ -812,8 +814,6 @@ struct ast_sip_endpoint {
unsigned int refer_blind_progress;
/*! Whether to notifies dialog-info 'early' on INUSE && RINGING state */
unsigned int notify_early_inuse_ringing;
- /*! If set, we'll push incoming MWI NOTIFYs to stasis using this mailbox */
- AST_STRING_FIELD_EXTENDED(incoming_mwi_mailbox);
};
/*! URI parameter for symmetric transport */
diff --git a/include/asterisk/stasis_bridges.h b/include/asterisk/stasis_bridges.h
index 05d356cc2..a455a5b02 100644
--- a/include/asterisk/stasis_bridges.h
+++ b/include/asterisk/stasis_bridges.h
@@ -46,6 +46,8 @@ struct ast_bridge_snapshot {
AST_STRING_FIELD(creator);
/*! Name given to the bridge by its creator */
AST_STRING_FIELD(name);
+ /*! Unique ID of the channel providing video, if one exists */
+ AST_STRING_FIELD(video_source_id);
);
/*! AO2 container of bare channel uniqueid strings participating in the bridge.
* Allocated from ast_str_container_alloc() */
@@ -60,8 +62,6 @@ struct ast_bridge_snapshot {
unsigned int num_active;
/*! The video mode of the bridge */
enum ast_bridge_video_mode_type video_mode;
- /*! Unique ID of the channel providing video, if one exists */
- AST_STRING_FIELD_EXTENDED(video_source_id);
};
/*!
diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h
index 4da7fa465..b892cda9e 100644
--- a/include/asterisk/utils.h
+++ b/include/asterisk/utils.h
@@ -578,6 +578,13 @@ void DO_CRASH_NORETURN __ast_assert_failed(int condition, const char *condition_
#ifdef AST_DEVMODE
#define ast_assert(a) _ast_assert(a, # a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ast_assert_return(a, ...) \
+({ \
+ if (__builtin_expect(!(a), 1)) { \
+ _ast_assert(0, # a, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ return __VA_ARGS__; \
+ }\
+})
static void force_inline _ast_assert(int condition, const char *condition_str, const char *file, int line, const char *function)
{
if (__builtin_expect(!condition, 1)) {
@@ -586,6 +593,12 @@ static void force_inline _ast_assert(int condition, const char *condition_str, c
}
#else
#define ast_assert(a)
+#define ast_assert_return(a, ...) \
+({ \
+ if (__builtin_expect(!(a), 1)) { \
+ return __VA_ARGS__; \
+ }\
+})
#endif
/*!
diff --git a/main/bridge.c b/main/bridge.c
index 8795081e2..2b347fd3f 100644
--- a/main/bridge.c
+++ b/main/bridge.c
@@ -3852,8 +3852,19 @@ void ast_bridge_set_video_update_discard(struct ast_bridge *bridge, unsigned int
void ast_bridge_set_remb_send_interval(struct ast_bridge *bridge, unsigned int remb_send_interval)
{
+ ast_assert(bridge->softmix.video_mode.mode == AST_BRIDGE_VIDEO_MODE_SFU);
+
+ ast_bridge_lock(bridge);
+ bridge->softmix.video_mode.mode_data.sfu_data.remb_send_interval = remb_send_interval;
+ ast_bridge_unlock(bridge);
+}
+
+void ast_brige_set_remb_behavior(struct ast_bridge *bridge, enum ast_bridge_video_sfu_remb_behavior behavior)
+{
+ ast_assert(bridge->softmix.video_mode.mode == AST_BRIDGE_VIDEO_MODE_SFU);
+
ast_bridge_lock(bridge);
- bridge->softmix.video_mode.remb_send_interval = remb_send_interval;
+ bridge->softmix.video_mode.mode_data.sfu_data.remb_behavior = behavior;
ast_bridge_unlock(bridge);
}
diff --git a/main/stasis_bridges.c b/main/stasis_bridges.c
index 4b68559de..59b9685ef 100644
--- a/main/stasis_bridges.c
+++ b/main/stasis_bridges.c
@@ -246,8 +246,7 @@ struct ast_bridge_snapshot *ast_bridge_snapshot_create(struct ast_bridge *bridge
return NULL;
}
- if (ast_string_field_init(snapshot, 128)
- || ast_string_field_init_extended(snapshot, video_source_id)) {
+ if (ast_string_field_init(snapshot, 128)) {
ao2_ref(snapshot, -1);
return NULL;
diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c
index 22da80577..6e79dc40b 100644
--- a/res/res_pjsip/location.c
+++ b/res/res_pjsip/location.c
@@ -133,11 +133,6 @@ static void *contact_alloc(const char *name)
return NULL;
}
- ast_string_field_init_extended(contact, endpoint_name);
- ast_string_field_init_extended(contact, reg_server);
- ast_string_field_init_extended(contact, via_addr);
- ast_string_field_init_extended(contact, call_id);
-
/* Dynamic contacts are delimited with ";@" and static ones with "@@" */
if ((aor_separator = strstr(id, ";@")) || (aor_separator = strstr(id, "@@"))) {
*aor_separator = '\0';
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 3094f248e..fb84a1f60 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -2248,8 +2248,6 @@ void *ast_sip_endpoint_alloc(const char *name)
return NULL;
}
- ast_string_field_init_extended(endpoint, incoming_mwi_mailbox);
-
if (!(endpoint->media.codecs = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
ao2_cleanup(endpoint);
return NULL;
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index 8a90849c0..0d815ad39 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -834,6 +834,8 @@ static int reregister_immediately_cb(void *obj)
*
* \param obj What is needed to initiate a reregister attempt.
*
+ * \note Normally executed by the pjsip monitor thread.
+ *
* \return Nothing
*/
static void registration_transport_shutdown_cb(void *obj)
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index 9e0718f51..d98491495 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -560,15 +560,52 @@ static void *publication_resource_alloc(const char *name)
return ast_sorcery_generic_alloc(sizeof(struct ast_sip_publication_resource), publication_resource_destroy);
}
-static void sub_tree_transport_cb(void *data) {
+static int sub_tree_subscription_terminate_cb(void *data)
+{
struct sip_subscription_tree *sub_tree = data;
- ast_debug(3, "Transport destroyed. Removing subscription '%s->%s' prune on restart: %d\n",
+ if (!sub_tree->evsub) {
+ /* Something else already terminated the subscription. */
+ ao2_ref(sub_tree, -1);
+ return 0;
+ }
+
+ ast_debug(3, "Transport destroyed. Removing subscription '%s->%s' prune on boot: %d\n",
sub_tree->persistence->endpoint, sub_tree->root->resource,
sub_tree->persistence->prune_on_boot);
sub_tree->state = SIP_SUB_TREE_TERMINATE_IN_PROGRESS;
pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
+
+ ao2_ref(sub_tree, -1);
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief The reliable transport we used as a subscription contact has shutdown.
+ *
+ * \param data What subscription needs to be terminated.
+ *
+ * \note Normally executed by the pjsip monitor thread.
+ *
+ * \return Nothing
+ */
+static void sub_tree_transport_cb(void *data)
+{
+ struct sip_subscription_tree *sub_tree = data;
+
+ /*
+ * Push off the subscription termination to the serializer to
+ * avoid deadlock. Another thread could be trying to send a
+ * message on the subscription that can deadlock with this
+ * thread.
+ */
+ ao2_ref(sub_tree, +1);
+ if (ast_sip_push_task(sub_tree->serializer, sub_tree_subscription_terminate_cb,
+ sub_tree)) {
+ ao2_ref(sub_tree, -1);
+ }
}
/*! \brief Destructor for subscription persistence */
@@ -621,7 +658,7 @@ static void subscription_persistence_update(struct sip_subscription_tree *sub_tr
return;
}
- ast_debug(3, "Updating persistence for '%s->%s' prune on restart: %s\n",
+ ast_debug(3, "Updating persistence for '%s->%s' prune on boot: %s\n",
sub_tree->persistence->endpoint, sub_tree->root->resource,
sub_tree->persistence->prune_on_boot ? "yes" : "no");
@@ -645,7 +682,7 @@ static void subscription_persistence_update(struct sip_subscription_tree *sub_tr
sub_tree->endpoint, rdata);
if (sub_tree->persistence->prune_on_boot) {
- ast_debug(3, "adding transport monitor on %s for '%s->%s' prune on restart: %d\n",
+ ast_debug(3, "adding transport monitor on %s for '%s->%s' prune on boot: %d\n",
rdata->tp_info.transport->obj_name,
sub_tree->persistence->endpoint, sub_tree->root->resource,
sub_tree->persistence->prune_on_boot);
diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c
index bdee91fb3..985933e2d 100644
--- a/res/res_pjsip_registrar.c
+++ b/res/res_pjsip_registrar.c
@@ -337,7 +337,7 @@ static int contact_transport_monitor_matcher(void *a, void *b)
&& strcmp(ma->contact_name, mb->contact_name) == 0;
}
-static void register_contact_transport_shutdown_cb(void *data)
+static int register_contact_transport_remove_cb(void *data)
{
struct contact_transport_monitor *monitor = data;
struct ast_sip_contact *contact;
@@ -345,7 +345,8 @@ static void register_contact_transport_shutdown_cb(void *data)
aor = ast_sip_location_retrieve_aor(monitor->aor_name);
if (!aor) {
- return;
+ ao2_ref(monitor, -1);
+ return 0;
}
ao2_lock(aor);
@@ -365,6 +366,35 @@ static void register_contact_transport_shutdown_cb(void *data)
}
ao2_unlock(aor);
ao2_ref(aor, -1);
+
+ ao2_ref(monitor, -1);
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief The reliable transport we registered as a contact has shutdown.
+ *
+ * \param data What contact needs to be removed.
+ *
+ * \note Normally executed by the pjsip monitor thread.
+ *
+ * \return Nothing
+ */
+static void register_contact_transport_shutdown_cb(void *data)
+{
+ struct contact_transport_monitor *monitor = data;
+
+ /*
+ * Push off to a default serializer. This is in case sorcery
+ * does database accesses for contacts. Database accesses may
+ * not be on this machine. We don't want to tie up the pjsip
+ * monitor thread with potentially long access times.
+ */
+ ao2_ref(monitor, +1);
+ if (ast_sip_push_task(NULL, register_contact_transport_remove_cb, monitor)) {
+ ao2_ref(monitor, -1);
+ }
}
AST_VECTOR(excess_contact_vector, struct ast_sip_contact *);
diff --git a/rest-api-templates/api.wiki.mustache b/rest-api-templates/api.wiki.mustache
index ad12bb695..a51c3e6ce 100644
--- a/rest-api-templates/api.wiki.mustache
+++ b/rest-api-templates/api.wiki.mustache
@@ -5,7 +5,7 @@ h1. {{name_title}}
{{#apis}}
{{#operations}}
-| {{http_method}} | [{{wiki_path}}|#{{nickname}}] | {{#response_class}}{{#is_primitive}}{{name}}{{/is_primitive}}{{^is_primitive}}[{{wiki_name}}|{{wiki_prefix}} REST Data Models#{{singular_name}}]{{/is_primitive}}{{/response_class}} | {{summary}} |
+| {{http_method}} | [{{wiki_path}}|#{{nickname}}] | {{#response_class}}{{#is_primitive}}{{name}}{{/is_primitive}}{{^is_primitive}}[{{wiki_name}}|{{wiki_prefix}} REST Data Models#{{singular_name}}]{{/is_primitive}}{{/response_class}} | {{{summary}}} |
{{/operations}}
{{/apis}}
{{#apis}}
diff --git a/rest-api-templates/ari_resource.h.mustache b/rest-api-templates/ari_resource.h.mustache
index df075af35..c1d880d30 100644
--- a/rest-api-templates/ari_resource.h.mustache
+++ b/rest-api-templates/ari_resource.h.mustache
@@ -76,7 +76,7 @@ int ast_ari_{{c_name}}_{{c_nickname}}_parse_body(
{{/parse_body}}
/*!
- * \brief {{summary}}
+ * \brief {{{summary}}}
{{#notes}}
*
* {{{notes}}}
@@ -99,7 +99,7 @@ void ast_ari_{{c_name}}_{{c_nickname}}(struct ast_tcptls_session_instance *ser,
{{#is_websocket}}
/*!
- * \brief {{summary}}
+ * \brief {{{summary}}}
{{#notes}}
*
* {{{notes}}}
@@ -111,7 +111,7 @@ void ast_ari_{{c_name}}_{{c_nickname}}(struct ast_tcptls_session_instance *ser,
int ast_ari_websocket_{{c_name}}_{{c_nickname}}_init(void);
/*!
- * \brief {{summary}}
+ * \brief {{{summary}}}
{{#notes}}
*
* {{{notes}}}
diff --git a/rest-api-templates/asterisk_processor.py b/rest-api-templates/asterisk_processor.py
index 981294673..5f8dbb576 100644
--- a/rest-api-templates/asterisk_processor.py
+++ b/rest-api-templates/asterisk_processor.py
@@ -23,7 +23,7 @@ Asterisk RESTful HTTP binding code.
import os
import re
-from swagger_model import *
+from swagger_model import Stringify, SwaggerError, SwaggerPostProcessor
try:
from collections import OrderedDict
@@ -183,7 +183,7 @@ class AsteriskProcessor(SwaggerPostProcessor):
raise SwaggerError(
"Should not mix resources in one API declaration", context)
# root_path isn't needed any more
- resource_api.root_path = resource_api.root_path.children()[0]
+ resource_api.root_path = list(resource_api.root_path.children())[0]
if resource_api.name != resource_api.root_path.name:
raise SwaggerError(
"API declaration name should match", context)
@@ -206,10 +206,10 @@ class AsteriskProcessor(SwaggerPostProcessor):
def process_parameter(self, parameter, context):
if parameter.param_type == 'body':
- parameter.is_body_parameter = True;
+ parameter.is_body_parameter = True;
parameter.c_data_type = 'struct ast_json *'
else:
- parameter.is_body_parameter = False;
+ parameter.is_body_parameter = False;
if not parameter.data_type in self.type_mapping:
raise SwaggerError(
"Invalid parameter type %s" % parameter.data_type, context)
diff --git a/rest-api-templates/make_ari_stubs.py b/rest-api-templates/make_ari_stubs.py
index 0aba06d6d..a25773df4 100755
--- a/rest-api-templates/make_ari_stubs.py
+++ b/rest-api-templates/make_ari_stubs.py
@@ -16,19 +16,20 @@
# at the top of the source tree.
#
+from __future__ import print_function
import sys
try:
import pystache
except ImportError:
- print >> sys.stderr, "Pystache required. Please sudo pip install pystache."
+ print("Pystache required. Please sudo pip install pystache.", file=sys.stderr)
sys.exit(1)
import os.path
from asterisk_processor import AsteriskProcessor
from optparse import OptionParser
-from swagger_model import *
+from swagger_model import ResourceListing
from transform import Transform
TOPDIR = os.path.dirname(os.path.abspath(__file__))
diff --git a/rest-api-templates/res_ari_resource.c.mustache b/rest-api-templates/res_ari_resource.c.mustache
index 67a04d898..85948fba1 100644
--- a/rest-api-templates/res_ari_resource.c.mustache
+++ b/rest-api-templates/res_ari_resource.c.mustache
@@ -55,7 +55,7 @@
#if defined(AST_DEVMODE)
#include "ari/ari_model_validators.h"
#endif
-{{^has_websocket}}
+{{#has_websocket}}
{{! Only include http_websocket if necessary. Otherwise we'll do a lot of
* unnecessary optional_api intialization, which makes optional_api harder
* to debug
@@ -278,7 +278,7 @@ static int load_module(void)
{{#apis}}
{{#operations}}
-{{#has_websocket}}
+{{#is_websocket}}
struct ast_websocket_protocol *protocol;
if (ast_ari_websocket_{{c_name}}_{{c_nickname}}_init() == -1) {
@@ -300,8 +300,6 @@ static int load_module(void)
}
protocol->session_attempted = ast_ari_{{c_name}}_{{c_nickname}}_ws_attempted_cb;
protocol->session_established = ast_ari_{{c_name}}_{{c_nickname}}_ws_established_cb;
-{{/has_websocket}}
-{{#is_websocket}}
res |= ast_websocket_server_add_protocol2({{full_name}}.ws_server, protocol);
{{/is_websocket}}
{{/operations}}
diff --git a/rest-api-templates/swagger_model.py b/rest-api-templates/swagger_model.py
index 3f729d8b5..50c5fb07b 100644
--- a/rest-api-templates/swagger_model.py
+++ b/rest-api-templates/swagger_model.py
@@ -26,6 +26,7 @@ missing, or have incorrect values).
See https://github.com/wordnik/swagger-core/wiki/API-Declaration for the spec.
"""
+from __future__ import print_function
import json
import os.path
import pprint
@@ -75,7 +76,7 @@ def compare_versions(lhs, rhs):
'''
lhs = [int(v) for v in lhs.split('.')]
rhs = [int(v) for v in rhs.split('.')]
- return cmp(lhs, rhs)
+ return (lhs > rhs) - (lhs < rhs)
class ParsingContext(object):
@@ -444,8 +445,7 @@ class Api(Stringify):
op_json = api_json.get('operations')
self.operations = [
Operation().load(j, processor, context) for j in op_json]
- self.has_websocket = \
- filter(lambda op: op.is_websocket, self.operations) != []
+ self.has_websocket = any(op.is_websocket for op in self.operations)
processor.process_api(self, context)
return self
@@ -611,7 +611,7 @@ class ApiDeclaration(Stringify):
except SwaggerError:
raise
except Exception as e:
- print >> sys.stderr, "Error: ", traceback.format_exc()
+ print("Error: ", traceback.format_exc(), file=sys.stderr)
raise SwaggerError(
"Error loading %s" % api_declaration_file, context, e)
@@ -624,8 +624,8 @@ class ApiDeclaration(Stringify):
.replace(".json", ".{format}")
if self.resource_path != expected_resource_path:
- print >> sys.stderr, \
- "%s != %s" % (self.resource_path, expected_resource_path)
+ print("%s != %s" % (self.resource_path, expected_resource_path),
+ file=sys.stderr)
raise SwaggerError("resourcePath has incorrect value", context)
return self
@@ -656,8 +656,7 @@ class ApiDeclaration(Stringify):
if api.path in paths:
raise SwaggerError("API with duplicated path: %s" % api.path, context)
paths.add(api.path)
- self.has_websocket = filter(lambda api: api.has_websocket,
- self.apis) == []
+ self.has_websocket = any(api.has_websocket for api in self.apis)
models = api_decl_json.get('models').items() or []
self.models = [Model().load(id, json, processor, context)
for (id, json) in models]
@@ -666,7 +665,7 @@ class ApiDeclaration(Stringify):
model_dict = dict((m.id, m) for m in self.models)
for m in self.models:
def link_subtype(name):
- res = model_dict.get(subtype)
+ res = model_dict.get(name)
if not res:
raise SwaggerError("%s has non-existing subtype %s",
m.id, name)
@@ -725,7 +724,7 @@ class ResourceListing(Stringify):
except SwaggerError:
raise
except Exception as e:
- print >> sys.stderr, "Error: ", traceback.format_exc()
+ print("Error: ", traceback.format_exc(), file=sys.stderr)
raise SwaggerError(
"Error loading %s" % resource_file, context, e)
diff --git a/rest-api-templates/transform.py b/rest-api-templates/transform.py
index c3a030064..88f7d2e67 100644
--- a/rest-api-templates/transform.py
+++ b/rest-api-templates/transform.py
@@ -21,6 +21,11 @@ import os.path
import pystache
import shutil
import tempfile
+import sys
+
+if sys.version_info[0] == 3:
+ def unicode(v):
+ return str(v)
class Transform(object):
@@ -52,10 +57,10 @@ class Transform(object):
dest_exists = os.path.exists(dest_file)
if dest_exists and not self.overwrite:
return
- with tempfile.NamedTemporaryFile() as out:
+ with tempfile.NamedTemporaryFile(mode='w+') as out:
out.write(renderer.render(self.template, model))
out.flush()
if not dest_exists or not filecmp.cmp(out.name, dest_file):
- print "Writing %s" % dest_file
+ print("Writing %s" % dest_file)
shutil.copyfile(out.name, dest_file)
diff --git a/utils/Makefile b/utils/Makefile
index d62d45f4f..ae2af08e2 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -164,7 +164,7 @@ aelparse.c: $(ASTTOPDIR)/res/ael/ael_lex.c
$(CMD_PREFIX) mv "$@.new" "$@"
aelparse.o: _ASTCFLAGS+=-I$(ASTTOPDIR)/res -Wno-unused
-aelparse: LIBS+=-lm
+aelparse: LIBS+=-lm $(AST_CLANG_BLOCKS_LIBS)
aelparse: aelparse.o aelbison.o pbx_ael.o hashtab.o lock.o ael_main.o ast_expr2f.o ast_expr2.o strcompat.o pval.o extconf.o astmm.o
threadstorage.c: $(ASTTOPDIR)/main/threadstorage.c
@@ -174,6 +174,7 @@ threadstorage.c: $(ASTTOPDIR)/main/threadstorage.c
extconf.o: extconf.c
+conf2ael: LIBS+=$(AST_CLANG_BLOCKS_LIBS)
conf2ael: conf2ael.o ast_expr2f.o ast_expr2.o hashtab.o lock.o aelbison.o aelparse.o pbx_ael.o pval.o extconf.o strcompat.o astmm.o
check_expr2: $(ASTTOPDIR)/main/ast_expr2f.c $(ASTTOPDIR)/main/ast_expr2.c $(ASTTOPDIR)/main/ast_expr2.h astmm.o