[Pkg-voip-commits] [asterisk] 01/07: New upstream version 13.13.1~dfsg

Bernhard Schmidt berni at moszumanska.debian.org
Tue Dec 20 21:03:05 UTC 2016


This is an automated email from the git hooks/post-receive script.

berni pushed a commit to branch master
in repository asterisk.

commit 696baf99b9f46118bf24f7fc0d5c49e1363aaec5
Author: Bernhard Schmidt <berni at debian.org>
Date:   Sun Dec 18 14:46:02 2016 +0100

    New upstream version 13.13.1~dfsg
---
 .version                                           |    2 +-
 CHANGES                                            |   95 ++
 ChangeLog                                          | 1354 +++++++++++++++++++-
 addons/ooh323c/src/ooCalls.c                       |    3 +-
 addons/ooh323c/src/ooGkClient.c                    |    1 +
 addons/ooh323c/src/oochannels.c                    |   43 +-
 addons/ooh323c/src/ooq931.c                        |    5 -
 apps/app_dial.c                                    |    1 +
 apps/app_echo.c                                    |    3 +-
 apps/app_queue.c                                   |   13 +-
 asterisk-13.12.2-summary.html                      |   16 -
 asterisk-13.12.2-summary.txt                       |  111 --
 asterisk-13.13.1-summary.html                      |   25 +
 asterisk-13.13.1-summary.txt                       |  115 ++
 bridges/bridge_builtin_features.c                  |    2 +
 bridges/bridge_softmix.c                           |   28 +-
 channels/chan_pjsip.c                              |  237 +++-
 channels/chan_rtp.c                                |    2 +-
 channels/chan_sip.c                                |   22 +-
 channels/chan_unistim.c                            |   11 +-
 configs/basic-pbx/pjsip.conf                       |    3 -
 configs/samples/asterisk.conf.sample               |    9 +
 configs/samples/codecs.conf.sample                 |   54 +
 configs/samples/pjsip.conf.sample                  |   11 +-
 configs/samples/rtp.conf.sample                    |   12 +
 configure                                          |  366 +++++-
 configure.ac                                       |   16 +-
 .../4468b4a91372_add_pjsip_asymmetric_rtp_codec.py |   31 +
 contrib/realtime/mssql/mssql_config.sql            |   14 +
 contrib/realtime/mysql/mysql_config.sql            |    6 +
 contrib/realtime/oracle/oracle_config.sql          |   14 +
 contrib/realtime/postgresql/postgresql_config.sql  |    6 +
 contrib/scripts/sip_to_pjsip/sip_to_pjsip.py       |    2 +-
 doc/appdocsxml.xslt                                |   20 +
 include/asterisk.h                                 |    9 +
 include/asterisk/_private.h                        |    1 +
 include/asterisk/autoconfig.h.in                   |    9 +
 include/asterisk/bridge.h                          |    9 +
 include/asterisk/channel.h                         |   61 +
 include/asterisk/channel_internal.h                |    2 +
 include/asterisk/file.h                            |   28 +
 include/asterisk/manager.h                         |    2 +-
 include/asterisk/module.h                          |    7 -
 include/asterisk/options.h                         |    2 +
 include/asterisk/res_pjsip.h                       |    2 +
 include/asterisk/rtp_engine.h                      |    3 +
 include/asterisk/stasis_app.h                      |   10 +
 include/asterisk/stasis_bridges.h                  |    4 +
 include/asterisk/tcptls.h                          |    1 +
 include/asterisk/vector.h                          |    8 +-
 main/Makefile                                      |   13 +-
 main/asterisk.c                                    |   48 +-
 main/astobj2.c                                     |    4 +-
 main/autoservice.c                                 |   66 +-
 main/bridge.c                                      |   34 +-
 main/bridge_channel.c                              |    3 +
 main/cdr.c                                         |   19 +-
 main/channel.c                                     |  160 ++-
 main/channel_internal_api.c                        |   29 +
 main/cli.c                                         |   14 +-
 main/codec_builtin.c                               |   16 +
 main/features_config.c                             |    2 -
 main/file.c                                        |  137 ++
 main/format_cap.c                                  |    2 +-
 main/loader.c                                      |    5 +-
 main/manager_bridges.c                             |   52 +-
 main/manager_channels.c                            |   11 +
 main/netsock.c                                     |    2 +-
 main/rtp_engine.c                                  |   87 +-
 main/stasis_bridges.c                              |   29 +-
 main/tcptls.c                                      |   67 +-
 main/utils.c                                       |  244 +++-
 makeopts.in                                        |    2 +
 menuselect/aclocal.m4                              |  281 +++-
 menuselect/configure                               |  197 ++-
 menuselect/configure.ac                            |    9 +-
 res/ari/ari_model_validators.c                     |  463 +++++++
 res/ari/ari_model_validators.h                     |   65 +
 res/ari/ari_websockets.c                           |    2 +
 res/ari/resource_bridges.c                         |   66 +
 res/ari/resource_bridges.h                         |   28 +
 res/ari/resource_channels.c                        |    7 +-
 res/res_agi.c                                      |   38 +-
 res/res_ari_bridges.c                              |  146 ++-
 res/res_ari_channels.c                             |    2 +
 res/res_format_attr_opus.c                         |   48 +-
 res/res_http_websocket.c                           |   19 +-
 res/res_pjsip.c                                    |  137 +-
 res/res_pjsip/include/res_pjsip_private.h          |   14 +
 res/res_pjsip/pjsip_configuration.c                |    1 +
 .../pjsip_message_ip_updater.c}                    |  176 ++-
 res/res_pjsip_caller_id.c                          |   14 +-
 res/res_pjsip_outbound_authenticator_digest.c      |   13 +-
 res/res_pjsip_outbound_registration.c              |    2 +
 res/res_pjsip_pubsub.c                             |   20 +-
 res/res_pjsip_registrar_expire.c                   |    2 +-
 res/res_pjsip_sdp_rtp.c                            |   54 +-
 res/res_pjsip_session.c                            |   15 -
 res/res_pjsip_t38.c                                |   13 +-
 res/res_rtp_asterisk.c                             |  107 +-
 res/res_stasis.c                                   |   22 +-
 res/stasis/app.c                                   |  105 ++
 res/stasis/app.h                                   |   26 +
 res/stasis/cli.c                                   |  216 ++++
 res/stasis/cli.h                                   |   43 +
 res/stasis_recording/stored.c                      |  217 +---
 rest-api/api-docs/applications.json                |    2 +-
 rest-api/api-docs/asterisk.json                    |    2 +-
 rest-api/api-docs/bridges.json                     |   84 +-
 rest-api/api-docs/channels.json                    |   10 +-
 rest-api/api-docs/deviceStates.json                |    2 +-
 rest-api/api-docs/endpoints.json                   |    2 +-
 rest-api/api-docs/events.json                      |   22 +-
 rest-api/api-docs/mailboxes.json                   |    2 +-
 rest-api/api-docs/playbacks.json                   |    2 +-
 rest-api/api-docs/recordings.json                  |    2 +-
 rest-api/api-docs/sounds.json                      |    2 +-
 rest-api/resources.json                            |    2 +-
 tests/test_astobj2_thrash.c                        |   11 +-
 tests/test_file.c                                  |  197 +++
 tests/test_res_stasis.c                            |    6 +-
 third-party/pjproject/Makefile                     |   75 +-
 third-party/pjproject/Makefile.rules               |   10 +-
 third-party/pjproject/apply_patches                |    4 -
 third-party/pjproject/configure.m4                 |    5 +-
 .../patches/0000-remove-third-party.patch          |  142 ++
 ...1-svn-backport-Various-fixes-for-DNS-IPv6.patch |  134 ++
 ...0006-r5473-svn-backport-Fix-pending-query.patch |   28 +
 ...r5475-svn-backport-Remove-DNS-cache-entry.patch |   70 +
 ...vn-backport-Fix-DNS-write-on-freed-memory.patch |   33 +
 third-party/pjproject/patches/config_site.h        |    8 +-
 third-party/pjproject/patches/user.mak             |    4 +-
 132 files changed, 6332 insertions(+), 954 deletions(-)

diff --git a/.version b/.version
index d786287..b88ad97 100644
--- a/.version
+++ b/.version
@@ -1 +1 @@
-13.12.2
\ No newline at end of file
+13.13.1
\ No newline at end of file
diff --git a/CHANGES b/CHANGES
index 11b3574..adba984 100644
--- a/CHANGES
+++ b/CHANGES
@@ -9,6 +9,89 @@
 ==============================================================================
 
 ------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 13.12.0 to Asterisk 13.13.0 ----------
+------------------------------------------------------------------------------
+
+AMI
+------------------
+ * Events that reference a bridge may now contain two new optional fields:
+   - 'BridgeVideoSourceMode': the video source mode for the bridge.
+     Can be one of 'none', 'talker', or 'single'.
+   - 'BridgeVideoSource': the unique ID of the channel that is the video
+     source in this bridge, if one exists.
+
+ * A new event, BridgeVideoSourceUpdate, has been added with a class
+   authorization of CALL. The event is raised when the video source changes
+   in a multi-party mixing bridge.
+
+ARI
+------------------
+ * The bridges resource now exposes two new operations:
+   - POST /bridges/{bridgeId}/videoSource/{channelId}: Set a video source in a
+     multi-party mixing bridge
+   - DELETE /bridges/{bridgeId}/videoSource: Remove the set video source,
+     reverting to talk detection for the video source
+
+ * The bridge model in any returned response or event now contains the following
+   optional fields:
+   - video_mode: the video source mode for the bridge. Can be one of 'none',
+     'talker', or 'single'.
+   - video_source_id: the unique ID of the channel that is the video source
+     in this bridge, if one exists.
+
+ * A new event, BridgeVideoSourceChanged, has been added for bridges.
+   Applications subscribed to a bridge will receive this event when the source
+   of video changes in a mixing bridge.
+
+res_pjsip
+------------------
+ * Automatic dual stack support is now implemented. Depending on DNS resolution
+   and the transport used for sending a message the SIP signaling and SDP will
+   be updated with the correct IP address and protocol version. This means that
+   the rtp_ipv6 and t38_udptl_ipv6 options no longer have any effect. The
+   res_pjsip_multihomed module has also been moved into core res_pjsip to ensure
+   that messages are updated with the correct address information in all cases.
+
+chan_pjsip
+------------------
+ * The default behavior for RTP codecs has been changed. The sending codec will
+   now match the receiving codec. This can be turned off and behavior reverted
+   to asymmetric using the "asymmetric_rtp_codec" endpoint option. If this
+   option is set then the sending and received codec are allowed to differ.
+
+CLI Commands
+------------------
+ * Three new CLI commands have been added for ARI:
+   - ari show apps:
+      Displays a listing of all registered ARI applications.
+   - ari show app <name>:
+      Display detailed information about a registered ARI application.
+   - ari set debug <name> <on|off>:
+      Enable/disable debugging of an ARI application. When debugged, verbose
+      information will be sent to the Asterisk CLI.
+
+RTP
+------------------
+ * New setting "rtp_pt_dynamic = 96" in asterisk.conf:
+   Normally the Dynamic RTP Payload Type numbers are 96-127, which allow 32
+   formats. When you use more and receive the message "No Dynamic RTP mapping
+   available", extend the dynamic range by going for rtp_pt_dynamic = 35 (or 0)
+   instead of 96. This allows 29 (or 64) additional formats. On default this is
+   disabled and the range is 96-127 because any number below might be rejected
+   by a remote implementation; although no such broken implementation is known.
+
+Queue
+------------------
+ * A new dialplan variable, ABANDONED, is set when the call is not answered
+   by an agent.
+
+Core
+------------------
+ * The TLS core in Asterisk now supports X.509 certificate subject alternative
+   names. This way one X.509 certificate can be used for hosts that can be
+   reached under multiple DNS names or for multiple hosts.
+
+------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 13.11.0 to Asterisk 13.12.0 ----------
 ------------------------------------------------------------------------------
 
@@ -71,6 +154,11 @@ res_pjsip
    Note: The caller-id and redirecting number strings obtained from incoming
    SIP URI user fields are now always truncated at the first semicolon.
 
+res_rtp_asterisk
+------------------
+  * An option, ice_blacklist, has been added which allows certain subnets to be
+    excluded from local ICE candidates.
+
 app_confbridge
 ------------------
   * Some sounds played into the bridge are played asynchronously. This, for
@@ -89,6 +177,13 @@ app_dial
    when another channel answers the call.  The default of ANSWERED_ELSEWHERE
    is unchanged.
 
+res_ari
+------------------
+ * ARI events will all now include a new field in the root of the JSON message,
+   'asterisk_id'.  This will be the unique ID for the Asterisk system
+   transmitting the event.  The value can be overridden using the 'entityid'
+   setting in asterisk.conf.
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 13.10.0 to Asterisk 13.11.0 ----------
 ------------------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 4b99df1..2b71a62 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,8 +1,707 @@
-2016-11-10 19:34 +0000  Asterisk Development Team <asteriskteam at digium.com>
+2016-12-08 17:30 +0000  Asterisk Development Team <asteriskteam at digium.com>
 
-	* asterisk 13.12.2 Released.
+	* asterisk 13.13.1 Released.
 
-2016-11-04 10:57 +0000 [a3614d75f6]  Kevin Harwell <kharwell at digium.com>
+2016-12-08 11:26 +0000 [5e06e6d8a2]  Kevin Harwell <kharwell at digium.com>
+
+	* Update for 13.13.1
+
+2016-11-30 09:31 +0000 [e80603b6d6]  Walter Doekes <walter+asterisk at wjd.nu>
+
+	* chan_sip: Do not allow non-SP/HTAB between header key and colon.
+
+	  RFC says SIP headers look like:
+
+	      HCOLON  =  *( SP / HTAB ) ":" SWS
+	      SWS     =  [LWS]                    ; sep whitespace
+	      LWS     =  [*WSP CRLF] 1*WSP        ; linear whitespace
+	      WSP     =  SP / HTAB                ; from rfc2234
+
+	  chan_sip implemented this:
+
+	      HCOLON  =  *( LOWCTL / SP ) ":" SWS
+	      LOWCTL  = %x00-1F                   ; CTL without DEL
+
+	  This discrepancy meant that SIP proxies in front of Asterisk with
+	  chan_sip could pass on unknown headers with \x00-\x1F in them, which
+	  would be treated by Asterisk as a different (known) header.  For
+	  example, the "To\x01:" header would gladly be forwarded by some proxies
+	  as irrelevant, but chan_sip would treat it as the relevant "To:" header.
+
+	  Those relying on a SIP proxy to scrub certain headers could mistakenly
+	  get unexpected and unvalidated data fed to Asterisk.
+
+	  This change fixes so chan_sip only considers SP/HTAB as valid tokens
+	  before the colon, making it agree on the headers with other speakers of
+	  SIP.
+
+	  ASTERISK-26433 #close
+	  AST-2016-009
+
+	  Change-Id: I78086fbc524ac733b8f7f78cb423c91075fd489b
+	  (cherry picked from commit 41c6319c4e1261f40813e60017e3b65f4115c94d)
+
+2016-11-14 18:18 +0000 [fa52ecb9fb]  Joshua Colp <jcolp at digium.com>
+
+	* res_format_attr_opus: Fix crash when fmtp contains spaces.
+
+	  When an opus offer or answer was received that contained an
+	  fmtp line with spaces between the attributes the module would
+	  fail to properly parse it and crash due to recursion.
+
+	  This change makes the module handle the space properly and
+	  also removes the recursion requirement.
+
+	  ASTERISK-26579
+
+	  Change-Id: I01f53e5d9fa9f1925a7365f8d25071b5b3ac2dc3
+
+2016-11-23 15:26 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.13.0 Released.
+
+2016-11-22 18:02 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.13.0-rc2 Released.
+
+2016-11-21 09:40 +0000 [e246b36a3c]  gtjoseph <gjoseph at digium.com>
+
+	* build:  Backport addition of librt check to configure.ac
+
+	  A while back, a master-only change was made to check for librt which
+	  should probably have been cherry-picked to 13 at that time.  Sometime
+	  between then and now, part of that change did make it into 13 but it
+	  was incomplete and non-functional.  This patch backports the rest
+	  of the librt check and allows the link of libasteriskpj to use the
+	  results.
+
+	  Change-Id: I1424008fd8c90f389dda53162ec4a340b253a3c1
+
+2016-11-22 11:20 +0000 [855f05e525]  Kevin Harwell <kharwell at digium.com>
+
+	* Update for 13.13.0
+
+2016-11-18 18:59 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.13.0-rc1 Released.
+
+2016-11-18 09:45 +0000 [cb624b10ae]  Mark Michelson <mmichelson at digium.com>
+
+	* Bump ARI version to 1.10.0
+
+	  The video-related bridge changes mean that the version needs to be
+	  bumped.
+
+	  Change-Id: I41c4495068562bef03aa76728f188b8ac4bd393d
+
+2016-11-17 10:50 +0000 [bde3d022a3]  Mark Michelson <mmichelson at digium.com>
+
+	* manager: update minor version
+
+	  Based on bridge video AMI event changes, bump the minor version of AMI.
+
+	  Change-Id: I02586bd6cafc0baa33ea98c2f75356c0f5e03435
+
+2016-11-16 20:24 +0000 [b213045fe4]  gtjoseph <gjoseph at digium.com>
+
+	* build:  Various OpenBSD issues
+
+	  OpenBSD's 'find' doesn't take the -delete argument so you have to pipe
+	  through 'xargs rm -rf'.
+
+	  'echo -e' doesn't like \t starting a line. It just prints 't' which
+	  causes the libasteriskpj.exports file to be garbage.  They were just
+	  cosmetic so they were removed.
+
+	  librt doesn't exist so the link of libasteriskpj.so fails. It's not
+	  actually needed for linux anyway so -lrt was removed from the link.
+
+	  res_rtp_asterisk was failing to load because of an undefined
+	  DTLS_method. '|| defined(LIBRESSL_VERSION_NUMBER)' was added to the #if
+	  so DTLSv1_method is used instead.
+
+	  ASTERISK-26608
+
+	  Change-Id: I926ec95b0b69633231e3ad1d6e803b977272c49c
+
+2016-11-14 18:45 +0000 [404596b790]  gtjoseph <gjoseph at digium.com>
+
+	* channel:  Fix issues in hangup scenarios caused by frame deferral
+
+	  ASTERISK-26343
+
+	  Change-Id: I06dbf7366e26028251964143454a77d017bb61c8
+
+2016-11-16 15:42 +0000 [2c031b67d3]  Mark Michelson <mmichelson at digium.com>
+
+	* res_format_attr_opus: Fix fmtp generation.
+
+	  res_format_attr_opus assumed that the string being passed into it was
+	  empty. It tried to determine if the only thing it had written was
+
+	  a=fmtp:<num>
+
+	  And if it had, it would reset the string. Its calculation was off when
+	  working with chan_sip, though. chan_sip passes the entire built SDP
+	  rather than an empty string. This resulted in always putting an empty
+	  fmtp line in the SDP.
+
+	  ASTERISK-26520 #close
+	  Reported by scgm11
+
+	  Change-Id: Ib2e8712d26a47067e5f36d5973577added01dbb5
+
+2016-11-15 16:23 +0000 [ed0f1afc8c]  Richard Mudgett <rmudgett at digium.com>
+
+	* codec_opus: Fix warning when Opus negotiated but codec_opus not loaded.
+
+	  When Opus is negotiated but not loaded, the log is spammed with messages
+	  because the system does not know how to calculate the number of samples in
+	  a frame.
+
+	  * Suppress the warning by supplying a function that assumes 20ms of
+	  samples in the frame.  For pass through support it doesn't really seem to
+	  matter what number of samples is returned anyway.
+
+	  ASTERISK-26605 #close
+
+	  Change-Id: Icf2273692f040dc2c45b01e72a790d11092f9e0f
+
+2016-11-14 14:36 +0000 [e632222bc4]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_outbound_authenticator_digest.c: Fix memory pool leak.
+
+	  Responding to authentication challenges leaks PJSIP memory pools.
+
+	  The leak was introduced with a pjproject 2.5.5 API change.
+	  https://trac.pjsip.org/repos/ticket/1929 changed the API usage of
+	  pjsip_auth_clt_init() to require the new API pjsip_auth_clt_deinit() to
+	  clean up cached authentication allocations that get allocated with
+	  pjsip_auth_clt_reinit_req().
+
+	  ASTERISK-26516 #close
+
+	  Change-Id: I4473141b8c3961d0dc91c382beb3876b3efb45c8
+
+2016-11-15 12:01 +0000 [c92dcc76da]  gtjoseph <gjoseph at digium.com>
+
+	* file.c/__ast_file_read_dirs:  Fix issues on filesystems without d_type
+
+	  One of the code paths in __ast_file_read_dirs will only get executed if
+	  the OS doesn't support dirent->d_type OR if the filesystem the
+	  particular file is on doesn't support it.  So, while standard Linux
+	  systems support the field, some filesystems like XFS do not.  In this
+	  case, we need to call stat() to determine whether the directory entry
+	  is a file or directory so we append the filename to the supplied
+	  directory path and call stat.  We forgot to truncate path back to just
+	  the directory afterwards though so we were passing a complete file name
+	  to the callback in the dir_name parameter instead of just the directory
+	  name.
+
+	  The logic has been re-written to only create a full_path if we need to
+	  call stat() or if we need to descend into another directory.
+
+	  Change-Id: I54e4228bd8355fad65200c6df3ec4c9c8a98dfba
+
+2015-05-14 17:12 +0000 [7b96e8cc3d]  Maciej Szmigiero <mail at maciej.szmigiero.name>
+
+	* Add X.509 subject alternative name support to TLS certificate
+	  verification.
+
+	  This way one X.509 certificate can be used for hosts that
+	  can be reached under multiple DNS names or for multiple hosts.
+
+	  Signed-off-by: Maciej Szmigiero <mail at maciej.szmigiero.name>
+
+	  ASTERISK-25063 #close
+
+	  Change-Id: I13302c80490a0b44c43f1b45376c9bd7b15a538f
+
+2016-11-14 15:57 +0000 [0790aa528a]  Matt Jordan <mjordan at digium.com>
+
+	* pjproject: Use a much higher limit for PJ_ICE_MAX_CHECKS
+
+	  The PJ_ICE_MAX_CHECKS constant is used by pjproject to determine how
+	  many pairs of local/remote candidates will be made. If for some reason
+	  we reach this upper bound, ICE will generally fail and no media will
+	  flow between the browser and Asterisk.
+
+	  This patch makes PJ_ICE_MAX_CHECKS set to the total possible number of
+	  pairs of candidates we'd theoretically allow, which is
+	  PJ_ICE_MAX_CAND^2. Prior to this patch, we simply multiplied
+	  PJ_ICE_MAX_CAND by two; on systems with multiple interfaces (I blame
+	  Docker), this is far too low to allow WebRTC calls to succeed.
+
+	  Setting this to be PJ_ICE_MAX_CAND^2 allowed WebRTC calls to succeed
+	  even when the system Asterisk was running on had quite a few virtual
+	  interfaces.
+
+	  Change-Id: Icd4f17de0ac9d3a83dddfc8bf1cb7616bc107d55
+
+2016-11-14 15:32 +0000 [993a6f96c7]  Matt Jordan <mjordan at digium.com>
+
+	* apps/app_echo: Only relay a single video source change frame
+
+	  In 9785e8d0, app_echo was updated to relay video source updates to the
+	  channel for the purposes of displaying video in WebRTC tests.
+	  Unfortunately, this can cause a Kafkaesque nightmare if two or more
+	  Local channels are in a bridge together where their ends are in
+	  app_echo. When this situation occurs, a video update sent into app_echo
+	  will cause the video update to be relayed to the other Local channels,
+	  causing another round of video updates, etc. In not much time at all,
+	  the channel length queues will be overwhelmed, channel alert pipes will
+	  fail, and all hell will break loose as Asterisk merrily continues to
+	  throw more video update requests onto the channels.
+
+	  This patch updates app_echo to *only* relay a single video update. Once
+	  a video update has been made, all further video updates are dropped.
+	  This meets the intended purpose of the original patch: if we get a video
+	  update and we're in app_echo, go ahead and ask the sender to update
+	  themselves. However, once we've got that video stream sync'd up, don't
+	  keep spamming the world.
+
+	  Change-Id: I9210780b08d4c17ddb38599d1c64453adfc34f74
+
+2016-11-08 10:11 +0000 [d23b4af477]  Matt Jordan <mjordan at digium.com>
+
+	* res/ari/resource_bridges: Add the ability to manipulate the video source
+
+	  In multi-party bridges, Asterisk currently supports two video modes:
+	   * Follow the talker, in which the speaker with the most energy is shown
+	     to all participants but the speaker, and the speaker sees the
+	     previous video source
+	   * Explicitly set video sources, in which all participants see a locked
+	     video source
+
+	  Prior to this patch, ARI had no ability to manipulate the video source.
+	  This isn't important for two-party bridges, in which Asterisk merely
+	  relays the video between the participants. However, in a multi-party
+	  bridge, it can be advantageous to allow an external application to
+	  manipulate the video source.
+
+	  This patch provides two new routes to accomplish this:
+	  (1) setVideoSource: POST /bridges/{bridgeId}/videoSource/{channelId}
+	      Sets a video source to an explicit channel
+	  (2) clearVideoSource: DELETE /bridges/{bridgeId}/videoSource
+	      Removes any explicit video source, and sets the video mode to talk
+	      detection
+
+	  ASTERISK-26595 #close
+
+	  Change-Id: I98e455d5bffc08ea5e8d6b84ccaf063c714e6621
+
+2016-11-14 14:22 +0000 [404a62eeee]  gtjoseph <gjoseph at digium.com>
+
+	* Revert "Revert "channel: Use frame deferral API for safe sleep.""
+
+	  This reverts commit 58c88cfbaa80cb43419cde9186d643d1c5d24baf.
+
+	  Change-Id: I72692e2b2e83ef6da9390075ff20b138b2c374b6
+
+2016-11-14 14:22 +0000 [09d8febc91]  gtjoseph <gjoseph at digium.com>
+
+	* Revert "Revert "autoservice: Use frame deferral API""
+
+	  This reverts commit 1df434e2b4bd7cc34b9b4addf405a3caa7ac16b8.
+
+	  Change-Id: Id2b8a8bccbb4bbdd82b792275d4cd6f32563e401
+
+2016-11-14 14:21 +0000 [ffad2b44df]  gtjoseph <gjoseph at digium.com>
+
+	* Revert "Revert "AGI: Only defer frames when in an interception routine.""
+
+	  This reverts commit 6be5d8de0da7e804544507f70382425af9a07b3f.
+
+	  Change-Id: I4b548137f52ae0686d8f09e21496b778d1c6a797
+
+2016-11-14 14:21 +0000 [2fefb6187f]  gtjoseph <gjoseph at digium.com>
+
+	* Revert "Revert "Add API for channel frame deferral.""
+
+	  This reverts commit 6b5a7ced136b7178ae0b2ba39221eba1cd2e37c9.
+
+	  Change-Id: I61d1dbb2e69e1977f684b7dfc8e98211024e1cd1
+
+2016-11-14 12:16 +0000 [5e0c224043]  gtjoseph <gjoseph at digium.com>
+
+	* cli:  Fix ast_el_read_char to work with libedit >= 3.1
+
+	  Libedit 3.1 is not build with unicode on as a default and so the
+	  prototype for the el_gets callback changed from expecting a char buffer
+	  to accepting a wchar buffer.  If ast_el_read_char isn't changed,
+	  the cli reads garbage from teh terminal.
+
+	  Added a configure test for (*el_rfunc_t)(EditLine *, wchar_t *) and
+	  updated ast_el_read_char to use the HAVE_ define to detemrine whether
+	  to use char or wchar.
+
+	  ASTERISK-26592 #close
+
+	  Change-Id: I9099b46f68e06d0202ff80e53022a2b68b08871a
+
+2016-11-11 02:41 +0000 [3faca1d4ff]  Igor Goncharovskiy <igor.goncharovsky at gmail.com>
+
+	* Fix closing rtp ports after call finished in chan_unistim.
+
+	  Fix ASTERISK-26565 by adding ast_rtp_instance_stop before
+	  rtp instance destroy for chan_unistim. Also several fixes
+	  for displayed text translation.
+
+	  Change-Id: If42a03eea09bd1633471406bdc829cf98bf6affc
+
+2016-09-23 17:54 +0000 [412d43fa21]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip.c: Rework endpt_send_request() req_wrapper code.
+
+	  * Don't hold the req_wrapper lock too long in endpt_send_request().  We
+	  could block the PJSIP monitor thread if the timeout timer expires.
+	  sip_get_tpselector_from_endpoint() does a sorcery access that could take
+	  awhile accessing a database.  pjsip_endpt_send_request() might take awhile
+	  if selecting a transport.
+
+	  * Shorten the time that the req_wrapper lock is held in the callback
+	  functions.
+
+	  * Simplify endpt_send_request() req_wrapper->timeout code.
+
+	  * Removed some redundant req_wrapper->timeout_timer->id assignments.
+
+	  Change-Id: I3195e3a8e0207bb8e7f49060ad2742cf21a6e4c9
+
+2016-09-21 15:10 +0000 [2e7fc56d3c]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip: Fix tdata leaks in off nominal paths.
+
+	  Change-Id: Ie83e06e88c2d60157775263b07e40b61718ac97b
+
+2016-10-24 12:41 +0000 [da68b185b3]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_registrar_expire.c: Remove extra linefeed in debug message.
+
+	  Change-Id: I1f9adb911f23376503396ec8867e8005b755eb94
+
+2016-11-10 10:57 +0000 [b70eb07c53]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip_sdp_rtp: Reject offer of required SRTP without res_srtp.
+
+	  When optimistic SRTP was on it was possible for us to still
+	  set up a call without an audio stream if an offer was received
+	  with required SRTP.
+
+	  This change makes it so this scenario will now fail with a 488
+	  response.
+
+	  ASTERISK-26575
+
+	  Change-Id: I7d14187037681f48879bd20319ac79d0877318f3
+
+2016-11-10 08:33 +0000 [71dc333565]  Joshua Colp <jcolp at digium.com>
+
+	* app_queue: Add mention of 'ABANDON' variable to CHANGES.
+
+	  ASTERISK-26558
+
+	  Change-Id: I1127010181e79c8ac291f72f036cb8e430dc7f7e
+
+2016-11-10 07:41 +0000 [6b5a7ced13]  gtjoseph <gjoseph at digium.com>
+
+	* Revert "Add API for channel frame deferral."
+
+	  This reverts commit 9231a56cf3d6f5eca1bf2d37d827453400690773.
+	  Multiple testsuite failures were detected after the fact.
+
+	  Change-Id: I3bac8d7c3ddb69a4ddf6c5d6de0ffa5ff7ff3af7
+
+2016-11-10 07:41 +0000 [6be5d8de0d]  gtjoseph <gjoseph at digium.com>
+
+	* Revert "AGI: Only defer frames when in an interception routine."
+
+	  This reverts commit 5c10091f3d1430c6fc04015226f8c3e3aa9d8282.
+	  Multiple testsuite failures were detected after the fact.
+
+	  Change-Id: I397a841acc17ae230c512449cd6bed89d2ef3b73
+
+2016-11-10 07:41 +0000 [1df434e2b4]  gtjoseph <gjoseph at digium.com>
+
+	* Revert "autoservice: Use frame deferral API"
+
+	  This reverts commit 2e3a3545754749de21873bfdc6d1a40ec7d8893f.
+	  Multiple testsuite failures were detected after the fact.
+
+	  Change-Id: Ia45fa4633fae74dca345b24bb6722737c63035de
+
+2016-11-10 07:40 +0000 [58c88cfbaa]  gtjoseph <gjoseph at digium.com>
+
+	* Revert "channel: Use frame deferral API for safe sleep."
+
+	  This reverts commit 44f7e252397fd87420b3374df26941d7436401b3.
+	  Multiple testsuite failures were detected after the fact.
+
+	  Change-Id: I56299087da22128a95f0c8f3955f740890d7ca65
+
+2016-11-09 18:18 +0000 [a562fbe618]  gtjoseph <gjoseph at digium.com>
+
+	* build:  Fix default values for some SANITIZER options
+
+	  2 of the sanitizers didn't have default values so in systems that
+	  don't support sanitizers menuselect would spit out warnings.  They
+	  were harmless but confusing.  They've now been set to "0".
+
+	  Change-Id: I08dc495e3b83f1feac3160b421f538c375fc5d58
+
+2016-11-06 06:04 +0000 [7fd5031c1c]  Sebastian Gutierrez <sgutierrez at integraccs.com>
+
+	* app_queue: new variable set when abandoned
+
+	  sets the variable ABANDONED to TRUE if the call was not answered.
+
+	  ASTERISK-26558
+
+	  Change-Id: I4729af9bff4eba436d8a776afd3374065d0036d3
+
+2016-11-08 10:48 +0000 [e043d1a55c]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_session: Do not call session supplements when it's too late.
+
+	  res_pjsip_sesssion was hooking into transaction and invite state
+	  changes. One of the reasons for doing so was due to the
+	  PJSIP_EVENT_TX_MSG event. The idea was that we were hooking into the
+	  message sending process, and so we should call session supplements to
+	  alter the outgoing message.
+
+	  In reality, this event was meant to indicate that the message either
+	  a) had already been sent, or
+	  b) required a DNS lookup and would be sent when the DNS query
+	  completed.
+
+	  In case (a), this meant we were altering an already-sent
+	  request/response for no reason. In case (b), this potentially meant we
+	  could be trying to alter a request/response at the same time that the
+	  DNS resolution completed. In this case, it meant we might be stomping on
+	  memory being used by the thread actually sending the message. This
+	  caused potential crashes and memory corruption.
+
+	  This patch removes the calls to session supplements from the case where
+	  the PJSIP_EVENT_TX_MSG event occurs. In all of these cases, trying to
+	  alter the message at this point is too late, and it can cause nothing
+	  but harm to try to do it. Because there were no longer any calls to the
+	  handle_outgoing() function, it has been removed.
+
+	  Change-Id: Ibcc223fb1c3a237927f38754e0429e80ee301e92
+
+2016-11-03 16:46 +0000 [44f7e25239]  Mark Michelson <mmichelson at digium.com>
+
+	* channel: Use frame deferral API for safe sleep.
+
+	  This is another case where manual frame deferral can be replaced with
+	  centralized routines instead.
+
+	  Change-Id: I42cdf205f8f29a7977e599751a57efbaac07c30e
+
+2016-11-03 16:46 +0000 [2e3a354575]  Mark Michelson <mmichelson at digium.com>
+
+	* autoservice: Use frame deferral API
+
+	  Rather than use manual frame deferral, just let the channel API do it
+	  for us.
+
+	  ASTERISK-26343
+
+	  Change-Id: I688386f36e765dbc07be863943a43f26bd5eac49
+
+2016-11-03 16:42 +0000 [5c10091f3d]  Mark Michelson <mmichelson at digium.com>
+
+	* AGI: Only defer frames when in an interception routine.
+
+	  AGI recently was modified to defer important frames. This was because
+	  when AGI was used in a connected line interception routine, the
+	  resulting connected line frame would end up getting discarded by the
+	  AGI.
+
+	  However, this caused bad behavior in other cases. Specifically, during a
+	  transfer, if someone attempted to manually set the Caller ID on a
+	  channel in an AGI, the deferred connected line frame would end up
+	  overwriting what had been manually set in the AGI.
+
+	  Since the initial issue was specific to interception routines, this
+	  change removes the manual frame deferral from AGI and instead uses the
+	  new frame deferral API in interception routines.
+
+	  ASTERISK-26343 #close
+	  Reported by Morton Tryfoss
+
+	  Change-Id: Iab7d39436d0ee99bfe32ad55ef91e9bd88db4208
+
+2016-11-03 16:36 +0000 [9231a56cf3]  Mark Michelson <mmichelson at digium.com>
+
+	* Add API for channel frame deferral.
+
+	  There are several places in Asterisk that have duplicated logic
+	  for deferring important frames until later.
+
+	  This commit adds a couple of API calls to facilitate this automatically.
+
+	  ast_channel_start_defer_frames(): Future reads of deferrable frames on
+	  this channel will be deferred until later.
+
+	  ast_channel_stop_defer_frames(): Any frames that have been deferred get
+	  requeued onto the channel.
+
+	  ASTERISK-26343
+
+	  Change-Id: I3e1b87bc6796f222442fa6f7d1b6a4706fb33641
+
+2016-11-03 07:42 +0000 [a9ac1f5de4]  Alexander Anikin <may213 at yandex.ru>
+
+	* chan_ooh323: Fixes to work right with Cisco devices
+
+	  Changed output packets queue processing algo to one read-one write
+	  instead of all read-all send
+
+	  Remove h.245 tunneling parameter from ReleaseComplete packet
+
+	  ASTERISK-24400 #close
+	  Reported by: Dmitry Melekhov
+	  Tested by: Dmitry Melekhov
+
+	  Change-Id: I0b31933b062a21011dbac9a82b8bcfe345f406f6
+
+2016-11-03 13:10 +0000 [0ee249075a]  Alexander Anikin <may213 at yandex.ru>
+
+	* chan_ooh323: reset rrq count on gk registration
+
+	  reset registration attempts count on success registration on gatekeeper
+
+	  Change-Id: I5f47351852e0ca76c9ac78421659600e0f106336
+
+2016-11-06 03:46 +0000 [59c23e1768]  Michael Kuron <m.kuron at gmx.de>
+
+	* automon: restore mixing of the both channels after recording stops
+
+	  This is a regression over Asterisk 11, introduced by
+	  2dc8a060064f359a17f5ebcd515d85fe5203c019. Previously, recordings started via
+	  the automon DTMF code would automatically be mixed together using sox because
+	  app_monitor would be called with the m option. This commit restores this
+	  behavior.
+
+	  Change-Id: Ibaf58684285c3f1b6ca3714524e6d638ae3b3759
+
+2016-11-04 15:42 +0000 [e79acaeb75]  Matt Jordan <mjordan at digium.com>
+
+	* res_http_websocket: Increase the buffer size for non-LOW_MEMORY systems
+
+	  Not surprisingly, using Respoke (and possibly other systems) it is
+	  possible to blow past the 16k limit for a WebSocket packet size. This
+	  patch bumps it up to 32k, which, at least for Respoke, is sufficient.
+	  For now.
+
+	  Because 32k is laughable on a LOW_MEMORY system (as is 16k, for that
+	  matter), this patch adds a LOW_MEMORY directive that sets the buffer to
+	  8k for systems who have asked for their reduced memory availability to
+	  be considered.
+
+	  Change-Id: Id235902537091b58608196844dc4b045e383cd2e
+
+2016-11-04 15:40 +0000 [7a83196985]  Matt Jordan <mjordan at digium.com>
+
+	* res_stasis: Set a video source mode on Stasis created bridges
+
+	  When a bridge is created via ARI (through res_stasis), no video source
+	  mode is set by default. As a result, any endpoint sending video media
+	  won't ever see any video reflected back to it.
+
+	  This patch defaults a bridge to a 'follow the talker' video mode.
+	  Further work can be done to add routes that allow for the video mode to
+	  be controlled through the /bridges resource.
+
+	  Change-Id: I7e9d530a5d7a97a4524a9ee4e468e1a6b3443866
+
+2016-11-04 15:37 +0000 [e7dc536b7a]  Matt Jordan <mjordan at digium.com>
+
+	* main/bridge_channel: Fix channel reference leak on video source
+
+	  When a channel is made the video source, the bridge holds a reference to
+	  it. Whenever the video source changes, that reference is released.
+	  However, a ref leak does occur if the channel leaves the bridge (such as
+	  being hung up) while it is the video source, as the bridge never
+	  releases the ref in such a case.
+
+	  This patch adds a line to the bridge_channel_internal_join routine such
+	  that, when a channel finishes its time in the bridge, it notifies the
+	  bridge via ast_bridge_remove_video_src that if it is a video source its
+	  reference should be released.
+
+	  ASTERISK-26555 #close
+
+	  Change-Id: I3a2f5238a9d2fc49c591f0e65199d782ab0be76a
+
+2016-11-04 15:36 +0000 [7c824b955d]  Matt Jordan <mjordan at digium.com>
+
+	* main/bridge: Add some verbose logging for video source changes
+
+	  It's actually quite useful to see the source of a video stream change.
+	  This doesn't happen terribly often, even with talk detection - but when
+	  it does, it's nice to know which channel is now providing your video
+	  stream.
+
+	  As a verbose 5 level message, it shouldn't be terribly spammy or costly
+	  to have, and is 'lower level' then most other verbose messages that the
+	  bridge system emits.
+
+	  ASTERISK-26555
+
+	  Change-Id: Ia1c20ecafa9670171fd38bddcf3beccae47fb15c
+
+2016-11-04 15:33 +0000 [fd6af2dee8]  Matt Jordan <mjordan at digium.com>
+
+	* bridges/bridge_softmix: Remove SSRC changes on join/leave; update video source
+
+	  WebRTC clients really, really want to know the SSRC of the media they're
+	  getting. Changing the SSRC is generally not a good thing.
+
+	  bridge_softmix, starting in Asterisk 12, started changing the SSRC of
+	  parties as they joined or left the bridge. With most phones, this isn't
+	  a problem: phones just play back the stream they're getting. With WebRTC
+	  clients, however, the SSRC is tied to a media stream that may be
+	  negotiated. When a new SSRC just shows up, the media can be dropped.
+
+	  As it turns out, the SSRC change shouldn't even be necessary. From the
+	  perspective of the client, it's still talking to Asterisk with the same
+	  media stream: why indicate that the far party has suddenly changed to a
+	  different source of media?
+
+	  This patch opts to just remove the SSRC changes. With this patch, video
+	  clients that join/leave a softmix bridge actually get the video stream
+	  instead of freaking out.
+
+	  ASTERISK-26555
+
+	  Change-Id: I27fec098b32e7c8718b4b65f3fd5fa73527968bf
+
+2016-10-28 15:11 +0000 [bd4d7d8ad0]  Kevin Harwell <kharwell at digium.com>
+
+	* stasis_recording/stored: remove calls to deprecated readdir_r function.
+
+	  The readdir_r function has been deprecated and should no longer be used. This
+	  patch removes the readdir_r dependency (replaced it with readdir) and also moves
+	  the directory search code to a more centralized spot (file.c)
+
+	  Also removed a strict dependency on the dirent structure's d_type field as it
+	  is not portable. The code now checks to see if the value is available. If so,
+	  it tries to use it, but defaults back to using the stats function if necessary.
+
+	  Lastly, for most implementations of readdir it *should* be thread-safe to make
+	  concurrent calls to it as long as different directory streams are specified.
+	  glibc falls into this category. However, since it is possible that there exist
+	  some implementations that are not safe, locking has been added for those other
+	  than glibc.
+
+	  ASTERISK-26412
+	  ASTERISK-26509 #close
+
+	  Change-Id: Id8f54689b1e2873e82a09d0d0d2faf41964e80ba
+
+2016-11-04 10:57 +0000 [cb30963d22]  Kevin Harwell <kharwell at digium.com>
 
 	* Revert "chan_sip: Fix lastrtprx always updated"
 
@@ -16,11 +715,364 @@
 
 	  Change-Id: Ibf5586adc303073a8eac667a4cbfdb6be184a64d
 
-2016-10-27 18:48 +0000  Asterisk Development Team <asteriskteam at digium.com>
+2016-11-02 10:52 +0000 [3a1f9c5dab]  Joshua Colp <jcolp at digium.com>
+
+	* res_stasis: Don't unsubscribe from a NULL bridge.
+
+	  A NULL bridge has special meaning in res_stasis for
+	  unsubscribing. It means that a subscription to ALL
+	  bridges should be removed. This should not be done
+	  as part of the normal subscription management in
+	  the res_stasis channel loop.
+
+	  ASTERISK-26468
+
+	  Change-Id: I6d5bea8246dd13a22ef86b736aefbf2a39c15af0
+
+2016-11-03 13:45 +0000 [eceab15f33]  Alexander Anikin <may213 at yandex.ru>
+
+	* chan_ooh323: Fix infinite loop on read second part of H.225 packet
+
+	  Fix logic on read second part of H.225 packet. There was infinite loop on
+	  wrong connections due to read before poll.
+
+	  Change-Id: I42b4bf75c46e4a5c5df5c5ca1f0bd74b8944e7ff
+
+2016-11-03 11:55 +0000 [a9992da4aa]  gtjoseph <gjoseph at digium.com>
+
+	* pjproject_bundled:  Fix issue with libasteriskpj needing libresample
+
+	  libresample is only needed by pjproject if we're building pjsua, which
+	  we only do if TEST_FRAMEWORK is selected.  It's required by pjsua to
+	  process audio which is needed by some testsuite tests.  Unfortunately,
+	  pjproject relies on a newer version of libresample than the version
+	  that ships by most distros so we need to compile the version that's
+	  bundled with pjproject.  Since we only need it for pjsua, we DON'T want
+	  it's symbols exposed when we actually build asterisk.
+
+	  There was a problem however... TEST_FRAMEWORK is only known AFTER we've
+	  already run ./configure on both asterisk and pjproject but pjproject's
+	  ./configure needs to test it to know whether to set up to build
+	  libresample or not.  The previous way of figuring this out was to
+	  always tell ./configure "yes" but not actually build the library.  This
+	  caused an issue where building libasteriskpj was being told to include
+	  libresample but it wasn't actually there.
+
+	  The solution is to still do a default pjproject configure during an
+	  asterisk ./configure but if makeopts or menuselect.makeopts changes
+	  subsequently, we now reconfigure pjproject, taking into account the
+	  current state of TEST_FRAMEWORK.  Previously, if makeopts or
+	  menuselect.makeopts changed, only a recompile of pjproject was done.
+
+	  Change-Id: I9b5d84c61384a3ae07fe30e85c49698378cc4685
+
+2016-11-01 19:48 +0000 [714412f6c4]  Sebastian Gutierrez <sgutierrez at integraccs.com>
+
+	* chan_sip: add missing account code
+
+	  Added missing account to AMI event of sip show peers
+
+	  ASTERISK-26176 #close
+
+	  Change-Id: Ieb6c2c80a838a1b59c82103eba4c63ba238dc482
+
+2016-09-13 04:08 +0000 [0cf1778eed]  Alexander Traud <pabstraud at compuserve.com>
+
+	* rtp_engine: Allow more than 32 dynamic payload types.
+
+	  The dynamic range (96-127) allows 32 RTP Payload Types. RFC 3551 section 3
+	  allows to reassign other ranges. Consequently, when the dynamic range is
+	  exhausted, you can go for "rtp_pt_dynamic = 35" (or 0) in asterisk.conf. This
+	  enables the range 35-63 (or 0-63) giving room for another 29 (or 64) payload
+	  types.
+
+	  ASTERISK-26311 #close
+
+	  Change-Id: I7bc96ab764bc30098a178b841cbf7146f9d64964
+	  (cherry picked from commit 9ac53877f688c06acaa7c377f15da8770e4ee88b)
+
+2016-11-02 09:15 +0000 [d971647949]  Joshua Colp <jcolp at digium.com>
+
+	* app_dial: Fix incorrect device state when channel is picked up.
+
+	  Given the scenario where multiple channels are dialed using Dial()
+	  but the caller is picked up using PickupChan() all outgoing channels
+	  except the channel specified to PickupChan() would be marked
+	  as ringing until the call had been hung up.
+
+	  When using the PickupChan application the channel executing the
+	  application is swapped into place of another channel. As part
+	  of this process the channel is answered. The Dial application
+	  has explicit logic which checks if the channel is answered,
+	  cancels all other outgoing channels, and bridges. This logic is
+	  different than the normal logic that is executed when an outgoing
+	  channel is answered. This different logic failed to publish dial
+	  events stating that the other outgoing channels had been canceled.
+	  As a result references to the outgoing channels were held onto by
+	  the dial masquerade process until the call had been ended and
+	  the channels had gone away. This would result in the channels
+	  appearing in the "core show channels" list despite not being present
+	  anymore and would also result in incorrect device state.
+
+	  This change makes it so that this logic also publishes
+	  dial events stating that the other outgoing channels have been
+	  canceled.
+
+	  ASTERISK-26549
+
+	  Change-Id: Iea7168e6e82f7d4609ec0366153804e4f55ea64f
+
+2016-11-01 13:13 +0000 [afecb2cfc0]  Richard Mudgett <rmudgett at digium.com>
+
+	* bundled pjproject: Fix DNS write to freed memory.
+
+	  PJPROJECT 2.5.5 introduced a race condition with the -r5349 IPv6 DNS
+	  patch.
+
+	  The patch below fixes a write to freed memory under cartain DNS lookup
+	  conditions.
+
+	  0006-r5477-svn-backport-Fix-DNS-write-on-freed-memory.patch
+
+	  ASTERISK-26516
+	  Reported by:  Richard Mudgett
+
+	  Change-Id: Ifdfae9ecf1e41b53080f33aab44ce1a220f349c5
+
+2016-11-01 06:56 +0000 [5f188bb7a8]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip_sdp_rtp: Limit number of formats to defined maximum.
+
+	  The res_pjsip_sdp_rtp module did not restrict the number of
+	  formats added to a media stream in the SDP to the defined
+	  limit. If allow=all was used with additional loaded codecs this
+	  could result in the next media stream being overwritten some.
+
+	  This change restricts the module to limit it to the defined
+	  maximum and also increases the maximum in our bundled pjproject.
+
+	  ASTERISK-26541 #close
+
+	  Change-Id: I0dc5f59d3891246cafa2f3df5ec406f088559ee8
+
+2016-11-01 04:18 +0000 [94c9496ed5]  Tzafrir Cohen <tzafrir.cohen at xorcom.com>
+
+	* netsock.c: fix includes for HURD
+
+	  ASTERISK-25070
+
+	  Change-Id: I43bf94d2d36d3d8a8d0df40cd6c027d65a462814
+
+2016-11-01 04:00 +0000 [c1c9487375]  Tzafrir Cohen <tzafrir.cohen at xorcom.com>
+
+	* define PATH_MAX for HURD
+
+	  PATH_MAX is not guaranteed to be defined. In parctice, all but the HURD
+	  define it to a constant. It is indeed not safe to assume there won't be
+	  longer paths and Asterisk generally does err safely on such cases.
+
+	  So even for HURD we'll just pretend PATH_MAX is 4096.
+
+	  ASTERISK-25070 #close
+
+	  Change-Id: I53d10ba18c34c132bcb640a5fd8e0da1d9b22db3
+
+2016-10-31 17:35 +0000 [50fa868ab8]  Kevin Harwell <kharwell at digium.com>
+
+	* codecs.conf.sample: Add sample and option descriptions for codec_opus
+
+	  codecs.conf.sample was missing codec opus's configuration options, descriptions,
+	  and examples. This patch adds the configuration options and examples to
+	  codecs.conf.sample that can be used with codec_opus.
+
+	  ASTERISK-26538 #close
+
+	  Change-Id: I1d89bb5e01d3e3b5bd78951b8dd0ff077a83dc8b
+
+2016-11-01 08:32 +0000 [b3f10b7b94]  Grachev Sergey <grachev at mcn.ru>
+
+	* chan_sip: Incorrect display option Outbound reg. retry 403
+
+	  If in sip.conf (general section) set option register_retry_403=no,
+	  the command "sip show settings" return value:
+	  Outbound reg. retry 403:0
+	  If in sip.conf (general section) set option register_retry_403=yes,
+	  the command "sip show settings" return value:
+	  Outbound reg. retry 403:-1
+
+	  * In static char "sip show settings" for "Outbound.reg. retry 403"
+	  option use AST_CLI_YESNO
+
+	  ASTERISK-26476 #close
+
+	  Change-Id: I3c14272f05f1067bd2aeaa8b3ef9cf8fcb12dcf9
+
+2016-10-20 07:27 +0000 [29692d4aa4]  Matt Jordan <mjordan at digium.com>
+
+	* res/stasis: Add CLI commands for displaying/debugging ARI apps
+
+	  This patch adds three new CLI commands:
+	   - ari show apps: list the registered ARI applications
+	   - ari show app: show detailed information about an ARI application
+	   - ari set debug: dump events being sent to an ARI application
+
+	  Note that while these CLI commands live in the res_stasis module, we use
+	  the 'ari' family for these commands. This was done as most users of
+	  Asterisk aren't aware of the semantic differences between ARI and
+	  res_stasis, and some 'ari' CLI commands already exist.
+
+	  ASTERISK-26488 #close
+
+	  Change-Id: I51ad6ff0cabee0d69db06858c13f18b1c513c9f5
+
+2016-10-31 16:12 +0000 [a36a7d0cf4]  gtjoseph <gjoseph at digium.com>
+
+	* pjproject_bundled:  Fix compile of pjsua so it handles audio
+
+	  In order for pjsua and its python binding to actually negotiate
+	  audio for the testsuite tests, it needs g711 and resample.  The
+	  pj* libraries themselves do not.  Unfortunately, pjproject relies
+	  on a brand new libresample that most distros don't ship so we need
+	  to use the libresample already bundled with pjproject.  Only the pjsua
+	  executable and the _pjsua.so python library are linked with it so it
+	  shouldn't interfere with asterisk itself.
+
+	  Also it was pointed out that apply_patches couldn't handle multiple
+	  patches that depended on each other during the dry-run, so the
+	  dry-run was removed.
+
+	  Change-Id: I24f397462b486dcdde0dcafe40e6c55a6593f098
+
+2016-10-31 13:46 +0000 [42bd70b29f]  Etienne Lessard <elessard at proformatique.com>
+
+	* manager: Add documentation for NewConnectedLine event.
+
+	  The NewConnectedLine event has been added by commit fe7671f, but the
+	  documentation was missing.
+
+	  ASTERISK-26537 #close
+
+	  Change-Id: I7fc331f18caa28492da9303e576f70884ca8c9e6
+
+2016-10-30 13:33 +0000 [30b1bc77d2]  Corey Farrell <git at cfware.com>
+
+	* vector: Prevent NULL argument to memcpy.
+
+	  Headers declare that memcpy does not accept NULL argument for the first
+	  two parameters.  Add a conditional block to prevent memcpy and ast_free
+	  from running on vectors with NULL element array.
+
+	  ASTERISK-26526 #close
+
+	  Change-Id: I988a476bb5fcfcbd3f6d6c6b3e7769e4f9629b71
+
+2016-10-29 10:31 +0000 [b96f18560b]  Corey Farrell <git at cfware.com>
+
+	* astobj2: Declare private variable data_size for AO2_DEBUG only.
+
+	  Every ao2 object contains storage for a private variable data_size,
+	  though the value is never read if AO2_DEBUG is disabled.  This change
+	  makes the variable conditional, reducing memory usage.
+
+	  ASTERISK-26524 #close
+
+	  Change-Id: If859929e507676ebc58b0f84247a4231e11da07f
+
+2016-10-28 16:59 +0000 [6b1c55dc9b]  gtjoseph <gjoseph at digium.com>
+
+	* pjproject_bundled:  Fix issue where "/version.mak" wasn't found
+
+	  main/Makefile includes third-party/pjproject/build.mak but
+	  doesn't set PJDIR beforehand so "include $(PJDIR)/version.mak"
+	  evaluates to "/version.mak".  Fix is to set PJDIR in main/Makefile
+	  before the include.
+
+	  Change-Id: I0f7c67d60209049056fe9c4b041bf0463aa95604
+
+2016-10-28 14:55 +0000 [d7f457e4c1]  Richard Mudgett <rmudgett at digium.com>
+
+	* bundled pjproject: Crashes while resolving DNS names.
+
+	  PJPROJECT 2.5.5 introduced a race condition with the -r5349 IPv6 DNS
+	  patch.
+
+	  The patches below fix the DNS lookup race condition crash caused by
+	  attempting to send the same message twice for the single DNS lookup.
+
+	  0006-r5471-svn-backport-Various-fixes-for-DNS-IPv6.patch
+	  0006-r5473-svn-backport-Fix-pending-query.patch
+
+	  The patch below removes a cached DNS response from the hash table when
+	  another thread is referencing the old entry.  The table still contained
+	  the entry when it was destroyed which can result in inexplicable crashes.
+
+	  0006-r5475-svn-backport-Remove-DNS-cache-entry.patch
 
-	* asterisk 13.12.1 Released.
+	  ASTERISK-26344 #close
+	  Reported by: Ian Gilmour
 
-2016-10-26 07:51 +0000 [9c761b8f45]  Joshua Colp <jcolp at digium.com>
+	  ASTERISK-26387 #close
+	  Reported by: Harley Peters
+
+	  Change-Id: I17fde80359e66f65a91341ceca58d914d0f61cc4
+
+2016-10-28 09:50 +0000 [87903a6848]  Rusty Newton <rnewton at digium.com>
+
+	* SAC documentation: don't specify transports for endpoints and registrations
+
+	  Removing explicit transport definition for endpoints and registrations. It
+	  isn't necessary and isn't generally advised.
+
+	  ASTERISK-26514 #close
+
+	  Change-Id: Ifdec5e631962438a4683600968dfa4bfd15909fb
+
+2016-10-27 21:49 +0000 [f373de3020]  Corey Farrell <git at cfware.com>
+
+	* Fix shutdown crash caused by modules being left open.
+
+	  It is only safe to run ast_register_cleanup callbacks when all modules
+	  have been unloaded.  Previously these callbacks were run during graceful
+	  shutdown, making it possible to crash during shutdown.
+
+	  ASTERISK-26513 #close
+
+	  Change-Id: Ibfa635bb688d1227ec54aa211d90d6bd45052e21
+
+2016-10-26 18:48 +0000 [61a5c3460e]  gtjoseph <gjoseph at digium.com>
+
+	* pjproject_bundled:  Remove usage of tar's --strip-components option
+
+	  Older versions of tar don't support the --strip-components option so
+	  instead of doing 'tar --strip-components=1 -C source', we now just
+	  untar to the tarball's root directory (pjproject-<version>) and
+	  rename that directory to 'source'.
+
+	  Also fixed an issue where the pjproject source directory is a hard
+	  coded absolute pathname.
+
+	  ASTERISK-26510 #close
+	  ASTERISK-22480 #close
+
+	  Change-Id: I9ec92952507a91ff4e4d01e0149e09fd8e8f32b0
+
+2016-10-27 08:07 +0000 [675c71ae8c]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip_caller_id: Fix crash on session timers UPDATE on inbound calls.
+
+	  The res_pjsip_caller_id module wrongly assumed that a
+	  saved From header would always exist on sessions. This
+	  is true until an inbound call is received and a session
+	  timer causes an UPDATE to be sent. In this case there will
+	  be no saved From header and a crash will occur. This change
+	  makes it fall back to the From header of the outgoing request
+	  if no saved From header is present.
+
+	  ASTERISK-26307 #close
+
+	  Change-Id: Iccc3bc8d243b5ede9b81abf960292930c908d4fa
+
+2016-10-26 07:51 +0000 [14496ce1e5]  Joshua Colp <jcolp at digium.com>
 
 	* app_voicemail: Clear voice mailbox in MailboxExists and MAILBOX_EXISTS.
 
@@ -32,9 +1084,295 @@
 
 	  Change-Id: Ie21ccfa1b80b9c59318e596f6b8e17da2b5a7cb3
 
-2016-10-25 19:13 +0000  Asterisk Development Team <asteriskteam at digium.com>
+2016-10-23 07:38 +0000 [e0bc17edff]  Joshua Colp <jcolp at digium.com>
+
+	* pjsip: Fix a few media bugs with reinvites and asymmetric payloads.
+
+	  When channel format changes occurred as a result of an RTP
+	  re-negotiation the bridge was not informed this had happened.
+	  As a result the bridge technology was not re-evaluated and the
+	  channel may have been in a bridge technology that was incompatible
+	  with its formats. The bridge is now unbridged and the technology
+	  re-evaluated when this occurs.
+
+	  The chan_pjsip module also allowed asymmetric codecs for sending
+	  and receiving. This did not work with all devices and caused one
+	  way audio problems. The default has been changed to NOT do this
+	  but to match the sending codec to the receiving codec. For users
+	  who want asymmetric codecs an option has been added, asymmetric_rtp_codec,
+	  which will return chan_pjsip to the previous behavior.
+
+	  The codecs returned by the chan_pjsip module when queried by
+	  the bridge_native_rtp module were also not reflective of the
+	  actual negotiated codecs. The nativeformats are now returned as
+	  they reflect the actual negotiated codecs.
+
+	  ASTERISK-26423 #close
+
+	  Change-Id: I6ec88c6e3912f52c334f1a26983ccb8f267020dc
+
+2016-10-26 06:32 +0000 [f534f67f52]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip_sdp_rtp: Fix address family of explicit media_address.
+
+	  When an explicit media_address is provided the address family
+	  in the SDP needs to be set to reflect it.
+
+	  ASTERISK-26309
+
+	  Change-Id: Ib9350cc91c120eb2f96f0623d3907d12af67eb79
+
+2016-10-25 11:20 +0000 [3a2092b722]  gtjoseph <gjoseph at digium.com>
+
+	* test_astobj2_thrash:  Fix multithreaded issues
+
+	  The test uses 4 threads to grow, count, lookup and shrink 15K objects
+	  in a container.  If there's only 1 execution engine available, the test
+	  will complete in <50ms.  If each threads gets its own execution engine,
+	  the test may timeout after 60 seconds because the count thread does a
+	  locked ao2_callback on the whole container in a tight loop with only
+	  a sched_yield to give up time.  The lock contention makes the test
+	  execution times wildly variable and mostly timeout.  2 execution
+	  engines are OK, 3 results in about 33% failure rate and >=4 causes
+	  a 80% failure rate.
+
+	  To fix, the sched_yield was changed to a usleep(500).
+
+	  Also, the number of buckets specified for the container was an even
+	  number so that was changed to the next prime number greater than
+	  (MAX_HASH_ENTRIES / 100).  That's 151 currently.
+
+	  Change-Id: I50cd2344161ea61bfe4b96d2a29a6ccf88385c77
+
+2016-10-24 14:13 +0000 [640203802e]  Pascal Cadotte Michaud <pcadotte at proformatique.com>
+
+	* typo: s/paranthesis/parenthesis/ in a comment
+
+	  Change-Id: I7c1f4eb051177ee22cbe97e063d4a3effe29be30
+
+2016-10-24 10:55 +0000 [9b3557e054]  gtjoseph <gjoseph at digium.com>
+
+	* pjproject_bundled:  Fixed various build issues
+
+	  * CFLAGS is now properly set when using older gcc.
+	  * All third-party pjproject targets have been removed.  This fixes
+	    an issue with older libsrtp in some distros.
+	  * Manually removing the source directory now causes a rebuild.
+	  * EXTERNALS_CACHE_DIR is now properly checked.
+	  * Whitespace fixes.
+
+	  Change-Id: I98fec6847efc5602a9f41cb95096fd660a49fa60
+
+2016-09-19 06:13 +0000 [bb982480d8]  Joshua Colp <jcolp at digium.com>
+
+	* pjsip: Support dual stack automatically.
+
+	  This change adds support for dual stack automatically. No
+	  configuration is required and the IP address and version
+	  in the SIP messages and SDP will be automatically changed
+	  based on the transport over which the message is being
+	  sent. RTP usage has also been changed to listen on both
+	  IPv4 and IPv6 simultaneously to allow media to flow, and
+	  to allow ICE support on both simultaneously. This also
+	  allows failover between IPv6 and IPv4 to work as expected.
+
+	  ASTERISK-26309 #close
+
+	  Change-Id: I235a421d8f9a326606d861b449fa6fe3a030572d
+
+2016-10-17 14:18 +0000 [eff97808fb]  Mark Michelson <mmichelson at digium.com>
+
+	* ARI: Detect duplicate channel IDs
+
+	  ARI and AMI allow for an explicit channel ID to be specified
+	  when originating channels. Unfortunately, there is nothing in
+	  place to prevent someone from using the same ID for multiple
+	  channels. Further complicating things, adding ID validation to channel
+	  allocation makes it impossible for ARI to discern why channel allocation
+	  failed, resulting in a vague error code being returned.
+
+	  The fix for this is to institute a new method for channel errors to be
+	  discerned. The method mirrors errno, in that when an error occurs, the
+	  caller can consult the channel errno value to determine what the error
+	  was. This initial iteration of the feature only introduces "unknown" and
+	  "channel ID exists" errors. However, it's possible to add more errors as
+	  needed.
+
+	  ARI uses this feature to determine why channel allocation failed and can
+	  return a 409 error during origination to show that a channel with the
+	  given ID already exists.
+
+	  ASTERISK-26421
+
+	  Change-Id: Ibba7ae68842dab6df0c2e9c45559208bc89d3d06
+
+2016-10-19 17:53 +0000 [c2036c827c]  snuffy <snuffy22 at gmail.com>
+
+	* Fix issue with CLI not returning to prompt after running "features show"
+
+	  ASTERISK-26444 #close
+
+	  Change-Id: I91d645b7e6e5dba35f8c410df2be77a8c0e3acb8
+
+2016-10-04 18:24 +0000 [3c62b60e56]  Michael Walton <mike at farsouthnet.com>
+
+	* res_rtp_asterisk: Add ice_blacklist option
+
+	  Introduces ice_blacklist configuration in rtp.conf. Subnets listed in the
+	  form ice_blacklist = <subnet spec>, e.g. ice_blacklist =
+	  192.168.1.0/255.255.255.0, are excluded from ICE host, srflx and relay
+	  discovery. This is useful for optimizing the ICE process where a system
+	  has multiple host address ranges and/or physical interfaces and certain
+	  of them are not expected to be used for RTP. Multiple ice_blacklist
+	  configuration lines may be used. If left unconfigured, all discovered
+	  host addresses are used, as per previous behavior.
+
+	  Documention in rtp.conf.sample.
+
+	  ASTERISK-26418 #close
+
+	  Change-Id: Ibee88f80d7693874fda1cceaef94a03bd86012c9
+
+2016-10-18 16:30 +0000 [012fda29d2]  Mark Michelson <mmichelson at digium.com>
+
+	* CDR: Alter destruction pattern for CDR chains.
+
+	  CDRs form chains. When the root of the chain is destroyed, it then
+	  unreferences the next CDR in the chain. That CDR is destroyed, and it
+	  then unreferences the next CDR in the chain. This repeats until the end
+	  of the chain is reached. While this typically does not cause any sort of
+	  problems, it is possible in strange scenarios for the CDR chain to grow
+	  way longer than expected. In such a scenario, the destruction pattern
+	  can result in a stack overflow.
+
+	  This patch fixes the problem by switching from a recursive pattern to an
+	  iterative pattern for destruction. When the root CDR is destroyed, it is
+	  responsible for iterating over the rest of the CDRs and unreferencing
+	  each one. Other CDRs in the chain, since they are not the root, will
+	  simply destroy themselves and be done. This causes the stack depth not
+	  to increase.
+
+	  ASTERISK-26421 #close
+	  Reported by Andrew Nagy
+
+	  Change-Id: I3ca90c2b8051f3b7ead2e0e43f60d2c18fb204b8
+
+2016-10-18 09:04 +0000 [6d462b9eaf]  Alexei Gradinari <alex2grad at gmail.com>
+
+	* chan_pjsip: segfault on already disconnected session
+
+	  On heavy loaded system the TCP/TLS incoming calls could be
+	  disconnected by pjproject while these calls are being
+	  processed by asterisk.
+
+	  This patch uses functions pjsip_inv_add_ref/pjsip_inv_dec_ref
+	  to inform pjproject that an INVITE session is in use.
+
+	  ASTERISK-26482 #close
+
+	  Change-Id: Ia2e3e2f75358cdb530252a9ce158af3d5d9fdf33
+
+2016-10-18 03:01 +0000 [662b560c35]  Alexander Traud <pabstraud at compuserve.com>
+
+	* cli: Auto-complete File not Module for core set debug.
+
+	  Since Asterisk 1.8, the command "core set debug" on the command-line interface
+	  asks not for a file (.c) but a module name. This change shows modules (.so) on
+	  the auto-completion via a tabulator or the question mark. Now, when you
+	  partially type a module name, TAB or ?, you get the correct candidiates.
+
+	  ASTERISK-26480
+
+	  Change-Id: I1213f1dd409bd4ff8de08ad80cb0c73cafb1bae0
+
+2016-09-11 10:13 +0000 [6f5880913f]  Tzafrir Cohen <tzafrir.cohen at xorcom.com>
+
+	* menuselect: invalid test for GTK2
+
+	  configuire.ac was only checking for the existence of pkg-config
+	  and not the gtk2 package itself.  Now it calls AST_PKG_CONFIG_CHECK
+	  for gtk+-2.0.
+
+	  ASTERISK-26356 #close
+
+	  Change-Id: I8079d515d6ea99f9ab320a7eaa71c2aaa101ccd5
+
+2016-10-13 02:06 +0000 [644fad7477]  Moises Silva <moises.silva at gmail.com>
+
+	* chan_rtp: Set a sane default rtp engine for unicast.
+
+	  ASTERISK-26439
+
+	  Change-Id: I7f5ee2eeba8906e9ecb3293dbe3a747770bb5011
+
+2016-10-15 20:05 +0000 [42cfdcd1b7]  Matt Jordan <mjordan at digium.com>
+
+	* res/ari: Add the Asterisk EID field to outgoing events
+
+	  This patch adds the Asterisk EID field to all outgoing ARI events.
+	  Because this field should be added to all events as they are
+	  transmitted, it is appended to the JSON message just prior to it being
+	  handed off to the application message handler. This makes it somewhat
+	  resilient to both new events being added to ARI, as well as other
+	  potential event transport mechanisms.
+
+	  ASTERISK-26470 #close
+
+	  Change-Id: Ieff0ecc24464e83f3f44e9c3e7bd9a5d70b87a1d
+
+2016-10-16 17:25 +0000 [74d9385273]  gtjoseph <gjoseph at digium.com>
+
+	* utils.c:  Fix ast_set_default_eid for multiple platforms
+
+	  ast_set_default_eid was searching for ethX, emX, enoX, ensX and even
+	  pciD#U interface names.  While this was a good attempt, it wasn't
+	  inclusive enough to capture interfaces like enp6s0 or ens6d1, etc.
+
+	  Rather than relying on interface names, we now simply find the first
+	  interface returned by the OS that has a hardware address and that
+	  address isn't all 0x00 or all 0xff.  The code IS different for BSD,
+	  Solaris and Linux based on what method is available for enumerating
+	  interfaces.
+
+	  Tested on:
+	  FreeBSD9
+	  CentOS6
+	  Ubuntu14
+	  Fedora24
+
+	  I was unable to test on Solaris at this time but the code for Solaris
+	  is used elsewhere at Digium.
+
+	  Change-Id: Iaa6db87ca78a9a375e47d70e043ae08c1448cb72
+
+2016-10-13 14:09 +0000 [0306869399]  Leandro Dardini <ldardini at gmail.com>
+
+	* app_queue: Added initialization for "context" parameter
+
+	  When using Asterisk Realtime Architecture, empty fields are skipped and the
+	  default values are used. If the "context" parameter in queue was set and then
+	  cleared from the database, the old value remains in memory and it continues
+	  to be used. This change initialize the "context" parameter with an empty value,
+	  allowing clearing the parameter.
+
+	  ASTERISK-26462 #close
+
+	  Change-Id: I64be73d5044ce38dd02408bd0e53de965ef65905
+
+2016-10-11 06:55 +0000 [a859bcb49c]  Alexander Traud <pabstraud at compuserve.com>
+
+	* chan_sip: Support nat=auto_comedia or nat=force_rport,auto_comedia.
+
+	  In the SIP channel driver chan_sip, auto_comedia was expected to be used in
+	  tandem with auto_force_rport. Or stated differently: Only when auto_force_rport
+	  was chosen (the default), auto_comedia worked. This change allows auto_comedia
+	  to be set independently of the state of (auto_)force_rport. For example,
+	  nat=force_rport,auto_comedia is useful for IPv4/IPv6 Dual Stack deployments
+	  when IPv6 clients are behind a Firewall.
+
+	  ASTERISK-26457 #close
 
-	* asterisk 13.12.0 Released.
+	  Change-Id: Ib29d66c6dbb61648e371e01fc36c6978ddae5bc2
 
 2016-10-17 19:08 +0000  Asterisk Development Team <asteriskteam at digium.com>
 
diff --git a/addons/ooh323c/src/ooCalls.c b/addons/ooh323c/src/ooCalls.c
index 70677ab..f43a0bf 100644
--- a/addons/ooh323c/src/ooCalls.c
+++ b/addons/ooh323c/src/ooCalls.c
@@ -223,7 +223,8 @@ int ooEndCall(OOH323CallData *call)
       call->callState = OO_CALL_CLEARED;
    }
 
-   if(call->callState == OO_CALL_CLEARED || call->callState == OO_CALL_CLEAR_RELEASESENT)
+   if(call->callState == OO_CALL_CLEARED || ((strcmp(call->callType, "incoming")) &&
+     call->callState == OO_CALL_CLEAR_RELEASESENT))
    {
       ooCleanCall(call); 
       call->callState = OO_CALL_REMOVED;
diff --git a/addons/ooh323c/src/ooGkClient.c b/addons/ooh323c/src/ooGkClient.c
index c090796..1262f29 100644
--- a/addons/ooh323c/src/ooGkClient.c
+++ b/addons/ooh323c/src/ooGkClient.c
@@ -1272,6 +1272,7 @@ int ooGkClientHandleRegistrationConfirm
       }
    }
    pGkClient->state = GkClientRegistered;
+   pGkClient->rrqRetries = 0;
    if(pGkClient->callbacks.onReceivedRegistrationConfirm)
       pGkClient->callbacks.onReceivedRegistrationConfirm(pRegistrationConfirm,
                                                               gH323ep.aliases);
diff --git a/addons/ooh323c/src/oochannels.c b/addons/ooh323c/src/oochannels.c
index ddc6bf6..0b0acd2 100644
--- a/addons/ooh323c/src/oochannels.c
+++ b/addons/ooh323c/src/oochannels.c
@@ -679,9 +679,9 @@ int ooProcessCallFDSETsAndTimers
     if (0 != call->pH245Channel && 0 != call->pH245Channel->sock)
     {
      if(ooPDWrite(pfds, nfds, call->pH245Channel->sock)) {
-      while (call->pH245Channel->outQueue.count>0) {
+      if (call->pH245Channel->outQueue.count>0) {
        if (ooSendMsg(call, OOH245MSG) != OO_OK)
-	break;
+	OOTRACEERR1("Error in sending h245 message\n");
       }
      }
     }
@@ -699,26 +699,24 @@ int ooProcessCallFDSETsAndTimers
     {
      if(ooPDWrite(pfds, nfds, call->pH225Channel->sock))
      {
-      while (call->pH225Channel->outQueue.count>0)
+      if (call->pH225Channel->outQueue.count>0)
       {
        OOTRACEDBGC3("Sending H225 message (%s, %s)\n", 
                         call->callType, call->callToken);
        if (ooSendMsg(call, OOQ931MSG) != OO_OK)
-	break;
+	OOTRACEERR1("Error in sending h225 message\n");
       }
       if(call->pH245Channel && 
          call->pH245Channel->outQueue.count>0 && 
         OO_TESTFLAG (call->flags, OO_M_TUNNELING)) {
-       while (call->pH245Channel->outQueue.count>0) {
         OOTRACEDBGC3("H245 message needs to be tunneled. "
                           "(%s, %s)\n", call->callType, 
                                call->callToken);
         if (ooSendMsg(call, OOH245MSG) != OO_OK)
-	 break;
+	  OOTRACEERR1("Error in sending h245 message\n");
        }
-      }
-     }                                
-    }
+      }                                
+     }
 
      if(ooTimerNextTimeout(&call->timerList, &toNext))
      {
@@ -1061,11 +1059,6 @@ int ooH2250Receive(OOH323CallData *call)
    while(total < len)
    {
       struct pollfd pfds;
-      recvLen = ooSocketRecv (call->pH225Channel->sock, message1, len-total);
-      memcpy(message+total, message1, recvLen);
-      total = total + recvLen;
-
-      if(total == len) break; /* Complete message is received */
       
       pfds.fd = call->pH225Channel->sock;
       pfds.events = POLLIN;
@@ -1085,8 +1078,9 @@ int ooH2250Receive(OOH323CallData *call)
          }
          return OO_FAILED;
       }
-      /* If remaining part of the message is not received in 3 seconds 
-         exit */
+
+      /* exit If remaining part of the message is not received in 3 seconds */
+
       if(!ooPDRead(&pfds, 1, call->pH225Channel->sock))
       {
          OOTRACEERR3("Error: Incomplete H.2250 message received - clearing "
@@ -1099,6 +1093,23 @@ int ooH2250Receive(OOH323CallData *call)
          }
          return OO_FAILED;
       }
+
+      recvLen = ooSocketRecv (call->pH225Channel->sock, message1, len-total);
+      if (recvLen == 0) {
+         OOTRACEERR3("Error in read while receiving H.2250 message - "
+                     "clearing call (%s, %s)\n", call->callType, 
+                     call->callToken);
+         ooFreeQ931Message(pctxt, pmsg);
+         if(call->callState < OO_CALL_CLEAR)
+         {
+            call->callEndReason = OO_REASON_TRANSPORTFAILURE;
+            call->callState = OO_CALL_CLEAR;
+         }
+         return OO_FAILED;
+      }
+      memcpy(message+total, message1, recvLen);
+      total = total + recvLen;
+
    }
 
    OOTRACEDBGC3("Received Q.931 message: (%s, %s)\n", 
diff --git a/addons/ooh323c/src/ooq931.c b/addons/ooh323c/src/ooq931.c
index cbc4afb..4d6d993 100644
--- a/addons/ooh323c/src/ooq931.c
+++ b/addons/ooh323c/src/ooq931.c
@@ -2124,9 +2124,6 @@ int ooSendReleaseComplete(OOH323CallData *call)
       return OO_FAILED;
    }
    memset(releaseComplete, 0, sizeof(H225ReleaseComplete_UUIE));
-   q931msg->userInfo->h323_uu_pdu.m.h245TunnelingPresent=1; 
-   q931msg->userInfo->h323_uu_pdu.h245Tunneling = OO_TESTFLAG(call->flags, 
-                                                              OO_M_TUNNELING); 
    q931msg->userInfo->h323_uu_pdu.h323_message_body.t = 
          T_H225H323_UU_PDU_h323_message_body_releaseComplete;
    
@@ -2143,8 +2140,6 @@ int ooSendReleaseComplete(OOH323CallData *call)
    releaseComplete->reason.t = h225ReasonCode;
 
    /* Add user-user ie */
-   q931msg->userInfo->h323_uu_pdu.m.h245TunnelingPresent=TRUE; 
-   q931msg->userInfo->h323_uu_pdu.h245Tunneling = OO_TESTFLAG (call->flags, OO_M_TUNNELING);
    q931msg->userInfo->h323_uu_pdu.h323_message_body.t = 
            T_H225H323_UU_PDU_h323_message_body_releaseComplete;
    
diff --git a/apps/app_dial.c b/apps/app_dial.c
index 8938981..ba3f332 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -1301,6 +1301,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
 						}
 					}
 					peer = c;
+					publish_dial_end_event(in, out_chans, peer, "CANCEL");
 					ast_copy_flags64(peerflags, o,
 						OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
 						OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
diff --git a/apps/app_echo.c b/apps/app_echo.c
index c6e37db..9146078 100644
--- a/apps/app_echo.c
+++ b/apps/app_echo.c
@@ -68,7 +68,8 @@ static int echo_exec(struct ast_channel *chan, const char *data)
 		f->delivery.tv_sec = 0;
 		f->delivery.tv_usec = 0;
 		if (f->frametype == AST_FRAME_CONTROL
-			&& f->subclass.integer == AST_CONTROL_VIDUPDATE) {
+			&& f->subclass.integer == AST_CONTROL_VIDUPDATE
+			&& !fir_sent) {
 			if (ast_write(chan, f) < 0) {
 				ast_frfree(f);
 				goto end;
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 0cc3b26..c9f3aee 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -259,7 +259,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 			any of the join options cause the caller to not enter the queue.</para>
 			<para>This application does not automatically answer and should be preceeded
 			by an application such as Answer(), Progress(), or Ringing().</para>
-			<para>This application sets the following channel variable upon completion:</para>
+			<para>This application sets the following channel variables upon completion:</para>
 			<variablelist>
 				<variable name="QUEUESTATUS">
 					<para>The status of the call as a text string.</para>
@@ -271,6 +271,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 					<value name="LEAVEUNAVAIL" />
 					<value name="CONTINUE" />
 				</variable>
+				<variable name="ABANDONED">
+					<para>If the call was not answered by an agent this variable will be TRUE.</para>
+					<value name="TRUE" />
+				</variable>
 			</variablelist>
 		</description>
 		<see-also>
@@ -2646,6 +2650,9 @@ static void init_queue(struct call_queue *q)
 	q->retry = DEFAULT_RETRY;
 	q->timeout = DEFAULT_TIMEOUT;
 	q->maxlen = 0;
+
+	ast_string_field_set(q, context, "");
+
 	q->announcefrequency = 0;
 	q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
 	q->announceholdtime = 1;
@@ -4591,6 +4598,8 @@ static void record_abandoned(struct queue_ent *qe)
 {
 	RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
 
+	pbx_builtin_setvar_helper(qe->chan, "ABANDONED", "TRUE");
+
 	set_queue_variables(qe->parent, qe->chan);
 	ao2_lock(qe->parent);
 	blob = ast_json_pack("{s: s, s: i, s: i, s: i}",
@@ -7922,6 +7931,8 @@ static int queue_exec(struct ast_channel *chan, const char *data)
 	/* Setup our queue entry */
 	qe.start = time(NULL);
 
+	pbx_builtin_setvar_helper(chan, "ABANDONED", NULL);
+
 	/* set the expire time based on the supplied timeout; */
 	if (!ast_strlen_zero(args.queuetimeoutstr)) {
 		qe.expire = qe.start + atoi(args.queuetimeoutstr);
diff --git a/asterisk-13.12.2-summary.html b/asterisk-13.12.2-summary.html
deleted file mode 100644
index 4a29dd4..0000000
--- a/asterisk-13.12.2-summary.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><title>Release Summary - asterisk-13.12.2</title><h1 align="center"><a name="top">Release Summary</a></h1><h3 align="center">asterisk-13.12.2</h3><h3 align="center">Date: 2016-11-10</h3><h3 align="center"><asteriskteam at digium.com></h3><hr><h2 align="center">Table of Contents</h2><ol>
-<li><a href="#summary">Summary</a></li>
-<li><a href="#contributors">Contributors</a></li>
-<li><a href="#closed_issues">Closed Issues</a></li>
-<li><a href="#open_issues">Open Issues</a></li>
-<li><a href="#diffstat">Diffstat</a></li>
-</ol><hr><a name="summary"><h2 align="center">Summary</h2></a><center><a href="#top">[Back to Top]</a></center><p>This release is a point release of an existing major version. The changes included were made to address problems that have been identified in this release series, or are minor, backwards compatible new features or improvements. Users should be able to safely upgrade to this version if this release series is already in use. Users considering upgrading from a previous version a [...]
-<tr><th width="33%">Coders</th><th width="33%">Testers</th><th width="33%">Reporters</th></tr>
-<tr valign="top"><td width="33%">1 Kevin Harwell <kharwell at digium.com><br/></td><td width="33%"><td width="33%">1 Michael Keuter <lists at mksolutions.info><br/>1 Florian Loyau <florian.loyau at astrium-eu-projects.eu><br/></td></tr>
-</table><hr><a name="closed_issues"><h2 align="center">Closed Issues</h2></a><center><a href="#top">[Back to Top]</a></center><p>This is a list of all issues from the issue tracker that were closed by changes that went into this release.</p><h3>Bug</h3><h4>Category: Channels/chan_sip/General</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26523">ASTERISK-26523</a>: chan_sip: Asterisk 13.12.1 disconnects incoming calls after 2 minutes - rtptimeout behaving badly - regression [...]
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=a3614d75f650abd3a9028fccabfcd88649381284">[a3614d75f6]</a> Kevin Harwell -- Revert "chan_sip: Fix lastrtprx always updated"</li>
-</ul><br><hr><a name="open_issues"><h2 align="center">Open Issues</h2></a><center><a href="#top">[Back to Top]</a></center><p>This is a list of all open issues from the issue tracker that were referenced by changes that went into this release.</p><h3>Bug</h3><h4>Category: Core/Jitterbuffer</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25270">ASTERISK-25270</a>: chan_sip: rtptimeout doesn't work at all when using JitterBuffers of any kind<br/>Reported by: Florian Loyau<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=a3614d75f650abd3a9028fccabfcd88649381284">[a3614d75f6]</a> Kevin Harwell -- Revert "chan_sip: Fix lastrtprx always updated"</li>
-</ul><br><h4>Category: Core/RTP</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25270">ASTERISK-25270</a>: chan_sip: rtptimeout doesn't work at all when using JitterBuffers of any kind<br/>Reported by: Florian Loyau<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=a3614d75f650abd3a9028fccabfcd88649381284">[a3614d75f6]</a> Kevin Harwell -- Revert "chan_sip: Fix lastrtprx always updated"</li>
-</ul><br><hr><a name="diffstat"><h2 align="center">Diffstat Results</h2></a><center><a href="#top">[Back to Top]</a></center><p>This is a summary of the changes to the source code that went into this release that was generated using the diffstat utility.</p><pre>0 files changed</pre><br></html>
\ No newline at end of file
diff --git a/asterisk-13.12.2-summary.txt b/asterisk-13.12.2-summary.txt
deleted file mode 100644
index bf3aa30..0000000
--- a/asterisk-13.12.2-summary.txt
+++ /dev/null
@@ -1,111 +0,0 @@
-                                Release Summary
-
-                                asterisk-13.12.2
-
-                                Date: 2016-11-10
-
-                           <asteriskteam at digium.com>
-
-     ----------------------------------------------------------------------
-
-                               Table of Contents
-
-    1. Summary
-    2. Contributors
-    3. Closed Issues
-    4. Open Issues
-    5. Diffstat
-
-     ----------------------------------------------------------------------
-
-                                    Summary
-
-                                 [Back to Top]
-
-   This release is a point release of an existing major version. The changes
-   included were made to address problems that have been identified in this
-   release series, or are minor, backwards compatible new features or
-   improvements. Users should be able to safely upgrade to this version if
-   this release series is already in use. Users considering upgrading from a
-   previous version are strongly encouraged to review the UPGRADE.txt
-   document as well as the CHANGES document for information about upgrading
-   to this release series.
-
-   The data in this summary reflects changes that have been made since the
-   previous release, asterisk-13.12.1.
-
-     ----------------------------------------------------------------------
-
-                                  Contributors
-
-                                 [Back to Top]
-
-   This table lists the people who have submitted code, those that have
-   tested patches, as well as those that reported issues on the issue tracker
-   that were resolved in this release. For coders, the number is how many of
-   their patches (of any size) were committed into this release. For testers,
-   the number is the number of times their name was listed as assisting with
-   testing a patch. Finally, for reporters, the number is the number of
-   issues that they reported that were affected by commits that went into
-   this release.
-
-   Coders                   Testers                  Reporters                
-   1 Kevin Harwell                                   1 Michael Keuter         
-                                                     1 Florian Loyau          
-
-     ----------------------------------------------------------------------
-
-                                 Closed Issues
-
-                                 [Back to Top]
-
-   This is a list of all issues from the issue tracker that were closed by
-   changes that went into this release.
-
-  Bug
-
-    Category: Channels/chan_sip/General
-
-   ASTERISK-26523: chan_sip: Asterisk 13.12.1 disconnects incoming calls
-   after 2 minutes - rtptimeout behaving badly - regression
-   Reported by: Michael Keuter
-     * [a3614d75f6] Kevin Harwell -- Revert "chan_sip: Fix lastrtprx always
-       updated"
-
-     ----------------------------------------------------------------------
-
-                                  Open Issues
-
-                                 [Back to Top]
-
-   This is a list of all open issues from the issue tracker that were
-   referenced by changes that went into this release.
-
-  Bug
-
-    Category: Core/Jitterbuffer
-
-   ASTERISK-25270: chan_sip: rtptimeout doesn't work at all when using
-   JitterBuffers of any kind
-   Reported by: Florian Loyau
-     * [a3614d75f6] Kevin Harwell -- Revert "chan_sip: Fix lastrtprx always
-       updated"
-
-    Category: Core/RTP
-
-   ASTERISK-25270: chan_sip: rtptimeout doesn't work at all when using
-   JitterBuffers of any kind
-   Reported by: Florian Loyau
-     * [a3614d75f6] Kevin Harwell -- Revert "chan_sip: Fix lastrtprx always
-       updated"
-
-     ----------------------------------------------------------------------
-
-                                Diffstat Results
-
-                                 [Back to Top]
-
-   This is a summary of the changes to the source code that went into this
-   release that was generated using the diffstat utility.
-
- 0 files changed
diff --git a/asterisk-13.13.1-summary.html b/asterisk-13.13.1-summary.html
new file mode 100644
index 0000000..f31a497
--- /dev/null
+++ b/asterisk-13.13.1-summary.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><title>Release Summary - asterisk-13.13.1</title><h1 align="center"><a name="top">Release Summary</a></h1><h3 align="center">asterisk-13.13.1</h3><h3 align="center">Date: 2016-12-08</h3><h3 align="center"><asteriskteam at digium.com></h3><hr><h2 align="center">Table of Contents</h2><ol>
+<li><a href="#summary">Summary</a></li>
+<li><a href="#contributors">Contributors</a></li>
+<li><a href="#closed_issues">Closed Issues</a></li>
+<li><a href="#commits">Other Changes</a></li>
+<li><a href="#diffstat">Diffstat</a></li>
+</ol><hr><a name="summary"><h2 align="center">Summary</h2></a><center><a href="#top">[Back to Top]</a></center><p>This release has been made to address one or more security vulnerabilities that have been identified. A security advisory document has been published for each vulnerability that includes additional information. Users of versions of Asterisk that are affected are strongly encouraged to review the advisories and determine what action they should take to protect their systems fr [...]
+<li><a href="http://downloads.asterisk.org/pub/security/AST-2016-008,AST-2016-009.html">AST-2016-008,AST-2016-009</a></li>
+</ul><p>The data in this summary reflects changes that have been made since the previous release, asterisk-13.13.0.</p><hr><a name="contributors"><h2 align="center">Contributors</h2></a><center><a href="#top">[Back to Top]</a></center><p>This table lists the people who have submitted code, those that have tested patches, as well as those that reported issues on the issue tracker that were resolved in this release. For coders, the number is how many of their patches (of any size) were com [...]
+<tr><th width="33%">Coders</th><th width="33%">Testers</th><th width="33%">Reporters</th></tr>
+<tr valign="top"><td width="33%">1 Kevin Harwell <kharwell at digium.com><br/>1 Walter Doekes <walter+asterisk at wjd.nu><br/>1 Joshua Colp <jcolp at digium.com><br/></td><td width="33%"><td width="33%">1 Walter Doekes <walter+asterisk at wjd.nu><br/>1 Jørgen H <asterisk.org at hovland.cx><br/></td></tr>
+</table><hr><a name="closed_issues"><h2 align="center">Closed Issues</h2></a><center><a href="#top">[Back to Top]</a></center><p>This is a list of all issues from the issue tracker that were closed by changes that went into this release.</p><h3>Bug</h3><h4>Category: Channels/chan_sip/Interoperability</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26433">ASTERISK-26433</a>: chan_sip: Allows To-tag checks to be bypassed, setting up new calls<br/>Reported by: Walter Doekes<ul>
+<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=e80603b6d6ce512b6f1549b944d9dacbac8b21f2">[e80603b6d6]</a> Walter Doekes -- chan_sip: Do not allow non-SP/HTAB between header key and colon.</li>
+</ul><br><h4>Category: Resources/res_format_attr_opus</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26579">ASTERISK-26579</a>: codec_opus: Recursiveness when parsing fmtp line<br/>Reported by: Jørgen H<ul>
+<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=fa52ecb9fb36c6ad99591728e5330b5c715c8899">[fa52ecb9fb]</a> Joshua Colp -- res_format_attr_opus: Fix crash when fmtp contains spaces.</li>
+</ul><br><hr><a name="commits"><h2 align="center">Commits Not Associated with an Issue</h2></a><center><a href="#top">[Back to Top]</a></center><p>This is a list of all changes that went into this release that did not reference a JIRA issue.</p><table width="100%" border="1">
+<tr><th>Revision</th><th>Author</th><th>Summary</th></tr>
+<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=5e06e6d8a2375dbfa92c6bbfee5e2de438ede11d">5e06e6d8a2</a></td><td>Kevin Harwell</td><td>Update for 13.13.1</td></tr>
+</table><hr><a name="diffstat"><h2 align="center">Diffstat Results</h2></a><center><a href="#top">[Back to Top]</a></center><p>This is a summary of the changes to the source code that went into this release that was generated using the diffstat utility.</p><pre>asterisk-13.13.0-summary.html   |  304 ---------------
+asterisk-13.13.0-summary.txt    |  767 ----------------------------------------
+b/.version                      |    2
+b/ChangeLog                     |   54 ++
+b/asterisk-13.13.1-summary.html |   15
+b/asterisk-13.13.1-summary.txt  |   92 ++++
+6 files changed, 162 insertions(+), 1072 deletions(-)</pre><br></html>
\ No newline at end of file
diff --git a/asterisk-13.13.1-summary.txt b/asterisk-13.13.1-summary.txt
new file mode 100644
index 0000000..0f84bd0
--- /dev/null
+++ b/asterisk-13.13.1-summary.txt
@@ -0,0 +1,115 @@
+                                Release Summary
+
+                                asterisk-13.13.1
+
+                                Date: 2016-12-08
+
+                           <asteriskteam at digium.com>
+
+     ----------------------------------------------------------------------
+
+                               Table of Contents
+
+    1. Summary
+    2. Contributors
+    3. Closed Issues
+    4. Other Changes
+    5. Diffstat
+
+     ----------------------------------------------------------------------
+
+                                    Summary
+
+                                 [Back to Top]
+
+   This release has been made to address one or more security vulnerabilities
+   that have been identified. A security advisory document has been published
+   for each vulnerability that includes additional information. Users of
+   versions of Asterisk that are affected are strongly encouraged to review
+   the advisories and determine what action they should take to protect their
+   systems from these issues.
+
+   Security Advisories:
+
+     * AST-2016-008,AST-2016-009
+
+   The data in this summary reflects changes that have been made since the
+   previous release, asterisk-13.13.0.
+
+     ----------------------------------------------------------------------
+
+                                  Contributors
+
+                                 [Back to Top]
+
+   This table lists the people who have submitted code, those that have
+   tested patches, as well as those that reported issues on the issue tracker
+   that were resolved in this release. For coders, the number is how many of
+   their patches (of any size) were committed into this release. For testers,
+   the number is the number of times their name was listed as assisting with
+   testing a patch. Finally, for reporters, the number is the number of
+   issues that they reported that were affected by commits that went into
+   this release.
+
+   Coders                   Testers                  Reporters                
+   1 Kevin Harwell                                   1 Walter Doekes          
+   1 Walter Doekes                                   1 JA,rgen H              
+   1 Joshua Colp            
+
+     ----------------------------------------------------------------------
+
+                                 Closed Issues
+
+                                 [Back to Top]
+
+   This is a list of all issues from the issue tracker that were closed by
+   changes that went into this release.
+
+  Bug
+
+    Category: Channels/chan_sip/Interoperability
+
+   ASTERISK-26433: chan_sip: Allows To-tag checks to be bypassed, setting up
+   new calls
+   Reported by: Walter Doekes
+     * [e80603b6d6] Walter Doekes -- chan_sip: Do not allow non-SP/HTAB
+       between header key and colon.
+
+    Category: Resources/res_format_attr_opus
+
+   ASTERISK-26579: codec_opus: Recursiveness when parsing fmtp line
+   Reported by: JA,rgen H
+     * [fa52ecb9fb] Joshua Colp -- res_format_attr_opus: Fix crash when fmtp
+       contains spaces.
+
+     ----------------------------------------------------------------------
+
+                      Commits Not Associated with an Issue
+
+                                 [Back to Top]
+
+   This is a list of all changes that went into this release that did not
+   reference a JIRA issue.
+
+   +------------------------------------------------------------------------+
+   | Revision           | Author                | Summary                   |
+   |--------------------+-----------------------+---------------------------|
+   | 5e06e6d8a2         | Kevin Harwell         | Update for 13.13.1        |
+   +------------------------------------------------------------------------+
+
+     ----------------------------------------------------------------------
+
+                                Diffstat Results
+
+                                 [Back to Top]
+
+   This is a summary of the changes to the source code that went into this
+   release that was generated using the diffstat utility.
+
+ asterisk-13.13.0-summary.html   |  304 ---------------
+ asterisk-13.13.0-summary.txt    |  767 ----------------------------------------
+ b/.version                      |    2
+ b/ChangeLog                     |   54 ++
+ b/asterisk-13.13.1-summary.html |   15
+ b/asterisk-13.13.1-summary.txt  |   92 ++++
+ 6 files changed, 162 insertions(+), 1072 deletions(-)
diff --git a/bridges/bridge_builtin_features.c b/bridges/bridge_builtin_features.c
index d09a7a2..43cf2c9 100644
--- a/bridges/bridge_builtin_features.c
+++ b/bridges/bridge_builtin_features.c
@@ -202,6 +202,8 @@ static void start_automonitor(struct ast_bridge_channel *bridge_channel, struct
 		return;
 	}
 
+	ast_monitor_setjoinfiles(peer_chan, 1);
+
 	if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
 		ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
 		ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c
index fe058e4..436fab7 100644
--- a/bridges/bridge_softmix.c
+++ b/bridges/bridge_softmix.c
@@ -442,21 +442,6 @@ static void softmix_bridge_unsuspend(struct ast_bridge *bridge, struct ast_bridg
 	}
 }
 
-/*!
- * \internal
- * \brief Indicate a source change to the channel.
- * \since 12.0.0
- *
- * \param bridge_channel Which channel source is changing.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-static int softmix_src_change(struct ast_bridge_channel *bridge_channel)
-{
-	return ast_bridge_channel_queue_control_data(bridge_channel, AST_CONTROL_SRCCHANGE, NULL, 0);
-}
-
 /*! \brief Function called when a channel is joined into the bridge */
 static int softmix_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
@@ -473,8 +458,6 @@ static int softmix_bridge_join(struct ast_bridge *bridge, struct ast_bridge_chan
 		return -1;
 	}
 
-	softmix_src_change(bridge_channel);
-
 	/* Can't forget the lock */
 	ast_mutex_init(&sc->lock);
 
@@ -501,8 +484,6 @@ static void softmix_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_ch
 	}
 	bridge_channel->tech_pvt = NULL;
 
-	softmix_src_change(bridge_channel);
-
 	/* Drop mutex lock */
 	ast_mutex_destroy(&sc->lock);
 
@@ -696,6 +677,15 @@ static int softmix_bridge_write_control(struct ast_bridge *bridge, struct ast_br
 	 * XXX Softmix needs to use channel roles to determine what to
 	 * do with control frames.
 	 */
+
+	switch (frame->subclass.integer) {
+	case AST_CONTROL_VIDUPDATE:
+		ast_bridge_queue_everyone_else(bridge, NULL, frame);
+		break;
+	default:
+		break;
+	}
+
 	return 0;
 }
 
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index 2354511..cc4b2ef 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -219,9 +219,7 @@ static enum ast_rtp_glue_result chan_pjsip_get_vrtp_peer(struct ast_channel *cha
 /*! \brief Function called by RTP engine to get peer capabilities */
 static void chan_pjsip_get_codec(struct ast_channel *chan, struct ast_format_cap *result)
 {
-	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(chan);
-
-	ast_format_cap_append_from_cap(result, channel->session->endpoint->media.codecs, AST_MEDIA_TYPE_UNKNOWN);
+	ast_format_cap_append_from_cap(result, ast_channel_nativeformats(chan), AST_MEDIA_TYPE_UNKNOWN);
 }
 
 /*! \brief Destructor function for \ref transport_info_data */
@@ -559,6 +557,12 @@ static int answer(void *data)
 	struct ast_sip_session *session = data;
 
 	if (session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
+		ast_log(LOG_ERROR, "Session already DISCONNECTED [reason=%d (%s)]\n",
+			session->inv_session->cause,
+			pjsip_get_status_text(session->inv_session->cause)->ptr);
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+		pjsip_inv_dec_ref(session->inv_session);
+#endif
 		return 0;
 	}
 
@@ -575,6 +579,10 @@ static int answer(void *data)
 		ast_sip_session_send_response(session, packet);
 	}
 
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+	pjsip_inv_dec_ref(session->inv_session);
+#endif
+
 	return (status == PJ_SUCCESS) ? 0 : -1;
 }
 
@@ -591,12 +599,23 @@ static int chan_pjsip_answer(struct ast_channel *ast)
 	ast_setstate(ast, AST_STATE_UP);
 	session = ao2_bump(channel->session);
 
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+	if (pjsip_inv_add_ref(session->inv_session) != PJ_SUCCESS) {
+		ast_log(LOG_ERROR, "Can't increase the session reference counter\n");
+		ao2_ref(session, -1);
+		return -1;
+	}
+#endif
+
 	/* the answer task needs to be pushed synchronously otherwise a race condition
 	   can occur between this thread and bridging (specifically when native bridging
 	   attempts to do direct media) */
 	ast_channel_unlock(ast);
 	if (ast_sip_push_task_synchronous(session->serializer, answer, session)) {
 		ast_log(LOG_WARNING, "Unable to push answer task to the threadpool. Cannot answer call\n");
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+		pjsip_inv_dec_ref(session->inv_session);
+#endif
 		ao2_ref(session, -1);
 		ast_channel_lock(ast);
 		return -1;
@@ -704,15 +723,28 @@ static struct ast_frame *chan_pjsip_read(struct ast_channel *ast)
 
 	session = channel->session;
 
-	if (ast_format_cap_iscompatible_format(session->endpoint->media.codecs, f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
-		ast_debug(1, "Oooh, got a frame with format of %s on channel '%s' when endpoint '%s' is not configured for it\n",
-			ast_format_get_name(f->subclass.format), ast_channel_name(ast),
-			ast_sorcery_object_get_id(session->endpoint));
+	if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
+		ast_debug(1, "Oooh, got a frame with format of %s on channel '%s' when it has not been negotiated\n",
+			ast_format_get_name(f->subclass.format), ast_channel_name(ast));
 
 		ast_frfree(f);
 		return &ast_null_frame;
 	}
 
+	if (!session->endpoint->asymmetric_rtp_codec &&
+		ast_format_cmp(ast_channel_rawwriteformat(ast), f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
+		/* For maximum compatibility we ensure that the write format matches that of the received media */
+		ast_debug(1, "Oooh, got a frame with format of %s on channel '%s' when we're sending '%s', switching to match\n",
+			ast_format_get_name(f->subclass.format), ast_channel_name(ast),
+			ast_format_get_name(ast_channel_rawwriteformat(ast)));
+		ast_channel_set_rawwriteformat(ast, f->subclass.format);
+		ast_set_write_format(ast, ast_channel_writeformat(ast));
+
+		if (ast_channel_is_bridged(ast)) {
+			ast_channel_set_unbridged_nolock(ast, 1);
+		}
+	}
+
 	if (session->dsp) {
 		int dsp_features;
 
@@ -1105,6 +1137,9 @@ static int indicate(void *data)
 		ast_sip_session_send_response(session, packet);
 	}
 
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+	pjsip_inv_dec_ref(session->inv_session);
+#endif
 	ao2_ref(ind_data, -1);
 
 	return 0;
@@ -1132,17 +1167,35 @@ static int transmit_info_with_vidupdate(void *data)
 	RAII_VAR(struct ast_sip_session *, session, data, ao2_cleanup);
 	struct pjsip_tx_data *tdata;
 
+	if (session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
+		ast_log(LOG_ERROR, "Session already DISCONNECTED [reason=%d (%s)]\n",
+			session->inv_session->cause,
+			pjsip_get_status_text(session->inv_session->cause)->ptr);
+		goto failure;
+	}
+
 	if (ast_sip_create_request("INFO", session->inv_session->dlg, session->endpoint, NULL, NULL, &tdata)) {
 		ast_log(LOG_ERROR, "Could not create text video update INFO request\n");
-		return -1;
+		goto failure;
 	}
 	if (ast_sip_add_body(tdata, &body)) {
 		ast_log(LOG_ERROR, "Could not add body to text video update INFO request\n");
-		return -1;
+		goto failure;
 	}
 	ast_sip_session_send_request(session, tdata);
 
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+	pjsip_inv_dec_ref(session->inv_session);
+#endif
+
 	return 0;
+
+failure:
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+	pjsip_inv_dec_ref(session->inv_session);
+#endif
+	return -1;
+
 }
 
 /*!
@@ -1185,6 +1238,17 @@ static int update_connected_line_information(void *data)
 {
 	struct ast_sip_session *session = data;
 
+	if (session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
+		ast_log(LOG_ERROR, "Session already DISCONNECTED [reason=%d (%s)]\n",
+			session->inv_session->cause,
+			pjsip_get_status_text(session->inv_session->cause)->ptr);
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+		pjsip_inv_dec_ref(session->inv_session);
+#endif
+		ao2_ref(session, -1);
+		return -1;
+	}
+
 	if (ast_channel_state(session->channel) == AST_STATE_UP
 		|| session->inv_session->role == PJSIP_ROLE_UAC) {
 		if (is_colp_update_allowed(session)) {
@@ -1222,6 +1286,10 @@ static int update_connected_line_information(void *data)
 		}
 	}
 
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+	pjsip_inv_dec_ref(session->inv_session);
+#endif
+
 	ao2_ref(session, -1);
 	return 0;
 }
@@ -1302,10 +1370,18 @@ static int chan_pjsip_indicate(struct ast_channel *ast, int condition, const voi
 				res = ast_rtp_instance_write(media->rtp, &fr);
 			} else {
 				ao2_ref(channel->session, +1);
-
-				if (ast_sip_push_task(channel->session->serializer, transmit_info_with_vidupdate, channel->session)) {
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+				if (pjsip_inv_add_ref(channel->session->inv_session) != PJ_SUCCESS) {
+					ast_log(LOG_ERROR, "Can't increase the session reference counter\n");
 					ao2_cleanup(channel->session);
+				} else {
+#endif
+					if (ast_sip_push_task(channel->session->serializer, transmit_info_with_vidupdate, channel->session)) {
+						ao2_cleanup(channel->session);
+					}
+#ifdef HAVE_PJSIP_INV_SESSION_REF
 				}
+#endif
 			}
 			ast_test_suite_event_notify("AST_CONTROL_VIDUPDATE", "Result: Success");
 		} else {
@@ -1315,7 +1391,17 @@ static int chan_pjsip_indicate(struct ast_channel *ast, int condition, const voi
 		break;
 	case AST_CONTROL_CONNECTED_LINE:
 		ao2_ref(channel->session, +1);
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+		if (pjsip_inv_add_ref(channel->session->inv_session) != PJ_SUCCESS) {
+			ast_log(LOG_ERROR, "Can't increase the session reference counter\n");
+			ao2_cleanup(channel->session);
+			return -1;
+		}
+#endif
 		if (ast_sip_push_task(channel->session->serializer, update_connected_line_information, channel->session)) {
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+			pjsip_inv_dec_ref(channel->session->inv_session);
+#endif
 			ao2_cleanup(channel->session);
 		}
 		break;
@@ -1392,9 +1478,23 @@ static int chan_pjsip_indicate(struct ast_channel *ast, int condition, const voi
 
 	if (response_code) {
 		struct indicate_data *ind_data = indicate_data_alloc(channel->session, condition, response_code, data, datalen);
-		if (!ind_data || ast_sip_push_task(channel->session->serializer, indicate, ind_data)) {
+
+		if (!ind_data) {
+			return -1;
+		}
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+		if (pjsip_inv_add_ref(ind_data->session->inv_session) != PJ_SUCCESS) {
+			ast_log(LOG_ERROR, "Can't increase the session reference counter\n");
+			ao2_cleanup(ind_data);
+			return -1;
+		}
+#endif
+		if (ast_sip_push_task(channel->session->serializer, indicate, ind_data)) {
 			ast_log(LOG_NOTICE, "Cannot send response code %d to endpoint %s. Could not queue task properly\n",
 					response_code, ast_sorcery_object_get_id(channel->session->endpoint));
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+			pjsip_inv_dec_ref(ind_data->session->inv_session);
+#endif
 			ao2_cleanup(ind_data);
 			res = -1;
 		}
@@ -1515,21 +1615,31 @@ static int transfer(void *data)
 	struct ast_sip_contact *contact = NULL;
 	const char *target = trnf_data->target;
 
-	/* See if we have an endpoint; if so, use its contact */
-	endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", target);
-	if (endpoint) {
-		contact = ast_sip_location_retrieve_contact_from_aor_list(endpoint->aors);
-		if (contact && !ast_strlen_zero(contact->uri)) {
-			target = contact->uri;
+	if (trnf_data->session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
+		ast_log(LOG_ERROR, "Session already DISCONNECTED [reason=%d (%s)]\n",
+			trnf_data->session->inv_session->cause,
+			pjsip_get_status_text(trnf_data->session->inv_session->cause)->ptr);
+	} else {
+		/* See if we have an endpoint; if so, use its contact */
+		endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", target);
+		if (endpoint) {
+			contact = ast_sip_location_retrieve_contact_from_aor_list(endpoint->aors);
+			if (contact && !ast_strlen_zero(contact->uri)) {
+				target = contact->uri;
+			}
 		}
-	}
 
-	if (ast_channel_state(trnf_data->session->channel) == AST_STATE_RING) {
-		transfer_redirect(trnf_data->session, target);
-	} else {
-		transfer_refer(trnf_data->session, target);
+		if (ast_channel_state(trnf_data->session->channel) == AST_STATE_RING) {
+			transfer_redirect(trnf_data->session, target);
+		} else {
+			transfer_refer(trnf_data->session, target);
+		}
 	}
 
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+	pjsip_inv_dec_ref(trnf_data->session->inv_session);
+#endif
+
 	ao2_ref(trnf_data, -1);
 	ao2_cleanup(endpoint);
 	ao2_cleanup(contact);
@@ -1546,8 +1656,19 @@ static int chan_pjsip_transfer(struct ast_channel *chan, const char *target)
 		return -1;
 	}
 
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+	if (pjsip_inv_add_ref(trnf_data->session->inv_session) != PJ_SUCCESS) {
+		ast_log(LOG_ERROR, "Can't increase the session reference counter\n");
+		ao2_cleanup(trnf_data);
+		return -1;
+	}
+#endif
+
 	if (ast_sip_push_task(channel->session->serializer, transfer, trnf_data)) {
 		ast_log(LOG_WARNING, "Error requesting transfer\n");
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+		pjsip_inv_dec_ref(trnf_data->session->inv_session);
+#endif
 		ao2_cleanup(trnf_data);
 		return -1;
 	}
@@ -1629,9 +1750,16 @@ static int transmit_info_dtmf(void *data)
 		.subtype = "dtmf-relay",
 	};
 
+	if (session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
+		ast_log(LOG_ERROR, "Session already DISCONNECTED [reason=%d (%s)]\n",
+			session->inv_session->cause,
+			pjsip_get_status_text(session->inv_session->cause)->ptr);
+		goto failure;
+	}
+
 	if (!(body_text = ast_str_create(32))) {
 		ast_log(LOG_ERROR, "Could not allocate buffer for INFO DTMF.\n");
-		return -1;
+		goto failure;
 	}
 	ast_str_set(&body_text, 0, "Signal=%c\r\nDuration=%u\r\n", dtmf_data->digit, dtmf_data->duration);
 
@@ -1639,16 +1767,27 @@ static int transmit_info_dtmf(void *data)
 
 	if (ast_sip_create_request("INFO", session->inv_session->dlg, session->endpoint, NULL, NULL, &tdata)) {
 		ast_log(LOG_ERROR, "Could not create DTMF INFO request\n");
-		return -1;
+		goto failure;
 	}
 	if (ast_sip_add_body(tdata, &body)) {
 		ast_log(LOG_ERROR, "Could not add body to DTMF INFO request\n");
 		pjsip_tx_data_dec_ref(tdata);
-		return -1;
+		goto failure;
 	}
 	ast_sip_session_send_request(session, tdata);
 
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+	pjsip_inv_dec_ref(session->inv_session);
+#endif
+
 	return 0;
+
+failure:
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+	pjsip_inv_dec_ref(session->inv_session);
+#endif
+	return -1;
+
 }
 
 /*! \brief Function called by core to stop a DTMF digit */
@@ -1668,8 +1807,19 @@ static int chan_pjsip_digit_end(struct ast_channel *ast, char digit, unsigned in
 			return -1;
 		}
 
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+		if (pjsip_inv_add_ref(dtmf_data->session->inv_session) != PJ_SUCCESS) {
+			ast_log(LOG_ERROR, "Can't increase the session reference counter\n");
+			ao2_cleanup(dtmf_data);
+			return -1;
+		}
+#endif
+
 		if (ast_sip_push_task(channel->session->serializer, transmit_info_dtmf, dtmf_data)) {
 			ast_log(LOG_WARNING, "Error sending DTMF via INFO.\n");
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+			pjsip_inv_dec_ref(dtmf_data->session->inv_session);
+#endif
 			ao2_cleanup(dtmf_data);
 			return -1;
 		}
@@ -2015,11 +2165,21 @@ static int sendtext(void *obj)
 		.body_text = data->text
 	};
 
-	ast_debug(3, "Sending in dialog SIP message\n");
+	if (data->session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
+		ast_log(LOG_ERROR, "Session already DISCONNECTED [reason=%d (%s)]\n",
+			data->session->inv_session->cause,
+			pjsip_get_status_text(data->session->inv_session->cause)->ptr);
+	} else {
+		ast_debug(3, "Sending in dialog SIP message\n");
+
+		ast_sip_create_request("MESSAGE", data->session->inv_session->dlg, data->session->endpoint, NULL, NULL, &tdata);
+		ast_sip_add_body(tdata, &body);
+		ast_sip_send_request(tdata, data->session->inv_session->dlg, data->session->endpoint, NULL, NULL);
+	}
 
-	ast_sip_create_request("MESSAGE", data->session->inv_session->dlg, data->session->endpoint, NULL, NULL, &tdata);
-	ast_sip_add_body(tdata, &body);
-	ast_sip_send_request(tdata, data->session->inv_session->dlg, data->session->endpoint, NULL, NULL);
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+	pjsip_inv_dec_ref(data->session->inv_session);
+#endif
 
 	return 0;
 }
@@ -2030,7 +2190,22 @@ static int chan_pjsip_sendtext(struct ast_channel *ast, const char *text)
 	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(ast);
 	struct sendtext_data *data = sendtext_data_create(channel->session, text);
 
-	if (!data || ast_sip_push_task(channel->session->serializer, sendtext, data)) {
+	if (!data) {
+		return -1;
+	}
+
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+	if (pjsip_inv_add_ref(data->session->inv_session) != PJ_SUCCESS) {
+		ast_log(LOG_ERROR, "Can't increase the session reference counter\n");
+		ao2_ref(data, -1);
+		return -1;
+	}
+#endif
+
+	if (ast_sip_push_task(channel->session->serializer, sendtext, data)) {
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+		pjsip_inv_dec_ref(data->session->inv_session);
+#endif
 		ao2_ref(data, -1);
 		return -1;
 	}
diff --git a/channels/chan_rtp.c b/channels/chan_rtp.c
index 0fe66bd..f1f4f05 100644
--- a/channels/chan_rtp.c
+++ b/channels/chan_rtp.c
@@ -314,7 +314,7 @@ static struct ast_channel *unicast_rtp_request(const char *type, struct ast_form
 	}
 
 	engine_name = S_COR(ast_test_flag(&opts, OPT_RTP_ENGINE),
-		opt_args[OPT_ARG_RTP_ENGINE], NULL);
+		opt_args[OPT_ARG_RTP_ENGINE], "asterisk");
 
 	ast_ouraddrfor(&address, &local_address);
 	instance = ast_rtp_instance_new(engine_name, NULL, &local_address, NULL);
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index afb94b7..38492b9 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -8446,8 +8446,6 @@ static const char *__get_header(const struct sip_request *req, const char *name,
 	 * one afterwards.  If you shouldn't do it, what absolute idiot decided it was
 	 * a good idea to say you can do it, and if you can do it, why in the hell would.
 	 * you say you shouldn't.
-	 * Anyways, pedanticsipchecking controls whether we allow spaces before ':',
-	 * and we always allow spaces after that for compatibility.
 	 */
 	const char *sname = find_alias(name, NULL);
 	int x, len = strlen(name), slen = (sname ? 1 : 0);
@@ -8460,10 +8458,10 @@ static const char *__get_header(const struct sip_request *req, const char *name,
 		if (match || smatch) {
 			/* skip name */
 			const char *r = header + (match ? len : slen );
-			if (sip_cfg.pedanticsipchecking) {
-				r = ast_skip_blanks(r);
+			/* HCOLON has optional SP/HTAB; skip past those */
+			while (*r == ' ' || *r == '\t') {
+				++r;
 			}
-
 			if (*r == ':') {
 				*start = x+1;
 				return ast_skip_blanks(r+1);
@@ -20070,7 +20068,9 @@ static struct sip_peer *_sip_show_peers_one(int fd, struct mansession *s, struct
 		"ACL: %s\r\n"
 		"Status: %s\r\n"
 		"RealtimeDevice: %s\r\n"
-		"Description: %s\r\n\r\n",
+		"Description: %s\r\n"
+		"Accountcode: %s\r\n"
+		"\r\n",
 		cont->idtext,
 		peer->name,
 		ast_sockaddr_isnull(&peer->addr) ? "-none-" : tmp_host,
@@ -20085,7 +20085,8 @@ static struct sip_peer *_sip_show_peers_one(int fd, struct mansession *s, struct
 		ast_acl_list_is_empty(peer->acl) ? "no" : "yes",       /* permit/deny/acl */
 		status,
 		cont->realtimepeers ? (peer->is_realtime ? "yes" : "no") : "no",
-		peer->description);
+		peer->description,
+		peer->accountcode);
 	}
 	ao2_unlock(peer);
 
@@ -21583,7 +21584,7 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
 	ast_cli(a->fd, "  Sub. max duration:      %d secs\n", max_subexpiry);
 	ast_cli(a->fd, "  Outbound reg. timeout:  %d secs\n", global_reg_timeout);
 	ast_cli(a->fd, "  Outbound reg. attempts: %d\n", global_regattempts_max);
-	ast_cli(a->fd, "  Outbound reg. retry 403:%d\n", global_reg_retry_403);
+	ast_cli(a->fd, "  Outbound reg. retry 403:%s\n", AST_CLI_YESNO(global_reg_retry_403));
 	ast_cli(a->fd, "  Notify ringing state:   %s\n", AST_CLI_YESNO(sip_cfg.notifyringing));
 	if (sip_cfg.notifyringing) {
 		ast_cli(a->fd, "    Include CID:          %s%s\n",
@@ -30386,9 +30387,10 @@ static struct ast_channel *sip_request_call(const char *type, struct ast_format_
 	if (p->relatedpeer) {
 
 		if (!ast_strlen_zero(p->relatedpeer->fullcontact) && !p->natdetected &&
-			(ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_RPORT) && !ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT))) {
+		    ((ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_RPORT) && !ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT)) ||
+		     (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA) && !ast_test_flag(&p->flags[1], SIP_PAGE2_SYMMETRICRTP)))) {
 			/* We need to make an attempt to determine if a peer is behind NAT
-			   if the peer has the auto_force_rport flag set. */
+			   if the peer has the flags auto_force_rport or auto_comedia set. */
 			struct ast_sockaddr tmpaddr;
 
 			__set_address_from_contact(p->relatedpeer->fullcontact, &tmpaddr, 0);
diff --git a/channels/chan_unistim.c b/channels/chan_unistim.c
index 37281bb..b3ecc82 100644
--- a/channels/chan_unistim.c
+++ b/channels/chan_unistim.c
@@ -4128,7 +4128,7 @@ static void show_main_page(struct unistimsession *pte)
 			send_date_time2(pte);
 			send_idle_clock(pte);
 			if (strlen(pte->device->maintext0)) {
-				send_text(TEXT_LINE0, TEXT_NORMAL, pte, pte->device->maintext0);
+				send_text(TEXT_LINE0, TEXT_NORMAL, pte, ustmtext(pte->device->maintext0, pte));
 			}
 		} else {
 			if (pte->device->missed_call == 1) {
@@ -4147,11 +4147,11 @@ static void show_main_page(struct unistimsession *pte)
 			strcat(tmpbuf, ast_inet_ntoa(pte->sin.sin_addr));
 			send_text(TEXT_LINE2, TEXT_NORMAL, pte, tmpbuf);
 		} else {
-			send_text(TEXT_LINE2, TEXT_NORMAL, pte, pte->device->maintext2);
+			send_text(TEXT_LINE2, TEXT_NORMAL, pte, ustmtext(pte->device->maintext2, pte));
 		}
 	}
 
-	send_texttitle(pte, pte->device->titledefault);
+	send_texttitle(pte, ustmtext(pte->device->titledefault, pte));
 	change_favorite_icon(pte, FAV_LINE_ICON);
 }
 
@@ -4406,7 +4406,7 @@ static void init_phone_step2(struct unistimsession *pte)
 			strcat(tmp, pte->macaddr);
 			send_text(TEXT_LINE2, TEXT_NORMAL, pte, tmp);
 			send_text_status(pte, "");
-			send_texttitle(pte, "UNISTIM for*");
+			send_texttitle(pte, ustmtext("UNISTIM for*", pte));
 			return;
 		}
 	}
@@ -4896,14 +4896,15 @@ static int unistim_hangup_clean(struct ast_channel *ast, struct unistim_subchann
 	ast_channel_tech_pvt_set(ast, NULL);
 	unistim_set_owner(sub, NULL);
 	sub->alreadygone = 0;
-	ast_mutex_unlock(&sub->lock);
 	if (sub->rtp) {
 		if (unistimdebug) {
 			ast_verb(0, "Destroying RTP session\n");
 		}
+		ast_rtp_instance_stop(sub->rtp);
 		ast_rtp_instance_destroy(sub->rtp);
 		sub->rtp = NULL;
 	}
+	ast_mutex_unlock(&sub->lock);
 	return 0;
 }
 
diff --git a/configs/basic-pbx/pjsip.conf b/configs/basic-pbx/pjsip.conf
index 00e386a..b20e0c6 100644
--- a/configs/basic-pbx/pjsip.conf
+++ b/configs/basic-pbx/pjsip.conf
@@ -15,7 +15,6 @@ bind = 0.0.0.0
 
 [dcs-trunk]
 type = registration
-transport = transport-udp-nat
 outbound_auth = dcs-trunk-auth
 server_uri = sip:sip.digiumcloud.net
 ;client_uri = sip:myaccountID at sip.digiumcloud.net
@@ -31,7 +30,6 @@ auth_type = userpass
 
 [dcs-endpoint]
 type=endpoint
-transport = transport-udp-nat
 context = DCS-Incoming
 allow = !all,g722,ulaw
 outbound_auth = dcs-auth
@@ -59,7 +57,6 @@ endpoint = dcs-endpoint
 ; Our primary endpoint template for internal desk phones.
 [endpoint-internal-d70](!)
 type = endpoint
-transport = transport-udp-nat
 context = Long-Distance
 allow = !all,g722,ulaw
 direct_media = no
diff --git a/configs/samples/asterisk.conf.sample b/configs/samples/asterisk.conf.sample
index b0543d8..38cee25 100644
--- a/configs/samples/asterisk.conf.sample
+++ b/configs/samples/asterisk.conf.sample
@@ -96,6 +96,15 @@ documentation_language = en_US	; Set the language you want documentation
 				; This is currently is used by DUNDi and
 				; Exchanging Device and Mailbox State
 				; using protocols: XMPP, Corosync and PJSIP.
+;rtp_pt_dynamic = 96		; Normally the Dynamic RTP Payload Type numbers
+				; are 96-127, which allow 32 formats. When you
+				; use more and receive the message "No Dynamic
+				; RTP mapping available", extend the dynamic
+				; range by going for 35 (or 0) instead of 96.
+				; This allows 29 (or 64) more formats. 96 is the
+				; default because any number below might be
+				; rejected by a remote implementation; although
+				; no such broken implementation is known, yet.
 
 ; Changing the following lines may compromise your security.
 ;[files]
diff --git a/configs/samples/codecs.conf.sample b/configs/samples/codecs.conf.sample
index 9b57385..63d0352 100644
--- a/configs/samples/codecs.conf.sample
+++ b/configs/samples/codecs.conf.sample
@@ -151,3 +151,57 @@ packetloss_percentage=10;
 ;[celt32]
 ;type=celt
 ;samprate=32000
+
+;============================ OPUS Section Options ============================
+;
+;[opus]
+;type= ; Must be of type "opus" (default: "")
+;packet_loss= ; Encoder's packet loss percentage. Can be any number between 0
+              ; and 100, inclusive. A higher value results in more loss
+              ; resistance. (default: 0)
+;complexity= ; Encoder's computational complexity. Can be any number between 0
+             ; and 10, inclusive. Note, 10 equals the highest complexity.
+             ; (default: 10)
+;max_bandwitdth= ; Encoder's maximum bandwidth allowed. Sets an upper bandwidth
+                 ; bound on the encoder. Can be any of the following: narrow,
+                 ; medium, wide, super_wide, full. (default: full)
+;signal= ; Encoder's signal type. Aids in mode selection on the encoder: Can
+         ; be any of the following: auto, voice, music. (default: auto)
+;application= ; Encoder's application type. Can be any of the following: voip,
+              ; audio, low_delay. (default: voip)
+;max_playback_rate= ; Override the maximum playback rate in the offer's SDP.
+                    ; Any value between 8000 and 48000 (inclusive) is valid,
+                    ; however typically it should match one of the usual opus
+                    ; bandwidths. A value of "sdp" is also allowed. When set
+                    ; to "sdp" then the value from the offer's SDP is used.
+                    ; (default: "sdp")
+;bitrate= ; Override the maximum average bitrate in the offer's SDP. Any value
+          ; between 500 and 512000 is valid. The following values are also
+          ; allowed: auto, max, sdp. When set to "sdp" then the value from
+          ; the offer's sdp is used. (default: "sdp")
+;cbr= ; Override the constant bit rate parameter in the offer's SDP. A value of
+      ; 0/false/no represents a variable bit rate whereas 1/true/yes represents
+      ; a constant bit rate. A value of "sdp" is also allowed. When set to "sdp"
+      ; then the value from the offer's sdp is used. (default: "sdp")
+;fec= ; Override the use inband fec parameter in the offer's SDP. A value of
+      ; 0/false/no represents disabled whereas 1/true/yes represents enabled.
+      ; A value of "sdp" is also allowed. When set to "sdp" then the value from
+      ; the offer's sdp is used. (default: "sdp")
+;dtx= ; Override the use dtx parameter in the offer's SDP. A value of 0/false/no
+      ; represents disabled whereas 1/true/yes represents enabled. A value of
+      ; "sdp" is also allowed. When set to "sdp" then the value from the offer's
+      ; sdp is used. (default: "sdp")
+
+;=============================== OPUS Examples ================================
+;
+;[opus]
+;type=opus
+;max_playback_rate=8000 ; Limit the maximum playback rate on the encoder
+;fec=no ; Force no inband fec on the encoder (i.e don't use what's on the SDP)
+
+;[myopus]
+;type=opus
+;max_bandwidth=wide ; Maximum encoded bandwidth set to wide band (0-8000 Hz
+;                   ; audio bandwidth at 16Khz sample rate)
+;cbr=yes ; Force a constant bit rate (i.e don't use what's on the SDP)
+
diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index 8d063f4..2ef8933 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -112,9 +112,6 @@
 ; the prefix "external_" will only apply to communication with addresses
 ; outside the range set with "local_net=".
 ;
-; IPv6: For endpoints using IPv6, remember to set "rtp_ipv6=yes" so that the RTP
-; engine will also be able to bind to an IPv6 address.
-;
 ; You can have more than one of any type of transport, as long as it doesn't
 ; use the same resources (bind address, port, etc) as the others.
 
@@ -294,8 +291,6 @@
 ; If using the TLS enabled transport, you may want the "media_encryption=sdes"
 ; option to additionally enable SRTP, though they are not mutually inclusive.
 ;
-; Use the "rtp_ipv6=yes" option if you want to utilize RTP over an ipv6 transport.
-;
 ; If this endpoint were remote, and it was using a transport configured for NAT
 ; then you likely want to use "direct_media=no" to prevent audio issues.
 
@@ -315,7 +310,6 @@
 ;transport=transport-tls
 ;media_encryption=sdes
 ;transport=transport-udp-ipv6
-;rtp_ipv6=yes
 ;transport=transport-udp-nat
 ;direct_media=no
 ;
@@ -644,7 +638,6 @@
                         ; must be provided (default: "")
 ;rewrite_contact=no     ; Allow Contact header to be rewritten with the source
                         ; IP address port (default: "no")
-;rtp_ipv6=no    ; Allow use of IPv6 for RTP traffic (default: "no")
 ;rtp_symmetric=no       ; Enforce that RTP must be symmetric (default: "no")
 ;send_diversion=yes     ; Send the Diversion header conveying the diversion
                         ; information to the called user agent (default: "yes")
@@ -697,8 +690,6 @@
                         ; (default: "0")
 ;t38_udptl_nat=no       ; Whether NAT support is enabled on UDPTL sessions
                         ; (default: "no")
-;t38_udptl_ipv6=no      ; Whether IPv6 is used for UDPTL Sessions (default:
-                        ; "no")
 ;tone_zone=     ; Set which country s indications to use for channels created
                 ; for this endpoint (default: "")
 ;language=      ; Set the default language to use for channels created for this
@@ -762,6 +753,8 @@
                    ; "0" or not enabled)
 ;contact_user= ; On outgoing requests, force the user portion of the Contact
                ; header to this value (default: "")
+;asymmetric_rtp_codec= ; Allow the sending and receiving codec to differ and
+                       ; not be automatically matched (default: "no")
 
 ;==========================AUTH SECTION OPTIONS=========================
 ;[auth]
diff --git a/configs/samples/rtp.conf.sample b/configs/samples/rtp.conf.sample
index 2ef5dd2..fdd1d53 100644
--- a/configs/samples/rtp.conf.sample
+++ b/configs/samples/rtp.conf.sample
@@ -59,6 +59,18 @@ rtpend=20000
 ; Password used to authenticate with TURN relay server.
 ; turnpassword=
 ;
+; Subnets to exclude from ICE host, srflx and relay discovery. This is useful
+; to optimize the ICE process where a system has multiple host address ranges
+; and/or physical interfaces and certain of them are not expected to be used
+; for RTP. For example, VPNs and local interconnections may not be suitable or
+; necessary for ICE. Multiple subnets may be listed. If left unconfigured,
+; all discovered host addresses are used.
+;
+; e.g. ice_blacklist = 192.168.1.0/255.255.255.0
+;      ice_blacklist = 10.32.77.0/255.255.255.0
+;
+; ice_blacklist =
+;
 [ice_host_candidates]
 ;
 ; When Asterisk is behind a static one-to-one NAT and ICE is in use, ICE will
diff --git a/configure b/configure
index c4d772e..8d1ad91 100755
--- a/configure
+++ b/configure
@@ -767,6 +767,10 @@ PBX_SUPPSERV
 SUPPSERV_DIR
 SUPPSERV_INCLUDE
 SUPPSERV_LIB
+PBX_RT
+RT_DIR
+RT_INCLUDE
+RT_LIB
 PBX_OPENSSL
 OPENSSL_DIR
 OPENSSL_INCLUDE
@@ -919,6 +923,10 @@ PBX_POPT
 POPT_DIR
 POPT_INCLUDE
 POPT_LIB
+PBX_PJSIP_AUTH_CLT_DEINIT
+PJSIP_AUTH_CLT_DEINIT_DIR
+PJSIP_AUTH_CLT_DEINIT_INCLUDE
+PJSIP_AUTH_CLT_DEINIT_LIB
 PBX_PJSIP_INV_SESSION_REF
 PJSIP_INV_SESSION_REF_DIR
 PJSIP_INV_SESSION_REF_INCLUDE
@@ -1031,6 +1039,10 @@ PBX_LIBXML2
 LIBXML2_DIR
 LIBXML2_INCLUDE
 LIBXML2_LIB
+PBX_LIBEDIT_IS_UNICODE
+LIBEDIT_IS_UNICODE_DIR
+LIBEDIT_IS_UNICODE_INCLUDE
+LIBEDIT_IS_UNICODE_LIB
 PBX_LIBEDIT
 LIBEDIT_DIR
 LIBEDIT_INCLUDE
@@ -1221,6 +1233,7 @@ COMPRESS
 FIND
 PYTHON
 FLEX
+CAT
 CMP
 BISON
 GNU_LD
@@ -6747,6 +6760,47 @@ $as_echo "no" >&6; }
 fi
 
 
+# Extract the first word of "cat", so it can be a program name with args.
+set dummy cat; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_CAT+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $CAT in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_CAT="$CAT" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_CAT="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  test -z "$ac_cv_path_CAT" && ac_cv_path_CAT=":"
+  ;;
+esac
+fi
+CAT=$ac_cv_path_CAT
+if test -n "$CAT"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CAT" >&5
+$as_echo "$CAT" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
 # Extract the first word of "flex", so it can be a program name with args.
 set dummy flex; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -9190,9 +9244,15 @@ $as_echo "configuring" >&6; }
 	if test "${NM}" = ":" ; then
 		as_fn_error $? "nm is required to build bundled pjproject" "$LINENO" 5
 	fi
+	if test "${MD5}" = ":" ; then
+		as_fn_error $? "md5sum is required to build bundled pjproject" "$LINENO" 5
+	fi
+	if test "${CAT}" = ":" ; then
+		as_fn_error $? "cat is required to build bundled pjproject" "$LINENO" 5
+	fi
 
 	export TAR PATCH SED NM EXTERNALS_CACHE_DIR DOWNLOAD_TO_STDOUT
-	${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} configure
+	${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} EXTERNALS_CACHE_DIR=${EXTERNALS_CACHE_DIR} configure
 	if test $? -ne 0 ; then
 		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
 $as_echo "failed" >&6; }
@@ -9204,7 +9264,7 @@ $as_echo "$as_me: Unable to configure ${PJPROJECT_DIR}" >&6;}
 	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for bundled pjproject" >&5
 $as_echo_n "checking for bundled pjproject... " >&6; }
 
-	PJPROJECT_INCLUDE=$(${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} echo_cflags)
+	PJPROJECT_INCLUDE=$(${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} EXTERNALS_CACHE_DIR=${EXTERNALS_CACHE_DIR} echo_cflags)
 	PJPROJECT_CFLAGS="$PJPROJECT_INCLUDE"
 	PBX_PJPROJECT=1
 
@@ -9243,6 +9303,9 @@ $as_echo "#define HAVE_PJSIP_EVSUB_GRP_LOCK 1" >>confdefs.h
 $as_echo "#define HAVE_PJSIP_INV_SESSION_REF 1" >>confdefs.h
 
 
+$as_echo "#define HAVE_PJSIP_AUTH_CLT_DEINIT 1" >>confdefs.h
+
+
 
 
 
@@ -10652,6 +10715,18 @@ fi
 
 
 
+LIBEDIT_IS_UNICODE_DESCRIP="Libedit compiled for unicode"
+LIBEDIT_IS_UNICODE_OPTION=libedit
+LIBEDIT_IS_UNICODE_DIR=${LIBEDIT_DIR}
+
+PBX_LIBEDIT_IS_UNICODE=0
+
+
+
+
+
+
+
     LIBXML2_DESCRIP="LibXML2"
     LIBXML2_OPTION="libxml2"
     PBX_LIBXML2=0
@@ -11381,6 +11456,18 @@ PBX_PJSIP_INV_SESSION_REF=0
 
 
 
+
+PJSIP_AUTH_CLT_DEINIT_DESCRIP="pjsip_auth_clt_deinit support"
+PJSIP_AUTH_CLT_DEINIT_OPTION=pjsip
+PJSIP_AUTH_CLT_DEINIT_DIR=${PJPROJECT_DIR}
+
+PBX_PJSIP_AUTH_CLT_DEINIT=0
+
+
+
+
+
+
 fi
 
 
@@ -12195,6 +12282,18 @@ fi
 
 
 
+RT_DESCRIP="Realtime functions"
+
+RT_DIR=${rt_DIR}
+
+PBX_RT=0
+
+
+
+
+
+
+
     SUPPSERV_DESCRIP="mISDN Supplemental Services"
     SUPPSERV_OPTION="suppserv"
     PBX_SUPPSERV=0
@@ -13855,6 +13954,112 @@ if test "x$JANSSON_LIB" == "x"; then
   as_fn_error $? "*** JSON support not found (this typically means the libjansson development package is missing)" "$LINENO" 5
 fi
 
+# See if clock_gettime is in librt
+
+if test "x${PBX_RT}" != "x1" -a "${USE_RT}" != "no"; then
+   pbxlibdir=""
+   # if --with-RT=DIR has been specified, use it.
+   if test "x${RT_DIR}" != "x"; then
+      if test -d ${RT_DIR}/lib; then
+         pbxlibdir="-L${RT_DIR}/lib"
+      else
+         pbxlibdir="-L${RT_DIR}"
+      fi
+   fi
+   pbxfuncname="clock_gettime"
+   if test "x${pbxfuncname}" = "x" ; then   # empty lib, assume only headers
+      AST_RT_FOUND=yes
+   else
+      ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
+      CFLAGS="${CFLAGS} "
+      as_ac_Lib=`$as_echo "ac_cv_lib_rt_${pbxfuncname}" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lrt" >&5
+$as_echo_n "checking for ${pbxfuncname} in -lrt... " >&6; }
+if eval \${$as_ac_Lib+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lrt ${pbxlibdir}  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  eval "$as_ac_Lib=yes"
+else
+  eval "$as_ac_Lib=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+eval ac_res=\$$as_ac_Lib
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
+  AST_RT_FOUND=yes
+else
+  AST_RT_FOUND=no
+fi
+
+      CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
+   fi
+
+   # now check for the header.
+   if test "${AST_RT_FOUND}" = "yes"; then
+      RT_LIB="${pbxlibdir} -lrt "
+      # if --with-RT=DIR has been specified, use it.
+      if test "x${RT_DIR}" != "x"; then
+         RT_INCLUDE="-I${RT_DIR}/include"
+      fi
+      RT_INCLUDE="${RT_INCLUDE} "
+      if test "x" = "x" ; then	# no header, assume found
+         RT_HEADER_FOUND="1"
+      else				# check for the header
+         ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
+         CPPFLAGS="${CPPFLAGS} ${RT_INCLUDE}"
+         ac_fn_c_check_header_mongrel "$LINENO" "" "ac_cv_header_" "$ac_includes_default"
+if test "x$ac_cv_header_" = xyes; then :
+  RT_HEADER_FOUND=1
+else
+  RT_HEADER_FOUND=0
+fi
+
+
+         CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
+      fi
+      if test "x${RT_HEADER_FOUND}" = "x0" ; then
+         RT_LIB=""
+         RT_INCLUDE=""
+      else
+         if test "x${pbxfuncname}" = "x" ; then		# only checking headers -> no library
+            RT_LIB=""
+         fi
+         PBX_RT=1
+         cat >>confdefs.h <<_ACEOF
+#define HAVE_RT 1
+_ACEOF
+
+      fi
+   fi
+fi
+
+
+
 
 		if test "x${PBX_LIBXML2}" != "x1" -a "${USE_LIBXML2}" != "no"; then
 		PBX_LIBXML2=0
@@ -18374,7 +18579,7 @@ if ac_fn_c_try_compile "$LINENO"; then :
 $as_echo "yes" >&6; }
 	AST_ADDRESS_SANITIZER=1
 else
-  AST_ADDRESS_SANITIZER=
+  AST_ADDRESS_SANITIZER=0
 	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
 
@@ -18406,7 +18611,7 @@ if ac_fn_c_try_compile "$LINENO"; then :
 $as_echo "yes" >&6; }
 	AST_THREAD_SANITIZER=1
 else
-  AST_THREAD_SANITIZER=
+  AST_THREAD_SANITIZER=0
 	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
 
@@ -20828,11 +21033,58 @@ fi
    fi
 
       if test "$PBX_LIBEDIT" = "1"; then
-	 LIBEDIT_INTERNAL="no"
+      LIBEDIT_INTERNAL="no"
       fi
    fi
    if test "${LIBEDIT_INTERNAL}" = "yes"; then
       PBX_LIBEDIT=1
+      LIBEDIT_IS_UNICODE=no
+   else
+
+    if test "x${PBX_LIBEDIT_IS_UNICODE}" != "x1" -a "${USE_LIBEDIT_IS_UNICODE}" != "no"; then
+        if test "xTesting for libedit unicode support" != "x"; then
+            { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Testing for libedit unicode support" >&5
+$as_echo_n "checking for Testing for libedit unicode support... " >&6; }
+	else
+            { $as_echo "$as_me:${as_lineno-$LINENO}: checking if \"el_rfunc_t *callback;\" compiles using histedit.h" >&5
+$as_echo_n "checking if \"el_rfunc_t *callback;\" compiles using histedit.h... " >&6; }
+	fi
+	saved_cppflags="${CPPFLAGS}"
+	if test "x${LIBEDIT_IS_UNICODE_DIR}" != "x"; then
+	    LIBEDIT_IS_UNICODE_INCLUDE="-I${LIBEDIT_IS_UNICODE_DIR}/include"
+	fi
+	CPPFLAGS="${CPPFLAGS} ${LIBEDIT_IS_UNICODE_INCLUDE}"
+
+	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+ #include <histedit.h>
+int
+main ()
+{
+ el_rfunc_t *callback;;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+     { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+		PBX_LIBEDIT_IS_UNICODE=1
+
+$as_echo "#define HAVE_LIBEDIT_IS_UNICODE 1" >>confdefs.h
+
+
+
+else
+         { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+	CPPFLAGS="${saved_cppflags}"
+    fi
+
    fi
 fi
 
@@ -25967,6 +26219,110 @@ _ACEOF
 fi
 
 
+
+if test "x${PBX_PJSIP_AUTH_CLT_DEINIT}" != "x1" -a "${USE_PJSIP_AUTH_CLT_DEINIT}" != "no"; then
+   pbxlibdir=""
+   # if --with-PJSIP_AUTH_CLT_DEINIT=DIR has been specified, use it.
+   if test "x${PJSIP_AUTH_CLT_DEINIT_DIR}" != "x"; then
+      if test -d ${PJSIP_AUTH_CLT_DEINIT_DIR}/lib; then
+         pbxlibdir="-L${PJSIP_AUTH_CLT_DEINIT_DIR}/lib"
+      else
+         pbxlibdir="-L${PJSIP_AUTH_CLT_DEINIT_DIR}"
+      fi
+   fi
+   pbxfuncname="pjsip_auth_clt_deinit"
+   if test "x${pbxfuncname}" = "x" ; then   # empty lib, assume only headers
+      AST_PJSIP_AUTH_CLT_DEINIT_FOUND=yes
+   else
+      ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
+      CFLAGS="${CFLAGS} $PJPROJECT_CFLAGS"
+      as_ac_Lib=`$as_echo "ac_cv_lib_pjsip_${pbxfuncname}" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpjsip" >&5
+$as_echo_n "checking for ${pbxfuncname} in -lpjsip... " >&6; }
+if eval \${$as_ac_Lib+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpjsip ${pbxlibdir} $PJPROJECT_LIB $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  eval "$as_ac_Lib=yes"
+else
+  eval "$as_ac_Lib=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+eval ac_res=\$$as_ac_Lib
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
+  AST_PJSIP_AUTH_CLT_DEINIT_FOUND=yes
+else
+  AST_PJSIP_AUTH_CLT_DEINIT_FOUND=no
+fi
+
+      CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
+   fi
+
+   # now check for the header.
+   if test "${AST_PJSIP_AUTH_CLT_DEINIT_FOUND}" = "yes"; then
+      PJSIP_AUTH_CLT_DEINIT_LIB="${pbxlibdir} -lpjsip $PJPROJECT_LIB"
+      # if --with-PJSIP_AUTH_CLT_DEINIT=DIR has been specified, use it.
+      if test "x${PJSIP_AUTH_CLT_DEINIT_DIR}" != "x"; then
+         PJSIP_AUTH_CLT_DEINIT_INCLUDE="-I${PJSIP_AUTH_CLT_DEINIT_DIR}/include"
+      fi
+      PJSIP_AUTH_CLT_DEINIT_INCLUDE="${PJSIP_AUTH_CLT_DEINIT_INCLUDE} $PJPROJECT_CFLAGS"
+      if test "xpjsip.h" = "x" ; then	# no header, assume found
+         PJSIP_AUTH_CLT_DEINIT_HEADER_FOUND="1"
+      else				# check for the header
+         ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
+         CPPFLAGS="${CPPFLAGS} ${PJSIP_AUTH_CLT_DEINIT_INCLUDE}"
+         ac_fn_c_check_header_mongrel "$LINENO" "pjsip.h" "ac_cv_header_pjsip_h" "$ac_includes_default"
+if test "x$ac_cv_header_pjsip_h" = xyes; then :
+  PJSIP_AUTH_CLT_DEINIT_HEADER_FOUND=1
+else
+  PJSIP_AUTH_CLT_DEINIT_HEADER_FOUND=0
+fi
+
+
+         CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
+      fi
+      if test "x${PJSIP_AUTH_CLT_DEINIT_HEADER_FOUND}" = "x0" ; then
+         PJSIP_AUTH_CLT_DEINIT_LIB=""
+         PJSIP_AUTH_CLT_DEINIT_INCLUDE=""
+      else
+         if test "x${pbxfuncname}" = "x" ; then		# only checking headers -> no library
+            PJSIP_AUTH_CLT_DEINIT_LIB=""
+         fi
+         PBX_PJSIP_AUTH_CLT_DEINIT=1
+         cat >>confdefs.h <<_ACEOF
+#define HAVE_PJSIP_AUTH_CLT_DEINIT 1
+_ACEOF
+
+      fi
+   fi
+fi
+
+
    fi
 fi
 
diff --git a/configure.ac b/configure.ac
index 42320e4..797770e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -479,6 +479,7 @@ AST_EXT_LIB_SETUP([KQUEUE], [kqueue support], [kqueue])
 AST_EXT_LIB_SETUP([LDAP], [OpenLDAP], [ldap])
 AST_LIBCURL_CHECK_CONFIG([], [7.10.1])
 AST_EXT_LIB_SETUP([LIBEDIT], [NetBSD Editline library], [libedit], [, use 'internal' Editline otherwise])
+AST_EXT_LIB_SETUP_OPTIONAL([LIBEDIT_IS_UNICODE], [Libedit compiled for unicode], [LIBEDIT], [libedit])
 AST_EXT_LIB_SETUP([LIBXML2], [LibXML2], [libxml2])
 AST_EXT_LIB_SETUP([LIBXSLT], [LibXSLT], [libxslt])
 AST_EXT_LIB_SETUP_OPTIONAL([LIBXSLT_CLEANUP], [LibXSLT Library Cleanup Function], [LIBXSLT], [libxslt])
@@ -510,6 +511,7 @@ AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_EXTERNAL_RESOLVER], [PJSIP External Resolver S
 AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_TLS_TRANSPORT_PROTO], [PJSIP TLS Transport proto field support], [PJPROJECT], [pjsip])
 AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_EVSUB_GRP_LOCK], [PJSIP EVSUB Group Lock support], [PJPROJECT], [pjsip])
 AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_INV_SESSION_REF], [PJSIP INVITE Session Reference Count support], [PJPROJECT], [pjsip])
+AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_AUTH_CLT_DEINIT], [pjsip_auth_clt_deinit support], [PJPROJECT], [pjsip])
 fi
 
 AST_EXT_LIB_SETUP([POPT], [popt], [popt])
@@ -555,6 +557,7 @@ AST_EXT_LIB_SETUP([SQLITE3], [SQLite], [sqlite3])
 AST_EXT_LIB_SETUP([SRTP], [Secure RTP], [srtp])
 AST_EXT_LIB_SETUP_OPTIONAL([SRTP_SHUTDOWN], [SRTP Library Shutdown Function], [SRTP], [srtp])
 AST_EXT_LIB_SETUP([OPENSSL], [OpenSSL Secure Sockets Layer], [ssl])
+AST_EXT_LIB_SETUP_OPTIONAL([RT], [Realtime functions], [rt])
 AST_EXT_LIB_SETUP([SUPPSERV], [mISDN Supplemental Services], [suppserv])
 AST_EXT_LIB_SETUP([FREETDS], [FreeTDS], [tds])
 AST_EXT_LIB_SETUP([TERMCAP], [Termcap], [termcap])
@@ -626,6 +629,9 @@ if test "x$JANSSON_LIB" == "x"; then
   AC_MSG_ERROR([*** JSON support not found (this typically means the libjansson development package is missing)])
 fi
 
+# See if clock_gettime is in librt
+AST_EXT_LIB_CHECK([RT], [rt], [clock_gettime])
+
 AST_EXT_TOOL_CHECK([LIBXML2], [xml2-config], , ,
         [#include <libxml/tree.h>
         #include <libxml/parser.h>],
@@ -1106,7 +1112,7 @@ AC_COMPILE_IFELSE(
 	[AC_LANG_PROGRAM([], [int x = 1;])],
 	AC_MSG_RESULT(yes)
 	[AST_ADDRESS_SANITIZER=1],
-	[AST_ADDRESS_SANITIZER=]
+	[AST_ADDRESS_SANITIZER=0]
 	AC_MSG_RESULT(no)
 )
 CFLAGS="${saved_sanitize_CFLAGS}"
@@ -1122,7 +1128,7 @@ AC_COMPILE_IFELSE(
 	[AC_LANG_PROGRAM([], [int x = 1;])],
 	AC_MSG_RESULT(yes)
 	[AST_THREAD_SANITIZER=1],
-	[AST_THREAD_SANITIZER=]
+	[AST_THREAD_SANITIZER=0]
 	AC_MSG_RESULT(no)
 )
 CFLAGS="${saved_sanitize_CFLAGS}"
@@ -1519,11 +1525,14 @@ if test "${USE_LIBEDIT}" != "no"; then
    if test "${LIBEDIT_SYSTEM}" = "yes"; then
       AST_PKG_CONFIG_CHECK(LIBEDIT, libedit)
       if test "$PBX_LIBEDIT" = "1"; then
-	 LIBEDIT_INTERNAL="no"
+      LIBEDIT_INTERNAL="no"
       fi
    fi
    if test "${LIBEDIT_INTERNAL}" = "yes"; then
       PBX_LIBEDIT=1
+      LIBEDIT_IS_UNICODE=no
+   else
+      AST_C_COMPILE_CHECK([LIBEDIT_IS_UNICODE], [el_rfunc_t *callback;], [histedit.h], [], [Testing for libedit unicode support])
    fi
 fi
 
@@ -2212,6 +2221,7 @@ if test "$USE_PJPROJECT" != "no" ; then
 
       AST_EXT_LIB_CHECK([PJSIP_EVSUB_GRP_LOCK], [pjsip], [pjsip_evsub_add_ref], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
       AST_EXT_LIB_CHECK([PJSIP_INV_SESSION_REF], [pjsip], [pjsip_inv_add_ref], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
+      AST_EXT_LIB_CHECK([PJSIP_AUTH_CLT_DEINIT], [pjsip], [pjsip_auth_clt_deinit], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
    fi
 fi
 
diff --git a/contrib/ast-db-manage/config/versions/4468b4a91372_add_pjsip_asymmetric_rtp_codec.py b/contrib/ast-db-manage/config/versions/4468b4a91372_add_pjsip_asymmetric_rtp_codec.py
new file mode 100644
index 0000000..c121495
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/4468b4a91372_add_pjsip_asymmetric_rtp_codec.py
@@ -0,0 +1,31 @@
+"""add pjsip asymmetric rtp codec
+
+Revision ID: 4468b4a91372
+Revises: a6ef36f1309
+Create Date: 2016-10-25 10:57:20.808815
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '4468b4a91372'
+down_revision = 'a6ef36f1309'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
+
+YESNO_NAME = 'yesno_values'
+YESNO_VALUES = ['yes', 'no']
+
+def upgrade():
+    ############################# Enums ##############################
+
+    # yesno_values have already been created, so use postgres enum object
+    # type to get around "already created" issue - works okay with mysql
+    yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
+
+    op.add_column('ps_endpoints', sa.Column('asymmetric_rtp_codec', yesno_values))
+
+
+def downgrade():
+    op.drop_column('ps_endpoints', 'asymmetric_rtp_codec')
diff --git a/contrib/realtime/mssql/mssql_config.sql b/contrib/realtime/mssql/mssql_config.sql
index 4f940f3..1582406 100644
--- a/contrib/realtime/mssql/mssql_config.sql
+++ b/contrib/realtime/mssql/mssql_config.sql
@@ -1537,6 +1537,20 @@ UPDATE alembic_version SET version_num='a6ef36f1309' WHERE alembic_version.versi
 
 GO
 
+-- Running upgrade a6ef36f1309 -> 4468b4a91372
+
+ALTER TABLE ps_endpoints ADD asymmetric_rtp_codec VARCHAR(3) NULL;
+
+GO
+
+ALTER TABLE ps_endpoints ADD CONSTRAINT yesno_values CHECK (asymmetric_rtp_codec IN ('yes', 'no'));
+
+GO
+
+UPDATE alembic_version SET version_num='4468b4a91372' WHERE alembic_version.version_num = 'a6ef36f1309';
+
+GO
+
 COMMIT;
 
 GO
diff --git a/contrib/realtime/mysql/mysql_config.sql b/contrib/realtime/mysql/mysql_config.sql
index fc20ce3..69329d9 100644
--- a/contrib/realtime/mysql/mysql_config.sql
+++ b/contrib/realtime/mysql/mysql_config.sql
@@ -950,3 +950,9 @@ ALTER TABLE ps_globals ADD COLUMN ignore_uri_user_options ENUM('yes','no');
 
 UPDATE alembic_version SET version_num='a6ef36f1309' WHERE alembic_version.version_num = '4e2493ef32e6';
 
+-- Running upgrade a6ef36f1309 -> 4468b4a91372
+
+ALTER TABLE ps_endpoints ADD COLUMN asymmetric_rtp_codec ENUM('yes','no');
+
+UPDATE alembic_version SET version_num='4468b4a91372' WHERE alembic_version.version_num = 'a6ef36f1309';
+
diff --git a/contrib/realtime/oracle/oracle_config.sql b/contrib/realtime/oracle/oracle_config.sql
index 12fc40f..90f1245 100644
--- a/contrib/realtime/oracle/oracle_config.sql
+++ b/contrib/realtime/oracle/oracle_config.sql
@@ -1535,3 +1535,17 @@ UPDATE alembic_version SET version_num='a6ef36f1309' WHERE alembic_version.versi
 
 /
 
+-- Running upgrade a6ef36f1309 -> 4468b4a91372
+
+ALTER TABLE ps_endpoints ADD asymmetric_rtp_codec VARCHAR(3 CHAR)
+
+/
+
+ALTER TABLE ps_endpoints ADD CONSTRAINT yesno_values CHECK (asymmetric_rtp_codec IN ('yes', 'no'))
+
+/
+
+UPDATE alembic_version SET version_num='4468b4a91372' WHERE alembic_version.version_num = 'a6ef36f1309'
+
+/
+
diff --git a/contrib/realtime/postgresql/postgresql_config.sql b/contrib/realtime/postgresql/postgresql_config.sql
index 80b90d0..436d32f 100644
--- a/contrib/realtime/postgresql/postgresql_config.sql
+++ b/contrib/realtime/postgresql/postgresql_config.sql
@@ -1026,5 +1026,11 @@ ALTER TABLE ps_globals ADD COLUMN ignore_uri_user_options yesno_values;
 
 UPDATE alembic_version SET version_num='a6ef36f1309' WHERE alembic_version.version_num = '4e2493ef32e6';
 
+-- Running upgrade a6ef36f1309 -> 4468b4a91372
+
+ALTER TABLE ps_endpoints ADD COLUMN asymmetric_rtp_codec yesno_values;
+
+UPDATE alembic_version SET version_num='4468b4a91372' WHERE alembic_version.version_num = 'a6ef36f1309';
+
 COMMIT;
 
diff --git a/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py b/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py
index 40e9354..98a5e95 100755
--- a/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py
+++ b/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py
@@ -374,7 +374,7 @@ def from_dtlsenable(key, val, section, pjsip, nmapped):
 ###############################################################################
 
 # options in pjsip.conf on an endpoint that have no sip.conf equivalent:
-# type, rtp_ipv6, 100rel, trust_id_outbound, aggregate_mwi,
+# type, 100rel, trust_id_outbound, aggregate_mwi,
 # connected_line_method
 
 # known sip.conf peer keys that can be mapped to a pjsip.conf section/key
diff --git a/doc/appdocsxml.xslt b/doc/appdocsxml.xslt
index f067dec..511011a 100644
--- a/doc/appdocsxml.xslt
+++ b/doc/appdocsxml.xslt
@@ -145,5 +145,25 @@
             </xsl:attribute>
             <para>Number of channels in the bridge</para>
         </xsl:element>
+        <xsl:element name="parameter">
+            <xsl:attribute name="name">
+                <xsl:value-of select="concat(@prefix, 'BridgeVideoSourceMode')" />
+            </xsl:attribute>
+            <enumlist>
+                <enum name="none"/>
+                <enum name="talker"/>
+                <enum name="single"/>
+            </enumlist>
+            <para>The video source mode for the bridge.</para>
+        </xsl:element>
+        <xsl:element name="parameter">
+            <xsl:attribute name="required">
+                false
+            </xsl:attribute>
+            <xsl:attribute name="name">
+                <xsl:value-of select="concat(@prefix, 'BridgeVideoSource')" />
+            </xsl:attribute>
+            <para>If there is a video source for the bridge, the unique ID of the channel that is the video source.</para>
+        </xsl:element>
     </xsl:template>
 </xsl:stylesheet>
diff --git a/include/asterisk.h b/include/asterisk.h
index c4cdfab..041f177 100644
--- a/include/asterisk.h
+++ b/include/asterisk.h
@@ -36,6 +36,15 @@
 #define AST_FILE_MODE 0666
 #endif
 
+/* Make sure PATH_MAX is defined on platforms (HURD) that don't define it.
+ * Also be sure to handle the case of a path larger than PATH_MAX
+ * (err safely) in the code.
+ */
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+
 #define DEFAULT_LANGUAGE "en"
 
 #define DEFAULT_SAMPLE_RATE 8000
diff --git a/include/asterisk/_private.h b/include/asterisk/_private.h
index 9255dc1..d4a0d72 100644
--- a/include/asterisk/_private.h
+++ b/include/asterisk/_private.h
@@ -16,6 +16,7 @@
 #define _ASTERISK__PRIVATE_H
 
 int load_modules(unsigned int);		/*!< Provided by loader.c */
+int modules_shutdown(void);		/*!< Provided by loader.c */
 int load_pbx(void);			/*!< Provided by pbx.c */
 int load_pbx_builtins(void);	/*!< Provided by pbx_builtins.c */
 int load_pbx_functions_cli(void);	/*!< Provided by pbx_functions.c */
diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in
index 55189e2..6d6a28b 100644
--- a/include/asterisk/autoconfig.h.in
+++ b/include/asterisk/autoconfig.h.in
@@ -394,6 +394,9 @@
 /* Define if your system has the LIBEDIT libraries. */
 #undef HAVE_LIBEDIT
 
+/* Define if your system has the LIBEDIT_IS_UNICODE headers. */
+#undef HAVE_LIBEDIT_IS_UNICODE
+
 /* Define to 1 if you have the <libintl.h> header file. */
 #undef HAVE_LIBINTL_H
 
@@ -584,6 +587,9 @@
 /* Define if your system has PJPROJECT_BUNDLED */
 #undef HAVE_PJPROJECT_BUNDLED
 
+/* Define to 1 if PJPROJECT has the pjsip_auth_clt_deinit support feature. */
+#undef HAVE_PJSIP_AUTH_CLT_DEINIT
+
 /* Define to 1 if PJPROJECT has the PJSIP Dialog Create UAS with Incremented
    Lock feature. */
 #undef HAVE_PJSIP_DLG_CREATE_UAS_AND_INC_LOCK
@@ -790,6 +796,9 @@
 /* Define to 1 if you have the `roundl' function. */
 #undef HAVE_ROUNDL
 
+/* Define to 1 if rt has the Realtime functions feature. */
+#undef HAVE_RT
+
 /* Define if your system has the RTLD_NOLOAD headers. */
 #undef HAVE_RTLD_NOLOAD
 
diff --git a/include/asterisk/bridge.h b/include/asterisk/bridge.h
index 30ac095..0d43767 100644
--- a/include/asterisk/bridge.h
+++ b/include/asterisk/bridge.h
@@ -903,6 +903,15 @@ int ast_bridge_is_video_src(struct ast_bridge *bridge, struct ast_channel *chan)
  */
 void ast_bridge_remove_video_src(struct ast_bridge *bridge, struct ast_channel *chan);
 
+/*!
+ * \brief Converts an enum representation of a bridge video mode to string
+ *
+ * \param video_mode The video mode
+ *
+ * \retval A string representation of \c video_mode
+ */
+const char *ast_bridge_video_mode_to_string(enum ast_bridge_video_mode_type video_mode);
+
 enum ast_transfer_result {
 	/*! The transfer completed successfully */
 	AST_BRIDGE_TRANSFER_SUCCESS,
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index e42307d..c9e537f 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -966,6 +966,16 @@ enum {
 	 * The channel is executing a subroutine or macro
 	 */
 	AST_FLAG_SUBROUTINE_EXEC = (1 << 27),
+	/*!
+	 * The channel is currently in an operation where
+	 * frames should be deferred.
+	 */
+	AST_FLAG_DEFER_FRAMES = (1 << 28),
+	/*!
+	 * The channel is currently deferring hangup frames
+	 * in addition to other frame types.
+	 */
+	AST_FLAG_DEFER_HANGUP_FRAMES = (1 << 29),
 };
 
 /*! \brief ast_bridge_config flags */
@@ -4669,4 +4679,55 @@ int ast_channel_feature_hooks_append(struct ast_channel *chan, struct ast_bridge
  */
 int ast_channel_feature_hooks_replace(struct ast_channel *chan, struct ast_bridge_features *features);
 
+enum ast_channel_error {
+	/* Unable to determine what error occurred. */
+	AST_CHANNEL_ERROR_UNKNOWN,
+	/* Channel with this ID already exists */
+	AST_CHANNEL_ERROR_ID_EXISTS,
+};
+
+/*!
+ * \brief Get error code for latest channel operation.
+ */
+enum ast_channel_error ast_channel_errno(void);
+
+/*!
+ * \brief Retrieve the deferred read queue.
+ */
+struct ast_readq_list *ast_channel_deferred_readq(struct ast_channel *chan);
+
+/*!
+ * \brief Start deferring deferrable frames on this channel
+ *
+ * Sometimes, a channel gets entered into a mode where a "main" application
+ * is tasked with servicing frames on the channel, but that application does
+ * not need to act on those frames. However, it would be imprudent to simply
+ * drop important frames. This function can be called so that important frames
+ * will be deferred, rather than placed in the channel frame queue as normal.
+ *
+ * Hangups are an interesting frame type. Hangups will always be detectable by
+ * a reader when a channel is deferring frames. If the defer_hangups parameter
+ * is non-zero, then the hangup frame will also be duplicated and deferred, so
+ * that the next reader of the channel will get the hangup frame, too.
+ *
+ * \pre chan MUST be locked before calling
+ *
+ * \param chan The channel on which frames should be deferred
+ * \param defer_hangups Defer hangups in addition to other deferrable frames
+ */
+void ast_channel_start_defer_frames(struct ast_channel *chan, int defer_hangups);
+
+/*!
+ * \brief Stop deferring deferrable frames on this channel
+ *
+ * When it is time to stop deferring frames on the channel, all deferred frames
+ * will be queued onto the channel's read queue so that the next servicer of
+ * the channel can handle those frames as necessary.
+ *
+ * \pre chan MUST be locked before calling
+ *
+ * \param chan The channel on which to stop deferring frames.
+ */
+void ast_channel_stop_defer_frames(struct ast_channel *chan);
+
 #endif /* _ASTERISK_CHANNEL_H */
diff --git a/include/asterisk/channel_internal.h b/include/asterisk/channel_internal.h
index d1231b4..2316e2f 100644
--- a/include/asterisk/channel_internal.h
+++ b/include/asterisk/channel_internal.h
@@ -25,3 +25,5 @@ int ast_channel_internal_is_finalized(struct ast_channel *chan);
 void ast_channel_internal_cleanup(struct ast_channel *chan);
 int ast_channel_internal_setup_topics(struct ast_channel *chan);
 
+void ast_channel_internal_errno_set(enum ast_channel_error error);
+enum ast_channel_error ast_channel_internal_errno(void);
diff --git a/include/asterisk/file.h b/include/asterisk/file.h
index c71866e..01e5797 100644
--- a/include/asterisk/file.h
+++ b/include/asterisk/file.h
@@ -138,6 +138,34 @@ int ast_filedelete(const char *filename, const char *fmt);
 int ast_filecopy(const char *oldname, const char *newname, const char *fmt);
 
 /*!
+ * \brief Callback called for each file found when reading directories
+ * \param dir_name the name of the directory
+ * \param filename the name of the file
+ * \param obj user data object
+ * \return non-zero to stop reading, otherwise zero to continue
+ */
+typedef int (*ast_file_on_file)(const char *dir_name, const char *filename, void *obj);
+
+/*!
+ * \brief Recursively iterate through files and directories up to max_depth
+ * \param dir_name the name of the directory to search
+ * \param on_file callback called on each file
+ * \param obj user data object
+ * \param max_depth re-curse into sub-directories up to a given maximum (-1 = infinite)
+ * \return -1 or errno on failure, otherwise 0
+ */
+int ast_file_read_dirs(const char *dir_name, ast_file_on_file on_file, void *obj, int max_depth);
+
+/*!
+ * \brief Iterate over each file in a given directory
+ * \param dir_name the name of the directory to search
+ * \param on_file callback called on each file
+ * \param obj user data object
+ * \return -1 or errno on failure, otherwise 0
+ */
+#define ast_file_read_dir(dir_name, on_file, obj) ast_file_read_dirs(dir_name, on_file, obj, 1)
+
+/*!
  * \brief Waits for a stream to stop or digit to be pressed
  * \param c channel to waitstream on
  * \param breakon string of DTMF digits to break upon
diff --git a/include/asterisk/manager.h b/include/asterisk/manager.h
index b5ede54..3f22d5f 100644
--- a/include/asterisk/manager.h
+++ b/include/asterisk/manager.h
@@ -54,7 +54,7 @@
 - \ref manager.c Main manager code file
  */
 
-#define AMI_VERSION                     "2.8.0"
+#define AMI_VERSION                     "2.9.0"
 #define DEFAULT_MANAGER_PORT 5038	/* Default port for Asterisk management via TCP */
 #define DEFAULT_MANAGER_TLS_PORT 5039	/* Default port for Asterisk management via TCP */
 
diff --git a/include/asterisk/module.h b/include/asterisk/module.h
index 35ee8bb..b92043b 100644
--- a/include/asterisk/module.h
+++ b/include/asterisk/module.h
@@ -228,13 +228,6 @@ int ast_loader_register(int (*updater)(void));
  */
 int ast_loader_unregister(int (*updater)(void));
 
-/*!
- * \brief Run the unload() callback for all loaded modules
- *
- * This function should be called when Asterisk is shutting down gracefully.
- */
-void ast_module_shutdown(void);
-
 /*! 
  * \brief Match modules names for the Asterisk cli.
  * \param line Unused by this function, but this should be the line we are
diff --git a/include/asterisk/options.h b/include/asterisk/options.h
index 0da5799..21bd7a7 100644
--- a/include/asterisk/options.h
+++ b/include/asterisk/options.h
@@ -155,6 +155,8 @@ extern int dahdi_chan_name_len;
 
 extern int ast_language_is_prefix;
 
+extern unsigned int ast_option_rtpptdynamic;
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index 28ecf7f..4ad6607 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -753,6 +753,8 @@ struct ast_sip_endpoint {
 	unsigned int faxdetect_timeout;
 	/*! Override the user on the outgoing Contact header with this value. */
 	char *contact_user;
+	/*! Do we allow an asymmetric RTP codec? */
+	unsigned int asymmetric_rtp_codec;
 };
 
 /*!
diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h
index b7be5e8..24af056 100644
--- a/include/asterisk/rtp_engine.h
+++ b/include/asterisk/rtp_engine.h
@@ -84,6 +84,9 @@ extern "C" {
 /*! First dynamic RTP payload type */
 #define AST_RTP_PT_FIRST_DYNAMIC 96
 
+/*! Last reassignable RTP payload type */
+#define AST_RTP_PT_LAST_REASSIGN 63
+
 /*! Maximum number of generations */
 #define AST_RED_MAX_GENERATION 5
 
diff --git a/include/asterisk/stasis_app.h b/include/asterisk/stasis_app.h
index 8ceeffb..53ce116 100644
--- a/include/asterisk/stasis_app.h
+++ b/include/asterisk/stasis_app.h
@@ -77,6 +77,16 @@ typedef void (*stasis_app_cb)(void *data, const char *app_name,
 struct ao2_container *stasis_app_get_all(void);
 
 /*!
+ * \brief Retrieve a handle to a Stasis application by its name
+ *
+ * \param name The name of the registered Stasis application
+ *
+ * \return \c stasis_app on success.
+ * \return \c NULL on error.
+ */
+struct stasis_app *stasis_app_get_by_name(const char *name);
+
+/*!
  * \brief Register a new Stasis application.
  *
  * If an application is already registered with the given name, the old
diff --git a/include/asterisk/stasis_bridges.h b/include/asterisk/stasis_bridges.h
index d549e46..05d356c 100644
--- a/include/asterisk/stasis_bridges.h
+++ b/include/asterisk/stasis_bridges.h
@@ -58,6 +58,10 @@ struct ast_bridge_snapshot {
 	unsigned int num_channels;
 	/*! Number of active channels in the bridge. */
 	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/tcptls.h b/include/asterisk/tcptls.h
index e1a632c..3c5f450 100644
--- a/include/asterisk/tcptls.h
+++ b/include/asterisk/tcptls.h
@@ -65,6 +65,7 @@
 #ifdef DO_SSL
 #include <openssl/ssl.h>
 #include <openssl/err.h>
+#include <openssl/x509v3.h>
 #else
 /* declare dummy types so we can define a pointer to them */
 typedef struct {} SSL;
diff --git a/include/asterisk/vector.h b/include/asterisk/vector.h
index 4306670..6b4e632 100644
--- a/include/asterisk/vector.h
+++ b/include/asterisk/vector.h
@@ -172,9 +172,11 @@
 			typeof((vec)->elems) new_elems = ast_calloc(1,		\
 				new_max * sizeof(*new_elems));					\
 			if (new_elems) {									\
-				memcpy(new_elems, (vec)->elems,					\
-					(vec)->current * sizeof(*new_elems)); 		\
-				ast_free((vec)->elems);							\
+				if ((vec)->elems) {								\
+					memcpy(new_elems, (vec)->elems,				\
+						(vec)->current * sizeof(*new_elems)); 	\
+					ast_free((vec)->elems);						\
+				}												\
 				(vec)->elems = new_elems;						\
 				(vec)->max = new_max;							\
 			} else {											\
diff --git a/main/Makefile b/main/Makefile
index e476969..37b9446 100644
--- a/main/Makefile
+++ b/main/Makefile
@@ -253,6 +253,7 @@ ifeq ($(PJPROJECT_BUNDLED),yes)
 ASTPJ_SO_VERSION=2
 ASTPJ_LDLIBS=-L. -lasteriskpj
 
+PJDIR=$(ASTTOPDIR)/$(PJPROJECT_DIR)/source
 -include $(ASTTOPDIR)/$(PJPROJECT_DIR)/build.mak
 
 PJPROJECT_LDLIBS := \
@@ -278,15 +279,15 @@ ASTPJ_LIB:=libasteriskpj.so
 libasteriskpj.exports: $(ASTTOPDIR)/$(PJPROJECT_DIR)/pjproject.symbols
 	$(ECHO_PREFIX) echo "   [GENERATE] libasteriskpj.exports"
 ifeq ($(GNU_LD),1)
-	$(CMD_PREFIX) echo -e "{\n\tglobal:" > libasteriskpj.exports
-	$(CMD_PREFIX) sed -r -e "s/.*/\t\t$(LINKER_SYMBOL_PREFIX)&;/" $(ASTTOPDIR)/$(PJPROJECT_DIR)/pjproject.symbols >> libasteriskpj.exports
-	$(CMD_PREFIX) echo -e "\t\t$(LINKER_SYMBOL_PREFIX)ast_pj_init;\n" >> libasteriskpj.exports
-	$(CMD_PREFIX) echo -e "\tlocal:\n\t\t*;\n};" >> libasteriskpj.exports
+	$(CMD_PREFIX) echo -e "{\nglobal:" > libasteriskpj.exports
+	$(CMD_PREFIX) sed -r -e "s/.*/$(LINKER_SYMBOL_PREFIX)&;/" $(ASTTOPDIR)/$(PJPROJECT_DIR)/pjproject.symbols >> libasteriskpj.exports
+	$(CMD_PREFIX) echo -e "$(LINKER_SYMBOL_PREFIX)ast_pj_init;\n" >> libasteriskpj.exports
+	$(CMD_PREFIX) echo -e "local:\n*;\n};" >> libasteriskpj.exports
 endif
 
 $(ASTPJ_LIB).$(ASTPJ_SO_VERSION): _ASTLDFLAGS+=-Wl,-soname=$(ASTPJ_LIB) $(PJ_LDFLAGS)
 $(ASTPJ_LIB).$(ASTPJ_SO_VERSION): _ASTCFLAGS+=-fPIC -DAST_MODULE=\"asteriskpj\" $(PJ_CFLAGS)
-$(ASTPJ_LIB).$(ASTPJ_SO_VERSION): LIBS+=$(PJPROJECT_LDLIBS) -lssl -lcrypto -luuid -lm -lrt -lpthread
+$(ASTPJ_LIB).$(ASTPJ_SO_VERSION): LIBS+=$(PJPROJECT_LDLIBS) -lssl -lcrypto -luuid -lm -lpthread $(RT_LIB)
 ifeq ($(GNU_LD),1)
     $(ASTPJ_LIB).$(ASTPJ_SO_VERSION): SO_SUPPRESS_SYMBOLS=-Wl,--version-script,libasteriskpj.exports,--warn-common
 endif
@@ -311,7 +312,7 @@ ASTPJ_LIB:=libasteriskpj.dylib
 # /lib or /usr/lib
 $(ASTPJ_LIB): _ASTLDFLAGS+=-dynamiclib -install_name $(ASTLIBDIR)/$(ASTPJ_LIB) $(PJ_LDFLAGS)
 $(ASTPJ_LIB): _ASTCFLAGS+=-fPIC -DAST_MODULE=\"asteriskpj\" $(PJ_CFLAGS)
-$(ASTPJ_LIB): LIBS+=$(PJPROJECT_LIBS)  -lssl -lcrypto -luuid -lm -lrt -lpthread
+$(ASTPJ_LIB): LIBS+=$(PJPROJECT_LIBS)  -lssl -lcrypto -luuid -lm -lpthread $(RT_LIB)
 $(ASTPJ_LIB): SOLINK=$(DYLINK)
 
 # Special rules for building a shared library (not a dynamically loadable module)
diff --git a/main/asterisk.c b/main/asterisk.c
index 92993d3..fa91993 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -249,6 +249,7 @@ int daemon(int, int);  /* defined in libresolv of all places */
 #include "asterisk/codec.h"
 #include "asterisk/format_cache.h"
 #include "asterisk/astdb.h"
+#include "asterisk/options.h"
 
 #include "../defaults.h"
 
@@ -331,6 +332,7 @@ unsigned int option_dtmfminduration;		/*!< Minimum duration of DTMF. */
 #if defined(HAVE_SYSINFO)
 long option_minmemfree;				/*!< Minimum amount of free system memory - stop accepting calls if free memory falls below this watermark */
 #endif
+unsigned int ast_option_rtpptdynamic;
 
 /*! @} */
 
@@ -670,6 +672,19 @@ static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_c
 	ast_cli(a->fd, "  Generic PLC:                 %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_GENERIC_PLC) ? "Enabled" : "Disabled");
 	ast_cli(a->fd, "  Min DTMF duration::          %u\n", option_dtmfminduration);
 
+	if (ast_option_rtpptdynamic == AST_RTP_PT_LAST_REASSIGN) {
+		ast_cli(a->fd, "  RTP dynamic payload types:   %u,%u-%u\n",
+		        ast_option_rtpptdynamic,
+		        AST_RTP_PT_FIRST_DYNAMIC, AST_RTP_MAX_PT - 1);
+	} else if (ast_option_rtpptdynamic < AST_RTP_PT_LAST_REASSIGN) {
+		ast_cli(a->fd, "  RTP dynamic payload types:   %u-%u,%u-%u\n",
+		        ast_option_rtpptdynamic, AST_RTP_PT_LAST_REASSIGN,
+		        AST_RTP_PT_FIRST_DYNAMIC, AST_RTP_MAX_PT - 1);
+	} else {
+		ast_cli(a->fd, "  RTP dynamic payload types:   %u-%u\n",
+		        AST_RTP_PT_FIRST_DYNAMIC, AST_RTP_MAX_PT - 1);
+	}
+
 	ast_cli(a->fd, "\n* Subsystems\n");
 	ast_cli(a->fd, "  -------------\n");
 	ast_cli(a->fd, "  Manager (AMI):               %s\n", check_manager_enabled() ? "Enabled" : "Disabled");
@@ -2140,8 +2155,9 @@ static void really_quit(int num, shutdown_nice_t niceness, int restart)
 	struct ast_json *json_object = NULL;
 	int run_cleanups = niceness >= SHUTDOWN_NICE;
 
-	if (run_cleanups) {
-		ast_module_shutdown();
+	if (run_cleanups && modules_shutdown()) {
+		ast_verb(0, "Some modules could not be unloaded, switching to fast shutdown\n");
+		run_cleanups = 0;
 	}
 
 	if (!restart) {
@@ -2818,7 +2834,11 @@ static void send_rasterisk_connect_commands(void)
 	}
 }
 
+#ifdef HAVE_LIBEDIT_IS_UNICODE
+static int ast_el_read_char(EditLine *editline, wchar_t *cp)
+#else
 static int ast_el_read_char(EditLine *editline, char *cp)
+#endif
 {
 	int num_read = 0;
 	int lastpos = 0;
@@ -2848,10 +2868,16 @@ static int ast_el_read_char(EditLine *editline, char *cp)
 		}
 
 		if (!ast_opt_exec && fds[1].revents) {
-			num_read = read(STDIN_FILENO, cp, 1);
+			char c = '\0';
+			num_read = read(STDIN_FILENO, &c, 1);
 			if (num_read < 1) {
 				break;
 			} else {
+#ifdef 	HAVE_LIBEDIT_IS_UNICODE
+				*cp = btowc(c);
+#else
+				*cp = c;
+#endif
 				return (num_read);
 			}
 		}
@@ -2895,7 +2921,11 @@ static int ast_el_read_char(EditLine *editline, char *cp)
 			console_print(buf, 0);
 
 			if ((res < EL_BUF_SIZE - 1) && ((buf[res-1] == '\n') || (res >= 2 && buf[res-2] == '\n'))) {
+#ifdef 	HAVE_LIBEDIT_IS_UNICODE
+				*cp = btowc(CC_REFRESH);
+#else
 				*cp = CC_REFRESH;
+#endif
 				return(1);
 			} else {
 				lastpos = 1;
@@ -2903,7 +2933,12 @@ static int ast_el_read_char(EditLine *editline, char *cp)
 		}
 	}
 
+#ifdef 	HAVE_LIBEDIT_IS_UNICODE
+	*cp = btowc('\0');
+#else
 	*cp = '\0';
+#endif
+
 	return (0);
 }
 
@@ -3605,6 +3640,7 @@ static void ast_readconfig(void)
 
 	/* Set default value */
 	option_dtmfminduration = AST_MIN_DTMF_DURATION;
+	ast_option_rtpptdynamic = AST_RTP_PT_FIRST_DYNAMIC;
 
 	if (ast_opt_override_config) {
 		cfg = ast_config_load2(ast_config_AST_CONFIG_FILE, "" /* core, can't reload */, config_flags);
@@ -3754,6 +3790,12 @@ static void ast_readconfig(void)
 			if (sscanf(v->value, "%30u", &option_dtmfminduration) != 1) {
 				option_dtmfminduration = AST_MIN_DTMF_DURATION;
 			}
+		/* http://www.iana.org/assignments/rtp-parameters
+		 * RTP dynamic payload types start at 96 normally; extend down to 0 */
+		} else if (!strcasecmp(v->name, "rtp_pt_dynamic")) {
+			ast_parse_arg(v->value, PARSE_UINT32|PARSE_IN_RANGE|PARSE_DEFAULT,
+			              &ast_option_rtpptdynamic, AST_RTP_PT_FIRST_DYNAMIC,
+			              0, AST_RTP_PT_LAST_REASSIGN);
 		} else if (!strcasecmp(v->name, "maxcalls")) {
 			if ((sscanf(v->value, "%30d", &ast_option_maxcalls) != 1) || (ast_option_maxcalls < 0)) {
 				ast_option_maxcalls = 0;
diff --git a/main/astobj2.c b/main/astobj2.c
index f5175ea..7320c5e 100644
--- a/main/astobj2.c
+++ b/main/astobj2.c
@@ -52,8 +52,10 @@ static FILE *ref_log;
 struct __priv_data {
 	int ref_counter;
 	ao2_destructor_fn destructor_fn;
+#if defined(AO2_DEBUG)
 	/*! User data size for stats */
 	size_t data_size;
+#endif
 	/*! The ao2 object option flags */
 	uint32_t options;
 	/*! magic number.  This is used to verify that a pointer passed in is a
@@ -570,11 +572,11 @@ static void *internal_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_f
 	/* Initialize common ao2 values. */
 	obj->priv_data.ref_counter = 1;
 	obj->priv_data.destructor_fn = destructor_fn;	/* can be NULL */
-	obj->priv_data.data_size = data_size;
 	obj->priv_data.options = options;
 	obj->priv_data.magic = AO2_MAGIC;
 
 #ifdef AO2_DEBUG
+	obj->priv_data.data_size = data_size;
 	ast_atomic_fetchadd_int(&ao2.total_objects, 1);
 	ast_atomic_fetchadd_int(&ao2.total_mem, data_size);
 	ast_atomic_fetchadd_int(&ao2.total_refs, 1);
diff --git a/main/autoservice.c b/main/autoservice.c
index 305ab23..81d267c 100644
--- a/main/autoservice.c
+++ b/main/autoservice.c
@@ -61,10 +61,6 @@ struct asent {
 	unsigned int use_count;
 	unsigned int orig_end_dtmf_flag:1;
 	unsigned int ignore_frame_types;
-	/*! Frames go on at the head of deferred_frames, so we have the frames
-	 *  from newest to oldest.  As we put them at the head of the readq, we'll
-	 *  end up with them in the right order for the channel's readq. */
-	AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames;
 	AST_LIST_ENTRY(asent) list;
 };
 
@@ -79,19 +75,13 @@ static int as_chan_list_state;
 static void *autoservice_run(void *ign)
 {
 	struct ast_callid *callid = NULL;
-	struct ast_frame hangup_frame = {
-		.frametype = AST_FRAME_CONTROL,
-		.subclass.integer = AST_CONTROL_HANGUP,
-	};
 
 	while (!asexit) {
 		struct ast_channel *mons[MAX_AUTOMONS];
-		struct asent *ents[MAX_AUTOMONS];
 		struct ast_channel *chan;
 		struct asent *as;
-		int i, x = 0, ms = 50;
+		int x = 0, ms = 50;
 		struct ast_frame *f = NULL;
-		struct ast_frame *defer_frame = NULL;
 
 		AST_LIST_LOCK(&aslist);
 
@@ -106,7 +96,6 @@ static void *autoservice_run(void *ign)
 		AST_LIST_TRAVERSE(&aslist, as, list) {
 			if (!ast_check_hangup(as->chan)) {
 				if (x < MAX_AUTOMONS) {
-					ents[x] = as;
 					mons[x++] = as->chan;
 				} else {
 					ast_log(LOG_WARNING, "Exceeded maximum number of automatic monitoring events.  Fix autoservice.c\n");
@@ -137,51 +126,9 @@ static void *autoservice_run(void *ign)
 		}
 
 		f = ast_read(chan);
-
-		if (!f) {
-			/* No frame means the channel has been hung up.
-			 * A hangup frame needs to be queued here as ast_waitfor() may
-			 * never return again for the condition to be detected outside
-			 * of autoservice.  So, we'll leave a HANGUP queued up so the
-			 * thread in charge of this channel will know. */
-
-			defer_frame = &hangup_frame;
-		} else if (ast_is_deferrable_frame(f)) {
-			defer_frame = f;
-		} else {
-			/* Can't defer. Discard and continue with next. */
+		if (f) {
 			ast_frfree(f);
-			continue;
 		}
-
-		for (i = 0; i < x; i++) {
-			struct ast_frame *dup_f;
-
-			if (mons[i] != chan) {
-				continue;
-			}
-
-			if (!f) { /* defer_frame == &hangup_frame */
-				if ((dup_f = ast_frdup(defer_frame))) {
-					AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list);
-				}
-			} else {
-				if ((dup_f = ast_frisolate(defer_frame))) {
-					AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list);
-				}
-				if (dup_f != defer_frame) {
-					ast_frfree(defer_frame);
-				}
-			}
-
-			break;
-		}
-		/* The ast_waitfor_n() call will only read frames from
-		 * the channels' file descriptors. If ast_waitfor_n()
-		 * returns non-NULL, then one of the channels in the
-		 * mons array must have triggered the return. It's
-		 * therefore impossible that we got here while (i >= x).
-		 * If we did, we'd need to ast_frfree(f) if (f). */
 	}
 
 	ast_callid_threadassoc_change(NULL);
@@ -220,6 +167,7 @@ int ast_autoservice_start(struct ast_channel *chan)
 	as->orig_end_dtmf_flag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY) ? 1 : 0;
 	if (!as->orig_end_dtmf_flag)
 		ast_set_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
+	ast_channel_start_defer_frames(chan, 1);
 	ast_channel_unlock(chan);
 
 	AST_LIST_LOCK(&aslist);
@@ -253,7 +201,6 @@ int ast_autoservice_stop(struct ast_channel *chan)
 {
 	int res = -1;
 	struct asent *as, *removed = NULL;
-	struct ast_frame *f;
 	int chan_list_state;
 
 	AST_LIST_LOCK(&aslist);
@@ -305,12 +252,7 @@ int ast_autoservice_stop(struct ast_channel *chan)
 	}
 
 	ast_channel_lock(chan);
-	while ((f = AST_LIST_REMOVE_HEAD(&as->deferred_frames, frame_list))) {
-		if (!((1 << f->frametype) & as->ignore_frame_types)) {
-			ast_queue_frame_head(chan, f);
-		}
-		ast_frfree(f);
-	}
+	ast_channel_stop_defer_frames(chan);
 	ast_channel_unlock(chan);
 
 	free(as);
diff --git a/main/bridge.c b/main/bridge.c
index a0f239b..6152b1b 100644
--- a/main/bridge.c
+++ b/main/bridge.c
@@ -3770,8 +3770,11 @@ void ast_bridge_set_single_src_video_mode(struct ast_bridge *bridge, struct ast_
 	cleanup_video_mode(bridge);
 	bridge->softmix.video_mode.mode = AST_BRIDGE_VIDEO_MODE_SINGLE_SRC;
 	bridge->softmix.video_mode.mode_data.single_src_data.chan_vsrc = ast_channel_ref(video_src_chan);
-	ast_test_suite_event_notify("BRIDGE_VIDEO_MODE", "Message: video mode set to single source\r\nVideo Mode: %u\r\nVideo Channel: %s",
-		bridge->softmix.video_mode.mode, ast_channel_name(video_src_chan));
+	ast_verb(5, "Video source in bridge '%s' (%s) is now '%s' (%s)\n",
+		bridge->name, bridge->uniqueid,
+		ast_channel_name(video_src_chan),
+		ast_channel_uniqueid(video_src_chan));
+	ast_bridge_publish_state(bridge);
 	ast_indicate(video_src_chan, AST_CONTROL_VIDUPDATE);
 	ast_bridge_unlock(bridge);
 }
@@ -3781,8 +3784,6 @@ void ast_bridge_set_talker_src_video_mode(struct ast_bridge *bridge)
 	ast_bridge_lock(bridge);
 	cleanup_video_mode(bridge);
 	bridge->softmix.video_mode.mode = AST_BRIDGE_VIDEO_MODE_TALKER_SRC;
-	ast_test_suite_event_notify("BRIDGE_VIDEO_MODE", "Message: video mode set to talker source\r\nVideo Mode: %u",
-		bridge->softmix.video_mode.mode);
 	ast_bridge_unlock(bridge);
 }
 
@@ -3810,14 +3811,22 @@ void ast_bridge_update_talker_src_video_mode(struct ast_bridge *bridge, struct a
 		}
 		data->chan_vsrc = ast_channel_ref(chan);
 		data->average_talking_energy = talker_energy;
-		ast_test_suite_event_notify("BRIDGE_VIDEO_SRC", "Message: video source updated\r\nVideo Channel: %s", ast_channel_name(data->chan_vsrc));
+		ast_verb(5, "Video source in bridge '%s' (%s) is now '%s' (%s)\n",
+			bridge->name, bridge->uniqueid,
+			ast_channel_name(data->chan_vsrc),
+			ast_channel_uniqueid(data->chan_vsrc));
+		ast_bridge_publish_state(bridge);
 		ast_indicate(data->chan_vsrc, AST_CONTROL_VIDUPDATE);
 	} else if ((data->average_talking_energy < talker_energy) && !is_keyframe) {
 		ast_indicate(chan, AST_CONTROL_VIDUPDATE);
 	} else if (!data->chan_vsrc && is_keyframe) {
 		data->chan_vsrc = ast_channel_ref(chan);
 		data->average_talking_energy = talker_energy;
-		ast_test_suite_event_notify("BRIDGE_VIDEO_SRC", "Message: video source updated\r\nVideo Channel: %s", ast_channel_name(data->chan_vsrc));
+		ast_verb(5, "Video source in bridge '%s' (%s) is now '%s' (%s)\n",
+			bridge->name, bridge->uniqueid,
+			ast_channel_name(data->chan_vsrc),
+			ast_channel_uniqueid(data->chan_vsrc));
+		ast_bridge_publish_state(bridge);
 		ast_indicate(chan, AST_CONTROL_VIDUPDATE);
 	} else if (!data->chan_old_vsrc && is_keyframe) {
 		data->chan_old_vsrc = ast_channel_ref(chan);
@@ -3908,6 +3917,19 @@ void ast_bridge_remove_video_src(struct ast_bridge *bridge, struct ast_channel *
 	ast_bridge_unlock(bridge);
 }
 
+const char *ast_bridge_video_mode_to_string(enum ast_bridge_video_mode_type video_mode)
+{
+	switch (video_mode) {
+	case AST_BRIDGE_VIDEO_MODE_TALKER_SRC:
+		return "talker";
+	case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC:
+		return "single";
+	case AST_BRIDGE_VIDEO_MODE_NONE:
+	default:
+		return "none";
+	}
+}
+
 static int channel_hash(const void *obj, int flags)
 {
 	const struct ast_channel *chan = obj;
diff --git a/main/bridge_channel.c b/main/bridge_channel.c
index 2fafdf9..652d40c 100644
--- a/main/bridge_channel.c
+++ b/main/bridge_channel.c
@@ -2743,6 +2743,9 @@ int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel)
 	bridge_channel_settle_owed_events(bridge_channel->bridge, bridge_channel);
 	bridge_reconfigured(bridge_channel->bridge, 1);
 
+	/* Remove ourselves if we are the video source */
+	ast_bridge_remove_video_src(bridge_channel->bridge, bridge_channel->chan);
+
 	ast_bridge_unlock(bridge_channel->bridge);
 
 	/* Must release any swap ref after unlocking the bridge. */
diff --git a/main/cdr.c b/main/cdr.c
index ab6530e..5e515d8 100644
--- a/main/cdr.c
+++ b/main/cdr.c
@@ -695,6 +695,7 @@ struct cdr_object {
 	);
 	struct cdr_object *next;                /*!< The next CDR object in the chain */
 	struct cdr_object *last;                /*!< The last CDR object in the chain */
+	int is_root;                            /*!< True if this is the first CDR in the chain */
 };
 
 /*!
@@ -850,7 +851,22 @@ static void cdr_object_dtor(void *obj)
 	}
 	ast_string_field_free_memory(cdr);
 
-	ao2_cleanup(cdr->next);
+	/* CDR destruction used to work by calling ao2_cleanup(next) and
+	 * allowing the chain to destroy itself neatly. Unfortunately, for
+	 * really long chains, this can result in a stack overflow. So now
+	 * when the root CDR is destroyed, it is responsible for unreffing
+	 * all CDRs in the chain
+	 */
+	if (cdr->is_root) {
+		struct cdr_object *curr = cdr->next;
+		struct cdr_object *next;
+
+		while (curr) {
+			next = curr->next;
+			ao2_cleanup(curr);
+			curr = next;
+		}
+	}
 }
 
 /*!
@@ -2094,6 +2110,7 @@ static void handle_channel_cache_message(void *data, struct stasis_subscription
 		if (!cdr) {
 			return;
 		}
+		cdr->is_root = 1;
 		ao2_link(active_cdrs_by_channel, cdr);
 	}
 
diff --git a/main/channel.c b/main/channel.c
index c6cb925..54b9130 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -767,6 +767,27 @@ static const struct ast_channel_tech null_tech = {
 
 static void ast_channel_destructor(void *obj);
 static void ast_dummy_channel_destructor(void *obj);
+static int ast_channel_by_uniqueid_cb(void *obj, void *arg, void *data, int flags);
+
+static int does_id_conflict(const char *uniqueid)
+{
+	struct ast_channel *conflict;
+	int length = 0;
+
+	if (ast_strlen_zero(uniqueid)) {
+		return 0;
+	}
+
+	conflict = ast_channel_callback(ast_channel_by_uniqueid_cb, (char *) uniqueid, &length, OBJ_NOLOCK);
+	if (conflict) {
+		ast_log(LOG_ERROR, "Channel Unique ID '%s' already in use by channel %s(%p)\n",
+			uniqueid, ast_channel_name(conflict), conflict);
+		ast_channel_unref(conflict);
+		return 1;
+	}
+
+	return 0;
+}
 
 /*! \brief Create a new channel structure */
 static struct ast_channel * attribute_malloc __attribute__((format(printf, 15, 0)))
@@ -940,16 +961,33 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
 		ast_channel_tech_set(tmp, &null_tech);
 	}
 
-	ast_channel_internal_finalize(tmp);
-
-	ast_atomic_fetchadd_int(&chancount, +1);
-
 	/* You might scream "locking inversion" at seeing this but it is actually perfectly fine.
 	 * Since the channel was just created nothing can know about it yet or even acquire it.
 	 */
 	ast_channel_lock(tmp);
 
-	ao2_link(channels, tmp);
+	ao2_lock(channels);
+
+	if (assignedids && (does_id_conflict(assignedids->uniqueid) || does_id_conflict(assignedids->uniqueid2))) {
+		ast_channel_internal_errno_set(AST_CHANNEL_ERROR_ID_EXISTS);
+		ao2_unlock(channels);
+		/* This is a bit unorthodox, but we can't just call ast_channel_stage_snapshot_done()
+		 * because that will result in attempting to publish the channel snapshot. That causes
+		 * badness in some places, such as CDRs. So we need to manually clear the flag on the
+		 * channel that says that a snapshot is being cleared.
+		 */
+		ast_clear_flag(ast_channel_flags(tmp), AST_FLAG_SNAPSHOT_STAGE);
+		ast_channel_unlock(tmp);
+		return ast_channel_unref(tmp);
+	}
+
+	ast_channel_internal_finalize(tmp);
+
+	ast_atomic_fetchadd_int(&chancount, +1);
+
+	ao2_link_flags(channels, tmp, OBJ_NOLOCK);
+
+	ao2_unlock(channels);
 
 	if (endpoint) {
 		ast_endpoint_add_channel(endpoint, tmp);
@@ -1023,6 +1061,26 @@ struct ast_channel *ast_dummy_channel_alloc(void)
 	return tmp;
 }
 
+void ast_channel_start_defer_frames(struct ast_channel *chan, int defer_hangups)
+{
+	ast_set_flag(ast_channel_flags(chan), AST_FLAG_DEFER_FRAMES);
+	ast_set2_flag(ast_channel_flags(chan), defer_hangups, AST_FLAG_DEFER_HANGUP_FRAMES);
+}
+
+void ast_channel_stop_defer_frames(struct ast_channel *chan)
+{
+	ast_clear_flag(ast_channel_flags(chan), AST_FLAG_DEFER_FRAMES);
+
+	/* Move the deferred frames onto the channel read queue, ahead of other queued frames */
+	ast_queue_frame_head(chan, AST_LIST_FIRST(ast_channel_deferred_readq(chan)));
+	/* ast_frfree will mosey down the list and free them all */
+	if (!AST_LIST_EMPTY(ast_channel_deferred_readq(chan))) {
+		ast_frfree(AST_LIST_FIRST(ast_channel_deferred_readq(chan)));
+	}
+	/* Reset the list to be empty */
+	AST_LIST_HEAD_INIT_NOLOCK(ast_channel_deferred_readq(chan));
+}
+
 static int __ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, int head, struct ast_frame *after)
 {
 	struct ast_frame *f;
@@ -1486,19 +1544,18 @@ int ast_safe_sleep_conditional(struct ast_channel *chan, int timeout_ms, int (*c
 	int res = 0;
 	struct timeval start;
 	int ms;
-	AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames;
-
-	AST_LIST_HEAD_INIT_NOLOCK(&deferred_frames);
 
 	/* If no other generator is present, start silencegen while waiting */
 	if (ast_opt_transmit_silence && !ast_channel_generatordata(chan)) {
 		silgen = ast_channel_start_silence_generator(chan);
 	}
 
+	ast_channel_lock(chan);
+	ast_channel_start_defer_frames(chan, 0);
+	ast_channel_unlock(chan);
+
 	start = ast_tvnow();
 	while ((ms = ast_remaining_ms(start, timeout_ms))) {
-		struct ast_frame *dup_f = NULL;
-
 		if (cond && ((*cond)(data) == 0)) {
 			break;
 		}
@@ -1513,18 +1570,7 @@ int ast_safe_sleep_conditional(struct ast_channel *chan, int timeout_ms, int (*c
 				res = -1;
 				break;
 			}
-
-			if (!ast_is_deferrable_frame(f)) {
-				ast_frfree(f);
-				continue;
-			}
-
-			if ((dup_f = ast_frisolate(f))) {
-				if (dup_f != f) {
-					ast_frfree(f);
-				}
-				AST_LIST_INSERT_HEAD(&deferred_frames, dup_f, frame_list);
-			}
+			ast_frfree(f);
 		}
 	}
 
@@ -1533,17 +1579,8 @@ int ast_safe_sleep_conditional(struct ast_channel *chan, int timeout_ms, int (*c
 		ast_channel_stop_silence_generator(chan, silgen);
 	}
 
-	/* We need to free all the deferred frames, but we only need to
-	 * queue the deferred frames if there was no error and no
-	 * hangup was received
-	 */
 	ast_channel_lock(chan);
-	while ((f = AST_LIST_REMOVE_HEAD(&deferred_frames, frame_list))) {
-		if (!res) {
-			ast_queue_frame_head(chan, f);
-		}
-		ast_frfree(f);
-	}
+	ast_channel_stop_defer_frames(chan);
 	ast_channel_unlock(chan);
 
 	return res;
@@ -3847,6 +3884,36 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
 	if (!AST_LIST_EMPTY(ast_channel_readq(chan))) {
 		int skip_dtmf = should_skip_dtmf(chan);
 
+		if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_DEFER_FRAMES)) {
+			AST_LIST_TRAVERSE_SAFE_BEGIN(ast_channel_readq(chan), f, frame_list) {
+				if (ast_is_deferrable_frame(f)) {
+					if(f->frametype == AST_FRAME_CONTROL && 
+						(f->subclass.integer == AST_CONTROL_HANGUP ||
+						 f->subclass.integer == AST_CONTROL_END_OF_Q)) {
+						/* Hangup is a special case. We want to defer the frame, but we also do not
+						 * want to remove it from the frame queue. So rather than just moving the frame
+						 * over, we duplicate it and move the copy to the deferred readq.
+						 *
+						 * The reason for this? This way, whoever calls ast_read() will get a NULL return
+						 * immediately and can tell the channel has hung up and do what it needs to. Also,
+						 * when frame deferral finishes, then whoever calls ast_read() next will also get
+						 * the hangup.
+						 */
+						if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_DEFER_HANGUP_FRAMES)) {
+							struct ast_frame *dup;
+
+							dup = ast_frdup(f);
+							AST_LIST_INSERT_TAIL(ast_channel_deferred_readq(chan), dup, frame_list);
+						}
+					} else {
+						AST_LIST_INSERT_TAIL(ast_channel_deferred_readq(chan), f, frame_list);
+						AST_LIST_REMOVE_CURRENT(frame_list);
+					}
+				}
+			}
+			AST_LIST_TRAVERSE_SAFE_END;
+		}
+
 		AST_LIST_TRAVERSE_SAFE_BEGIN(ast_channel_readq(chan), f, frame_list) {
 			/* We have to be picky about which frame we pull off of the readq because
 			 * there are cases where we want to leave DTMF frames on the queue until
@@ -10178,9 +10245,15 @@ int ast_channel_connected_line_macro(struct ast_channel *autoservice_chan, struc
 
 		ast_party_connected_line_copy(ast_channel_connected(macro_chan), connected);
 	}
+	ast_channel_start_defer_frames(macro_chan, 0);
 	ast_channel_unlock(macro_chan);
 
 	retval = ast_app_run_macro(autoservice_chan, macro_chan, macro, macro_args);
+
+	ast_channel_lock(macro_chan);
+	ast_channel_stop_defer_frames(macro_chan);
+	ast_channel_unlock(macro_chan);
+
 	if (!retval) {
 		struct ast_party_connected_line saved_connected;
 
@@ -10228,9 +10301,15 @@ int ast_channel_redirecting_macro(struct ast_channel *autoservice_chan, struct a
 
 		ast_party_redirecting_copy(ast_channel_redirecting(macro_chan), redirecting);
 	}
+	ast_channel_start_defer_frames(macro_chan, 0);
 	ast_channel_unlock(macro_chan);
 
 	retval = ast_app_run_macro(autoservice_chan, macro_chan, macro, macro_args);
+
+	ast_channel_lock(macro_chan);
+	ast_channel_stop_defer_frames(macro_chan);
+	ast_channel_unlock(macro_chan);
+
 	if (!retval) {
 		struct ast_party_redirecting saved_redirecting;
 
@@ -10271,9 +10350,15 @@ int ast_channel_connected_line_sub(struct ast_channel *autoservice_chan, struct
 
 		ast_party_connected_line_copy(ast_channel_connected(sub_chan), connected);
 	}
+	ast_channel_start_defer_frames(sub_chan, 0);
 	ast_channel_unlock(sub_chan);
 
 	retval = ast_app_run_sub(autoservice_chan, sub_chan, sub, sub_args, 0);
+
+	ast_channel_lock(sub_chan);
+	ast_channel_stop_defer_frames(sub_chan);
+	ast_channel_unlock(sub_chan);
+
 	if (!retval) {
 		struct ast_party_connected_line saved_connected;
 
@@ -10314,9 +10399,15 @@ int ast_channel_redirecting_sub(struct ast_channel *autoservice_chan, struct ast
 
 		ast_party_redirecting_copy(ast_channel_redirecting(sub_chan), redirecting);
 	}
+	ast_channel_start_defer_frames(sub_chan, 0);
 	ast_channel_unlock(sub_chan);
 
 	retval = ast_app_run_sub(autoservice_chan, sub_chan, sub, sub_args, 0);
+
+	ast_channel_lock(sub_chan);
+	ast_channel_stop_defer_frames(sub_chan);
+	ast_channel_unlock(sub_chan);
+
 	if (!retval) {
 		struct ast_party_redirecting saved_redirecting;
 
@@ -10842,3 +10933,8 @@ int ast_channel_feature_hooks_replace(struct ast_channel *chan, struct ast_bridg
 {
 	return channel_feature_hooks_set_full(chan, features, 1);
 }
+
+enum ast_channel_error ast_channel_errno(void)
+{
+	return ast_channel_internal_errno();
+}
diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c
index 51d49c2..4bc770d 100644
--- a/main/channel_internal_api.c
+++ b/main/channel_internal_api.c
@@ -223,6 +223,7 @@ struct ast_channel {
 	struct stasis_cp_single *topics;		/*!< Topic for all channel's events */
 	struct stasis_forward *endpoint_forward;	/*!< Subscription for event forwarding to endpoint's topic */
 	struct stasis_forward *endpoint_cache_forward; /*!< Subscription for cache updates to endpoint's topic */
+	struct ast_readq_list deferred_readq;
 };
 
 /*! \brief The monotonically increasing integer counter for channel uniqueids */
@@ -1484,6 +1485,7 @@ static int pvt_cause_cmp_fn(void *obj, void *vstr, int flags)
 struct ast_channel *__ast_channel_internal_alloc(void (*destructor)(void *obj), const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *file, int line, const char *function)
 {
 	struct ast_channel *tmp;
+
 #if defined(REF_DEBUG)
 	tmp = __ao2_alloc_debug(sizeof(*tmp), destructor,
 		AO2_ALLOC_OPT_LOCK_MUTEX, "", file, line, function, 1);
@@ -1675,3 +1677,30 @@ int ast_channel_internal_setup_topics(struct ast_channel *chan)
 
 	return 0;
 }
+
+AST_THREADSTORAGE(channel_errno);
+
+void ast_channel_internal_errno_set(enum ast_channel_error error)
+{
+	enum ast_channel_error *error_code = ast_threadstorage_get(&channel_errno, sizeof(*error_code));
+	if (!error_code) {
+		return;
+	}
+
+	*error_code = error;
+}
+
+enum ast_channel_error ast_channel_internal_errno(void)
+{
+	enum ast_channel_error *error_code = ast_threadstorage_get(&channel_errno, sizeof(*error_code));
+	if (!error_code) {
+		return AST_CHANNEL_ERROR_UNKNOWN;
+	}
+
+	return *error_code;
+}
+
+struct ast_readq_list *ast_channel_deferred_readq(struct ast_channel *chan)
+{
+	return &chan->deferred_readq;
+}
diff --git a/main/cli.c b/main/cli.c
index 852b32d..91350e9 100644
--- a/main/cli.c
+++ b/main/cli.c
@@ -450,19 +450,11 @@ static char *handle_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args
 	case CLI_INIT:
 		e->command = "core set debug";
 		e->usage =
-#if !defined(LOW_MEMORY)
 			"Usage: core set debug [atleast] <level> [module]\n"
-#else
-			"Usage: core set debug [atleast] <level>\n"
-#endif
 			"       core set debug off\n"
 			"\n"
-#if !defined(LOW_MEMORY)
 			"       Sets level of debug messages to be displayed or\n"
 			"       sets a module name to display debug messages from.\n"
-#else
-			"       Sets level of debug messages to be displayed.\n"
-#endif
 			"       0 or off means no messages should be displayed.\n"
 			"       Equivalent to -d[d[...]] on startup\n";
 		return NULL;
@@ -490,13 +482,9 @@ static char *handle_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args
 			} else if (a->n == (22 - numbermatch) && a->pos == 3 && ast_strlen_zero(argv3)) {
 				return ast_strdup("atleast");
 			}
-#if !defined(LOW_MEMORY)
 		} else if ((a->pos == 4 && !atleast && strcasecmp(argv3, "off") && strcasecmp(argv3, "channel"))
 			|| (a->pos == 5 && atleast)) {
-			const char *pos = S_OR(a->argv[a->pos], "");
-
-			return ast_complete_source_filename(pos, a->n);
-#endif
+			return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
 		}
 		return NULL;
 	}
diff --git a/main/codec_builtin.c b/main/codec_builtin.c
index 973423d..da03cce 100644
--- a/main/codec_builtin.c
+++ b/main/codec_builtin.c
@@ -701,6 +701,21 @@ static struct ast_codec g719 = {
 	.get_length = g719_length,
 };
 
+static int opus_samples(struct ast_frame *frame)
+{
+	/*
+	 * XXX This is likely not at all what's intended from this
+	 * callback.  If you have codec_opus.so loaded then this
+	 * function is overridden anyway.  However, since opus is
+	 * variable bit rate and I cannot extract the calculation code
+	 * from the opus library, I am going to punt and assume 20ms
+	 * worth of samples.  In testing, this has worked just fine.
+	 * Pass through support doesn't seem to care about the value
+	 * returned anyway.
+	 */
+	return ast_format_get_sample_rate(frame->subclass.format) / 50;
+}
+
 static struct ast_codec opus = {
 	.name = "opus",
 	.description = "Opus Codec",
@@ -709,6 +724,7 @@ static struct ast_codec opus = {
 	.minimum_ms = 20,
 	.maximum_ms = 60,
 	.default_ms = 20,
+	.samples_count = opus_samples,
 	.minimum_bytes = 10,
 };
 
diff --git a/main/features_config.c b/main/features_config.c
index 9126a86..4a86f4b 100644
--- a/main/features_config.c
+++ b/main/features_config.c
@@ -1949,8 +1949,6 @@ static char *handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cl
 		ao2_callback(cfg->featuregroups, 0, print_featuregroups, a);
 	}
 
-	ast_cli(a->fd, "\n");
-
 	return CLI_SUCCESS;
 }
 
diff --git a/main/file.c b/main/file.c
index 1c51177..11617fb 100644
--- a/main/file.c
+++ b/main/file.c
@@ -1095,6 +1095,143 @@ int ast_filecopy(const char *filename, const char *filename2, const char *fmt)
 	return filehelper(filename, filename2, fmt, ACTION_COPY);
 }
 
+static int __ast_file_read_dirs(const char *path, ast_file_on_file on_file,
+				void *obj, int max_depth)
+{
+	DIR *dir;
+	struct dirent *entry;
+	int res;
+
+	if (!(dir = opendir(path))) {
+		ast_log(LOG_ERROR, "Error opening directory - %s: %s\n",
+			path, strerror(errno));
+		return -1;
+	}
+
+	--max_depth;
+
+	res = 0;
+
+	while ((entry = readdir(dir)) != NULL && !errno) {
+		int is_file = 0;
+		int is_dir = 0;
+		RAII_VAR(char *, full_path, NULL, ast_free);
+
+		if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
+			continue;
+		}
+
+/*
+ * If the dirent structure has a d_type use it to determine if we are dealing with
+ * a file or directory. Unfortunately if it doesn't have it, or if the type is
+ * unknown, or a link then we'll need to use the stat function instead.
+ */
+#ifdef _DIRENT_HAVE_D_TYPE
+		if (entry->d_type != DT_UNKNOWN && entry->d_type != DT_LNK) {
+			is_file = entry->d_type == DT_REG;
+			is_dir = entry->d_type == DT_DIR;
+		} else
+#endif
+		{
+			struct stat statbuf;
+
+			/*
+			 * Don't use alloca or we risk blowing out the stack if recursing
+			 * into subdirectories.
+			 */
+			full_path = ast_malloc(strlen(path) + strlen(entry->d_name) + 2);
+			if (!full_path) {
+				return -1;
+			}
+			sprintf(full_path, "%s/%s", path, entry->d_name);
+
+			if (stat(full_path, &statbuf)) {
+				ast_log(LOG_ERROR, "Error reading path stats - %s: %s\n",
+					full_path, strerror(errno));
+				/*
+				 * Output an error, but keep going. It could just be
+				 * a broken link and other files could be fine.
+				 */
+				continue;
+			}
+
+			is_file = S_ISREG(statbuf.st_mode);
+			is_dir = S_ISDIR(statbuf.st_mode);
+		}
+
+		if (is_file) {
+			/* If the handler returns non-zero then stop */
+			if ((res = on_file(path, entry->d_name, obj))) {
+				break;
+			}
+			/* Otherwise move on to next item in directory */
+			continue;
+		}
+
+		if (!is_dir) {
+			ast_debug(5, "Skipping %s: not a regular file or directory\n", full_path);
+			continue;
+		}
+
+		/* Only re-curse into sub-directories if not at the max depth */
+		if (max_depth != 0) {
+			if (!full_path) {
+				/* Don't use alloca.  See note above. */
+				full_path = ast_malloc(strlen(path) + strlen(entry->d_name) + 2);
+				if (!full_path) {
+					return -1;
+				}
+				sprintf(full_path, "%s/%s", path, entry->d_name);
+			}
+
+			if ((res = __ast_file_read_dirs(full_path, on_file, obj, max_depth))) {
+				break;
+			}
+		}
+	}
+
+	closedir(dir);
+
+	if (!res && errno) {
+		ast_log(LOG_ERROR, "Error while reading directories - %s: %s\n",
+			path, strerror(errno));
+		res = -1;
+	}
+
+	return res;
+}
+
+#if !defined(__GLIBC__)
+/*!
+ * \brief Lock to hold when iterating over directories.
+ *
+ * Currently, 'readdir' is not required to be thread-safe. In most modern implementations
+ * it should be safe to make concurrent calls into 'readdir' that specify different directory
+ * streams (glibc would be one of these). However, since it is potentially unsafe for some
+ * implementations we'll use our own locking in order to achieve synchronization for those.
+ */
+AST_MUTEX_DEFINE_STATIC(read_dirs_lock);
+#endif
+
+int ast_file_read_dirs(const char *dir_name, ast_file_on_file on_file, void *obj, int max_depth)
+{
+	int res;
+
+	errno = 0;
+
+#if !defined(__GLIBC__)
+	ast_mutex_lock(&read_dirs_lock);
+#endif
+
+	res = __ast_file_read_dirs(dir_name, on_file, obj, max_depth);
+
+#if !defined(__GLIBC__)
+	ast_mutex_unlock(&read_dirs_lock);
+#endif
+
+	return res;
+}
+
 int ast_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
 {
 	struct ast_filestream *fs;
diff --git a/main/format_cap.c b/main/format_cap.c
index 5d8a6e5..fc56866 100644
--- a/main/format_cap.c
+++ b/main/format_cap.c
@@ -361,7 +361,7 @@ int ast_format_cap_update_by_allow_disallow(struct ast_format_cap *cap, const ch
 	parse = ast_strdupa(list);
 
 	/* If the list is being fed to us as a result of ast_format_cap_get_names,
-	 * strip off the paranthesis and immediately apply the inverse of the
+	 * strip off the parenthesis and immediately apply the inverse of the
 	 * allowing option
 	 */
 	if (parse[0] == '(' && parse[strlen(parse) - 1] == ')') {
diff --git a/main/loader.c b/main/loader.c
index 85aeb24..74254b4 100644
--- a/main/loader.c
+++ b/main/loader.c
@@ -613,7 +613,7 @@ static struct ast_module *load_dynamic_module(const char *resource_in, unsigned
 
 #endif
 
-void ast_module_shutdown(void)
+int modules_shutdown(void)
 {
 	struct ast_module *mod;
 	int somethingchanged = 1, final = 0;
@@ -663,7 +663,10 @@ void ast_module_shutdown(void)
 		}
 	} while (somethingchanged && !final);
 
+	final = AST_DLLIST_EMPTY(&module_list);
 	AST_DLLIST_UNLOCK(&module_list);
+
+	return !final;
 }
 
 int ast_unload_resource(const char *resource_name, enum ast_module_unload_mode force)
diff --git a/main/manager_bridges.c b/main/manager_bridges.c
index 8fcdf13..ef632ce 100644
--- a/main/manager_bridges.c
+++ b/main/manager_bridges.c
@@ -93,6 +93,21 @@ static struct stasis_message_router *bridge_state_router;
 			</see-also>
 		</managerEventInstance>
 	</managerEvent>
+	<managerEvent language="en_US" name="BridgeVideoSourceUpdate">
+		<managerEventInstance class="EVENT_FLAG_CALL">
+			<synopsis>Raised when the channel that is the source of video in a bridge changes.</synopsis>
+			<syntax>
+				<bridge_snapshot/>
+				<parameter name="BridgePreviousVideoSource">
+					<para>The unique ID of the channel that was the video source.</para>
+				</parameter>
+			</syntax>
+			<see-also>
+				<ref type="managerEvent">BridgeCreate</ref>
+				<ref type="managerEvent">BridgeDestroy</ref>
+			</see-also>
+		</managerEventInstance>
+	</managerEvent>
 	<manager name="BridgeList" language="en_US">
 		<synopsis>
 			Get a list of bridges in the system.
@@ -224,18 +239,30 @@ struct ast_str *ast_manager_build_bridge_state_string_prefix(
 		"%sBridgeTechnology: %s\r\n"
 		"%sBridgeCreator: %s\r\n"
 		"%sBridgeName: %s\r\n"
-		"%sBridgeNumChannels: %u\r\n",
+		"%sBridgeNumChannels: %u\r\n"
+		"%sBridgeVideoSourceMode: %s\r\n",
 		prefix, snapshot->uniqueid,
 		prefix, snapshot->subclass,
 		prefix, snapshot->technology,
 		prefix, ast_strlen_zero(snapshot->creator) ? "<unknown>": snapshot->creator,
 		prefix, ast_strlen_zero(snapshot->name) ? "<unknown>": snapshot->name,
-		prefix, snapshot->num_channels);
+		prefix, snapshot->num_channels,
+		prefix, ast_bridge_video_mode_to_string(snapshot->video_mode));
 	if (!res) {
 		ast_free(out);
 		return NULL;
 	}
 
+	if (snapshot->video_mode != AST_BRIDGE_VIDEO_MODE_NONE
+		&& !ast_strlen_zero(snapshot->video_source_id)) {
+		res = ast_str_append(&out, 0, "%sBridgeVideoSource: %s\r\n",
+			prefix, snapshot->video_source_id);
+		if (!res) {
+			ast_free(out);
+			return NULL;
+		}
+	}
+
 	return out;
 }
 
@@ -263,6 +290,25 @@ static struct ast_manager_event_blob *bridge_create(
 		EVENT_FLAG_CALL, "BridgeCreate", NO_EXTRA_FIELDS);
 }
 
+/* \brief Handle video source updates */
+static struct ast_manager_event_blob *bridge_video_update(
+	struct ast_bridge_snapshot *old_snapshot,
+	struct ast_bridge_snapshot *new_snapshot)
+{
+	if (!new_snapshot || !old_snapshot) {
+		return NULL;
+	}
+
+	if (!strcmp(old_snapshot->video_source_id, new_snapshot->video_source_id)) {
+		return NULL;
+	}
+
+	return ast_manager_event_blob_create(
+		EVENT_FLAG_CALL, "BridgeVideoSourceUpdate",
+		"BridgePreviousVideoSource: %s\r\n",
+		old_snapshot->video_source_id);
+}
+
 /*! \brief Handle bridge destruction */
 static struct ast_manager_event_blob *bridge_destroy(
 	struct ast_bridge_snapshot *old_snapshot,
@@ -276,9 +322,9 @@ static struct ast_manager_event_blob *bridge_destroy(
 		EVENT_FLAG_CALL, "BridgeDestroy", NO_EXTRA_FIELDS);
 }
 
-
 bridge_snapshot_monitor bridge_monitors[] = {
 	bridge_create,
+	bridge_video_update,
 	bridge_destroy,
 };
 
diff --git a/main/manager_channels.c b/main/manager_channels.c
index 7a4bd23..ffcd7f7 100644
--- a/main/manager_channels.c
+++ b/main/manager_channels.c
@@ -141,6 +141,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 			</see-also>
 		</managerEventInstance>
 	</managerEvent>
+	<managerEvent language="en_US" name="NewConnectedLine">
+		<managerEventInstance class="EVENT_FLAG_CALL">
+			<synopsis>Raised when a channel's connected line information is changed.</synopsis>
+			<syntax>
+				<channel_snapshot/>
+			</syntax>
+			<see-also>
+				<ref type="function">CONNECTEDLINE</ref>
+			</see-also>
+		</managerEventInstance>
+	</managerEvent>
 	<managerEvent language="en_US" name="NewAccountCode">
 		<managerEventInstance class="EVENT_FLAG_CALL">
 			<synopsis>Raised when a Channel's AccountCode is changed.</synopsis>
diff --git a/main/netsock.c b/main/netsock.c
index c11f14a..d16cf11 100644
--- a/main/netsock.c
+++ b/main/netsock.c
@@ -33,7 +33,7 @@
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
-#ifndef __linux__
+#if !defined (__linux__) && !defined (__GNU__)
 #if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__Darwin__) || defined(__GLIBC__)
 #include <net/if_dl.h>
 #endif
diff --git a/main/rtp_engine.c b/main/rtp_engine.c
index 7a83783..0512531 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -145,23 +145,36 @@
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
-#include <math.h>
-
-#include "asterisk/channel.h"
-#include "asterisk/frame.h"
-#include "asterisk/module.h"
-#include "asterisk/rtp_engine.h"
+#include <math.h>                       /* for sqrt, MAX */
+#include <sched.h>                      /* for sched_yield */
+#include <sys/time.h>                   /* for timeval */
+#include <time.h>                       /* for time_t */
+
+#include "asterisk/_private.h"          /* for ast_rtp_engine_init prototype */
+#include "asterisk/astobj2.h"           /* for ao2_cleanup, ao2_ref, etc */
+#include "asterisk/channel.h"           /* for ast_channel_name, etc */
+#include "asterisk/codec.h"             /* for ast_codec_media_type2str, etc */
+#include "asterisk/format.h"            /* for ast_format_cmp, etc */
+#include "asterisk/format_cache.h"      /* for ast_format_adpcm, etc */
+#include "asterisk/format_cap.h"        /* for ast_format_cap_alloc, etc */
+#include "asterisk/json.h"              /* for ast_json_ref, etc */
+#include "asterisk/linkedlists.h"       /* for ast_rtp_engine::<anonymous>, etc */
+#include "asterisk/lock.h"              /* for ast_rwlock_unlock, etc */
+#include "asterisk/logger.h"            /* for ast_log, ast_debug, etc */
 #include "asterisk/manager.h"
-#include "asterisk/options.h"
-#include "asterisk/astobj2.h"
-#include "asterisk/pbx.h"
-#include "asterisk/translate.h"
-#include "asterisk/netsock2.h"
-#include "asterisk/_private.h"
-#include "asterisk/framehook.h"
-#include "asterisk/stasis.h"
-#include "asterisk/json.h"
-#include "asterisk/stasis_channels.h"
+#include "asterisk/module.h"            /* for ast_module_unref, etc */
+#include "asterisk/netsock2.h"          /* for ast_sockaddr_copy, etc */
+#include "asterisk/options.h"           /* for ast_option_rtpptdynamic */
+#include "asterisk/pbx.h"               /* for pbx_builtin_setvar_helper */
+#include "asterisk/res_srtp.h"          /* for ast_srtp_res */
+#include "asterisk/rtp_engine.h"        /* for ast_rtp_codecs, etc */
+#include "asterisk/stasis.h"            /* for stasis_message_data, etc */
+#include "asterisk/stasis_channels.h"   /* for ast_channel_stage_snapshot, etc */
+#include "asterisk/strings.h"           /* for ast_str_append, etc */
+#include "asterisk/time.h"              /* for ast_tvdiff_ms, ast_tvnow */
+#include "asterisk/translate.h"         /* for ast_translate_available_formats */
+#include "asterisk/utils.h"             /* for ast_free, ast_strdup, etc */
+#include "asterisk/vector.h"            /* for AST_VECTOR_GET, etc */
 
 struct ast_srtp_res *res_srtp = NULL;
 struct ast_srtp_policy_res *res_srtp_policy = NULL;
@@ -1796,6 +1809,48 @@ static void add_static_payload(int map, struct ast_format *format, int rtp_code)
 			}
 		}
 
+		/* http://www.iana.org/assignments/rtp-parameters
+		 * RFC 3551, Section 3: "[...] applications which need to define more
+		 * than 32 dynamic payload types MAY bind codes below 96, in which case
+		 * it is RECOMMENDED that unassigned payload type numbers be used
+		 * first". Updated by RFC 5761, Section 4: "[...] values in the range
+		 * 64-95 MUST NOT be used [to avoid conflicts with RTCP]". Summaries:
+		 * https://tools.ietf.org/html/draft-roach-mmusic-unified-plan#section-3.2.1.2
+		 * https://tools.ietf.org/html/draft-wu-avtcore-dynamic-pt-usage#section-3
+		 */
+		if (map < 0) {
+			for (x = MAX(ast_option_rtpptdynamic, 35); x <= AST_RTP_PT_LAST_REASSIGN; ++x) {
+				if (!static_RTP_PT[x]) {
+					map = x;
+					break;
+				}
+			}
+		}
+		/* Yet, reusing mappings below 35 is not supported in Asterisk because
+		 * when Compact Headers are activated, no rtpmap is send for those below
+		 * 35. If you want to use 35 and below
+		 * A) do not use Compact Headers,
+		 * B) remove that code in chan_sip/res_pjsip, or
+		 * C) add a flag that this RTP Payload Type got reassigned dynamically
+		 *    and requires a rtpmap even with Compact Headers enabled.
+		 */
+		if (map < 0) {
+			for (x = MAX(ast_option_rtpptdynamic, 20); x < 35; ++x) {
+				if (!static_RTP_PT[x]) {
+					map = x;
+					break;
+				}
+			}
+		}
+		if (map < 0) {
+			for (x = MAX(ast_option_rtpptdynamic, 0); x < 20; ++x) {
+				if (!static_RTP_PT[x]) {
+					map = x;
+					break;
+				}
+			}
+		}
+
 		if (map < 0) {
 			if (format) {
 				ast_log(LOG_WARNING, "No Dynamic RTP mapping available for format %s\n",
diff --git a/main/stasis_bridges.c b/main/stasis_bridges.c
index 3688a46..8f97e98 100644
--- a/main/stasis_bridges.c
+++ b/main/stasis_bridges.c
@@ -240,7 +240,13 @@ struct ast_bridge_snapshot *ast_bridge_snapshot_create(struct ast_bridge *bridge
 
 	snapshot = ao2_alloc_options(sizeof(*snapshot), bridge_snapshot_dtor,
 		AO2_ALLOC_OPT_LOCK_NOLOCK);
-	if (!snapshot || ast_string_field_init(snapshot, 128)) {
+	if (!snapshot) {
+		return NULL;
+	}
+
+	if (ast_string_field_init(snapshot, 128)
+		|| ast_string_field_init_extended(snapshot, video_source_id)) {
+		ao2_ref(snapshot, -1);
 		return NULL;
 	}
 
@@ -266,6 +272,16 @@ struct ast_bridge_snapshot *ast_bridge_snapshot_create(struct ast_bridge *bridge
 	snapshot->capabilities = bridge->technology->capabilities;
 	snapshot->num_channels = bridge->num_channels;
 	snapshot->num_active = bridge->num_active;
+	snapshot->video_mode = bridge->softmix.video_mode.mode;
+	if (snapshot->video_mode == AST_BRIDGE_VIDEO_MODE_SINGLE_SRC
+		&& bridge->softmix.video_mode.mode_data.single_src_data.chan_vsrc) {
+		ast_string_field_set(snapshot, video_source_id,
+			ast_channel_uniqueid(bridge->softmix.video_mode.mode_data.single_src_data.chan_vsrc));
+	} else if (snapshot->video_mode == AST_BRIDGE_VIDEO_MODE_TALKER_SRC
+		&& bridge->softmix.video_mode.mode_data.talker_src_data.chan_vsrc) {
+		ast_string_field_set(snapshot, video_source_id,
+			ast_channel_uniqueid(bridge->softmix.video_mode.mode_data.talker_src_data.chan_vsrc));
+	}
 
 	ao2_ref(snapshot, +1);
 	return snapshot;
@@ -579,18 +595,25 @@ struct ast_json *ast_bridge_snapshot_to_json(
 		return NULL;
 	}
 
-	json_bridge = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: o}",
+	json_bridge = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: o, s: s}",
 		"id", snapshot->uniqueid,
 		"technology", snapshot->technology,
 		"bridge_type", capability2str(snapshot->capabilities),
 		"bridge_class", snapshot->subclass,
 		"creator", snapshot->creator,
 		"name", snapshot->name,
-		"channels", json_channels);
+		"channels", json_channels,
+		"video_mode", ast_bridge_video_mode_to_string(snapshot->video_mode));
 	if (!json_bridge) {
 		return NULL;
 	}
 
+	if (snapshot->video_mode != AST_BRIDGE_VIDEO_MODE_NONE
+		&& !ast_strlen_zero(snapshot->video_source_id)) {
+		ast_json_object_set(json_bridge, "video_source_id",
+			ast_json_string_create(snapshot->video_source_id));
+	}
+
 	return ast_json_ref(json_bridge);
 }
 
diff --git a/main/tcptls.c b/main/tcptls.c
index 34baf9a..bccb03d 100644
--- a/main/tcptls.c
+++ b/main/tcptls.c
@@ -555,6 +555,34 @@ static void session_instance_destructor(void *obj)
 	ao2_cleanup(i->private_data);
 }
 
+#ifdef DO_SSL
+static int check_tcptls_cert_name(ASN1_STRING *cert_str, const char *hostname, const char *desc)
+{
+	unsigned char *str;
+	int ret;
+
+	ret = ASN1_STRING_to_UTF8(&str, cert_str);
+	if (ret < 0 || !str) {
+		return -1;
+	}
+
+	if (strlen((char *) str) != ret) {
+		ast_log(LOG_WARNING, "Invalid certificate %s length (contains NULL bytes?)\n", desc);
+
+		ret = -1;
+	} else if (!strcasecmp(hostname, (char *) str)) {
+		ret = 0;
+	} else {
+		ret = -1;
+	}
+
+	ast_debug(3, "SSL %s compare s1='%s' s2='%s'\n", desc, hostname, str);
+	OPENSSL_free(str);
+
+	return ret;
+}
+#endif
+
 /*! \brief
 * creates a FILE * from the fd passed by the accept thread.
 * This operation is potentially expensive (certificate verification),
@@ -631,8 +659,8 @@ static void *handle_tcptls_connection(void *data)
 				}
 				if (!ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_IGNORE_COMMON_NAME)) {
 					ASN1_STRING *str;
-					unsigned char *str2;
 					X509_NAME *name = X509_get_subject_name(peer);
+					STACK_OF(GENERAL_NAME) *alt_names;
 					int pos = -1;
 					int found = 0;
 
@@ -643,25 +671,36 @@ static void *handle_tcptls_connection(void *data)
 						if (pos < 0) {
 							break;
 						}
+
 						str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, pos));
-						ret = ASN1_STRING_to_UTF8(&str2, str);
-						if (ret < 0) {
-							continue;
+						if (!check_tcptls_cert_name(str, tcptls_session->parent->hostname, "common name")) {
+							found = 1;
+							break;
 						}
+					}
+
+					if (!found) {
+						alt_names = X509_get_ext_d2i(peer, NID_subject_alt_name, NULL, NULL);
+						if (alt_names != NULL) {
+							int alt_names_count = sk_GENERAL_NAME_num(alt_names);
 
-						if (str2) {
-							if (strlen((char *) str2) != ret) {
-								ast_log(LOG_WARNING, "Invalid certificate common name length (contains NULL bytes?)\n");
-							} else if (!strcasecmp(tcptls_session->parent->hostname, (char *) str2)) {
-								found = 1;
+							for (pos = 0; pos < alt_names_count; pos++) {
+								const GENERAL_NAME *alt_name = sk_GENERAL_NAME_value(alt_names, pos);
+
+								if (alt_name->type != GEN_DNS) {
+									continue;
+								}
+
+								if (!check_tcptls_cert_name(alt_name->d.dNSName, tcptls_session->parent->hostname, "alt name")) {
+									found = 1;
+									break;
+								}
 							}
-							ast_debug(3, "SSL Common Name compare s1='%s' s2='%s'\n", tcptls_session->parent->hostname, str2);
-							OPENSSL_free(str2);
-						}
-						if (found) {
-							break;
+
+							sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
 						}
 					}
+
 					if (!found) {
 						ast_log(LOG_ERROR, "Certificate common name did not match (%s)\n", tcptls_session->parent->hostname);
 						X509_free(peer);
diff --git a/main/utils.c b/main/utils.c
index af0ee7f..03bead2 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -2478,72 +2478,226 @@ char *ast_eid_to_str(char *s, int maxlen, struct ast_eid *eid)
 	return os;
 }
 
+#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__Darwin__)
+#include <ifaddrs.h>
+#include <net/if_dl.h>
+
 void ast_set_default_eid(struct ast_eid *eid)
 {
-#if defined(SIOCGIFHWADDR) && defined(HAVE_STRUCT_IFREQ_IFR_IFRU_IFRU_HWADDR)
-	int s, x = 0;
+	struct ifaddrs *ifap, *ifaphead;
+	int rtnerr;
+	const struct sockaddr_dl *sdl;
+	int alen;
+	caddr_t ap;
 	char eid_str[20];
-	struct ifreq ifr;
-	static const unsigned int MAXIF = 10;
+	unsigned char empty_mac[6] = {0, 0, 0, 0, 0, 0};
+	unsigned char full_mac[6]  = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
 
-	s = socket(AF_INET, SOCK_STREAM, 0);
-	if (s < 0) {
+	rtnerr = getifaddrs(&ifaphead);
+	if (rtnerr) {
+		ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
+			"You will have to set it manually.\n");
 		return;
 	}
-	for (x = 0; x < MAXIF; x++) {
-		static const char *prefixes[] = { "eth", "em", "eno", "ens" };
-		unsigned int i;
 
-		for (i = 0; i < ARRAY_LEN(prefixes); i++) {
-			memset(&ifr, 0, sizeof(ifr));
-			snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", prefixes[i], x);
-			if (!ioctl(s, SIOCGIFHWADDR, &ifr)) {
-				break;
-			}
+	if (!ifaphead) {
+		ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
+			"You will have to set it manually.\n");
+		return;
+	}
+
+	for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) {
+		if (ifap->ifa_addr->sa_family != AF_LINK) {
+			continue;
 		}
 
-		if (i == ARRAY_LEN(prefixes)) {
-			/* Try pciX#[1..N] */
-			for (i = 0; i < MAXIF; i++) {
-				memset(&ifr, 0, sizeof(ifr));
-				snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "pci%d#%u", x, i);
-				if (!ioctl(s, SIOCGIFHWADDR, &ifr)) {
-					break;
-				}
-			}
-			if (i == MAXIF) {
-				continue;
-			}
+		sdl = (const struct sockaddr_dl *) ifap->ifa_addr;
+		ap = ((caddr_t) ((sdl)->sdl_data + (sdl)->sdl_nlen));
+		alen = sdl->sdl_alen;
+		if (alen != 6 || !(memcmp(ap, &empty_mac, 6) && memcmp(ap, &full_mac, 6))) {
+			continue;
 		}
 
-		memcpy(eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(*eid));
-		ast_debug(1, "Seeding global EID '%s' from '%s' using 'siocgifhwaddr'\n", ast_eid_to_str(eid_str, sizeof(eid_str), eid), ifr.ifr_name);
+		memcpy(eid, ap, sizeof(*eid));
+		ast_debug(1, "Seeding global EID '%s'\n",
+				ast_eid_to_str(eid_str, sizeof(eid_str), eid));
+		freeifaddrs(ifaphead);
+		return;
+	}
+
+	ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
+		"You will have to set it manually.\n");
+	freeifaddrs(ifaphead);
+
+	return;
+}
+
+#elif defined(SOLARIS)
+#include <sys/sockio.h>
+#include <net/if_arp.h>
+
+void ast_set_default_eid(struct ast_eid *eid)
+{
+	int s;
+	int x;
+	int res = 0;
+	struct lifreq *ifr = NULL;
+	struct lifnum ifn;
+	struct lifconf ifc;
+	struct arpreq ar;
+	struct sockaddr_in *sa, *sa2;
+	char *buf = NULL;
+	char eid_str[20];
+	int bufsz;
+	unsigned char empty_mac[6] = {0, 0, 0, 0, 0, 0};
+	unsigned char full_mac[6]  = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+	s = socket(AF_INET, SOCK_STREAM, 0);
+	if (s <= 0) {
+		ast_log(LOG_WARNING, "Unable to open a socket for seeding global EID. "
+			" You will have to set it manually.\n");
+		return;
+	}
+
+	/* Get a count of interfaces on the machine */
+	ifn.lifn_family = AF_UNSPEC;
+	ifn.lifn_flags = 0;
+	ifn.lifn_count = 0;
+	if (ioctl(s, SIOCGLIFNUM, &ifn) < 0) {
+		ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
+			" You will have to set it manually.\n");
 		close(s);
 		return;
 	}
+
+	bufsz = ifn.lifn_count * sizeof(struct lifreq);
+	if (!(buf = ast_malloc(bufsz))) {
+		ast_log(LOG_WARNING, "Unable to allocate memory for seeding global EID. "
+			"You will have to set it manually.\n");
+		close(s);
+		return;
+	}
+	memset(buf, 0, bufsz);
+
+	/* Get a list of interfaces on the machine */
+	ifc.lifc_len = bufsz;
+	ifc.lifc_buf = buf;
+	ifc.lifc_family = AF_UNSPEC;
+	ifc.lifc_flags = 0;
+	if (ioctl(s, SIOCGLIFCONF, &ifc) < 0) {
+		ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
+			"You will have to set it manually.\n");
+		ast_free(buf);
+		close(s);
+		return;
+	}
+
+	for (ifr = (struct lifreq *)buf, x = 0; x < ifn.lifn_count; ifr++, x++) {
+		unsigned char *p;
+
+		sa = (struct sockaddr_in *)&(ifr->lifr_addr);
+		sa2 = (struct sockaddr_in *)&(ar.arp_pa);
+		*sa2 = *sa;
+
+		if(ioctl(s, SIOCGARP, &ar) >= 0) {
+			p = (unsigned char *)&(ar.arp_ha.sa_data);
+			if (!(memcmp(p, &empty_mac, 6) && memcmp(p, &full_mac, 6))) {
+				continue;
+			}
+
+			memcpy(eid, p, sizeof(*eid));
+			ast_debug(1, "Seeding global EID '%s'\n",
+				ast_eid_to_str(eid_str, sizeof(eid_str), eid));
+			ast_free(buf);
+			close(s);
+			return;
+		}
+	}
+
+	ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
+		"You will have to set it manually.\n");
+	ast_free(buf);
 	close(s);
+
+	return;
+}
+
 #else
-#if defined(ifa_broadaddr) && !defined(SOLARIS)
+void ast_set_default_eid(struct ast_eid *eid)
+{
+	int s;
+	int i;
+	struct ifreq *ifr;
+	struct ifreq *ifrp;
+	struct ifconf ifc;
+	char *buf = NULL;
 	char eid_str[20];
-	struct ifaddrs *ifap;
-
-	if (getifaddrs(&ifap) == 0) {
-		struct ifaddrs *p;
-		for (p = ifap; p; p = p->ifa_next) {
-			if ((p->ifa_addr->sa_family == AF_LINK) && !(p->ifa_flags & IFF_LOOPBACK) && (p->ifa_flags & IFF_RUNNING)) {
-				struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
-				memcpy(&(eid->eid), sdp->sdl_data + sdp->sdl_nlen, 6);
-				ast_debug(1, "Seeding global EID '%s' from '%s' using 'getifaddrs'\n", ast_eid_to_str(eid_str, sizeof(eid_str), eid), p->ifa_name);
-				freeifaddrs(ifap);
-				return;
+	int bufsz, num_interfaces;
+	unsigned char empty_mac[6] = {0, 0, 0, 0, 0, 0};
+	unsigned char full_mac[6]  = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+	s = socket(AF_INET, SOCK_STREAM, 0);
+	if (s <= 0) {
+		ast_log(LOG_WARNING, "Unable to open socket for seeding global EID. "
+			"You will have to set it manually.\n");
+		return;
+	}
+
+	ifc.ifc_len = 0;
+	ifc.ifc_buf = NULL;
+	if (ioctl(s, SIOCGIFCONF, &ifc) || ifc.ifc_len <= 0) {
+		ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
+			"You will have to set it manually.\n");
+		close(s);
+		return;
+	}
+	bufsz = ifc.ifc_len;
+
+	if (!(buf = ast_malloc(bufsz))) {
+		ast_log(LOG_WARNING, "Unable to allocate memory for seeding global EID. "
+			"You will have to set it manually.\n");
+		close(s);
+		return;
+	}
+
+	ifc.ifc_buf = buf;
+	if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
+		ast_log(LOG_WARNING, "Unable to retrieve ethernet interfaces for seeding global EID. "
+			"You will have to set it manually.\n");
+		ast_free(buf);
+		close(s);
+		return;
+	}
+
+	ifrp = ifc.ifc_req;
+	num_interfaces = ifc.ifc_len / sizeof(*ifr);
+
+	for (i = 0; i < num_interfaces; i++) {
+		ifr = &ifrp[i];
+		if (!ioctl(s, SIOCGIFHWADDR, ifr)) {
+			unsigned char *hwaddr = (unsigned char *) ifr->ifr_hwaddr.sa_data;
+
+			if (!(memcmp(hwaddr, &empty_mac, 6) && memcmp(hwaddr, &full_mac, 6))) {
+				continue;
 			}
+
+			memcpy(eid, hwaddr, sizeof(*eid));
+			ast_debug(1, "Seeding global EID '%s' from '%s' using 'siocgifhwaddr'\n",
+				ast_eid_to_str(eid_str, sizeof(eid_str), eid), ifr->ifr_name);
+			ast_free(buf);
+			close(s);
+			return;
 		}
-		freeifaddrs(ifap);
 	}
-#endif
-#endif
-	ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. You will have to set it manually.\n");
+
+	ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
+		"You will have to set it manually.\n");
+	ast_free(buf);
+	close(s);
+
+	return;
 }
+#endif /* LINUX */
 
 int ast_str_to_eid(struct ast_eid *eid, const char *s)
 {
diff --git a/makeopts.in b/makeopts.in
index fed5030..5ad2ced 100644
--- a/makeopts.in
+++ b/makeopts.in
@@ -256,6 +256,8 @@ PYTHONDEV_LIB=@PYTHONDEV_LIB@
 RESAMPLE_INCLUDE=@RESAMPLE_INCLUDE@
 RESAMPLE_LIB=@RESAMPLE_LIB@
 
+RT_LIB=@RT_LIB@
+
 SS7_INCLUDE=@SS7_INCLUDE@
 SS7_LIB=@SS7_LIB@
 
diff --git a/menuselect/aclocal.m4 b/menuselect/aclocal.m4
index 8b54715..e67774c 100644
--- a/menuselect/aclocal.m4
+++ b/menuselect/aclocal.m4
@@ -1,6 +1,6 @@
-# generated automatically by aclocal 1.14.1 -*- Autoconf -*-
+# generated automatically by aclocal 1.15 -*- Autoconf -*-
 
-# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
 
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -12,8 +12,285 @@
 # PARTICULAR PURPOSE.
 
 m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
+dnl pkg.m4 - Macros to locate and utilise pkg-config.   -*- Autoconf -*-
+dnl serial 11 (pkg-config-0.29)
+dnl
+dnl Copyright © 2004 Scott James Remnant <scott at netsplit.com>.
+dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists at gmail.com>
+dnl
+dnl This program is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 2 of the License, or
+dnl (at your option) any later version.
+dnl
+dnl This program is distributed in the hope that it will be useful, but
+dnl WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+dnl General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with this program; if not, write to the Free Software
+dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+dnl 02111-1307, USA.
+dnl
+dnl As a special exception to the GNU General Public License, if you
+dnl distribute this file as part of a program that contains a
+dnl configuration script generated by Autoconf, you may include it under
+dnl the same distribution terms that you use for the rest of that
+dnl program.
+
+dnl PKG_PREREQ(MIN-VERSION)
+dnl -----------------------
+dnl Since: 0.29
+dnl
+dnl Verify that the version of the pkg-config macros are at least
+dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's
+dnl installed version of pkg-config, this checks the developer's version
+dnl of pkg.m4 when generating configure.
+dnl
+dnl To ensure that this macro is defined, also add:
+dnl m4_ifndef([PKG_PREREQ],
+dnl     [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])])
+dnl
+dnl See the "Since" comment for each macro you use to see what version
+dnl of the macros you require.
+m4_defun([PKG_PREREQ],
+[m4_define([PKG_MACROS_VERSION], [0.29])
+m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
+    [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
+])dnl PKG_PREREQ
+
+dnl PKG_PROG_PKG_CONFIG([MIN-VERSION])
+dnl ----------------------------------
+dnl Since: 0.16
+dnl
+dnl Search for the pkg-config tool and set the PKG_CONFIG variable to
+dnl first found in the path. Checks that the version of pkg-config found
+dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is
+dnl used since that's the first version where most current features of
+dnl pkg-config existed.
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
+m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
+AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
+AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+	AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+	_pkg_min_version=m4_default([$1], [0.9.0])
+	AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+	if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+		AC_MSG_RESULT([yes])
+	else
+		AC_MSG_RESULT([no])
+		PKG_CONFIG=""
+	fi
+fi[]dnl
+])dnl PKG_PROG_PKG_CONFIG
+
+dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl -------------------------------------------------------------------
+dnl Since: 0.18
+dnl
+dnl Check to see whether a particular set of modules exists. Similar to
+dnl PKG_CHECK_MODULES(), but does not set variables or print errors.
+dnl
+dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+dnl only at the first occurence in configure.ac, so if the first place
+dnl it's called might be skipped (such as if it is within an "if", you
+dnl have to call PKG_CHECK_EXISTS manually
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+    AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+  m4_default([$2], [:])
+m4_ifvaln([$3], [else
+  $3])dnl
+fi])
+
+dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+dnl ---------------------------------------------
+dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting
+dnl pkg_failed based on the result.
+m4_define([_PKG_CONFIG],
+[if test -n "$$1"; then
+    pkg_cv_[]$1="$$1"
+ elif test -n "$PKG_CONFIG"; then
+    PKG_CHECK_EXISTS([$3],
+                     [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes ],
+		     [pkg_failed=yes])
+ else
+    pkg_failed=untried
+fi[]dnl
+])dnl _PKG_CONFIG
+
+dnl _PKG_SHORT_ERRORS_SUPPORTED
+dnl ---------------------------
+dnl Internal check to see if pkg-config supports short errors.
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi[]dnl
+])dnl _PKG_SHORT_ERRORS_SUPPORTED
+
+
+dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+dnl   [ACTION-IF-NOT-FOUND])
+dnl --------------------------------------------------------------
+dnl Since: 0.4.0
+dnl
+dnl Note that if there is a possibility the first call to
+dnl PKG_CHECK_MODULES might not happen, you should be sure to include an
+dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $1])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+   	AC_MSG_RESULT([no])
+        _PKG_SHORT_ERRORS_SUPPORTED
+        if test $_pkg_short_errors_supported = yes; then
+	        $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
+        else 
+	        $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+	m4_default([$4], [AC_MSG_ERROR(
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT])[]dnl
+        ])
+elif test $pkg_failed = untried; then
+     	AC_MSG_RESULT([no])
+	m4_default([$4], [AC_MSG_FAILURE(
+[The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
+        ])
+else
+	$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+	$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+        AC_MSG_RESULT([yes])
+	$3
+fi[]dnl
+])dnl PKG_CHECK_MODULES
+
+
+dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+dnl   [ACTION-IF-NOT-FOUND])
+dnl ---------------------------------------------------------------------
+dnl Since: 0.29
+dnl
+dnl Checks for existence of MODULES and gathers its build flags with
+dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags
+dnl and VARIABLE-PREFIX_LIBS from --libs.
+dnl
+dnl Note that if there is a possibility the first call to
+dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to
+dnl include an explicit call to PKG_PROG_PKG_CONFIG in your
+dnl configure.ac.
+AC_DEFUN([PKG_CHECK_MODULES_STATIC],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+_save_PKG_CONFIG=$PKG_CONFIG
+PKG_CONFIG="$PKG_CONFIG --static"
+PKG_CHECK_MODULES($@)
+PKG_CONFIG=$_save_PKG_CONFIG[]dnl
+])dnl PKG_CHECK_MODULES_STATIC
+
+
+dnl PKG_INSTALLDIR([DIRECTORY])
+dnl -------------------------
+dnl Since: 0.27
+dnl
+dnl Substitutes the variable pkgconfigdir as the location where a module
+dnl should install pkg-config .pc files. By default the directory is
+dnl $libdir/pkgconfig, but the default can be changed by passing
+dnl DIRECTORY. The user can override through the --with-pkgconfigdir
+dnl parameter.
+AC_DEFUN([PKG_INSTALLDIR],
+[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
+m4_pushdef([pkg_description],
+    [pkg-config installation directory @<:@]pkg_default[@:>@])
+AC_ARG_WITH([pkgconfigdir],
+    [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
+    [with_pkgconfigdir=]pkg_default)
+AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
+m4_popdef([pkg_default])
+m4_popdef([pkg_description])
+])dnl PKG_INSTALLDIR
+
+
+dnl PKG_NOARCH_INSTALLDIR([DIRECTORY])
+dnl --------------------------------
+dnl Since: 0.27
+dnl
+dnl Substitutes the variable noarch_pkgconfigdir as the location where a
+dnl module should install arch-independent pkg-config .pc files. By
+dnl default the directory is $datadir/pkgconfig, but the default can be
+dnl changed by passing DIRECTORY. The user can override through the
+dnl --with-noarch-pkgconfigdir parameter.
+AC_DEFUN([PKG_NOARCH_INSTALLDIR],
+[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
+m4_pushdef([pkg_description],
+    [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
+AC_ARG_WITH([noarch-pkgconfigdir],
+    [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
+    [with_noarch_pkgconfigdir=]pkg_default)
+AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
+m4_popdef([pkg_default])
+m4_popdef([pkg_description])
+])dnl PKG_NOARCH_INSTALLDIR
+
+
+dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
+dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl -------------------------------------------
+dnl Since: 0.28
+dnl
+dnl Retrieves the value of the pkg-config variable for the given module.
+AC_DEFUN([PKG_CHECK_VAR],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
+
+_PKG_CONFIG([$1], [variable="][$3]["], [$2])
+AS_VAR_COPY([$1], [pkg_cv_][$1])
+
+AS_VAR_IF([$1], [""], [$5], [$4])dnl
+])dnl PKG_CHECK_VAR
+
 m4_include([../autoconf/ast_check_gnu_make.m4])
 m4_include([../autoconf/ast_ext_lib.m4])
 m4_include([../autoconf/ast_ext_tool_check.m4])
 m4_include([../autoconf/ast_gcc_attribute.m4])
+m4_include([../autoconf/ast_pkgconfig.m4])
 m4_include([../autoconf/ast_prog_sed.m4])
diff --git a/menuselect/configure b/menuselect/configure
index 648091e..4235ea0 100755
--- a/menuselect/configure
+++ b/menuselect/configure
@@ -1,5 +1,5 @@
 #! /bin/sh
-# From configure.ac Revision: 418850 .
+# From configure.ac Revision.
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.69.
 #
@@ -628,7 +628,11 @@ LIBOBJS
 GTK2_LIB
 GTK2_INCLUDE
 PBX_GTK2
-PKGCONFIG
+GTK2_LIBS
+GTK2_CFLAGS
+PKG_CONFIG_LIBDIR
+PKG_CONFIG_PATH
+PKG_CONFIG
 CONFIG_LIBXML2
 SED
 PBX_LIBXML2
@@ -725,7 +729,12 @@ CFLAGS
 LDFLAGS
 LIBS
 CPPFLAGS
-CPP'
+CPP
+PKG_CONFIG
+PKG_CONFIG_PATH
+PKG_CONFIG_LIBDIR
+GTK2_CFLAGS
+GTK2_LIBS'
 
 
 # Initialize some variables set by options.
@@ -1356,6 +1365,13 @@ Some influential environment variables:
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
               you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
+  PKG_CONFIG  path to pkg-config utility
+  PKG_CONFIG_PATH
+              directories to add to pkg-config's search path
+  PKG_CONFIG_LIBDIR
+              path overriding pkg-config's built-in search path
+  GTK2_CFLAGS C compiler flags for GTK2, overriding pkg-config
+  GTK2_LIBS   linker flags for GTK2, overriding pkg-config
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -4752,26 +4768,35 @@ if test "${PBX_LIBXML2}" != 1; then
   as_fn_error $? "Could not find required 'Libxml2' development package" "$LINENO" 5
 fi
 
-PBX_GTK2=0
-if test -n "$ac_tool_prefix"; then
+
+
+
+
+
+
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+	if test -n "$ac_tool_prefix"; then
   # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
 set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 $as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_PKGCONFIG+:} false; then :
+if ${ac_cv_path_PKG_CONFIG+:} false; then :
   $as_echo_n "(cached) " >&6
 else
-  if test -n "$PKGCONFIG"; then
-  ac_cv_prog_PKGCONFIG="$PKGCONFIG" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+  case $PKG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
 for as_dir in $PATH
 do
   IFS=$as_save_IFS
   test -z "$as_dir" && as_dir=.
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_prog_PKGCONFIG="${ac_tool_prefix}pkg-config"
+    ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
     break 2
   fi
@@ -4779,12 +4804,13 @@ done
   done
 IFS=$as_save_IFS
 
+  ;;
+esac
 fi
-fi
-PKGCONFIG=$ac_cv_prog_PKGCONFIG
-if test -n "$PKGCONFIG"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKGCONFIG" >&5
-$as_echo "$PKGCONFIG" >&6; }
+PKG_CONFIG=$ac_cv_path_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
+$as_echo "$PKG_CONFIG" >&6; }
 else
   { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
@@ -4792,26 +4818,28 @@ fi
 
 
 fi
-if test -z "$ac_cv_prog_PKGCONFIG"; then
-  ac_ct_PKGCONFIG=$PKGCONFIG
+if test -z "$ac_cv_path_PKG_CONFIG"; then
+  ac_pt_PKG_CONFIG=$PKG_CONFIG
   # Extract the first word of "pkg-config", so it can be a program name with args.
 set dummy pkg-config; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 $as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_ac_ct_PKGCONFIG+:} false; then :
+if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then :
   $as_echo_n "(cached) " >&6
 else
-  if test -n "$ac_ct_PKGCONFIG"; then
-  ac_cv_prog_ac_ct_PKGCONFIG="$ac_ct_PKGCONFIG" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+  case $ac_pt_PKG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
 for as_dir in $PATH
 do
   IFS=$as_save_IFS
   test -z "$as_dir" && as_dir=.
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_prog_ac_ct_PKGCONFIG="pkg-config"
+    ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
     break 2
   fi
@@ -4819,19 +4847,20 @@ done
   done
 IFS=$as_save_IFS
 
+  ;;
+esac
 fi
-fi
-ac_ct_PKGCONFIG=$ac_cv_prog_ac_ct_PKGCONFIG
-if test -n "$ac_ct_PKGCONFIG"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_PKGCONFIG" >&5
-$as_echo "$ac_ct_PKGCONFIG" >&6; }
+ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
+if test -n "$ac_pt_PKG_CONFIG"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5
+$as_echo "$ac_pt_PKG_CONFIG" >&6; }
 else
   { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
 fi
 
-  if test "x$ac_ct_PKGCONFIG" = x; then
-    PKGCONFIG="No"
+  if test "x$ac_pt_PKG_CONFIG" = x; then
+    PKG_CONFIG=""
   else
     case $cross_compiling:$ac_tool_warned in
 yes:)
@@ -4839,20 +4868,114 @@ yes:)
 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
 ac_tool_warned=yes ;;
 esac
-    PKGCONFIG=$ac_ct_PKGCONFIG
+    PKG_CONFIG=$ac_pt_PKG_CONFIG
   fi
 else
-  PKGCONFIG="$ac_cv_prog_PKGCONFIG"
+  PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
+fi
+
+fi
+if test -n "$PKG_CONFIG"; then
+	_pkg_min_version=0.9.0
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5
+$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
+	if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+	else
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+		PKG_CONFIG=""
+	fi
 fi
 
-if test ! "x${PKGCONFIG}" = xNo; then
-   GTK2_INCLUDE=$(${PKGCONFIG} gtk+-2.0 --cflags 2>/dev/null)
-   GTK2_LIB=$(${PKGCONFIG} gtk+-2.0 --libs)
-   PBX_GTK2=1
+   if test "x${PBX_GTK2}" != "x1" -a "${USE_GTK2}" != "no"; then
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK2" >&5
+$as_echo_n "checking for GTK2... " >&6; }
+
+if test -n "$GTK2_CFLAGS"; then
+    pkg_cv_GTK2_CFLAGS="$GTK2_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gtk+-2.0\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "gtk+-2.0") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_GTK2_CFLAGS=`$PKG_CONFIG --cflags "gtk+-2.0" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$GTK2_LIBS"; then
+    pkg_cv_GTK2_LIBS="$GTK2_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gtk+-2.0\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "gtk+-2.0") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_GTK2_LIBS=`$PKG_CONFIG --libs "gtk+-2.0" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+	        GTK2_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "gtk+-2.0" 2>&1`
+        else
+	        GTK2_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "gtk+-2.0" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$GTK2_PKG_ERRORS" >&5
+
+
+            PBX_GTK2=0
+
+
+elif test $pkg_failed = untried; then
+     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+            PBX_GTK2=0
+
+
+else
+	GTK2_CFLAGS=$pkg_cv_GTK2_CFLAGS
+	GTK2_LIBS=$pkg_cv_GTK2_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+            PBX_GTK2=1
+            GTK2_INCLUDE="$GTK2_CFLAGS"
+            GTK2_LIB="$GTK2_LIBS"
 
 $as_echo "#define HAVE_GTK2 1" >>confdefs.h
 
+
 fi
+   fi
+
 
 
 
diff --git a/menuselect/configure.ac b/menuselect/configure.ac
index 5989f5c..29c43cb 100644
--- a/menuselect/configure.ac
+++ b/menuselect/configure.ac
@@ -134,14 +134,7 @@ if test "${PBX_LIBXML2}" != 1; then
   AC_MSG_ERROR([Could not find required 'Libxml2' development package])
 fi
 
-PBX_GTK2=0
-AC_CHECK_TOOL(PKGCONFIG, pkg-config, No)
-if test ! "x${PKGCONFIG}" = xNo; then
-   GTK2_INCLUDE=$(${PKGCONFIG} gtk+-2.0 --cflags 2>/dev/null)
-   GTK2_LIB=$(${PKGCONFIG} gtk+-2.0 --libs)   
-   PBX_GTK2=1
-   AC_DEFINE([HAVE_GTK2], 1, [Define if your system has the GTK2 libraries.])
-fi
+AST_PKG_CONFIG_CHECK([GTK2], [gtk+-2.0])
 AC_SUBST(PBX_GTK2)
 AC_SUBST(GTK2_INCLUDE)
 AC_SUBST(GTK2_LIB)
diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c
index 389f83b..171cb29 100644
--- a/res/ari/ari_model_validators.c
+++ b/res/ari/ari_model_validators.c
@@ -1360,6 +1360,24 @@ int ast_ari_validate_bridge(struct ast_json *json)
 				res = 0;
 			}
 		} else
+		if (strcmp("video_mode", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Bridge field video_mode failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("video_source_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Bridge field video_source_id failed validation\n");
+				res = 0;
+			}
+		} else
 		{
 			ast_log(LOG_ERROR,
 				"ARI Bridge has undocumented field %s\n",
@@ -1932,6 +1950,15 @@ int ast_ari_validate_application_replaced(struct ast_json *json)
 	int has_application = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ApplicationReplaced field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -2000,6 +2027,15 @@ int ast_ari_validate_bridge_attended_transfer(struct ast_json *json)
 	int has_transferer_second_leg = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI BridgeAttendedTransfer field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -2242,6 +2278,15 @@ int ast_ari_validate_bridge_blind_transfer(struct ast_json *json)
 	int has_result = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI BridgeBlindTransfer field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -2408,6 +2453,15 @@ int ast_ari_validate_bridge_created(struct ast_json *json)
 	int has_bridge = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI BridgeCreated field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -2487,6 +2541,15 @@ int ast_ari_validate_bridge_destroyed(struct ast_json *json)
 	int has_bridge = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI BridgeDestroyed field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -2567,6 +2630,15 @@ int ast_ari_validate_bridge_merged(struct ast_json *json)
 	int has_bridge_from = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI BridgeMerged field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -2652,6 +2724,103 @@ ari_validator ast_ari_validate_bridge_merged_fn(void)
 	return ast_ari_validate_bridge_merged;
 }
 
+int ast_ari_validate_bridge_video_source_changed(struct ast_json *json)
+{
+	int res = 1;
+	struct ast_json_iter *iter;
+	int has_type = 0;
+	int has_application = 0;
+	int has_bridge = 0;
+
+	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI BridgeVideoSourceChanged field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_type = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI BridgeVideoSourceChanged field type failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_application = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI BridgeVideoSourceChanged field application failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_date(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI BridgeVideoSourceChanged field timestamp failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("bridge", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_bridge = 1;
+			prop_is_valid = ast_ari_validate_bridge(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI BridgeVideoSourceChanged field bridge failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("old_video_source_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI BridgeVideoSourceChanged field old_video_source_id failed validation\n");
+				res = 0;
+			}
+		} else
+		{
+			ast_log(LOG_ERROR,
+				"ARI BridgeVideoSourceChanged has undocumented field %s\n",
+				ast_json_object_iter_key(iter));
+			res = 0;
+		}
+	}
+
+	if (!has_type) {
+		ast_log(LOG_ERROR, "ARI BridgeVideoSourceChanged missing required field type\n");
+		res = 0;
+	}
+
+	if (!has_application) {
+		ast_log(LOG_ERROR, "ARI BridgeVideoSourceChanged missing required field application\n");
+		res = 0;
+	}
+
+	if (!has_bridge) {
+		ast_log(LOG_ERROR, "ARI BridgeVideoSourceChanged missing required field bridge\n");
+		res = 0;
+	}
+
+	return res;
+}
+
+ari_validator ast_ari_validate_bridge_video_source_changed_fn(void)
+{
+	return ast_ari_validate_bridge_video_source_changed;
+}
+
 int ast_ari_validate_channel_caller_id(struct ast_json *json)
 {
 	int res = 1;
@@ -2663,6 +2832,15 @@ int ast_ari_validate_channel_caller_id(struct ast_json *json)
 	int has_channel = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelCallerId field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -2772,6 +2950,15 @@ int ast_ari_validate_channel_connected_line(struct ast_json *json)
 	int has_channel = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelConnectedLine field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -2851,6 +3038,15 @@ int ast_ari_validate_channel_created(struct ast_json *json)
 	int has_channel = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelCreated field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -2932,6 +3128,15 @@ int ast_ari_validate_channel_destroyed(struct ast_json *json)
 	int has_channel = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelDestroyed field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -3043,6 +3248,15 @@ int ast_ari_validate_channel_dialplan(struct ast_json *json)
 	int has_dialplan_app_data = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelDialplan field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -3154,6 +3368,15 @@ int ast_ari_validate_channel_dtmf_received(struct ast_json *json)
 	int has_duration_ms = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelDtmfReceived field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -3263,6 +3486,15 @@ int ast_ari_validate_channel_entered_bridge(struct ast_json *json)
 	int has_bridge = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelEnteredBridge field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -3351,6 +3583,15 @@ int ast_ari_validate_channel_hangup_request(struct ast_json *json)
 	int has_channel = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelHangupRequest field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -3448,6 +3689,15 @@ int ast_ari_validate_channel_hold(struct ast_json *json)
 	int has_channel = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelHold field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -3537,6 +3787,15 @@ int ast_ari_validate_channel_left_bridge(struct ast_json *json)
 	int has_channel = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelLeftBridge field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -3631,6 +3890,15 @@ int ast_ari_validate_channel_state_change(struct ast_json *json)
 	int has_channel = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelStateChange field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -3711,6 +3979,15 @@ int ast_ari_validate_channel_talking_finished(struct ast_json *json)
 	int has_duration = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelTalkingFinished field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -3805,6 +4082,15 @@ int ast_ari_validate_channel_talking_started(struct ast_json *json)
 	int has_channel = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelTalkingStarted field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -3884,6 +4170,15 @@ int ast_ari_validate_channel_unhold(struct ast_json *json)
 	int has_channel = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelUnhold field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -3964,6 +4259,15 @@ int ast_ari_validate_channel_userevent(struct ast_json *json)
 	int has_userevent = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelUserevent field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -4086,6 +4390,15 @@ int ast_ari_validate_channel_varset(struct ast_json *json)
 	int has_variable = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelVarset field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -4269,6 +4582,15 @@ int ast_ari_validate_contact_status_change(struct ast_json *json)
 	int has_endpoint = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ContactStatusChange field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -4363,6 +4685,15 @@ int ast_ari_validate_device_state_changed(struct ast_json *json)
 	int has_device_state = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI DeviceStateChanged field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -4443,6 +4774,15 @@ int ast_ari_validate_dial(struct ast_json *json)
 	int has_peer = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Dial field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -4573,6 +4913,15 @@ int ast_ari_validate_endpoint_state_change(struct ast_json *json)
 	int has_endpoint = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI EndpointStateChange field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -4678,6 +5027,9 @@ int ast_ari_validate_event(struct ast_json *json)
 	if (strcmp("BridgeMerged", discriminator) == 0) {
 		return ast_ari_validate_bridge_merged(json);
 	} else
+	if (strcmp("BridgeVideoSourceChanged", discriminator) == 0) {
+		return ast_ari_validate_bridge_video_source_changed(json);
+	} else
 	if (strcmp("ChannelCallerId", discriminator) == 0) {
 		return ast_ari_validate_channel_caller_id(json);
 	} else
@@ -4772,6 +5124,15 @@ int ast_ari_validate_event(struct ast_json *json)
 	}
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Event field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -4861,6 +5222,9 @@ int ast_ari_validate_message(struct ast_json *json)
 	if (strcmp("BridgeMerged", discriminator) == 0) {
 		return ast_ari_validate_bridge_merged(json);
 	} else
+	if (strcmp("BridgeVideoSourceChanged", discriminator) == 0) {
+		return ast_ari_validate_bridge_video_source_changed(json);
+	} else
 	if (strcmp("ChannelCallerId", discriminator) == 0) {
 		return ast_ari_validate_channel_caller_id(json);
 	} else
@@ -4961,6 +5325,15 @@ int ast_ari_validate_message(struct ast_json *json)
 	}
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Message field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -5000,6 +5373,15 @@ int ast_ari_validate_missing_params(struct ast_json *json)
 	int has_params = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI MissingParams field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -5131,6 +5513,15 @@ int ast_ari_validate_peer_status_change(struct ast_json *json)
 	int has_peer = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI PeerStatusChange field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -5225,6 +5616,15 @@ int ast_ari_validate_playback_finished(struct ast_json *json)
 	int has_playback = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI PlaybackFinished field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -5304,6 +5704,15 @@ int ast_ari_validate_playback_started(struct ast_json *json)
 	int has_playback = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI PlaybackStarted field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -5383,6 +5792,15 @@ int ast_ari_validate_recording_failed(struct ast_json *json)
 	int has_recording = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI RecordingFailed field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -5462,6 +5880,15 @@ int ast_ari_validate_recording_finished(struct ast_json *json)
 	int has_recording = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI RecordingFinished field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -5541,6 +5968,15 @@ int ast_ari_validate_recording_started(struct ast_json *json)
 	int has_recording = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI RecordingStarted field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -5620,6 +6056,15 @@ int ast_ari_validate_stasis_end(struct ast_json *json)
 	int has_channel = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI StasisEnd field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -5700,6 +6145,15 @@ int ast_ari_validate_stasis_start(struct ast_json *json)
 	int has_channel = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI StasisStart field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
@@ -5804,6 +6258,15 @@ int ast_ari_validate_text_message_received(struct ast_json *json)
 	int has_message = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI TextMessageReceived field asterisk_id failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_type = 1;
diff --git a/res/ari/ari_model_validators.h b/res/ari/ari_model_validators.h
index 0bcdb0f..403a5fb 100644
--- a/res/ari/ari_model_validators.h
+++ b/res/ari/ari_model_validators.h
@@ -717,6 +717,24 @@ int ast_ari_validate_bridge_merged(struct ast_json *json);
 ari_validator ast_ari_validate_bridge_merged_fn(void);
 
 /*!
+ * \brief Validator for BridgeVideoSourceChanged.
+ *
+ * Notification that the source of video in a bridge has changed.
+ *
+ * \param json JSON object to validate.
+ * \returns True (non-zero) if valid.
+ * \returns False (zero) if invalid.
+ */
+int ast_ari_validate_bridge_video_source_changed(struct ast_json *json);
+
+/*!
+ * \brief Function pointer to ast_ari_validate_bridge_video_source_changed().
+ *
+ * See \ref ast_ari_model_validators.h for more details.
+ */
+ari_validator ast_ari_validate_bridge_video_source_changed_fn(void);
+
+/*!
  * \brief Validator for ChannelCallerId.
  *
  * Channel changed Caller ID.
@@ -1434,6 +1452,8 @@ ari_validator ast_ari_validate_application_fn(void);
  * - id: string (required)
  * - name: string (required)
  * - technology: string (required)
+ * - video_mode: string
+ * - video_source_id: string
  * LiveRecording
  * - cause: string
  * - duration: int
@@ -1467,10 +1487,12 @@ ari_validator ast_ari_validate_application_fn(void);
  * - new_messages: int (required)
  * - old_messages: int (required)
  * ApplicationReplaced
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * BridgeAttendedTransfer
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
@@ -1491,6 +1513,7 @@ ari_validator ast_ari_validate_application_fn(void);
  * - transferer_second_leg: Channel (required)
  * - transferer_second_leg_bridge: Bridge
  * BridgeBlindTransfer
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
@@ -1503,22 +1526,33 @@ ari_validator ast_ari_validate_application_fn(void);
  * - result: string (required)
  * - transferee: Channel
  * BridgeCreated
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - bridge: Bridge (required)
  * BridgeDestroyed
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - bridge: Bridge (required)
  * BridgeMerged
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - bridge: Bridge (required)
  * - bridge_from: Bridge (required)
+ * BridgeVideoSourceChanged
+ * - asterisk_id: string
+ * - type: string (required)
+ * - application: string (required)
+ * - timestamp: Date
+ * - bridge: Bridge (required)
+ * - old_video_source_id: string
  * ChannelCallerId
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
@@ -1526,16 +1560,19 @@ ari_validator ast_ari_validate_application_fn(void);
  * - caller_presentation_txt: string (required)
  * - channel: Channel (required)
  * ChannelConnectedLine
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - channel: Channel (required)
  * ChannelCreated
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - channel: Channel (required)
  * ChannelDestroyed
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
@@ -1543,6 +1580,7 @@ ari_validator ast_ari_validate_application_fn(void);
  * - cause_txt: string (required)
  * - channel: Channel (required)
  * ChannelDialplan
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
@@ -1550,6 +1588,7 @@ ari_validator ast_ari_validate_application_fn(void);
  * - dialplan_app: string (required)
  * - dialplan_app_data: string (required)
  * ChannelDtmfReceived
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
@@ -1557,12 +1596,14 @@ ari_validator ast_ari_validate_application_fn(void);
  * - digit: string (required)
  * - duration_ms: int (required)
  * ChannelEnteredBridge
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - bridge: Bridge (required)
  * - channel: Channel
  * ChannelHangupRequest
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
@@ -1570,39 +1611,46 @@ ari_validator ast_ari_validate_application_fn(void);
  * - channel: Channel (required)
  * - soft: boolean
  * ChannelHold
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - channel: Channel (required)
  * - musicclass: string
  * ChannelLeftBridge
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - bridge: Bridge (required)
  * - channel: Channel (required)
  * ChannelStateChange
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - channel: Channel (required)
  * ChannelTalkingFinished
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - channel: Channel (required)
  * - duration: int (required)
  * ChannelTalkingStarted
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - channel: Channel (required)
  * ChannelUnhold
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - channel: Channel (required)
  * ChannelUserevent
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
@@ -1612,6 +1660,7 @@ ari_validator ast_ari_validate_application_fn(void);
  * - eventname: string (required)
  * - userevent: object (required)
  * ChannelVarset
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
@@ -1624,17 +1673,20 @@ ari_validator ast_ari_validate_application_fn(void);
  * - roundtrip_usec: string
  * - uri: string (required)
  * ContactStatusChange
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - contact_info: ContactInfo (required)
  * - endpoint: Endpoint (required)
  * DeviceStateChanged
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - device_state: DeviceState (required)
  * Dial
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
@@ -1645,17 +1697,21 @@ ari_validator ast_ari_validate_application_fn(void);
  * - forwarded: Channel
  * - peer: Channel (required)
  * EndpointStateChange
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - endpoint: Endpoint (required)
  * Event
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * Message
+ * - asterisk_id: string
  * - type: string (required)
  * MissingParams
+ * - asterisk_id: string
  * - type: string (required)
  * - params: List[string] (required)
  * Peer
@@ -1665,42 +1721,50 @@ ari_validator ast_ari_validate_application_fn(void);
  * - port: string
  * - time: string
  * PeerStatusChange
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - endpoint: Endpoint (required)
  * - peer: Peer (required)
  * PlaybackFinished
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - playback: Playback (required)
  * PlaybackStarted
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - playback: Playback (required)
  * RecordingFailed
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - recording: LiveRecording (required)
  * RecordingFinished
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - recording: LiveRecording (required)
  * RecordingStarted
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - recording: LiveRecording (required)
  * StasisEnd
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
  * - channel: Channel (required)
  * StasisStart
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
@@ -1708,6 +1772,7 @@ ari_validator ast_ari_validate_application_fn(void);
  * - channel: Channel (required)
  * - replace_channel: Channel
  * TextMessageReceived
+ * - asterisk_id: string
  * - type: string (required)
  * - application: string (required)
  * - timestamp: Date
diff --git a/res/ari/ari_websockets.c b/res/ari/ari_websockets.c
index 8e11629..f06a667 100644
--- a/res/ari/ari_websockets.c
+++ b/res/ari/ari_websockets.c
@@ -174,7 +174,9 @@ int ast_ari_websocket_session_write(struct ast_ari_websocket_session *session,
 		return -1;
 	}
 
+#ifdef AST_DEVMODE
 	ast_debug(3, "Examining ARI event (length %u): \n%s\n", (unsigned int) strlen(str), str);
+#endif
 	if (ast_websocket_write_string(session->ws_session, str)) {
 		ast_log(LOG_NOTICE, "Problem occurred during websocket write, websocket closed\n");
 		return -1;
diff --git a/res/ari/resource_bridges.c b/res/ari/resource_bridges.c
index 39709d0..8c058c4 100644
--- a/res/ari/resource_bridges.c
+++ b/res/ari/resource_bridges.c
@@ -37,6 +37,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/stasis.h"
 #include "asterisk/stasis_bridges.h"
 #include "asterisk/stasis_app.h"
+#include "asterisk/stasis_app_impl.h"
 #include "asterisk/stasis_app_playback.h"
 #include "asterisk/stasis_app_recording.h"
 #include "asterisk/stasis_channels.h"
@@ -998,3 +999,68 @@ void ast_ari_bridges_create_with_id(struct ast_variable *headers,
 	ast_ari_response_ok(response,
 		ast_bridge_snapshot_to_json(snapshot, stasis_app_get_sanitizer()));
 }
+
+static int bridge_set_video_source_cb(struct stasis_app_control *control,
+	struct ast_channel *chan, void *data)
+{
+	struct ast_bridge *bridge = data;
+
+	ast_bridge_lock(bridge);
+	ast_bridge_set_single_src_video_mode(bridge, chan);
+	ast_bridge_unlock(bridge);
+
+	return 0;
+}
+
+void ast_ari_bridges_set_video_source(struct ast_variable *headers,
+	struct ast_ari_bridges_set_video_source_args *args, struct ast_ari_response *response)
+{
+	struct ast_bridge *bridge;
+	struct stasis_app_control *control;
+
+	bridge = find_bridge(response, args->bridge_id);
+	if (!bridge) {
+		return;
+	}
+
+	control = find_channel_control(response, args->channel_id);
+	if (!control) {
+		ao2_ref(bridge, -1);
+		return;
+	}
+
+	if (stasis_app_get_bridge(control) != bridge) {
+		ast_ari_response_error(response, 422,
+			"Unprocessable Entity",
+			"Channel not in this bridge");
+		ao2_ref(bridge, -1);
+		ao2_ref(control, -1);
+		return;
+	}
+
+	stasis_app_send_command(control, bridge_set_video_source_cb,
+		ao2_bump(bridge), __ao2_cleanup);
+
+	ao2_ref(bridge, -1);
+	ao2_ref(control, -1);
+
+	ast_ari_response_no_content(response);
+}
+
+void ast_ari_bridges_clear_video_source(struct ast_variable *headers,
+	struct ast_ari_bridges_clear_video_source_args *args, struct ast_ari_response *response)
+{
+	struct ast_bridge *bridge;
+
+	bridge = find_bridge(response, args->bridge_id);
+	if (!bridge) {
+		return;
+	}
+
+	ast_bridge_lock(bridge);
+	ast_bridge_set_talker_src_video_mode(bridge);
+	ast_bridge_unlock(bridge);
+
+	ao2_ref(bridge, -1);
+	ast_ari_response_no_content(response);
+}
diff --git a/res/ari/resource_bridges.h b/res/ari/resource_bridges.h
index 36ff6a0..61fc6bb 100644
--- a/res/ari/resource_bridges.h
+++ b/res/ari/resource_bridges.h
@@ -200,6 +200,34 @@ int ast_ari_bridges_remove_channel_parse_body(
  * \param[out] response HTTP response
  */
 void ast_ari_bridges_remove_channel(struct ast_variable *headers, struct ast_ari_bridges_remove_channel_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_bridges_set_video_source() */
+struct ast_ari_bridges_set_video_source_args {
+	/*! Bridge's id */
+	const char *bridge_id;
+	/*! Channel's id */
+	const char *channel_id;
+};
+/*!
+ * \brief Set a channel as the video source in a multi-party mixing bridge. This operation has no effect on bridges with two or fewer participants.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_bridges_set_video_source(struct ast_variable *headers, struct ast_ari_bridges_set_video_source_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_bridges_clear_video_source() */
+struct ast_ari_bridges_clear_video_source_args {
+	/*! Bridge's id */
+	const char *bridge_id;
+};
+/*!
+ * \brief Removes any explicit video source in a multi-party mixing bridge. This operation has no effect on bridges with two or fewer participants. When no explicit video source is set, talk detection will be used to determine the active video stream.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_bridges_clear_video_source(struct ast_variable *headers, struct ast_ari_bridges_clear_video_source_args *args, struct ast_ari_response *response);
 /*! Argument struct for ast_ari_bridges_start_moh() */
 struct ast_ari_bridges_start_moh_args {
 	/*! Bridge's id */
diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c
index 6baac7a..04db704 100644
--- a/res/ari/resource_channels.c
+++ b/res/ari/resource_channels.c
@@ -1109,7 +1109,12 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint,
 	}
 
 	if (ast_dial_prerun(dial, other, format_cap)) {
-		ast_ari_response_alloc_failed(response);
+		if (ast_channel_errno() == AST_CHANNEL_ERROR_ID_EXISTS) {
+			ast_ari_response_error(response, 409, "Conflict",
+				"Channel with given unique ID already exists");
+		} else {
+			ast_ari_response_alloc_failed(response);
+		}
 		ast_dial_destroy(dial);
 		ast_free(origination);
 		ast_channel_cleanup(other);
diff --git a/res/res_agi.c b/res/res_agi.c
index 06df752..969c62d 100644
--- a/res/res_agi.c
+++ b/res/res_agi.c
@@ -4093,23 +4093,6 @@ static enum agi_result agi_handle_command(struct ast_channel *chan, AGI *agi, ch
 	return AGI_RESULT_SUCCESS;
 }
 
-AST_LIST_HEAD_NOLOCK(deferred_frames, ast_frame);
-
-static void queue_deferred_frames(struct deferred_frames *deferred_frames,
-	struct ast_channel *chan)
-{
-	struct ast_frame *f;
-
-	if (!AST_LIST_EMPTY(deferred_frames)) {
-		ast_channel_lock(chan);
-		while ((f = AST_LIST_REMOVE_HEAD(deferred_frames, frame_list))) {
-			ast_queue_frame_head(chan, f);
-			ast_frfree(f);
-		}
-		ast_channel_unlock(chan);
-	}
-}
-
 static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int *status, int dead, int argc, char *argv[])
 {
 	struct ast_channel *c;
@@ -4128,9 +4111,6 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
 	const char *sighup_str;
 	const char *exit_on_hangup_str;
 	int exit_on_hangup;
-	struct deferred_frames deferred_frames;
-
-	AST_LIST_HEAD_INIT_NOLOCK(&deferred_frames);
 
 	ast_channel_lock(chan);
 	sighup_str = pbx_builtin_getvar_helper(chan, "AGISIGHUP");
@@ -4192,20 +4172,8 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
 					/* Write, ignoring errors */
 					if (write(agi->audio, f->data.ptr, f->datalen) < 0) {
 					}
-					ast_frfree(f);
-				} else if (ast_is_deferrable_frame(f)) {
-					struct ast_frame *dup_f;
-
-					if ((dup_f = ast_frisolate(f))) {
-						AST_LIST_INSERT_HEAD(&deferred_frames, dup_f, frame_list);
-					}
-
-					if (dup_f != f) {
-						ast_frfree(f);
-					}
-				} else {
-					ast_frfree(f);
 				}
+				ast_frfree(f);
 			}
 		} else if (outfd > -1) {
 			size_t len = sizeof(buf);
@@ -4253,8 +4221,6 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
 				buf[buflen - 1] = '\0';
 			}
 
-			queue_deferred_frames(&deferred_frames, chan);
-
 			if (agidebug)
 				ast_verbose("<%s>AGI Rx << %s\n", ast_channel_name(chan), buf);
 			cmd_status = agi_handle_command(chan, agi, buf, dead);
@@ -4277,8 +4243,6 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
 		}
 	}
 
-	queue_deferred_frames(&deferred_frames, chan);
-
 	if (agi->speech) {
 		ast_speech_destroy(agi->speech);
 	}
diff --git a/res/res_ari_bridges.c b/res/res_ari_bridges.c
index 889d422..458192d 100644
--- a/res/res_ari_bridges.c
+++ b/res/res_ari_bridges.c
@@ -769,6 +769,129 @@ fin: __attribute__((unused))
 	ast_free(args.channel);
 	return;
 }
+/*!
+ * \brief Parameter parsing callback for /bridges/{bridgeId}/videoSource/{channelId}.
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_bridges_set_video_source_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_ari_response *response)
+{
+	struct ast_ari_bridges_set_video_source_args args = {};
+	struct ast_variable *i;
+	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "bridgeId") == 0) {
+			args.bridge_id = (i->value);
+		} else
+		if (strcmp(i->name, "channelId") == 0) {
+			args.channel_id = (i->value);
+		} else
+		{}
+	}
+	ast_ari_bridges_set_video_source(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 404: /* Bridge or Channel not found */
+	case 409: /* Channel not in Stasis application */
+	case 422: /* Channel not in this Bridge */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_void(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /bridges/{bridgeId}/videoSource/{channelId}\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /bridges/{bridgeId}/videoSource/{channelId}\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
+/*!
+ * \brief Parameter parsing callback for /bridges/{bridgeId}/videoSource.
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_bridges_clear_video_source_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_ari_response *response)
+{
+	struct ast_ari_bridges_clear_video_source_args args = {};
+	struct ast_variable *i;
+	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "bridgeId") == 0) {
+			args.bridge_id = (i->value);
+		} else
+		{}
+	}
+	ast_ari_bridges_clear_video_source(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 404: /* Bridge not found */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_void(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /bridges/{bridgeId}/videoSource\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /bridges/{bridgeId}/videoSource\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
 int ast_ari_bridges_start_moh_parse_body(
 	struct ast_json *body,
 	struct ast_ari_bridges_start_moh_args *args)
@@ -1337,6 +1460,25 @@ static struct stasis_rest_handlers bridges_bridgeId_removeChannel = {
 	.children = {  }
 };
 /*! \brief REST handler for /api-docs/bridges.json */
+static struct stasis_rest_handlers bridges_bridgeId_videoSource_channelId = {
+	.path_segment = "channelId",
+	.is_wildcard = 1,
+	.callbacks = {
+		[AST_HTTP_POST] = ast_ari_bridges_set_video_source_cb,
+	},
+	.num_children = 0,
+	.children = {  }
+};
+/*! \brief REST handler for /api-docs/bridges.json */
+static struct stasis_rest_handlers bridges_bridgeId_videoSource = {
+	.path_segment = "videoSource",
+	.callbacks = {
+		[AST_HTTP_DELETE] = ast_ari_bridges_clear_video_source_cb,
+	},
+	.num_children = 1,
+	.children = { &bridges_bridgeId_videoSource_channelId, }
+};
+/*! \brief REST handler for /api-docs/bridges.json */
 static struct stasis_rest_handlers bridges_bridgeId_moh = {
 	.path_segment = "moh",
 	.callbacks = {
@@ -1383,8 +1525,8 @@ static struct stasis_rest_handlers bridges_bridgeId = {
 		[AST_HTTP_GET] = ast_ari_bridges_get_cb,
 		[AST_HTTP_DELETE] = ast_ari_bridges_destroy_cb,
 	},
-	.num_children = 5,
-	.children = { &bridges_bridgeId_addChannel,&bridges_bridgeId_removeChannel,&bridges_bridgeId_moh,&bridges_bridgeId_play,&bridges_bridgeId_record, }
+	.num_children = 6,
+	.children = { &bridges_bridgeId_addChannel,&bridges_bridgeId_removeChannel,&bridges_bridgeId_videoSource,&bridges_bridgeId_moh,&bridges_bridgeId_play,&bridges_bridgeId_record, }
 };
 /*! \brief REST handler for /api-docs/bridges.json */
 static struct stasis_rest_handlers bridges = {
diff --git a/res/res_ari_channels.c b/res/res_ari_channels.c
index 9dc19cc..8cb3388 100644
--- a/res/res_ari_channels.c
+++ b/res/res_ari_channels.c
@@ -253,6 +253,7 @@ static void ast_ari_channels_originate_cb(
 	case 500: /* Internal Server Error */
 	case 501: /* Not Implemented */
 	case 400: /* Invalid parameters for originating a channel. */
+	case 409: /* Channel with given unique ID already exists. */
 		is_valid = 1;
 		break;
 	default:
@@ -483,6 +484,7 @@ static void ast_ari_channels_originate_with_id_cb(
 	case 500: /* Internal Server Error */
 	case 501: /* Not Implemented */
 	case 400: /* Invalid parameters for originating a channel. */
+	case 409: /* Channel with given unique ID already exists. */
 		is_valid = 1;
 		break;
 	default:
diff --git a/res/res_format_attr_opus.c b/res/res_format_attr_opus.c
index 12b9105..857b943 100644
--- a/res/res_format_attr_opus.c
+++ b/res/res_format_attr_opus.c
@@ -102,27 +102,35 @@ static int opus_clone(const struct ast_format *src, struct ast_format *dst)
 
 static void sdp_fmtp_get(const char *attributes, const char *name, int *attr)
 {
-	const char *kvp = "";
+	const char *kvp = attributes;
 	int val;
 
-	if (attributes && !(kvp = strstr(attributes, name))) {
+	if (ast_strlen_zero(attributes)) {
 		return;
 	}
 
-	/*
-	 * If the named attribute is not at the start of the given attributes, and
-	 * the preceding character is not a space or semicolon then it's not the
-	 * attribute we are looking for. It's an attribute with the name embedded
-	 * within it (e.g. ptime in maxptime, stereo in sprop-stereo).
+	/* This logic goes through each attribute in the fmtp line looking for the
+	 * requested named attribute.
 	 */
-	if (kvp != attributes && *(kvp - 1) != ' ' && *(kvp - 1) != ';') {
-		/* Keep searching as it might still be in the attributes string */
-		sdp_fmtp_get(strchr(kvp, ';'), name, attr);
-	/*
-	 * Otherwise it's a match, so retrieve the value and set the attribute.
-	 */
-	} else if (sscanf(kvp, "%*[^=]=%30d", &val) == 1) {
-		*attr = val;
+	while (*kvp) {
+		/* Skip any preceeding blanks as some implementations separate attributes using spaces too */
+		kvp = ast_skip_blanks(kvp);
+
+		/* If we are at at the requested attribute get its value and return */
+		if (!strncmp(kvp, name, strlen(name)) && kvp[strlen(name)] == '=') {
+			if (sscanf(kvp, "%*[^=]=%30d", &val) == 1) {
+				*attr = val;
+				break;
+			}
+		}
+
+		/* Move on to the next attribute if possible */
+		kvp = strchr(kvp, ';');
+		if (!kvp) {
+			break;
+		}
+
+		kvp++;
 	}
 }
 
@@ -158,7 +166,8 @@ static struct ast_format *opus_parse_sdp_fmtp(const struct ast_format *format, c
 static void opus_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
 {
 	struct opus_attr *attr = ast_format_get_attribute_data(format);
-	int size;
+	int base_fmtp_size;
+	int original_size;
 
 	if (!attr) {
 		/*
@@ -169,7 +178,8 @@ static void opus_generate_sdp_fmtp(const struct ast_format *format, unsigned int
 		attr = &default_opus_attr;
 	}
 
-	size = ast_str_append(str, 0, "a=fmtp:%u ", payload);
+	original_size = ast_str_strlen(*str);
+	base_fmtp_size = ast_str_append(str, 0, "a=fmtp:%u ", payload);
 
 	if (CODEC_OPUS_DEFAULT_SAMPLE_RATE != attr->maxplayrate) {
 		ast_str_append(str, 0, "%s=%d;",
@@ -211,8 +221,8 @@ static void opus_generate_sdp_fmtp(const struct ast_format *format, unsigned int
 			CODEC_OPUS_ATTR_DTX, attr->dtx);
 	}
 
-	if (size == ast_str_strlen(*str)) {
-		ast_str_reset(*str);
+	if (base_fmtp_size == ast_str_strlen(*str) - original_size) {
+		ast_str_truncate(*str, original_size);
 	} else {
 		ast_str_truncate(*str, -1);
 		ast_str_append(str, 0, "\r\n");
diff --git a/res/res_http_websocket.c b/res/res_http_websocket.c
index 5e0591b..71d838a 100644
--- a/res/res_http_websocket.c
+++ b/res/res_http_websocket.c
@@ -51,16 +51,29 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 /*! \brief Number of buckets for registered protocols */
 #define MAX_PROTOCOL_BUCKETS 7
 
+#ifdef LOW_MEMORY
 /*! \brief Size of the pre-determined buffer for WebSocket frames */
-#define MAXIMUM_FRAME_SIZE 16384
+#define MAXIMUM_FRAME_SIZE 8192
 
 /*! \brief Default reconstruction size for multi-frame payload reconstruction. If exceeded the next frame will start a
  *         payload.
  */
-#define DEFAULT_RECONSTRUCTION_CEILING 16384
+#define DEFAULT_RECONSTRUCTION_CEILING 8192
 
 /*! \brief Maximum reconstruction size for multi-frame payload reconstruction. */
-#define MAXIMUM_RECONSTRUCTION_CEILING 16384
+#define MAXIMUM_RECONSTRUCTION_CEILING 8192
+#else
+/*! \brief Size of the pre-determined buffer for WebSocket frames */
+#define MAXIMUM_FRAME_SIZE 32768
+
+/*! \brief Default reconstruction size for multi-frame payload reconstruction. If exceeded the next frame will start a
+ *         payload.
+ */
+#define DEFAULT_RECONSTRUCTION_CEILING 32768
+
+/*! \brief Maximum reconstruction size for multi-frame payload reconstruction. */
+#define MAXIMUM_RECONSTRUCTION_CEILING 32768
+#endif
 
 /*! \brief Maximum size of a websocket frame header
  * 1 byte flags and opcode
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 039495d..dd4a619 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -919,6 +919,14 @@
 						On outbound requests, force the user portion of the Contact header to this value.
 					</para></description>
 				</configOption>
+                                <configOption name="asymmetric_rtp_codec" default="no">
+                                        <synopsis>Allow the sending and receiving RTP codec to differ</synopsis>
+                                        <description><para>
+                                                When set to "yes" the codec in use for sending will be allowed to differ from
+                                                that of the received one. PJSIP will not automatically switch the sending one
+                                                to the receiving one.
+                                        </para></description>
+                                </configOption>
 			</configObject>
 			<configObject name="auth">
 				<synopsis>Authentication type</synopsis>
@@ -3386,6 +3394,7 @@ struct send_request_wrapper {
 static void endpt_send_request_cb(void *token, pjsip_event *e)
 {
 	struct send_request_wrapper *req_wrapper = token;
+	unsigned int cb_called;
 
 	if (e->body.tsx_state.type == PJSIP_EVENT_TIMER) {
 		ast_debug(2, "%p: PJSIP tsx timer expired\n", req_wrapper);
@@ -3415,7 +3424,6 @@ static void endpt_send_request_cb(void *token, pjsip_event *e)
 		timers_cancelled = pj_timer_heap_cancel_if_active(
 			pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()),
 			req_wrapper->timeout_timer, TIMER_INACTIVE);
-
 		if (timers_cancelled > 0) {
 			/* If the timer was cancelled the callback will never run so
 			 * clean up its reference to the wrapper.
@@ -3423,25 +3431,27 @@ static void endpt_send_request_cb(void *token, pjsip_event *e)
 			ast_debug(3, "%p: Timer cancelled\n", req_wrapper);
 			ao2_ref(req_wrapper, -1);
 		} else {
-			/* If it wasn't cancelled, it MAY be in the callback already
-			 * waiting on the lock so set the id to INACTIVE so
-			 * when the callback comes out of the lock, it knows to not
-			 * proceed.
+			/*
+			 * If it wasn't cancelled, it MAY be in the callback already
+			 * waiting on the lock.  When we release the lock, it will
+			 * now know not to proceed.
 			 */
 			ast_debug(3, "%p: Timer already expired\n", req_wrapper);
-			req_wrapper->timeout_timer->id = TIMER_INACTIVE;
 		}
 	}
 
+	cb_called = req_wrapper->cb_called;
+	req_wrapper->cb_called = 1;
+	ao2_unlock(req_wrapper);
+
 	/* It's possible that our own timer expired and called the callbacks
 	 * so no need to call them again.
 	 */
-	if (!req_wrapper->cb_called && req_wrapper->callback) {
+	if (!cb_called && req_wrapper->callback) {
 		req_wrapper->callback(req_wrapper->token, e);
-		req_wrapper->cb_called = 1;
 		ast_debug(2, "%p: Callbacks executed\n", req_wrapper);
 	}
-	ao2_unlock(req_wrapper);
+
 	ao2_ref(req_wrapper, -1);
 }
 
@@ -3452,15 +3462,16 @@ static void endpt_send_request_cb(void *token, pjsip_event *e)
  */
 static void send_request_timer_callback(pj_timer_heap_t *theap, pj_timer_entry *entry)
 {
-	pjsip_event event;
 	struct send_request_wrapper *req_wrapper = entry->user_data;
+	unsigned int cb_called;
 
 	ast_debug(2, "%p: Internal tsx timer expired after %d msec\n",
 		req_wrapper, req_wrapper->timeout);
 
 	ao2_lock(req_wrapper);
-	/* If the id is not TIMEOUT_TIMER2 then the timer was cancelled above
-	 * while the lock was being held so just clean up.
+	/*
+	 * If the id is not TIMEOUT_TIMER2 then the timer was cancelled
+	 * before we got the lock or it was already handled so just clean up.
 	 */
 	if (entry->id != TIMEOUT_TIMER2) {
 		ao2_unlock(req_wrapper);
@@ -3468,20 +3479,24 @@ static void send_request_timer_callback(pj_timer_heap_t *theap, pj_timer_entry *
 		ao2_ref(req_wrapper, -1);
 		return;
 	}
+	entry->id = TIMER_INACTIVE;
 
 	ast_debug(3, "%p: Timer handled here\n", req_wrapper);
 
-	PJSIP_EVENT_INIT_TX_MSG(event, req_wrapper->tdata);
-	event.body.tsx_state.type = PJSIP_EVENT_TIMER;
-	entry->id = TIMER_INACTIVE;
+	cb_called = req_wrapper->cb_called;
+	req_wrapper->cb_called = 1;
+	ao2_unlock(req_wrapper);
+
+	if (!cb_called && req_wrapper->callback) {
+		pjsip_event event;
+
+		PJSIP_EVENT_INIT_TX_MSG(event, req_wrapper->tdata);
+		event.body.tsx_state.type = PJSIP_EVENT_TIMER;
 
-	if (!req_wrapper->cb_called && req_wrapper->callback) {
 		req_wrapper->callback(req_wrapper->token, &event);
-		req_wrapper->cb_called = 1;
 		ast_debug(2, "%p: Callbacks executed\n", req_wrapper);
 	}
 
-	ao2_unlock(req_wrapper);
 	ao2_ref(req_wrapper, -1);
 }
 
@@ -3501,6 +3516,11 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
 	pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
 	pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
 
+	if (!cb && token) {
+		/* Silly.  Without a callback we cannot do anything with token. */
+		return PJ_EINVAL;
+	}
+
 	/* Create wrapper to detect if the callback was actually called on an error. */
 	req_wrapper = ao2_alloc(sizeof(*req_wrapper), send_request_wrapper_destructor);
 	if (!req_wrapper) {
@@ -3518,7 +3538,10 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
 	/* Add a reference to tdata.  The wrapper destructor cleans it up. */
 	pjsip_tx_data_add_ref(tdata);
 
-	ao2_lock(req_wrapper);
+	if (endpoint) {
+		sip_get_tpselector_from_endpoint(endpoint, &selector);
+		pjsip_tx_data_set_transport(tdata, &selector);
+	}
 
 	if (timeout > 0) {
 		pj_time_val timeout_timer_val = { timeout / 1000, timeout % 1000 };
@@ -3530,9 +3553,6 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
 		pj_timer_entry_init(req_wrapper->timeout_timer, TIMEOUT_TIMER2,
 			req_wrapper, send_request_timer_callback);
 
-		pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(endpt),
-			req_wrapper->timeout_timer, TIMER_INACTIVE);
-
 		/* We need to insure that the wrapper and tdata are available if/when the
 		 * timer callback is executed.
 		 */
@@ -3540,42 +3560,31 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
 		ret_val = pj_timer_heap_schedule(pjsip_endpt_get_timer_heap(endpt),
 			req_wrapper->timeout_timer, &timeout_timer_val);
 		if (ret_val != PJ_SUCCESS) {
-			ao2_unlock(req_wrapper);
 			ast_log(LOG_ERROR,
 				"Failed to set timer.  Not sending %.*s request to endpoint %s.\n",
 				(int) pj_strlen(&tdata->msg->line.req.method.name),
 				pj_strbuf(&tdata->msg->line.req.method.name),
 				endpoint ? ast_sorcery_object_get_id(endpoint) : "<unknown>");
 			ao2_t_ref(req_wrapper, -2, "Drop timer and routine ref");
+			pjsip_tx_data_dec_ref(tdata);
 			return ret_val;
 		}
-
-		req_wrapper->timeout_timer->id = TIMEOUT_TIMER2;
-	} else {
-		req_wrapper->timeout_timer = NULL;
 	}
 
 	/* We need to insure that the wrapper and tdata are available when the
 	 * transaction callback is executed.
 	 */
 	ao2_ref(req_wrapper, +1);
-
-	if (endpoint) {
-		sip_get_tpselector_from_endpoint(endpoint, &selector);
-		pjsip_tx_data_set_transport(tdata, &selector);
-	}
-
 	ret_val = pjsip_endpt_send_request(endpt, tdata, -1, req_wrapper, endpt_send_request_cb);
 	if (ret_val != PJ_SUCCESS) {
 		char errmsg[PJ_ERR_MSG_SIZE];
 
-		if (timeout > 0) {
-			int timers_cancelled = pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(endpt),
-				req_wrapper->timeout_timer, TIMER_INACTIVE);
-			if (timers_cancelled > 0) {
-				ao2_ref(req_wrapper, -1);
-			}
-		}
+		/*
+		 * endpt_send_request_cb is not expected to ever be called
+		 * because the request didn't get far enough to attempt
+		 * sending.
+		 */
+		ao2_ref(req_wrapper, -1);
 
 		/* Complain of failure to send the request. */
 		pj_strerror(ret_val, errmsg, sizeof(errmsg));
@@ -3584,20 +3593,37 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
 			pj_strbuf(&tdata->msg->line.req.method.name),
 			endpoint ? ast_sorcery_object_get_id(endpoint) : "<unknown>");
 
-		/* Was the callback called? */
-		if (req_wrapper->cb_called) {
-			/*
-			 * Yes so we cannot report any error.  The callback
-			 * has already freed any resources associated with
-			 * token.
-			 */
-			ret_val = PJ_SUCCESS;
-		} else {
-			/* No and it is not expected to ever be called. */
-			ao2_ref(req_wrapper, -1);
+		if (timeout > 0) {
+			int timers_cancelled;
+
+			ao2_lock(req_wrapper);
+			timers_cancelled = pj_timer_heap_cancel_if_active(
+				pjsip_endpt_get_timer_heap(endpt),
+				req_wrapper->timeout_timer, TIMER_INACTIVE);
+			if (timers_cancelled > 0) {
+				ao2_ref(req_wrapper, -1);
+			}
+
+			/* Was the callback called? */
+			if (req_wrapper->cb_called) {
+				/*
+				 * Yes so we cannot report any error.  The callback
+				 * has already freed any resources associated with
+				 * token.
+				 */
+				ret_val = PJ_SUCCESS;
+			} else {
+				/*
+				 * No so we claim it is called so our caller can free
+				 * any resources associated with token because of
+				 * failure.
+				 */
+				req_wrapper->cb_called = 1;
+			}
+			ao2_unlock(req_wrapper);
 		}
 	}
-	ao2_unlock(req_wrapper);
+
 	ao2_ref(req_wrapper, -1);
 	return ret_val;
 }
@@ -4291,6 +4317,7 @@ static int unload_pjsip(void *data)
 	if (ast_pjsip_endpoint && serializer_pool[0]) {
 		ast_res_pjsip_cleanup_options_handling();
 		internal_sip_destroy_outbound_authentication();
+		ast_res_pjsip_cleanup_message_ip_updater();
 		ast_sip_destroy_distributor();
 		ast_res_pjsip_destroy_configuration();
 		ast_sip_destroy_system();
@@ -4460,6 +4487,12 @@ static int load_module(void)
 	}
 
 	ast_res_pjsip_init_options_handling(0);
+
+	if (ast_res_pjsip_init_message_ip_updater()) {
+		ast_log(LOG_ERROR, "Failed to initialize message IP updating. Aborting load\n");
+		goto error;
+	}
+
 	ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
 
 	AST_TEST_REGISTER(xml_sanitization_end_null);
diff --git a/res/res_pjsip/include/res_pjsip_private.h b/res/res_pjsip/include/res_pjsip_private.h
index 04cd854..11ad12c 100644
--- a/res/res_pjsip/include/res_pjsip_private.h
+++ b/res/res_pjsip/include/res_pjsip_private.h
@@ -184,6 +184,14 @@ void ast_sip_destroy_global_headers(void);
 int ast_res_pjsip_init_options_handling(int reload);
 
 /*!
+ * \internal Initialize message IP updating handling.
+ *
+ * \retval 0 on success
+ * \retval other on failure
+ */
+int ast_res_pjsip_init_message_ip_updater(void);
+
+/*!
  * \internal
  * \brief Initialize transport storage for contacts.
  *
@@ -248,6 +256,12 @@ void ast_res_pjsip_cleanup_options_handling(void);
 
 /*!
  * \internal
+ * \brief Clean up res_pjsip message ip updating handling
+ */
+void ast_res_pjsip_cleanup_message_ip_updater(void);
+
+/*!
+ * \internal
  * \brief Get threadpool options
  */
 void sip_get_threadpool_options(struct ast_threadpool_options *threadpool_options);
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 478e5c7..84dfa22 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -1939,6 +1939,7 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_acl", "", endpoint_acl_handler, contact_acl_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "subscribe_context", "", OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct ast_sip_endpoint, subscription.context));
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_user", "", contact_user_handler, contact_user_to_str, NULL, 0, 0);
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "asymmetric_rtp_codec", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, asymmetric_rtp_codec));
 
 	if (ast_sip_initialize_sorcery_transport()) {
 		ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
diff --git a/res/res_pjsip_multihomed.c b/res/res_pjsip/pjsip_message_ip_updater.c
similarity index 60%
rename from res/res_pjsip_multihomed.c
rename to res/res_pjsip/pjsip_message_ip_updater.c
index d52f6e4..7671ad0 100644
--- a/res/res_pjsip_multihomed.c
+++ b/res/res_pjsip/pjsip_message_ip_updater.c
@@ -1,7 +1,7 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 2014, Digium, Inc.
+ * Copyright (C) 2014-2016, Digium, Inc.
  *
  * Joshua Colp <jcolp at digium.com>
  *
@@ -16,19 +16,78 @@
  * at the top of the source tree.
  */
 
-/*** MODULEINFO
-	<depend>pjproject</depend>
-	<depend>res_pjsip</depend>
-	<support_level>core</support_level>
- ***/
-
 #include "asterisk.h"
 
 #include <pjsip.h>
 #include <pjsip_ua.h>
 
 #include "asterisk/res_pjsip.h"
-#include "asterisk/module.h"
+#include "asterisk/res_pjsip_session.h"
+#include "include/res_pjsip_private.h"
+
+#define MOD_DATA_RESTRICTIONS "restrictions"
+
+static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata);
+
+/*! \brief Outgoing message modification restrictions */
+struct multihomed_message_restrictions {
+	/*! \brief Disallow modification of the From domain */
+	unsigned int disallow_from_domain_modification;
+};
+
+static pjsip_module multihomed_module = {
+	.name = { "Multihomed Routing", 18 },
+	.id = -1,
+	.priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 1,
+	.on_tx_request = multihomed_on_tx_message,
+	.on_tx_response = multihomed_on_tx_message,
+};
+
+/*! \brief Helper function to get (or allocate if not already present) restrictions on a message */
+static struct multihomed_message_restrictions *multihomed_get_restrictions(pjsip_tx_data *tdata)
+{
+	struct multihomed_message_restrictions *restrictions;
+
+	restrictions = ast_sip_mod_data_get(tdata->mod_data, multihomed_module.id, MOD_DATA_RESTRICTIONS);
+	if (restrictions) {
+		return restrictions;
+	}
+
+	restrictions = PJ_POOL_ALLOC_T(tdata->pool, struct multihomed_message_restrictions);
+	ast_sip_mod_data_set(tdata->pool, tdata->mod_data, multihomed_module.id, MOD_DATA_RESTRICTIONS, restrictions);
+
+	return restrictions;
+}
+
+/*! \brief Callback invoked on non-session outgoing messages */
+static void multihomed_outgoing_message(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, struct pjsip_tx_data *tdata)
+{
+	struct multihomed_message_restrictions *restrictions = multihomed_get_restrictions(tdata);
+
+	restrictions->disallow_from_domain_modification = !ast_strlen_zero(endpoint->fromdomain);
+}
+
+/*! \brief PJSIP Supplement for tagging messages with restrictions */
+static struct ast_sip_supplement multihomed_supplement = {
+	.priority = AST_SIP_SUPPLEMENT_PRIORITY_FIRST,
+	.outgoing_request = multihomed_outgoing_message,
+	.outgoing_response = multihomed_outgoing_message,
+};
+
+/*! \brief Callback invoked on session outgoing messages */
+static void multihomed_session_outgoing_message(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
+{
+	struct multihomed_message_restrictions *restrictions = multihomed_get_restrictions(tdata);
+
+	restrictions->disallow_from_domain_modification = !ast_strlen_zero(session->endpoint->fromdomain);
+}
+
+/*! \brief PJSIP Session Supplement for tagging messages with restrictions */
+static struct ast_sip_session_supplement multihomed_session_supplement = {
+	.priority = 1,
+	.outgoing_request = multihomed_session_outgoing_message,
+	.outgoing_response = multihomed_session_outgoing_message,
+};
 
 /*! \brief Helper function which returns a UDP transport bound to the given address and port */
 static pjsip_transport *multihomed_get_udp_transport(pj_str_t *address, int port)
@@ -59,6 +118,21 @@ static pjsip_transport *multihomed_get_udp_transport(pj_str_t *address, int port
 	return sip_transport;
 }
 
+/*! \brief Helper function which determines if a transport is bound to any */
+static int multihomed_bound_any(pjsip_transport *transport)
+{
+	pj_uint32_t loop6[4] = {0, 0, 0, 0};
+
+	if ((transport->local_addr.addr.sa_family == pj_AF_INET() &&
+		transport->local_addr.ipv4.sin_addr.s_addr == PJ_INADDR_ANY) ||
+		(transport->local_addr.addr.sa_family == pj_AF_INET6() &&
+		!pj_memcmp(&transport->local_addr.ipv6.sin6_addr, loop6, sizeof(loop6)))) {
+		return 1;
+	}
+
+	return 0;
+}
+
 /*! \brief Helper function which determines if the address within SDP should be rewritten */
 static int multihomed_rewrite_sdp(struct pjmedia_sdp_session *sdp)
 {
@@ -77,26 +151,13 @@ static int multihomed_rewrite_sdp(struct pjmedia_sdp_session *sdp)
 	return 0;
 }
 
-/*! \brief Helper function which determines if a transport is bound to any */
-static int multihomed_bound_any(pjsip_transport *transport)
-{
-	pj_uint32_t loop6[4] = {0, 0, 0, 0};
-
-	if ((transport->local_addr.addr.sa_family == pj_AF_INET() &&
-		transport->local_addr.ipv4.sin_addr.s_addr == PJ_INADDR_ANY) ||
-		(transport->local_addr.addr.sa_family == pj_AF_INET6() &&
-		!pj_memcmp(&transport->local_addr.ipv6.sin6_addr, loop6, sizeof(loop6)))) {
-		return 1;
-	}
-
-	return 0;
-}
-
 static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata)
 {
+	struct multihomed_message_restrictions *restrictions = ast_sip_mod_data_get(tdata->mod_data, multihomed_module.id, MOD_DATA_RESTRICTIONS);
 	pjsip_tpmgr_fla2_param prm;
 	pjsip_cseq_hdr *cseq;
 	pjsip_via_hdr *via;
+	pjsip_fromto_hdr *from;
 
 	/* Use the destination information to determine what local interface this message will go out on */
 	pjsip_tpmgr_fla2_param_default(&prm);
@@ -153,6 +214,13 @@ static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata)
 			ast_debug(4, "Re-wrote Contact URI host/port to %.*s:%d\n",
 				(int)pj_strlen(&uri->host), pj_strbuf(&uri->host), uri->port);
 
+			if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP ||
+				tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP6) {
+				uri->transport_param.slen = 0;
+			} else {
+				pj_strdup2(tdata->pool, &uri->transport_param, pjsip_transport_get_type_name(tdata->tp_info.transport->key.type));
+			}
+
 			pjsip_tx_data_invalidate_msg(tdata);
 		}
 	}
@@ -164,17 +232,38 @@ static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata)
 		pjsip_tx_data_invalidate_msg(tdata);
 	}
 
+	if (tdata->msg->type == PJSIP_REQUEST_MSG && (from = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL)) &&
+		(restrictions && !restrictions->disallow_from_domain_modification)) {
+		pjsip_name_addr *id_name_addr = (pjsip_name_addr *)from->uri;
+		pjsip_sip_uri *uri = pjsip_uri_get_uri(id_name_addr);
+		pj_sockaddr ip;
+
+		if (pj_strcmp2(&uri->host, "localhost") && pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &uri->host, &ip) == PJ_SUCCESS) {
+			pj_strassign(&uri->host, &prm.ret_addr);
+			pjsip_tx_data_invalidate_msg(tdata);
+		}
+	}
+
 	/* Update the SDP if it is present */
 	if (tdata->msg->body && ast_sip_is_content_type(&tdata->msg->body->content_type, "application", "sdp") &&
 		multihomed_rewrite_sdp(tdata->msg->body->data)) {
 		struct pjmedia_sdp_session *sdp = tdata->msg->body->data;
+		static const pj_str_t STR_IP4 = { "IP4", 3 };
+		static const pj_str_t STR_IP6 = { "IP6", 3 };
+		pj_str_t STR_IP;
 		int stream;
 
+		STR_IP = tdata->tp_info.transport->key.type & PJSIP_TRANSPORT_IPV6 ? STR_IP6 : STR_IP4;
+
+		pj_strassign(&sdp->origin.addr, &prm.ret_addr);
+		sdp->origin.addr_type = STR_IP;
 		pj_strassign(&sdp->conn->addr, &prm.ret_addr);
+		sdp->conn->addr_type = STR_IP;
 
 		for (stream = 0; stream < sdp->media_count; ++stream) {
 			if (sdp->media[stream]->conn) {
 				pj_strassign(&sdp->media[stream]->conn->addr, &prm.ret_addr);
+				sdp->media[stream]->conn->addr_type = STR_IP;
 			}
 		}
 
@@ -184,42 +273,31 @@ static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata)
 	return PJ_SUCCESS;
 }
 
-static pjsip_module multihomed_module = {
-	.name = { "Multihomed Routing", 18 },
-	.id = -1,
-	.priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 1,
-	.on_tx_request = multihomed_on_tx_message,
-	.on_tx_response = multihomed_on_tx_message,
-};
-
-static int unload_module(void)
+void ast_res_pjsip_cleanup_message_ip_updater(void)
 {
 	ast_sip_unregister_service(&multihomed_module);
-	return 0;
+	ast_sip_unregister_supplement(&multihomed_supplement);
+	ast_sip_session_unregister_supplement(&multihomed_session_supplement);
 }
 
-static int load_module(void)
+int ast_res_pjsip_init_message_ip_updater(void)
 {
-	char hostname[MAXHOSTNAMELEN] = "";
-
-	CHECK_PJSIP_MODULE_LOADED();
+	if (ast_sip_session_register_supplement(&multihomed_session_supplement)) {
+		ast_log(LOG_ERROR, "Could not register multihomed session supplement for outgoing requests\n");
+		return -1;
+	}
 
-	if (!gethostname(hostname, sizeof(hostname) - 1)) {
-		ast_verb(2, "Performing DNS resolution of local hostname '%s' to get local IPv4 and IPv6 address\n",
-			hostname);
+	if (ast_sip_register_supplement(&multihomed_supplement)) {
+		ast_log(LOG_ERROR, "Could not register multihomed supplement for outgoing requests\n");
+		ast_res_pjsip_cleanup_message_ip_updater();
+		return -1;
 	}
 
 	if (ast_sip_register_service(&multihomed_module)) {
 		ast_log(LOG_ERROR, "Could not register multihomed module for incoming and outgoing requests\n");
-		return AST_MODULE_LOAD_FAILURE;
+		ast_res_pjsip_cleanup_message_ip_updater();
+		return -1;
 	}
 
-	return AST_MODULE_LOAD_SUCCESS;
+	return 0;
 }
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Multihomed Routing Support",
-		.support_level = AST_MODULE_SUPPORT_CORE,
-		.load = load_module,
-		.unload = unload_module,
-		.load_pri = AST_MODPRI_APP_DEPEND,
-	       );
diff --git a/res/res_pjsip_caller_id.c b/res/res_pjsip_caller_id.c
index c3aa338..1b453cf 100644
--- a/res/res_pjsip_caller_id.c
+++ b/res/res_pjsip_caller_id.c
@@ -523,8 +523,11 @@ static void add_pai_header(const struct ast_sip_session *session, pjsip_tx_data
 		}
 	}
 
-	base = tdata->msg->type == PJSIP_REQUEST_MSG ? session->saved_from_hdr :
-		PJSIP_MSG_TO_HDR(tdata->msg);
+	if (tdata->msg->type == PJSIP_REQUEST_MSG) {
+		base = session->saved_from_hdr ? session->saved_from_hdr : PJSIP_MSG_FROM_HDR(tdata->msg);
+	} else {
+		base = PJSIP_MSG_TO_HDR(tdata->msg);
+	}
 
 	pai_hdr = create_new_id_hdr(&pj_pai_name, base, tdata, id);
 	if (!pai_hdr) {
@@ -629,8 +632,11 @@ static void add_rpid_header(const struct ast_sip_session *session, pjsip_tx_data
 		}
 	}
 
-	base = tdata->msg->type == PJSIP_REQUEST_MSG ? session->saved_from_hdr :
-		PJSIP_MSG_TO_HDR(tdata->msg);
+	if (tdata->msg->type == PJSIP_REQUEST_MSG) {
+		base = session->saved_from_hdr ? session->saved_from_hdr : PJSIP_MSG_FROM_HDR(tdata->msg);
+	} else {
+		base = PJSIP_MSG_TO_HDR(tdata->msg);
+	}
 
 	rpid_hdr = create_new_id_hdr(&pj_rpid_name, base, tdata, id);
 	if (!rpid_hdr) {
diff --git a/res/res_pjsip_outbound_authenticator_digest.c b/res/res_pjsip_outbound_authenticator_digest.c
index 8616b6c..d4dfce4 100644
--- a/res/res_pjsip_outbound_authenticator_digest.c
+++ b/res/res_pjsip_outbound_authenticator_digest.c
@@ -106,6 +106,7 @@ static int digest_create_request_with_auth_from_old(const struct ast_sip_auth_ve
 {
 	pjsip_auth_clt_sess auth_sess;
 	pjsip_cseq_hdr *cseq;
+	pj_status_t status;
 
 	if (pjsip_auth_clt_init(&auth_sess, ast_sip_get_pjsip_endpoint(),
 				old_request->pool, 0) != PJ_SUCCESS) {
@@ -115,11 +116,19 @@ static int digest_create_request_with_auth_from_old(const struct ast_sip_auth_ve
 
 	if (set_outbound_authentication_credentials(&auth_sess, auths, challenge)) {
 		ast_log(LOG_WARNING, "Failed to set authentication credentials\n");
+#if defined(HAVE_PJSIP_AUTH_CLT_DEINIT)
+		/* In case it is not a noop here in the future. */
+		pjsip_auth_clt_deinit(&auth_sess);
+#endif
 		return -1;
 	}
 
-	switch (pjsip_auth_clt_reinit_req(&auth_sess, challenge,
-				old_request, new_request)) {
+	status = pjsip_auth_clt_reinit_req(&auth_sess, challenge, old_request, new_request);
+#if defined(HAVE_PJSIP_AUTH_CLT_DEINIT)
+	/* Release any cached auths */
+	pjsip_auth_clt_deinit(&auth_sess);
+#endif
+	switch (status) {
 	case PJ_SUCCESS:
 		/* PJSIP creates a new transaction for new_request (meaning it creates a new
 		 * branch). However, it recycles the Call-ID, from-tag, and CSeq from the
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index 6f17b20..ff66194 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -514,6 +514,7 @@ static pj_status_t registration_client_send(struct sip_outbound_registration_cli
 
 	callback_invoked = ast_threadstorage_get(&register_callback_invoked, sizeof(int));
 	if (!callback_invoked) {
+		pjsip_tx_data_dec_ref(tdata);
 		return PJ_ENOMEM;
 	}
 	*callback_invoked = 0;
@@ -567,6 +568,7 @@ static int handle_client_registration(void *data)
 			/* insert a new Supported header */
 			hdr = pjsip_supported_hdr_create(tdata->pool);
 			if (!hdr) {
+				pjsip_tx_data_dec_ref(tdata);
 				return -1;
 			}
 
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index 2fa7f34..f6b6efc 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -1824,6 +1824,7 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree,
 
 	if (allocate_tdata_buffer(tdata)) {
 		ast_log(LOG_ERROR, "SIP request %s is too large to send.\n", tdata->info);
+		pjsip_tx_data_dec_ref(tdata);
 		return -1;
 	}
 
@@ -2977,7 +2978,6 @@ static struct ast_sip_publication *sip_create_publication(struct ast_sip_endpoin
 static int sip_publication_respond(struct ast_sip_publication *pub, int status_code,
 		pjsip_rx_data *rdata)
 {
-	pj_status_t status;
 	pjsip_tx_data *tdata;
 	pjsip_transaction *tsx;
 
@@ -2986,26 +2986,24 @@ static int sip_publication_respond(struct ast_sip_publication *pub, int status_c
 	}
 
 	if (PJSIP_IS_STATUS_IN_CLASS(status_code, 200)) {
-		RAII_VAR(char *, entity_tag, NULL, ast_free_ptr);
-		RAII_VAR(char *, expires, NULL, ast_free_ptr);
+		char buf[30];
 
-		if ((ast_asprintf(&entity_tag, "%d", pub->entity_tag) < 0) ||
-			(ast_asprintf(&expires, "%d", pub->expires) < 0)) {
-			pjsip_tx_data_dec_ref(tdata);
-			return -1;
-		}
+		snprintf(buf, sizeof(buf), "%d", pub->entity_tag);
+		ast_sip_add_header(tdata, "SIP-ETag", buf);
 
-		ast_sip_add_header(tdata, "SIP-ETag", entity_tag);
-		ast_sip_add_header(tdata, "Expires", expires);
+		snprintf(buf, sizeof(buf), "%d", pub->expires);
+		ast_sip_add_header(tdata, "Expires", buf);
 	}
 
-	if ((status = pjsip_tsx_create_uas(&pubsub_module, rdata, &tsx)) != PJ_SUCCESS) {
+	if (pjsip_tsx_create_uas(&pubsub_module, rdata, &tsx) != PJ_SUCCESS) {
+		pjsip_tx_data_dec_ref(tdata);
 		return -1;
 	}
 
 	pjsip_tsx_recv_msg(tsx, rdata);
 
 	if (pjsip_tsx_send_msg(tsx, tdata) != PJ_SUCCESS) {
+		pjsip_tx_data_dec_ref(tdata);
 		return -1;
 	}
 
diff --git a/res/res_pjsip_registrar_expire.c b/res/res_pjsip_registrar_expire.c
index aeefc3f..e802733 100644
--- a/res/res_pjsip_registrar_expire.c
+++ b/res/res_pjsip_registrar_expire.c
@@ -82,7 +82,7 @@ static void *check_expiration_thread(void *data)
 
 		ast_variables_destroy(var);
 		if (contacts) {
-			ast_debug(3, "Expiring %d contacts\n\n", ao2_container_count(contacts));
+			ast_debug(3, "Expiring %d contacts\n", ao2_container_count(contacts));
 			ao2_callback(contacts, OBJ_NODATA, expire_contact, NULL);
 			ao2_ref(contacts, -1);
 		}
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index 029eb5d..66550a2 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -58,11 +58,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 /*! \brief Scheduler for RTCP purposes */
 static struct ast_sched_context *sched;
 
-/*! \brief Address for IPv4 RTP */
-static struct ast_sockaddr address_ipv4;
-
-/*! \brief Address for IPv6 RTP */
-static struct ast_sockaddr address_ipv6;
+/*! \brief Address for RTP */
+static struct ast_sockaddr address_rtp;
 
 static const char STR_AUDIO[] = "audio";
 static const int FD_AUDIO = 0;
@@ -172,11 +169,11 @@ static int rtp_check_timeout(const void *data)
 }
 
 /*! \brief Internal function which creates an RTP instance */
-static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_media *session_media, unsigned int ipv6)
+static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_media *session_media)
 {
 	struct ast_rtp_engine_ice *ice;
 	struct ast_sockaddr temp_media_address;
-	struct ast_sockaddr *media_address =  ipv6 ? &address_ipv6 : &address_ipv4;
+	struct ast_sockaddr *media_address =  &address_rtp;
 
 	if (session->endpoint->media.bind_rtp_to_media_address && !ast_strlen_zero(session->endpoint->media.address)) {
 		ast_sockaddr_parse(&temp_media_address, session->endpoint->media.address, 0);
@@ -373,6 +370,11 @@ static int set_caps(struct ast_sip_session *session, struct ast_sip_session_medi
 				session->dsp = NULL;
 			}
 		}
+
+		if (ast_channel_is_bridged(session->channel)) {
+			ast_channel_set_unbridged_nolock(session->channel, 1);
+		}
+
 		ast_channel_unlock(session->channel);
 	}
 
@@ -888,15 +890,17 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
 	}
 
 	/* Using the connection information create an appropriate RTP instance */
-	if (!session_media->rtp && create_rtp(session, session_media, ast_sockaddr_is_ipv6(addrs))) {
+	if (!session_media->rtp && create_rtp(session, session_media)) {
 		return -1;
 	}
 
 	res = setup_media_encryption(session, session_media, sdp, stream);
 	if (res) {
-		if (!session->endpoint->media.rtp.encryption_optimistic) {
+		if (!session->endpoint->media.rtp.encryption_optimistic ||
+			!pj_strncmp2(&stream->desc.transport, "RTP/SAVP", 8)) {
 			/* If optimistic encryption is disabled and crypto should have been enabled
-			 * but was not this session must fail.
+			 * but was not this session must fail. This must also fail if crypto was
+			 * required in the offer but could not be set up.
 			 */
 			return -1;
 		}
@@ -1056,7 +1060,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
 	    (!use_override_prefs && !ast_format_cap_has_type(session->endpoint->media.codecs, media_type))) {
 		/* If no type formats are configured don't add a stream */
 		return 0;
-	} else if (!session_media->rtp && create_rtp(session, session_media, session->endpoint->media.rtp.ipv6)) {
+	} else if (!session_media->rtp && create_rtp(session, session_media)) {
 		return -1;
 	}
 
@@ -1097,8 +1101,19 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
 	}
 
 	media->conn->net_type = STR_IN;
-	media->conn->addr_type = session->endpoint->media.rtp.ipv6 ? STR_IP6 : STR_IP4;
+	/* Assume that the connection will use IPv4 until proven otherwise */
+	media->conn->addr_type = STR_IP4;
 	pj_strdup2(pool, &media->conn->addr, hostip);
+
+	if (!ast_strlen_zero(session->endpoint->media.address)) {
+		pj_sockaddr ip;
+
+		if ((pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &media->conn->addr, &ip) == PJ_SUCCESS) &&
+			(ip.addr.sa_family == pj_AF_INET6())) {
+			media->conn->addr_type = STR_IP6;
+		}
+	}
+
 	ast_rtp_instance_get_local_address(session_media->rtp, &addr);
 	media->desc.port = direct_media_enabled ? ast_sockaddr_port(&session_media->direct_media_addr) : (pj_uint16_t) ast_sockaddr_port(&addr);
 	media->desc.port_count = 1;
@@ -1149,10 +1164,14 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
 			max_packet_size = ast_format_get_maximum_ms(format);
 		}
 		ao2_ref(format, -1);
+
+		if (media->desc.fmt_count == PJMEDIA_MAX_SDP_FMT) {
+			break;
+		}
 	}
 
 	/* Add non-codec formats */
-	if (media_type != AST_MEDIA_TYPE_VIDEO) {
+	if (media_type != AST_MEDIA_TYPE_VIDEO && media->desc.fmt_count < PJMEDIA_MAX_SDP_FMT) {
 		for (index = 1LL; index <= AST_RTP_MAX; index <<= 1) {
 			if (!(noncodec & index)) {
 				continue;
@@ -1174,6 +1193,10 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
 				attr = pjmedia_sdp_attr_create(pool, "fmtp", pj_cstr(&stmp, tmp));
 				media->attr[media->attr_count++] = attr;
 			}
+
+			if (media->desc.fmt_count == PJMEDIA_MAX_SDP_FMT) {
+				break;
+			}
 		}
 	}
 
@@ -1234,7 +1257,7 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
 	}
 
 	/* Create an RTP instance if need be */
-	if (!session_media->rtp && create_rtp(session, session_media, session->endpoint->media.rtp.ipv6)) {
+	if (!session_media->rtp && create_rtp(session, session_media)) {
 		return -1;
 	}
 
@@ -1470,8 +1493,7 @@ static int load_module(void)
 {
 	CHECK_PJSIP_SESSION_MODULE_LOADED();
 
-	ast_sockaddr_parse(&address_ipv4, "0.0.0.0", 0);
-	ast_sockaddr_parse(&address_ipv6, "::", 0);
+	ast_sockaddr_parse(&address_rtp, "::", 0);
 
 	if (!(sched = ast_sched_context_create())) {
 		ast_log(LOG_ERROR, "Unable to create scheduler context.\n");
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 8b8e9d1..9e363a1 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -61,7 +61,6 @@ static int handle_incoming(struct ast_sip_session *session, pjsip_rx_data *rdata
 		enum ast_sip_session_response_priority response_priority);
 static void handle_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata);
 static void handle_outgoing_response(struct ast_sip_session *session, pjsip_tx_data *tdata);
-static void handle_outgoing(struct ast_sip_session *session, pjsip_tx_data *tdata);
 
 /*! \brief NAT hook for modifying outgoing messages with SDP */
 static struct ast_sip_nat_hook *nat_hook;
@@ -2501,17 +2500,6 @@ static void handle_outgoing_response(struct ast_sip_session *session, pjsip_tx_d
 	}
 }
 
-static void handle_outgoing(struct ast_sip_session *session, pjsip_tx_data *tdata)
-{
-	ast_debug(3, "Sending %s\n", tdata->msg->type == PJSIP_REQUEST_MSG ?
-			"request" : "response");
-	if (tdata->msg->type == PJSIP_REQUEST_MSG) {
-		handle_outgoing_request(session, tdata);
-	} else {
-		handle_outgoing_response(session, tdata);
-	}
-}
-
 static int session_end(void *vsession)
 {
 	struct ast_sip_session *session = vsession;
@@ -2599,7 +2587,6 @@ static void session_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
 
 	switch(type) {
 	case PJSIP_EVENT_TX_MSG:
-		handle_outgoing(session, e->body.tx_msg.tdata);
 		break;
 	case PJSIP_EVENT_RX_MSG:
 		handle_incoming_before_media(inv, session, e->body.rx_msg.rdata);
@@ -2609,7 +2596,6 @@ static void session_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
 		/* Transaction state changes are prompted by some other underlying event. */
 		switch(e->body.tsx_state.type) {
 		case PJSIP_EVENT_TX_MSG:
-			handle_outgoing(session, e->body.tsx_state.src.tdata);
 			break;
 		case PJSIP_EVENT_RX_MSG:
 			handle_incoming_before_media(inv, session, e->body.tsx_state.src.rdata);
@@ -2670,7 +2656,6 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
 	}
 	switch (e->body.tsx_state.type) {
 	case PJSIP_EVENT_TX_MSG:
-		handle_outgoing(session, e->body.tsx_state.src.tdata);
 		/* When we create an outgoing request, we do not have access to the transaction that
 		 * is created. Instead, We have to place transaction-specific data in the tdata. Here,
 		 * we transfer the data into the transaction. This way, when we receive a response, we
diff --git a/res/res_pjsip_t38.c b/res/res_pjsip_t38.c
index 01bfefd..cf12111 100644
--- a/res/res_pjsip_t38.c
+++ b/res/res_pjsip_t38.c
@@ -51,11 +51,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 /*! \brief The number of seconds after receiving a T.38 re-invite before automatically rejecting it */
 #define T38_AUTOMATIC_REJECTION_SECONDS 5
 
-/*! \brief Address for IPv4 UDPTL */
-static struct ast_sockaddr address_ipv4;
-
-/*! \brief Address for IPv6 UDPTL */
-static struct ast_sockaddr address_ipv6;
+/*! \brief Address for UDPTL */
+static struct ast_sockaddr address;
 
 /*! \brief T.38 state information */
 struct t38_state {
@@ -259,8 +256,7 @@ static int t38_initialize_session(struct ast_sip_session *session, struct ast_si
 		return 0;
 	}
 
-	if (!(session_media->udptl = ast_udptl_new_with_bindaddr(NULL, NULL, 0,
-		session->endpoint->media.t38.ipv6 ? &address_ipv6 : &address_ipv4))) {
+	if (!(session_media->udptl = ast_udptl_new_with_bindaddr(NULL, NULL, 0, &address))) {
 		return -1;
 	}
 
@@ -922,8 +918,7 @@ static int load_module(void)
 {
 	CHECK_PJSIP_SESSION_MODULE_LOADED();
 
-	ast_sockaddr_parse(&address_ipv4, "0.0.0.0", 0);
-	ast_sockaddr_parse(&address_ipv6, "::", 0);
+	ast_sockaddr_parse(&address, "::", 0);
 
 	if (ast_sip_session_register_supplement(&t38_supplement)) {
 		ast_log(LOG_ERROR, "Unable to register T.38 session supplement\n");
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index 95fe6d3..75bace2 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -51,6 +51,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include <pjlib.h>
 #include <pjlib-util.h>
 #include <pjnath.h>
+#include <ifaddrs.h>
 #endif
 
 #include "asterisk/stun.h"
@@ -145,6 +146,9 @@ static pj_str_t turnaddr;
 static int turnport = DEFAULT_TURN_PORT;
 static pj_str_t turnusername;
 static pj_str_t turnpassword;
+static struct ast_ha *ice_blacklist = NULL;    /*!< Blacklisted ICE networks */
+static ast_rwlock_t ice_blacklist_lock = AST_RWLOCK_INIT_VALUE;
+
 
 /*! \brief Pool factory used by pjlib to allocate memory. */
 static pj_caching_pool cachingpool;
@@ -1363,7 +1367,7 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
 		return 0;
 	}
 
-#if OPENSSL_VERSION_NUMBER < 0x10002000L
+#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)
 	rtp->ssl_ctx = SSL_CTX_new(DTLSv1_method());
 #else
 	rtp->ssl_ctx = SSL_CTX_new(DTLS_method());
@@ -2446,11 +2450,38 @@ static int rtp_learning_rtp_seq_update(struct rtp_learning_info *info, uint16_t
 }
 
 #ifdef HAVE_PJPROJECT
+/*!
+ * \internal
+ * \brief Checks an address against the ICE blacklist
+ * \note If there is no ice_blacklist list, always returns 0
+ *
+ * \param address The address to consider
+ * \retval 0 if address is not ICE blacklisted
+ * \retval 1 if address is ICE blacklisted
+ */
+static int rtp_address_is_ice_blacklisted(const pj_sockaddr_t *address)
+{
+	char buf[PJ_INET6_ADDRSTRLEN];
+	struct ast_sockaddr saddr;
+	int result = 1;
+
+	ast_sockaddr_parse(&saddr, pj_sockaddr_print(address, buf, sizeof(buf), 0), 0);
+
+	ast_rwlock_rdlock(&ice_blacklist_lock);
+	if (!ice_blacklist || (ast_apply_ha(ice_blacklist, &saddr) == AST_SENSE_ALLOW)) {
+		result = 0;
+	}
+	ast_rwlock_unlock(&ice_blacklist_lock);
+
+	return result;
+}
+
 static void rtp_add_candidates_to_ice(struct ast_rtp_instance *instance, struct ast_rtp *rtp, struct ast_sockaddr *addr, int port, int component,
 				      int transport)
 {
 	pj_sockaddr address[16];
 	unsigned int count = PJ_ARRAY_SIZE(address), pos = 0;
+	int basepos = -1;
 
 	/* Add all the local interface IP addresses */
 	if (ast_sockaddr_is_ipv4(addr)) {
@@ -2464,9 +2495,18 @@ static void rtp_add_candidates_to_ice(struct ast_rtp_instance *instance, struct
 	host_candidate_overrides_apply(count, address);
 
 	for (pos = 0; pos < count; pos++) {
-		pj_sockaddr_set_port(&address[pos], port);
-		ast_rtp_ice_add_cand(rtp, component, transport, PJ_ICE_CAND_TYPE_HOST, 65535, &address[pos], &address[pos], NULL,
+		if (!rtp_address_is_ice_blacklisted(&address[pos])) {
+			if (basepos == -1) {
+				basepos = pos;
+			}
+			pj_sockaddr_set_port(&address[pos], port);
+			ast_rtp_ice_add_cand(rtp, component, transport, PJ_ICE_CAND_TYPE_HOST, 65535, &address[pos], &address[pos], NULL,
 				     pj_sockaddr_get_len(&address[pos]));
+		}
+	}
+	if (basepos == -1) {
+		/* start with first address unless excluded above */
+		basepos = 0;
 	}
 
 	/* If configured to use a STUN server to get our external mapped address do so */
@@ -2475,15 +2515,27 @@ static void rtp_add_candidates_to_ice(struct ast_rtp_instance *instance, struct
 
 		if (!ast_stun_request(component == AST_RTP_ICE_COMPONENT_RTCP ? rtp->rtcp->s : rtp->s, &stunaddr, NULL, &answer)) {
 			pj_sockaddr base;
+			pj_sockaddr ext;
 			pj_str_t mapped = pj_str(ast_strdupa(ast_inet_ntoa(answer.sin_addr)));
+			int srflx = 1;
 
 			/* Use the first local host candidate as the base */
-			pj_sockaddr_cp(&base, &address[0]);
+			pj_sockaddr_cp(&base, &address[basepos]);
 
-			pj_sockaddr_init(pj_AF_INET(), &address[0], &mapped, ntohs(answer.sin_port));
+			pj_sockaddr_init(pj_AF_INET(), &ext, &mapped, ntohs(answer.sin_port));
 
-			ast_rtp_ice_add_cand(rtp, component, transport, PJ_ICE_CAND_TYPE_SRFLX, 65535, &address[0], &base,
-					     &base, pj_sockaddr_get_len(&address[0]));
+			/* If the returned address is the same as one of our host candidates, don't send the srflx */
+			for (pos = 0; pos < count; pos++) {
+				if ((pj_sockaddr_cmp(&address[pos], &ext) == 0) && !rtp_address_is_ice_blacklisted(&address[pos])) {
+					srflx = 0;
+					break;
+				}
+			}
+
+			if (srflx) {
+				ast_rtp_ice_add_cand(rtp, component, transport, PJ_ICE_CAND_TYPE_SRFLX, 65535, &ext, &base,
+							 &base, pj_sockaddr_get_len(&ext));
+			}
 		}
 	}
 
@@ -4920,6 +4972,15 @@ static int ast_rtp_fd(struct ast_rtp_instance *instance, int rtcp)
 static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct ast_sockaddr *addr)
 {
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+	struct ast_sockaddr local, us;
+
+	if (!ast_sockaddr_isnull(addr)) {
+		/* Update the local RTP address with what is being used */
+		ast_ouraddrfor(addr, &us);
+		ast_rtp_instance_get_local_address(instance, &local);
+		ast_sockaddr_set_port(&us, ast_sockaddr_port(&local));
+		ast_rtp_instance_set_local_address(instance, &us);
+	}
 
 	if (rtp->rtcp) {
 		ast_debug(1, "Setting RTCP address on RTP instance '%p'\n", instance);
@@ -4928,6 +4989,15 @@ static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct
 			ast_sockaddr_set_port(&rtp->rtcp->them,
 					      ast_sockaddr_port(addr) + 1);
 		}
+
+		if (!ast_sockaddr_isnull(addr)) {
+			/* Update the local RTCP address with what is being used */
+			ast_sockaddr_set_port(&us, ast_sockaddr_port(&local) + 1);
+			ast_sockaddr_copy(&rtp->rtcp->us, &us);
+
+			ast_free(rtp->rtcp->local_addr_str);
+			rtp->rtcp->local_addr_str = ast_strdup(ast_sockaddr_stringify(&us));
+		}
 	}
 
 	rtp->rxseqno = 0;
@@ -5394,6 +5464,10 @@ static int rtp_reload(int reload)
 	turnusername = pj_str(NULL);
 	turnpassword = pj_str(NULL);
 	host_candidate_overrides_clear();
+	ast_rwlock_wrlock(&ice_blacklist_lock);
+	ast_free_ha(ice_blacklist);
+	ice_blacklist = NULL;
+	ast_rwlock_unlock(&ice_blacklist_lock);
 #endif
 
 	if (cfg) {
@@ -5503,6 +5577,25 @@ static int rtp_reload(int reload)
 			AST_RWLIST_INSERT_TAIL(&host_candidates, candidate, next);
 		}
 		AST_RWLIST_UNLOCK(&host_candidates);
+
+		/* Read ICE blacklist configuration lines */
+		ast_rwlock_wrlock(&ice_blacklist_lock);
+		for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
+			if (!strcasecmp(var->name, "ice_blacklist")) {
+				struct ast_ha *na;
+				int ha_error = 0;
+				if (!(na = ast_append_ha("d", var->value, ice_blacklist, &ha_error))) {
+					ast_log(LOG_WARNING, "Invalid ice_blacklist value: %s\n", var->value);
+				} else {
+					ice_blacklist = na;
+				}
+				if (ha_error) {
+					ast_log(LOG_ERROR, "Bad ice_blacklist configuration value line %d : %s\n", var->lineno, var->value);
+				}
+			}
+		}
+		ast_rwlock_unlock(&ice_blacklist_lock);
+
 #endif
 		ast_config_destroy(cfg);
 	}
diff --git a/res/res_stasis.c b/res/res_stasis.c
index 11aeb43..72b536b 100644
--- a/res/res_stasis.c
+++ b/res/res_stasis.c
@@ -67,6 +67,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "stasis/app.h"
 #include "stasis/control.h"
 #include "stasis/messaging.h"
+#include "stasis/cli.h"
 #include "stasis/stasis_bridge.h"
 #include "asterisk/core_unreal.h"
 #include "asterisk/musiconhold.h"
@@ -797,6 +798,7 @@ struct ast_bridge *stasis_app_bridge_create(const char *type, const char *name,
 
 	bridge = bridge_stasis_new(capabilities, flags, name, id);
 	if (bridge) {
+		ast_bridge_set_talker_src_video_mode(bridge);
 		if (!ao2_link(app_bridges, bridge)) {
 			ast_bridge_destroy(bridge, 0);
 			bridge = NULL;
@@ -1314,7 +1316,9 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
 		bridge = ao2_bump(stasis_app_get_bridge(control));
 
 		if (bridge != last_bridge) {
-			app_unsubscribe_bridge(app, last_bridge);
+			if (last_bridge) {
+				app_unsubscribe_bridge(app, last_bridge);
+			}
 			if (bridge) {
 				app_subscribe_bridge(app, bridge);
 			}
@@ -1375,7 +1379,9 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
 		ast_bridge_depart(chan);
 	}
 
-	app_unsubscribe_bridge(app, stasis_app_get_bridge(control));
+	if (stasis_app_get_bridge(control)) {
+		app_unsubscribe_bridge(app, stasis_app_get_bridge(control));
+	}
 	ao2_cleanup(bridge);
 
 	/* Only publish a stasis_end event if it hasn't already been published */
@@ -1475,6 +1481,11 @@ static struct stasis_app *find_app_by_name(const char *app_name)
 	return res;
 }
 
+struct stasis_app *stasis_app_get_by_name(const char *name)
+{
+	return find_app_by_name(name);
+}
+
 static int append_name(void *obj, void *arg, int flags)
 {
 	struct stasis_app *app = obj;
@@ -1947,6 +1958,8 @@ static int unload_module(void)
 {
 	stasis_app_unregister_event_sources();
 
+	cli_cleanup();
+
 	messaging_cleanup();
 
 	cleanup();
@@ -2104,6 +2117,11 @@ static int load_module(void)
 		return AST_MODULE_LOAD_FAILURE;
 	}
 
+	if (cli_init()) {
+		unload_module();
+		return AST_MODULE_LOAD_FAILURE;
+	}
+
 	bridge_stasis_init();
 
 	stasis_app_register_event_sources();
diff --git a/res/stasis/app.c b/res/stasis/app.c
index 957ed7f..0bef0ee 100644
--- a/res/stasis/app.c
+++ b/res/stasis/app.c
@@ -32,6 +32,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "messaging.h"
 
 #include "asterisk/callerid.h"
+#include "asterisk/cli.h"
 #include "asterisk/stasis_app.h"
 #include "asterisk/stasis_bridges.h"
 #include "asterisk/stasis_channels.h"
@@ -61,6 +62,8 @@ struct stasis_app {
 	void *data;
 	/*! Subscription model for the application */
 	enum stasis_app_subscription_model subscription_model;
+	/*! Whether or not someone wants to see debug messages about this app */
+	int debug;
 	/*! Name of the Stasis application */
 	char name[];
 };
@@ -698,6 +701,13 @@ static void sub_bridge_update_handler(void *data,
 		json = simple_bridge_event("BridgeDestroyed", old_snapshot, tv);
 	} else if (!old_snapshot) {
 		json = simple_bridge_event("BridgeCreated", new_snapshot, tv);
+	} else if (new_snapshot && old_snapshot
+		&& strcmp(new_snapshot->video_source_id, old_snapshot->video_source_id)) {
+		json = simple_bridge_event("BridgeVideoSourceChanged", new_snapshot, tv);
+		if (json && !ast_strlen_zero(old_snapshot->video_source_id)) {
+			ast_json_object_set(json, "old_video_source_id",
+				ast_json_string_create(old_snapshot->video_source_id));
+		}
 	}
 
 	if (json) {
@@ -833,6 +843,18 @@ static void bridge_default_handler(void *data, struct stasis_subscription *sub,
 	}
 }
 
+void app_set_debug(struct stasis_app *app, int debug)
+{
+	if (!app) {
+		return;
+	}
+
+	{
+		SCOPED_AO2LOCK(lock, app);
+		app->debug = debug;
+	}
+}
+
 struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data, enum stasis_app_subscription_model subscription_model)
 {
 	RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup);
@@ -930,11 +952,20 @@ struct stasis_topic *ast_app_get_topic(struct stasis_app *app)
 void app_send(struct stasis_app *app, struct ast_json *message)
 {
 	stasis_app_cb handler;
+	int debug;
+	char eid[20];
 	RAII_VAR(void *, data, NULL, ao2_cleanup);
 
+	if (ast_json_object_set(message, "asterisk_id", ast_json_string_create(
+			ast_eid_to_str(eid, sizeof(eid), &ast_eid_default)))) {
+		ast_log(AST_LOG_WARNING, "Failed to append EID to outgoing event %s\n",
+			ast_json_string_get(ast_json_object_get(message, "type")));
+	}
+
 	/* Copy off mutable state with lock held */
 	{
 		SCOPED_AO2LOCK(lock, app);
+		debug = app->debug;
 		handler = app->handler;
 		if (app->data) {
 			ao2_ref(app->data, +1);
@@ -943,6 +974,13 @@ void app_send(struct stasis_app *app, struct ast_json *message)
 		/* Name is immutable; no need to copy */
 	}
 
+	if (debug) {
+		char *dump = ast_json_dump_string_format(message, AST_JSON_PRETTY);
+		ast_verb(0, "Dispatching message to Stasis app '%s':\n%s\n",
+			app->name, dump);
+		ast_json_free(dump);
+	}
+
 	if (!handler) {
 		ast_verb(3,
 			"Inactive Stasis app '%s' missed message\n", app->name);
@@ -1020,6 +1058,73 @@ const char *app_name(const struct stasis_app *app)
 	return app->name;
 }
 
+static int forwards_filter_by_type(void *obj, void *arg, int flags)
+{
+	struct app_forwards *forward = obj;
+	enum forward_type *forward_type = arg;
+
+	if (forward->forward_type == *forward_type) {
+		return CMP_MATCH;
+	}
+
+	return 0;
+}
+
+void app_to_cli(const struct stasis_app *app, struct ast_cli_args *a)
+{
+	struct ao2_iterator *channels;
+	struct ao2_iterator *endpoints;
+	struct ao2_iterator *bridges;
+	struct app_forwards *forward;
+	enum forward_type forward_type;
+
+	ast_cli(a->fd, "Name: %s\n"
+		"  Debug: %s\n"
+		"  Subscription Model: %s\n",
+		app->name,
+		app->debug ? "Yes" : "No",
+		app->subscription_model == STASIS_APP_SUBSCRIBE_ALL ?
+			"Global Resource Subscription" :
+			"Application/Explicit Resource Subscription");
+	ast_cli(a->fd, "  Subscriptions: %d\n", ao2_container_count(app->forwards));
+
+	ast_cli(a->fd, "    Channels:\n");
+	forward_type = FORWARD_CHANNEL;
+	channels = ao2_callback(app->forwards, OBJ_MULTIPLE,
+		forwards_filter_by_type, &forward_type);
+	if (channels) {
+		while ((forward = ao2_iterator_next(channels))) {
+			ast_cli(a->fd, "      %s (%d)\n", forward->id, forward->interested);
+			ao2_ref(forward, -1);
+		}
+		ao2_iterator_destroy(channels);
+	}
+
+	ast_cli(a->fd, "    Bridges:\n");
+	forward_type = FORWARD_BRIDGE;
+	bridges = ao2_callback(app->forwards, OBJ_MULTIPLE,
+		forwards_filter_by_type, &forward_type);
+	if (bridges) {
+		while ((forward = ao2_iterator_next(bridges))) {
+			ast_cli(a->fd, "      %s (%d)\n", forward->id, forward->interested);
+			ao2_ref(forward, -1);
+		}
+		ao2_iterator_destroy(bridges);
+	}
+
+	ast_cli(a->fd, "    Endpoints:\n");
+	forward_type = FORWARD_ENDPOINT;
+	endpoints = ao2_callback(app->forwards, OBJ_MULTIPLE,
+		forwards_filter_by_type, &forward_type);
+	if (endpoints) {
+		while ((forward = ao2_iterator_next(endpoints))) {
+			ast_cli(a->fd, "      %s (%d)\n", forward->id, forward->interested);
+			ao2_ref(forward, -1);
+		}
+		ao2_iterator_destroy(endpoints);
+	}
+}
+
 struct ast_json *app_to_json(const struct stasis_app *app)
 {
 	RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
diff --git a/res/stasis/app.h b/res/stasis/app.h
index 2c8db1c..6ed6a29 100644
--- a/res/stasis/app.h
+++ b/res/stasis/app.h
@@ -127,8 +127,26 @@ void app_send(struct stasis_app *app, struct ast_json *message);
 
 struct app_forwards;
 
+/*!
+ * \brief Create a JSON representation of a \c stasis_app
+ *
+ * \param app The application
+ *
+ * \return \c JSON blob on success
+ * \return \c NULL on error
+ */
 struct ast_json *app_to_json(const struct stasis_app *app);
 
+struct ast_cli_args;
+
+/*!
+ * \brief Dump properties of a \c stasis_app to the CLI
+ *
+ * \param app The application
+ * \param a The CLI arguments
+ */
+void app_to_cli(const struct stasis_app *app, struct ast_cli_args *a);
+
 /*!
  * \brief Subscribes an application to a channel.
  *
@@ -282,4 +300,12 @@ char *app_get_replace_channel_app(struct ast_channel *chan);
  */
 int app_send_end_msg(struct stasis_app *app, struct ast_channel *chan);
 
+/*!
+ * \brief Enable/disable debugging on an application
+ *
+ * \param app The app to debug
+ * \param debug If non-zero, enable debugging. If zero, disable.
+ */
+void app_set_debug(struct stasis_app *app, int debug);
+
 #endif /* _ASTERISK_RES_STASIS_APP_H */
diff --git a/res/stasis/cli.c b/res/stasis/cli.c
new file mode 100644
index 0000000..f1dee55
--- /dev/null
+++ b/res/stasis/cli.c
@@ -0,0 +1,216 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2016, Digium, Inc.
+ *
+ * Matt Jordan <mjordan at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Stasis CLI commands.
+ *
+ * \author Matt Jordan <mjordan at digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/cli.h"
+#include "asterisk/astobj2.h"
+
+#include "cli.h"
+#include "app.h"
+
+
+static char *ari_show_apps(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct ao2_container *apps;
+	struct ao2_iterator it_apps;
+	char *app;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "ari show apps";
+		e->usage =
+			"Usage: ari show apps\n"
+			"       Lists all registered applications.\n"
+			;
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	default:
+		break;
+	}
+
+	if (a->argc != 3) {
+		return CLI_SHOWUSAGE;
+	}
+
+	apps = stasis_app_get_all();
+	if (!apps) {
+		ast_cli(a->fd, "Unable to retrieve registered applications!\n");
+		return CLI_FAILURE;
+	}
+
+	ast_cli(a->fd, "Application Name         \n");
+	ast_cli(a->fd, "=========================\n");
+	it_apps = ao2_iterator_init(apps, 0);
+	while ((app = ao2_iterator_next(&it_apps))) {
+		ast_cli(a->fd, "%-25.25s\n", app);
+		ao2_ref(app, -1);
+	}
+
+	ao2_iterator_destroy(&it_apps);
+	ao2_ref(apps, -1);
+
+	return CLI_SUCCESS;
+}
+
+struct app_complete {
+	/*! Nth app to search for */
+	int state;
+	/*! Which app currently on */
+	int which;
+};
+
+static int complete_ari_app_search(void *obj, void *arg, void *data, int flags)
+{
+	struct app_complete *search = data;
+
+	if (++search->which > search->state) {
+		return CMP_MATCH;
+	}
+	return 0;
+}
+
+static char *complete_ari_app(struct ast_cli_args *a)
+{
+	RAII_VAR(struct ao2_container *, apps, stasis_app_get_all(), ao2_cleanup);
+	RAII_VAR(char *, app, NULL, ao2_cleanup);
+
+	struct app_complete search = {
+		.state = a->n,
+	};
+
+	if (!apps) {
+		ast_cli(a->fd, "Error getting ARI applications\n");
+		return CLI_FAILURE;
+	}
+
+	app = ao2_callback_data(apps,
+		ast_strlen_zero(a->word) ? 0 : OBJ_PARTIAL_KEY,
+		complete_ari_app_search, (char*)a->word, &search);
+
+	return app ? ast_strdup(app) : NULL;
+}
+
+static char *complete_ari_show_app(struct ast_cli_args *a)
+{
+	if (a->pos == 3) {
+		return complete_ari_app(a);
+	}
+
+	return NULL;
+}
+
+static char *ari_show_app(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	void *app;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "ari show app";
+		e->usage =
+			"Usage: ari show app <application>\n"
+			"       Provide detailed information about a registered application.\n"
+			;
+		return NULL;
+	case CLI_GENERATE:
+		return complete_ari_show_app(a);
+	default:
+		break;
+	}
+
+	if (a->argc != 4) {
+		return CLI_SHOWUSAGE;
+	}
+
+	app = stasis_app_get_by_name(a->argv[3]);
+	if (!app) {
+		return CLI_FAILURE;
+	}
+
+	app_to_cli(app, a);
+
+	ao2_ref(app, -1);
+
+	return CLI_SUCCESS;
+}
+
+static char *ari_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	void *app;
+	int debug;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "ari set debug";
+		e->usage =
+			"Usage: ari set debug <application> <on|off>\n"
+			"       Enable or disable debugging on a specific application.\n"
+			;
+		return NULL;
+	case CLI_GENERATE:
+		return complete_ari_show_app(a);
+	default:
+		break;
+	}
+
+	if (a->argc != 5) {
+		return CLI_SHOWUSAGE;
+	}
+
+	app = stasis_app_get_by_name(a->argv[3]);
+	if (!app) {
+		return CLI_FAILURE;
+	}
+
+	debug = !strcmp(a->argv[4], "on");
+	app_set_debug(app, debug);
+	ast_cli(a->fd, "Debugging on '%s' %s\n",
+		app_name(app),
+		debug ? "enabled" : "disabled");
+
+	ao2_ref(app, -1);
+
+	return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_ari[] = {
+	AST_CLI_DEFINE(ari_show_apps, "List registered ARI applications"),
+	AST_CLI_DEFINE(ari_show_app, "Display details of a registered ARI application"),
+	AST_CLI_DEFINE(ari_set_debug, "Enable/disable debugging of an ARI application"),
+};
+
+
+int cli_init(void)
+{
+	return ast_cli_register_multiple(cli_ari, ARRAY_LEN(cli_ari));
+}
+
+void cli_cleanup(void)
+{
+	ast_cli_unregister_multiple(cli_ari, ARRAY_LEN(cli_ari));
+}
diff --git a/res/stasis/cli.h b/res/stasis/cli.h
new file mode 100644
index 0000000..49235c7
--- /dev/null
+++ b/res/stasis/cli.h
@@ -0,0 +1,43 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2016, Digium, Inc.
+ *
+ * Matt Jordan <mjordan at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+#ifndef _ASTERISK_RES_STASIS_CLI_H
+#define _ASTERISK_RES_STASIS_CLI_H
+
+/*! \file
+ *
+ * \brief Internal API for Stasis application CLI commands
+ *
+ * \author Matt Jordan <mjordan at digium.com>
+ * \since 13.13.0
+ */
+
+/*!
+ * \brief Initialize the CLI commands
+ *
+ * \retval 0 on success
+ * \retval non-zero on error
+ */
+int cli_init(void);
+
+/*!
+ * \brief Cleanup the CLI commands
+ */
+void cli_cleanup(void);
+
+#endif /* _ASTERISK_RES_STASIS_CLI_H */
diff --git a/res/stasis_recording/stored.c b/res/stasis_recording/stored.c
index 59c07f8..ab05687 100644
--- a/res/stasis_recording/stored.c
+++ b/res/stasis_recording/stored.c
@@ -31,7 +31,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/paths.h"
 #include "asterisk/stasis_app_recording.h"
 
-#include <dirent.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -122,12 +121,47 @@ static int split_path(const char *path, char **dir, char **file)
 	return 0;
 }
 
-static void safe_closedir(DIR *dirp)
+struct match_recording_data {
+	const char *file;
+	char *file_with_ext;
+};
+
+static int is_recording(const char *filename)
 {
-	if (!dirp) {
-		return;
+	const char *ext = strrchr(filename, '.');
+
+	if (!ext) {
+		/* No file extension; not us */
+		return 0;
+	}
+	++ext;
+
+	if (!ast_get_format_for_file_ext(ext)) {
+		ast_debug(5, "Recording %s: unrecognized format %s\n",
+			filename, ext);
+		/* Keep looking */
+		return 0;
 	}
-	closedir(dirp);
+
+	/* Return the index to the .ext */
+	return ext - filename - 1;
+}
+
+static int handle_find_recording(const char *dir_name, const char *filename, void *obj)
+{
+	struct match_recording_data *data = obj;
+	int num;
+
+	/* If not a recording or the names do not match the keep searching */
+	if (!(num = is_recording(filename)) || strncmp(data->file, filename, num)) {
+		return 0;
+	}
+
+	if (ast_asprintf(&data->file_with_ext, "%s/%s", dir_name, filename)) {
+		return -1;
+	}
+
+	return 1;
 }
 
 /*!
@@ -143,46 +177,15 @@ static void safe_closedir(DIR *dirp)
  */
 static char *find_recording(const char *dir_name, const char *file)
 {
-	RAII_VAR(DIR *, dir, NULL, safe_closedir);
-	struct dirent entry;
-	struct dirent *result = NULL;
-	char *ext = NULL;
-	char *file_with_ext = NULL;
-
-	dir = opendir(dir_name);
-	if (!dir) {
-		return NULL;
-	}
-
-	while (readdir_r(dir, &entry, &result) == 0 && result != NULL) {
-		ext = strrchr(result->d_name, '.');
+	struct match_recording_data data = {
+		.file = file,
+		.file_with_ext = NULL
+	};
 
-		if (!ext) {
-			/* No file extension; not us */
-			continue;
-		}
-		*ext++ = '\0';
-
-		if (strcmp(file, result->d_name) == 0) {
-			if (!ast_get_format_for_file_ext(ext)) {
-				ast_log(LOG_WARNING,
-					"Recording %s: unrecognized format %s\n",
-					result->d_name,
-					ext);
-				/* Keep looking */
-				continue;
-			}
-			/* We have a winner! */
-			break;
-		}
-	}
+	ast_file_read_dir(dir_name, handle_find_recording, &data);
 
-	if (!result) {
-		return NULL;
-	}
-
-	ast_asprintf(&file_with_ext, "%s/%s.%s", dir_name, file, ext);
-	return file_with_ext;
+	/* Note, string potentially allocated in handle_file_recording */
+	return data.file_with_ext;
 }
 
 /*!
@@ -238,43 +241,33 @@ static int recording_sort(const void *obj_left, const void *obj_right, int flags
 	return cmp;
 }
 
-static int scan(struct ao2_container *recordings,
-	const char *base_dir, const char *subdir, struct dirent *entry);
-
-static int scan_file(struct ao2_container *recordings,
-	const char *base_dir, const char *subdir, const char *filename,
-	const char *path)
+static int handle_scan_file(const char *dir_name, const char *filename, void *obj)
 {
-	RAII_VAR(struct stasis_app_stored_recording *, recording, NULL,
-		ao2_cleanup);
-	const char *ext;
-	char *dot;
-
-	ext = strrchr(filename, '.');
+	struct ao2_container *recordings = obj;
+	struct stasis_app_stored_recording *recording;
+	char *dot, *filepath;
 
-	if (!ext) {
-		ast_verb(4, "  Ignore file without extension: %s\n",
-			filename);
-		/* No file extension; not us */
+	/* Skip if it is not a recording */
+	if (!is_recording(filename)) {
 		return 0;
 	}
-	++ext;
 
-	if (!ast_get_format_for_file_ext(ext)) {
-		ast_verb(4, "  Not a media file: %s\n", filename);
-		/* Not a media file */
-		return 0;
+	if (ast_asprintf(&filepath, "%s/%s", dir_name, filename)) {
+		return -1;
 	}
 
 	recording = recording_alloc();
 	if (!recording) {
+		ast_free(filepath);
 		return -1;
 	}
 
-	ast_string_field_set(recording, file_with_ext, path);
-
+	ast_string_field_set(recording, file_with_ext, filepath);
 	/* Build file and format from full path */
-	ast_string_field_set(recording, file, path);
+	ast_string_field_set(recording, file, filepath);
+
+	ast_free(filepath);
+
 	dot = strrchr(recording->file, '.');
 	*dot = '\0';
 	recording->format = dot + 1;
@@ -285,92 +278,14 @@ static int scan_file(struct ao2_container *recordings,
 
 	/* Add it to the recordings container */
 	ao2_link(recordings, recording);
-
-	return 0;
-}
-
-static int scan_dir(struct ao2_container *recordings,
-	const char *base_dir, const char *subdir, const char *dirname,
-	const char *path)
-{
-	RAII_VAR(DIR *, dir, NULL, safe_closedir);
-	RAII_VAR(struct ast_str *, rel_dirname, NULL, ast_free);
-	struct dirent entry;
-	struct dirent *result = NULL;
-
-	if (strcmp(dirname, ".") == 0 ||
-		strcmp(dirname, "..") == 0) {
-		ast_verb(4, "  Ignoring self/parent dir\n");
-		return 0;
-	}
-
-	/* Build relative dirname */
-	rel_dirname = ast_str_create(80);
-	if (!rel_dirname) {
-		return -1;
-	}
-	if (!ast_strlen_zero(subdir)) {
-		ast_str_append(&rel_dirname, 0, "%s/", subdir);
-	}
-	if (!ast_strlen_zero(dirname)) {
-		ast_str_append(&rel_dirname, 0, "%s", dirname);
-	}
-
-	/* Read the directory */
-	dir = opendir(path);
-	if (!dir) {
-		ast_log(LOG_WARNING, "Error reading dir '%s'\n", path);
-		return -1;
-	}
-	while (readdir_r(dir, &entry, &result) == 0 && result != NULL) {
-		scan(recordings, base_dir, ast_str_buffer(rel_dirname), result);
-	}
-
-	return 0;
-}
-
-static int scan(struct ao2_container *recordings,
-	const char *base_dir, const char *subdir, struct dirent *entry)
-{
-	RAII_VAR(struct ast_str *, path, NULL, ast_free);
-
-	path = ast_str_create(255);
-	if (!path) {
-		return -1;
-	}
-
-	/* Build file path */
-	ast_str_append(&path, 0, "%s", base_dir);
-	if (!ast_strlen_zero(subdir)) {
-		ast_str_append(&path, 0, "/%s", subdir);
-	}
-	if (entry) {
-		ast_str_append(&path, 0, "/%s", entry->d_name);
-	}
-	ast_verb(4, "Scanning '%s'\n", ast_str_buffer(path));
-
-	/* Handle this file */
-	switch (entry->d_type) {
-	case DT_REG:
-		scan_file(recordings, base_dir, subdir, entry->d_name,
-			ast_str_buffer(path));
-		break;
-	case DT_DIR:
-		scan_dir(recordings, base_dir, subdir, entry->d_name,
-			ast_str_buffer(path));
-		break;
-	default:
-		ast_log(LOG_WARNING, "Skipping %s: not a regular file\n",
-			ast_str_buffer(path));
-		break;
-	}
+	ao2_ref(recording, -1);
 
 	return 0;
 }
 
 struct ao2_container *stasis_app_stored_recording_find_all(void)
 {
-	RAII_VAR(struct ao2_container *, recordings, NULL, ao2_cleanup);
+	struct ao2_container *recordings;
 	int res;
 
 	recordings = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
@@ -379,13 +294,13 @@ struct ao2_container *stasis_app_stored_recording_find_all(void)
 		return NULL;
 	}
 
-	res = scan_dir(recordings, ast_config_AST_RECORDING_DIR, "", "",
-		ast_config_AST_RECORDING_DIR);
-	if (res != 0) {
+	res = ast_file_read_dirs(ast_config_AST_RECORDING_DIR,
+				 handle_scan_file, recordings, -1);
+	if (res) {
+		ao2_ref(recordings, -1);
 		return NULL;
 	}
 
-	ao2_ref(recordings, +1);
 	return recordings;
 }
 
diff --git a/rest-api/api-docs/applications.json b/rest-api/api-docs/applications.json
index c8660cf..5ed720d 100644
--- a/rest-api/api-docs/applications.json
+++ b/rest-api/api-docs/applications.json
@@ -2,7 +2,7 @@
 	"_copyright": "Copyright (C) 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
 	"_svn_revision": "$Revision$",
-	"apiVersion": "1.9.0",
+	"apiVersion": "1.10.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/applications.{format}",
diff --git a/rest-api/api-docs/asterisk.json b/rest-api/api-docs/asterisk.json
index 9dbf382..9ae965d 100644
--- a/rest-api/api-docs/asterisk.json
+++ b/rest-api/api-docs/asterisk.json
@@ -2,7 +2,7 @@
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
 	"_svn_revision": "$Revision$",
-	"apiVersion": "1.9.0",
+	"apiVersion": "1.10.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/asterisk.{format}",
diff --git a/rest-api/api-docs/bridges.json b/rest-api/api-docs/bridges.json
index 0e726a5..3eefa16 100644
--- a/rest-api/api-docs/bridges.json
+++ b/rest-api/api-docs/bridges.json
@@ -2,7 +2,7 @@
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
 	"_svn_revision": "$Revision$",
-	"apiVersion": "1.9.0",
+	"apiVersion": "1.10.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/bridges.{format}",
@@ -241,6 +241,78 @@
 			]
 		},
 		{
+			"path": "/bridges/{bridgeId}/videoSource/{channelId}",
+			"description": "Set a channel as the video source in a multi-party bridge",
+			"operations": [
+				{
+					"httpMethod": "POST",
+					"summary": "Set a channel as the video source in a multi-party mixing bridge. This operation has no effect on bridges with two or fewer participants.",
+					"nickname": "setVideoSource",
+					"responseClass": "void",
+					"parameters": [
+						{
+							"name": "bridgeId",
+							"description": "Bridge's id",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						},
+						{
+							"name": "channelId",
+							"description": "Channel's id",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 404,
+							"reason": "Bridge or Channel not found"
+						},
+						{
+							"code": 409,
+							"reason": "Channel not in Stasis application"
+						},
+						{
+							"code": 422,
+							"reason": "Channel not in this Bridge"
+						}
+					]
+				}
+			]
+		},
+		{
+			"path": "/bridges/{bridgeId}/videoSource",
+			"description": "Removes any explicit video source",
+			"operations": [
+				{
+					"httpMethod": "DELETE",
+					"summary": "Removes any explicit video source in a multi-party mixing bridge. This operation has no effect on bridges with two or fewer participants. When no explicit video source is set, talk detection will be used to determine the active video stream.",
+					"nickname": "clearVideoSource",
+					"responseClass": "void",
+					"parameters": [
+						{
+							"name": "bridgeId",
+							"description": "Bridge's id",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 404,
+							"reason": "Bridge not found"
+						}
+					]
+				}
+			]
+		},
+		{
 			"path": "/bridges/{bridgeId}/moh",
 			"description": "Play music on hold to a bridge",
 			"operations": [
@@ -649,6 +721,16 @@
 					"type": "List[string]",
 					"description": "Ids of channels participating in this bridge",
 					"required": true
+				},
+				"video_mode": {
+					"type": "string",
+					"description": "The video mode the bridge is using. One of 'none', 'talker', or 'single'.",
+					"required": false
+				},
+				"video_source_id": {
+					"type": "string",
+					"description": "The ID of the channel that is the source of video in this bridge, if one exists.",
+					"required": false
 				}
 			}
 		}
diff --git a/rest-api/api-docs/channels.json b/rest-api/api-docs/channels.json
index 8eaa5eb..487db44 100644
--- a/rest-api/api-docs/channels.json
+++ b/rest-api/api-docs/channels.json
@@ -2,7 +2,7 @@
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
 	"_svn_revision": "$Revision$",
-	"apiVersion": "1.9.0",
+	"apiVersion": "1.10.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/channels.{format}",
@@ -142,6 +142,10 @@
 						{
 							"code": 400,
 							"reason": "Invalid parameters for originating a channel."
+						},
+						{
+							"code": 409,
+							"reason": "Channel with given unique ID already exists."
 						}
 					]
 				}
@@ -298,6 +302,10 @@
 						{
 							"code": 400,
 							"reason": "Invalid parameters for originating a channel."
+						},
+						{
+							"code": 409,
+							"reason": "Channel with given unique ID already exists."
 						}
 					]
 				},
diff --git a/rest-api/api-docs/deviceStates.json b/rest-api/api-docs/deviceStates.json
index a268a18..16d3af7 100644
--- a/rest-api/api-docs/deviceStates.json
+++ b/rest-api/api-docs/deviceStates.json
@@ -2,7 +2,7 @@
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "Kevin Harwell <kharwell at digium.com>",
 	"_svn_revision": "$Revision$",
-	"apiVersion": "1.9.0",
+	"apiVersion": "1.10.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/deviceStates.{format}",
diff --git a/rest-api/api-docs/endpoints.json b/rest-api/api-docs/endpoints.json
index c6a3020..13a5bed 100644
--- a/rest-api/api-docs/endpoints.json
+++ b/rest-api/api-docs/endpoints.json
@@ -2,7 +2,7 @@
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
 	"_svn_revision": "$Revision$",
-	"apiVersion": "1.9.0",
+	"apiVersion": "1.10.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/endpoints.{format}",
diff --git a/rest-api/api-docs/events.json b/rest-api/api-docs/events.json
index 464e06f..2d174cf 100644
--- a/rest-api/api-docs/events.json
+++ b/rest-api/api-docs/events.json
@@ -2,7 +2,7 @@
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
 	"_svn_revision": "$Revision$",
-	"apiVersion": "1.9.0",
+	"apiVersion": "1.10.0",
 	"swaggerVersion": "1.2",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/events.{format}",
@@ -110,6 +110,11 @@
 					"type": "string",
 					"required": true,
 					"description": "Indicates the type of this message."
+				},
+				"asterisk_id": {
+					"type": "string",
+					"required": false,
+					"description": "The unique ID for the Asterisk instance that raised this event."
 				}
 			},
 			"subTypes": [
@@ -156,6 +161,7 @@
 				"BridgeMerged",
 				"BridgeBlindTransfer",
 				"BridgeAttendedTransfer",
+				"BridgeVideoSourceChanged",
 				"ChannelCreated",
 				"ChannelDestroyed",
 				"ChannelEnteredBridge",
@@ -353,6 +359,20 @@
 				}
 			}
 		},
+		"BridgeVideoSourceChanged": {
+			"id": "BridgeVideoSourceChanged",
+			"description": "Notification that the source of video in a bridge has changed.",
+			"properties": {
+				"bridge": {
+					"required": true,
+					"type": "Bridge"
+				},
+				"old_video_source_id": {
+					"required": false,
+					"type": "string"
+				}
+			}
+		},
 		"BridgeBlindTransfer": {
 			"id": "BridgeBlindTransfer",
 			"description": "Notification that a blind transfer has occurred.",
diff --git a/rest-api/api-docs/mailboxes.json b/rest-api/api-docs/mailboxes.json
index 5b290fc..999cd8d 100644
--- a/rest-api/api-docs/mailboxes.json
+++ b/rest-api/api-docs/mailboxes.json
@@ -2,7 +2,7 @@
 	"_copyright": "Copyright (C) 2013, Digium, Inc.",
 	"_author": "Jonathan Rose <jrose at digium.com>",
 	"_svn_revision": "$Revision$",
-	"apiVersion": "1.9.0",
+	"apiVersion": "1.10.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/mailboxes.{format}",
diff --git a/rest-api/api-docs/playbacks.json b/rest-api/api-docs/playbacks.json
index e6cb749..1a12432 100644
--- a/rest-api/api-docs/playbacks.json
+++ b/rest-api/api-docs/playbacks.json
@@ -2,7 +2,7 @@
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
 	"_svn_revision": "$Revision$",
-	"apiVersion": "1.9.0",
+	"apiVersion": "1.10.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/playbacks.{format}",
diff --git a/rest-api/api-docs/recordings.json b/rest-api/api-docs/recordings.json
index bc2b902..48499bf 100644
--- a/rest-api/api-docs/recordings.json
+++ b/rest-api/api-docs/recordings.json
@@ -2,7 +2,7 @@
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
 	"_svn_revision": "$Revision$",
-	"apiVersion": "1.9.0",
+	"apiVersion": "1.10.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/recordings.{format}",
diff --git a/rest-api/api-docs/sounds.json b/rest-api/api-docs/sounds.json
index 70d65a4..4a8eb2c 100644
--- a/rest-api/api-docs/sounds.json
+++ b/rest-api/api-docs/sounds.json
@@ -2,7 +2,7 @@
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
 	"_svn_revision": "$Revision$",
-	"apiVersion": "1.9.0",
+	"apiVersion": "1.10.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/sounds.{format}",
diff --git a/rest-api/resources.json b/rest-api/resources.json
index bacbc78..ecaf723 100644
--- a/rest-api/resources.json
+++ b/rest-api/resources.json
@@ -2,7 +2,7 @@
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
 	"_svn_revision": "$Revision$",
-	"apiVersion": "1.9.0",
+	"apiVersion": "1.10.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"apis": [
diff --git a/tests/test_astobj2_thrash.c b/tests/test_astobj2_thrash.c
index 814234c..324c4f4 100644
--- a/tests/test_astobj2_thrash.c
+++ b/tests/test_astobj2_thrash.c
@@ -46,6 +46,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/utils.h"
 
 #define MAX_HASH_ENTRIES 15000
+/*
+ * Use one of the online calculators to find the first prime number
+ * greater than MAX_HASH_ENTRIES / 100.
+ */
+#define HASH_BUCKETS 151
+
+#define COUNT_SLEEP_US 500
 #define MAX_TEST_SECONDS 60
 
 struct hash_test {
@@ -207,7 +214,7 @@ static void *hash_test_count(void *d)
 
 		if (last_count == count) {
 			/* Allow other threads to run. */
-			sched_yield();
+			usleep(COUNT_SLEEP_US);
 		} else if (last_count > count) {
 			/* Make sure the ao2 container never shrinks */
 			return "ao2 container unexpectedly shrank";
@@ -261,7 +268,7 @@ AST_TEST_DEFINE(hash_test)
 	data.preload = MAX_HASH_ENTRIES / 2;
 	data.max_grow = MAX_HASH_ENTRIES - data.preload;
 	data.deadline = ast_tvadd(ast_tvnow(), ast_tv(MAX_TEST_SECONDS, 0));
-	data.to_be_thrashed = ao2_container_alloc(MAX_HASH_ENTRIES / 100, hash_string,
+	data.to_be_thrashed = ao2_container_alloc(HASH_BUCKETS, hash_string,
 		compare_strings);
 
 	if (data.to_be_thrashed == NULL) {
diff --git a/tests/test_file.c b/tests/test_file.c
new file mode 100644
index 0000000..378cdc0
--- /dev/null
+++ b/tests/test_file.c
@@ -0,0 +1,197 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2016, Digium, Inc.
+ *
+ * Kevin Harwell <kharwell at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*** MODULEINFO
+	<depend>TEST_FRAMEWORK</depend>
+	<support_level>core</support_level>
+ ***/
+
+
+#include "asterisk.h"
+#include <sys/stat.h>
+#include <stdio.h>
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/paths.h"
+#include "asterisk/test.h"
+#include "asterisk/module.h"
+#include "asterisk/strings.h"
+#include "asterisk/vector.h"
+
+#define FOUND -7
+
+AST_VECTOR(_filenames, struct ast_str *);
+
+static void rm_file(struct ast_str *filename)
+{
+	if (unlink(ast_str_buffer(filename))) {
+		ast_log(LOG_ERROR, "Unable to remove file: %s\n", ast_str_buffer(filename));
+	}
+
+	ast_free(filename);
+}
+
+static int test_files_destroy(struct ast_test *test, char *dir_name,
+			      struct _filenames *filenames)
+{
+	int res;
+
+	if (filenames) {
+		AST_VECTOR_CALLBACK_VOID(filenames, rm_file);
+		AST_VECTOR_FREE(filenames);
+	}
+
+	if ((res = rmdir(dir_name)) < 0) {
+		ast_test_status_update(test, "Failed to remove directory: %s\n", dir_name);
+	}
+
+	return res;
+}
+
+static int test_files_create(struct ast_test *test, char *dir_name,
+			     struct _filenames *filenames, int num)
+{
+	int i;
+
+	if (!(mkdtemp(dir_name))) {
+		ast_test_status_update(test, "Failed to create directory: %s\n", dir_name);
+		return -1;
+	}
+
+
+	AST_VECTOR_INIT(filenames, num);
+
+	/*
+	 * Create "num" files under the specified directory
+	 */
+	for (i = 0; i < num; ++i) {
+		int fd;
+		struct ast_str *filename = ast_str_create(32);
+
+		if (!filename) {
+			break;
+		}
+
+		ast_str_set(&filename, 0, "%s/XXXXXX", dir_name);
+
+		fd = mkstemp(ast_str_buffer(filename));
+		if (fd < 0) {
+			ast_test_status_update(test, "Failed to create file: %s\n",
+					       ast_str_buffer(filename));
+			ast_free(filename);
+			break;
+		}
+		close(fd);
+
+		AST_VECTOR_APPEND(filenames, filename);
+	}
+
+	if (i != num) {
+		test_files_destroy(test, dir_name, filenames);
+		return -1;
+	}
+
+	return 0;
+}
+
+static char *test_files_get_one(struct _filenames *filenames, int num)
+{
+	/* Every file is in a directory and contains a '/' so okay to do this */
+	return strrchr(ast_str_buffer(
+		       AST_VECTOR_GET(filenames, ast_random() % (num - 1))), '/') + 1;
+}
+
+static int handle_find_file(const char *dir_name, const char *filename, void *obj)
+{
+	struct stat statbuf;
+	char *full_path = ast_alloca(strlen(dir_name) + strlen(filename) + 2);
+
+	sprintf(full_path, "%s/%s", dir_name, filename);
+
+	errno = 0;
+	if (stat(full_path, &statbuf)) {
+		ast_log(LOG_ERROR, "Error reading path stats - %s: %s\n",
+			full_path, strerror(errno));
+		return 0;
+	}
+	/* obj contains the name of the file we are looking for */
+	return strcmp(obj, filename) ? 0 : FOUND;
+}
+
+AST_TEST_DEFINE(read_dirs_test)
+{
+	char tmp_dir[] = "/tmp/tmpdir.XXXXXX";
+	struct ast_str *tmp_sub_dir;
+	struct _filenames filenames;
+	enum ast_test_result_state res;
+	const int num_files = 10 + (ast_random() % 10); /* 10-19 random files */
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "read_dir_test";
+		info->category = "/main/file/";
+		info->summary = "Read a directory's content";
+		info->description = "Iterate over directories looking for a file.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	/*
+	 * We want to test recursively searching into a subdirectory, so
+	 * create a top level tmp directory where we will start the search.
+	 */
+	if (!(mkdtemp(tmp_dir))) {
+		ast_test_status_update(test, "Failed to create directory: %s\n", tmp_dir);
+		return AST_TEST_FAIL;
+	}
+
+	tmp_sub_dir = ast_str_alloca(32);
+	ast_str_set(&tmp_sub_dir, 0, "%s/XXXXXX", tmp_dir);
+
+	if (test_files_create(test, ast_str_buffer(tmp_sub_dir), &filenames, num_files)) {
+		test_files_destroy(test, tmp_dir, NULL);
+		return AST_TEST_FAIL;
+	}
+
+	res = ast_file_read_dirs(tmp_dir, handle_find_file, test_files_get_one(
+		 &filenames, num_files), 2) == FOUND ? AST_TEST_PASS : AST_TEST_FAIL;
+
+	if (test_files_destroy(test, ast_str_buffer(tmp_sub_dir), &filenames) ||
+	    test_files_destroy(test, tmp_dir, NULL)) {
+		res = AST_TEST_FAIL;
+	}
+
+	return res;
+}
+
+static int unload_module(void)
+{
+	AST_TEST_UNREGISTER(read_dirs_test);
+	return 0;
+}
+
+static int load_module(void)
+{
+	AST_TEST_REGISTER(read_dirs_test);
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "File test module");
diff --git a/tests/test_res_stasis.c b/tests/test_res_stasis.c
index 5865f09..805634f 100644
--- a/tests/test_res_stasis.c
+++ b/tests/test_res_stasis.c
@@ -137,6 +137,7 @@ AST_TEST_DEFINE(app_replaced)
 	RAII_VAR(struct ast_json *, expected_message1, NULL, ast_json_unref);
 	RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
 	RAII_VAR(struct ast_json *, expected_message2, NULL, ast_json_unref);
+	char eid[20];
 	int res;
 
 	switch (cmd) {
@@ -157,9 +158,10 @@ AST_TEST_DEFINE(app_replaced)
 
 	stasis_app_register(app_name, test_handler, app_data1);
 	stasis_app_register(app_name, test_handler, app_data2);
-	expected_message1 = ast_json_pack("[{s: s, s: s}]",
+	expected_message1 = ast_json_pack("[{s: s, s: s, s: s}]",
 		"type", "ApplicationReplaced",
-		"application", app_name);
+		"application", app_name,
+		"asterisk_id", ast_eid_to_str(eid, sizeof(eid), &ast_eid_default));
 	message = ast_json_pack("{ s: o }", "test-message", ast_json_null());
 	expected_message2 = ast_json_pack("[o]", ast_json_ref(message));
 
diff --git a/third-party/pjproject/Makefile b/third-party/pjproject/Makefile
index 6587522..5a4c2d1 100644
--- a/third-party/pjproject/Makefile
+++ b/third-party/pjproject/Makefile
@@ -1,6 +1,7 @@
 .PHONY: _all all _install install clean distclean echo_cflags configure
 
 include ../versions.mak
+export PJDIR := $(shell pwd -P)/source
 
 SPECIAL_TARGETS :=
 
@@ -23,6 +24,8 @@ ifneq ($(wildcard ../../makeopts),)
     include ../../makeopts
 endif
 
+TARGETS = build.mak
+
 ifeq ($(SPECIAL_TARGETS),)
 # Run locally or from $(ASTTOPDIR)/Makefile.  All include files should be present
     ifeq ($(wildcard ../../makeopts),)
@@ -40,6 +43,7 @@ ifeq ($(SPECIAL_TARGETS),)
         install: _install
 
         include source/user.mak
+        include source/version.mak
         include source/build.mak
         CF := $(filter-out -W%,$(CC_CFLAGS))
         CF := $(filter-out -I%,$(CF))
@@ -74,67 +78,78 @@ ECHO_PREFIX := $(ECHO_PREFIX) echo '[pjproject] '
 
 _all: $(TARGETS)
 
-EXTERNALS_CACHE_DIR ?= $(or $(TMPDIR),$(wildcard /tmp),.)
+DOWNLOAD_DIR := $(or $(EXTERNALS_CACHE_DIR),$(TMPDIR),$(wildcard /tmp),.)
 
-$(EXTERNALS_CACHE_DIR)/pjproject-$(PJPROJECT_VERSION).tar.bz2 : ../versions.mak
+$(DOWNLOAD_DIR)/pjproject-$(PJPROJECT_VERSION).tar.bz2: ../versions.mak
 	$(ECHO_PREFIX) Downloading $(PJPROJECT_URL)/$(@F) to $@
 	$(CMD_PREFIX) $(DOWNLOAD_TO_STDOUT) $(PJPROJECT_URL)/$(@F) > $@
 
-source/.unpacked: $(EXTERNALS_CACHE_DIR)/pjproject-$(PJPROJECT_VERSION).tar.bz2
+source/.unpacked: $(DOWNLOAD_DIR)/pjproject-$(PJPROJECT_VERSION).tar.bz2
 	$(ECHO_PREFIX) Unpacking $<
-	- at rm -rf source >/dev/null 2>&1
-	- at mkdir source >/dev/null 2>&1
-	$(CMD_PREFIX) $(TAR) --strip-components=1 -C source -xjf $<
+	- at rm -rf source pjproject-* >/dev/null 2>&1
+	$(CMD_PREFIX) $(TAR) -xjf $<
+	@mv pjproject-$(PJPROJECT_VERSION) source
 	$(ECHO_PREFIX) Applying patches
-	$(CMD_PREFIX) ./apply_patches $(QUIET_CONFIGURE) ./patches ./source
+	$(CMD_PREFIX) ./apply_patches $(QUIET_CONFIGURE) patches source
 	- at touch source/.unpacked
 
-source/user.mak: source/.unpacked ./patches/user.mak
+source/version.mak: source/.unpacked
+
+source/user.mak: source/.unpacked patches/user.mak
 	$(ECHO_PREFIX) Applying user.mak
-	$(CMD_PREFIX) cp -f ./patches/user.mak ./source/
+	$(CMD_PREFIX) cp -f patches/user.mak source/
 
-source/pjlib/include/pj/%.h : ./patches/%.h
+source/pjlib/include/pj/%.h: patches/%.h
 	$(ECHO_PREFIX) Applying custom include file $<
-	$(CMD_PREFIX) cp -f $< ./source/pjlib/include/pj/
+	$(CMD_PREFIX) cp -f $< source/pjlib/include/pj/
+
+.rebuild_needed: $(wildcard ../../makeopts) $(wildcard ../../menuselect.makeopts)
+	$(ECHO_PREFIX) Rebuilding
+	$(CMD_PREFIX) $(MAKE) clean $(REALLY_QUIET)
+	@touch  .rebuild_needed
 
-build.mak: source/.unpacked $(addprefix source/pjlib/include/pj/,$(notdir $(wildcard ./patches/*.h))) source/user.mak Makefile.rules
+source/build.mak: Makefile.rules source/version.mak source/user.mak $(addprefix source/pjlib/include/pj/,$(notdir $(wildcard patches/*.h))) .rebuild_needed
 	$(ECHO_PREFIX) Configuring with $(PJPROJECT_CONFIG_OPTS)
 	$(CMD_PREFIX) (cd source ; ./aconfigure $(QUIET_CONFIGURE) $(PJPROJECT_CONFIG_OPTS))
-	$(SED) -r -e "/prefix|export PJ_SHARED_LIBRARIES|MACHINE_NAME|OS_NAME|HOST_NAME|CC_NAME|CROSS_COMPILE|LINUX_POLL/d" source/build.mak > build.mak
 
-configure: build.mak
+build.mak: source/build.mak
+	$(CMD_PREFIX) $(SED) -r -e "/prefix|export PJ_SHARED_LIBRARIES|MACHINE_NAME|OS_NAME|HOST_NAME|CC_NAME|CROSS_COMPILE|LINUX_POLL/d" source/build.mak > build.mak
 
-echo_cflags: build.mak
+configure: source/build.mak
+
+echo_cflags: source/build.mak
 	@echo $(PJ_CFLAGS)
 
-.rebuild_needed: ../../menuselect.makeopts
-	$(ECHO_PREFIX) Rebuilding
-	$(CMD_PREFIX)$(MAKE) clean $(REALLY_QUIET)
-	@touch  .rebuild_needed
+libpj%.a: source/build.mak
+	$(ECHO_PREFIX) Compiling lib $(@F)
+	$(CMD_PREFIX) $(MAKE) -C $(dir $(shell dirname $@))/build $(@F) $(REALLY_QUIET)
+	- at rm -rf .rebuild_needed
 
-libpj%.a:  .rebuild_needed  build.mak
+# pjsua needs resample and g711 to successfully run the testsuite
+libresample%.a: .rebuild_needed source/build.mak
 	$(ECHO_PREFIX) Compiling lib $(@F)
-	$(CMD_PREFIX)$(MAKE) -C $(dir $(shell dirname $@))/build $(@F) $(REALLY_QUIET)
+	$(CMD_PREFIX) $(MAKE) -C $(dir $(shell dirname $@))/build/resample all $(REALLY_QUIET)
+	- at rm -rf .rebuild_needed
 
 # We need to compile pjlib, then pjlib-util, then the rest
 # so we separate them out and create the dependencies
 PJLIB_LIB_FILES = $(foreach lib,$(PJ_LIB_FILES),$(if $(findstring libpj-,$(lib)),$(lib),))
 PJLIB_UTIL_LIB_FILES = $(foreach lib,$(PJ_LIB_FILES),$(if $(findstring libpjlib-util,$(lib)),$(lib),))
-LIB_FILES = $(filter-out $(PJLIB_LIB_FILES) $(PJLIB_UTIL_LIB_FILES),$(PJ_LIB_FILES))
-ALL_LIB_FILES = $(PJLIB_LIB_FILES) $(PJLIB_UTIL_LIB_FILES) $(LIB_FILES)
+PJSIP_LIB_FILES = $(filter-out $(PJLIB_LIB_FILES) $(PJLIB_UTIL_LIB_FILES) $(APP_THIRD_PARTY_LIB_FILES),$(PJ_LIB_FILES))
+ALL_LIB_FILES = $(PJLIB_LIB_FILES) $(PJLIB_UTIL_LIB_FILES) $(PJSIP_LIB_FILES)
 
 $(PJLIB_UTIL_LIB_FILES): $(PJLIB_LIB_FILES)
-$(LIB_FILES): $(PJLIB_UTIL_LIB_FILES)
+$(PJSIP_LIB_FILES): $(PJLIB_UTIL_LIB_FILES)
 
 pjproject.symbols: $(ALL_LIB_FILES)
 	$(ECHO_PREFIX) Generating symbols
-	$(CMD_PREFIX) $(NM) -Pog $(PJ_LIB_FILES) | $(SED) -n -r -e "s/.+: ([pP][jJ][^ ]+) .+/\1/gp" | sort -u > pjproject.symbols
+	$(CMD_PREFIX) $(NM) -Pog $(ALL_LIB_FILES) | $(SED) -n -r -e "s/.+: ([pP][jJ][^ ]+) .+/\1/gp" | sort -u > pjproject.symbols
 
 source/pjsip-apps/src/asterisk_malloc_debug.c: patches/asterisk_malloc_debug.c
 	$(ECHO_PREFIX) Copying $< to $@
 	$(CMD_PREFIX) cp -f $< $@
 
-source/pjsip-apps/lib/asterisk_malloc_debug.o: source/pjsip-apps/src/asterisk_malloc_debug.c  .rebuild_needed
+source/pjsip-apps/lib/asterisk_malloc_debug.o: source/pjsip-apps/src/asterisk_malloc_debug.c .rebuild_needed
 	$(ECHO_PREFIX) Compiling asterisk debug malloc stubs
 	$(CMD_PREFIX) $(CC) -fPIC  $(PJ_CFLAGS) -c $< -o $@
 
@@ -143,7 +158,7 @@ source/pjsip-apps/lib/libasterisk_malloc_debug.a: source/pjsip-apps/lib/asterisk
 	$(CMD_PREFIX) ar qs $@ $< >/dev/null 2>&1
 
 $(apps): APP = $(filter pj%,$(subst -, ,$(notdir $@)))
-$(apps): pjproject.symbols
+$(apps): pjproject.symbols $(APP_THIRD_PARTY_LIB_FILES)
 	$(ECHO_PREFIX) Compiling $(APP)
 	$(CMD_PREFIX) +$(MAKE) -C source/pjsip-apps/build $(filter pj%,$(subst -, ,$(notdir $@))) $(REALLY_QUIET)
 
@@ -178,9 +193,9 @@ clean:
 	$(ECHO_PREFIX) Cleaning
 	+-$(CMD_PREFIX) test -d source && ($(SUBMAKE) -C source clean || : ;\
 		rm -rf source/pjsip-apps/bin/* || : ;\
-		find source -name *.a -delete ;\
-		find source -name *.o -delete ;\
-		find source -name *.so -delete ; ) || :
+		find source -name *.a | xargs rm -rf  ;\
+		find source -name *.o | xargs rm -rf  ;\
+		find source -name *.so  | xargs rm -rf ; ) || :
 	-$(CMD_PREFIX) rm -rf pjproject.symbols
 
 distclean:
diff --git a/third-party/pjproject/Makefile.rules b/third-party/pjproject/Makefile.rules
index 739193a..ae4b6a5 100644
--- a/third-party/pjproject/Makefile.rules
+++ b/third-party/pjproject/Makefile.rules
@@ -9,7 +9,6 @@ PJPROJECT_CONFIG_OPTS = --prefix=/opt/pjproject \
 	--disable-gsm-codec \
 	--disable-ilbc-codec \
 	--disable-l16-codec \
-	--disable-g711-codec \
 	--disable-g722-codec \
 	--disable-g7221-codec \
 	--disable-opencore-amr \
@@ -23,13 +22,16 @@ PJPROJECT_CONFIG_OPTS = --prefix=/opt/pjproject \
 	--disable-oss \
 	--disable-sdl \
 	--disable-libyuv \
-	--disable-resample \
 	--disable-ffmpeg \
 	--disable-openh264 \
 	--disable-ipp \
 	--without-external-pa \
-	--with-external-srtp
+	--without-external-srtp
+
+ifeq ($(findstring TEST_FRAMEWORK,$(MENUSELECT_CFLAGS)),)
+    PJPROJECT_CONFIG_OPTS += --disable-resample --disable-g711-codec
+endif
 
 ifeq ($(shell uname -s),Linux)
-   PJPROJECT_CONFIG_OPTS +=  --enable-epoll
+    PJPROJECT_CONFIG_OPTS +=  --enable-epoll
 endif
diff --git a/third-party/pjproject/apply_patches b/third-party/pjproject/apply_patches
index c28f403..5f9fde2 100755
--- a/third-party/pjproject/apply_patches
+++ b/third-party/pjproject/apply_patches
@@ -28,10 +28,6 @@ if [ ! "$(ls -A $patchdir/*.patch 2>/dev/null)" ] ; then
 	exit 0
 fi
 
-for patchfile in $patchdir/*.patch ; do
-	${PATCH} -d $sourcedir -p1 -s -r- -f -N --dry-run -i "$patchfile" || (echo "Patchfile $(basename $patchfile) failed to apply" >&2 ; exit 1) || exit 1
-done
-
 for patchfile in "$patchdir"/*.patch ; do
 	[ -z $quiet ] && echo "Applying patch $(basename $patchfile)"
 	${PATCH} -d "$sourcedir" -p1 -s -i "$patchfile" || exit 1
diff --git a/third-party/pjproject/configure.m4 b/third-party/pjproject/configure.m4
index 7b62c0f..7c60c2a 100644
--- a/third-party/pjproject/configure.m4
+++ b/third-party/pjproject/configure.m4
@@ -30,7 +30,7 @@ AC_DEFUN([_PJPROJECT_CONFIGURE],
 	fi
 
 	export TAR PATCH SED NM EXTERNALS_CACHE_DIR DOWNLOAD_TO_STDOUT
-	${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} configure
+	${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} EXTERNALS_CACHE_DIR=${EXTERNALS_CACHE_DIR} configure
 	if test $? -ne 0 ; then
 		AC_MSG_RESULT(failed)
 		AC_MSG_NOTICE(Unable to configure ${PJPROJECT_DIR})
@@ -39,7 +39,7 @@ AC_DEFUN([_PJPROJECT_CONFIGURE],
 
 	AC_MSG_CHECKING(for bundled pjproject)
 
-	PJPROJECT_INCLUDE=$(${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} echo_cflags)
+	PJPROJECT_INCLUDE=$(${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} EXTERNALS_CACHE_DIR=${EXTERNALS_CACHE_DIR} echo_cflags)
 	PJPROJECT_CFLAGS="$PJPROJECT_INCLUDE"
 	PBX_PJPROJECT=1
 
@@ -55,6 +55,7 @@ AC_DEFUN([_PJPROJECT_CONFIGURE],
 	AC_DEFINE([HAVE_PJSIP_TLS_TRANSPORT_PROTO], 1, [Define if your system has PJSIP_TLS_TRANSPORT_PROTO])
 	AC_DEFINE([HAVE_PJSIP_EVSUB_GRP_LOCK], 1, [Define if your system has PJSIP_EVSUB_GRP_LOCK])
 	AC_DEFINE([HAVE_PJSIP_INV_SESSION_REF], 1, [Define if your system has PJSIP_INV_SESSION_REF])
+	AC_DEFINE([HAVE_PJSIP_AUTH_CLT_DEINIT], 1, [Define if your system has pjsip_auth_clt_deinit declared.])
 
 	AC_SUBST([PJPROJECT_BUNDLED])
 	AC_SUBST([PJPROJECT_DIR])
diff --git a/third-party/pjproject/patches/0000-remove-third-party.patch b/third-party/pjproject/patches/0000-remove-third-party.patch
new file mode 100644
index 0000000..2e3bb5f
--- /dev/null
+++ b/third-party/pjproject/patches/0000-remove-third-party.patch
@@ -0,0 +1,142 @@
+diff --git a/build.mak.in b/build.mak.in
+index 802211c..25fd05e 100644
+--- a/build.mak.in
++++ b/build.mak.in
+@@ -1,4 +1,3 @@
+-export PJDIR := @ac_pjdir@
+ include $(PJDIR)/version.mak
+ export PJ_DIR := $(PJDIR)
+ 
+@@ -9,7 +8,7 @@ export HOST_NAME := unix
+ export CC_NAME := gcc
+ export TARGET_NAME := @target@
+ export CROSS_COMPILE := @ac_cross_compile@
+-export LINUX_POLL := @ac_linux_poll@ 
++export LINUX_POLL := @ac_linux_poll@
+ export SHLIB_SUFFIX := @ac_shlib_suffix@
+ 
+ export prefix := @prefix@
+@@ -28,19 +27,6 @@ export APP_THIRD_PARTY_EXT :=
+ export APP_THIRD_PARTY_LIBS :=
+ export APP_THIRD_PARTY_LIB_FILES :=
+ 
+-ifeq (@ac_external_srtp@,1)
+-# External SRTP library
+-APP_THIRD_PARTY_EXT += -lsrtp
+-else
+-APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libsrtp-$(LIB_SUFFIX)
+-ifeq ($(PJ_SHARED_LIBRARIES),)
+-APP_THIRD_PARTY_LIBS += -lsrtp-$(TARGET_NAME)
+-else
+-APP_THIRD_PARTY_LIBS += -lsrtp
+-APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libsrtp.$(SHLIB_SUFFIX).$(PJ_VERSION_MAJOR) $(PJ_DIR)/third_party/lib/libsrtp.$(SHLIB_SUFFIX)
+-endif
+-endif
+-
+ ifeq (@ac_pjmedia_resample@,libresample)
+ APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libresample-$(LIB_SUFFIX)
+ ifeq ($(PJ_SHARED_LIBRARIES),)
+@@ -57,85 +43,6 @@ APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libresample.$(SHLIB_SUFFI
+ endif
+ endif
+ 
+-ifneq (@ac_no_gsm_codec@,1)
+-ifeq (@ac_external_gsm@,1)
+-# External GSM library
+-APP_THIRD_PARTY_EXT += -lgsm
+-else
+-APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libgsmcodec-$(LIB_SUFFIX)
+-ifeq ($(PJ_SHARED_LIBRARIES),)
+-APP_THIRD_PARTY_LIBS += -lgsmcodec-$(TARGET_NAME)
+-else
+-APP_THIRD_PARTY_LIBS += -lgsmcodec
+-APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libgsmcodec.$(SHLIB_SUFFIX).$(PJ_VERSION_MAJOR) $(PJ_DIR)/third_party/lib/libgsmcodec.$(SHLIB_SUFFIX)
+-endif
+-endif
+-endif
+-
+-ifneq (@ac_no_speex_codec@,1)
+-ifeq (@ac_external_speex@,1)
+-APP_THIRD_PARTY_EXT += -lspeex -lspeexdsp
+-else
+-APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libspeex-$(LIB_SUFFIX)
+-ifeq ($(PJ_SHARED_LIBRARIES),)
+-APP_THIRD_PARTY_LIBS += -lspeex-$(TARGET_NAME)
+-else
+-APP_THIRD_PARTY_LIBS += -lspeex
+-APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libspeex.$(SHLIB_SUFFIX).$(PJ_VERSION_MAJOR) $(PJ_DIR)/third_party/lib/libspeex.$(SHLIB_SUFFIX)
+-endif
+-endif
+-endif
+-
+-ifneq (@ac_no_ilbc_codec@,1)
+-APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libilbccodec-$(LIB_SUFFIX)
+-ifeq ($(PJ_SHARED_LIBRARIES),)
+-APP_THIRD_PARTY_LIBS += -lilbccodec-$(TARGET_NAME)
+-else
+-APP_THIRD_PARTY_LIBS += -lilbccodec
+-APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libilbccodec.$(SHLIB_SUFFIX).$(PJ_VERSION_MAJOR) $(PJ_DIR)/third_party/lib/libilbccodec.$(SHLIB_SUFFIX)
+-endif
+-endif
+-
+-ifneq (@ac_no_g7221_codec@,1)
+-APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libg7221codec-$(LIB_SUFFIX)
+-ifeq ($(PJ_SHARED_LIBRARIES),)
+-APP_THIRD_PARTY_LIBS += -lg7221codec-$(TARGET_NAME)
+-else
+-APP_THIRD_PARTY_LIBS += -lg7221codec
+-APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libg7221codec.$(SHLIB_SUFFIX).$(PJ_VERSION_MAJOR) $(PJ_DIR)/third_party/lib/libg7221codec.$(SHLIB_SUFFIX)
+-endif
+-endif
+-
+-ifneq ($(findstring pa, at ac_pjmedia_snd@),)
+-ifeq (@ac_external_pa@,1)
+-# External PA
+-APP_THIRD_PARTY_EXT += -lportaudio
+-else
+-APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libportaudio-$(LIB_SUFFIX)
+-ifeq ($(PJ_SHARED_LIBRARIES),)
+-APP_THIRD_PARTY_LIBS += -lportaudio-$(TARGET_NAME)
+-else
+-APP_THIRD_PARTY_LIBS += -lportaudio
+-APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libportaudio.$(SHLIB_SUFFIX).$(PJ_VERSION_MAJOR) $(PJ_DIR)/third_party/lib/libportaudio.$(SHLIB_SUFFIX)
+-endif
+-endif
+-endif
+-
+-ifneq (@ac_no_yuv@,1)
+-ifeq (@ac_external_yuv@,1)
+-APP_THIRD_PARTY_EXT += -lyuv
+-else
+-APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libyuv-$(LIB_SUFFIX)
+-ifeq ($(PJ_SHARED_LIBRARIES),)
+-APP_THIRD_PARTY_LIBS += -lyuv-$(TARGET_NAME)
+-else
+-APP_THIRD_PARTY_LIBS += -lyuv
+-APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libyuv.$(SHLIB_SUFFIX).$(PJ_VERSION_MAJOR) $(PJ_DIR)/third_party/lib/libyuv.$(SHLIB_SUFFIX)
+-endif
+-endif
+-endif
+-
+-
+ # Additional flags
+ @ac_build_mak_vars@
+ 
+@@ -149,7 +56,7 @@ SDL_CFLAGS = @ac_sdl_cflags@
+ SDL_LDFLAGS = @ac_sdl_ldflags@
+ 
+ # FFMPEG flags
+-FFMPEG_CFLAGS = @ac_ffmpeg_cflags@ 
++FFMPEG_CFLAGS = @ac_ffmpeg_cflags@
+ FFMPEG_LDFLAGS =  @ac_ffmpeg_ldflags@
+ 
+ # Video4Linux2
+@@ -157,7 +64,7 @@ V4L2_CFLAGS = @ac_v4l2_cflags@
+ V4L2_LDFLAGS = @ac_v4l2_ldflags@
+ 
+ # OPENH264 flags
+-OPENH264_CFLAGS = @ac_openh264_cflags@ 
++OPENH264_CFLAGS = @ac_openh264_cflags@
+ OPENH264_LDFLAGS =  @ac_openh264_ldflags@
+ 
+ # QT
diff --git a/third-party/pjproject/patches/0006-r5471-svn-backport-Various-fixes-for-DNS-IPv6.patch b/third-party/pjproject/patches/0006-r5471-svn-backport-Various-fixes-for-DNS-IPv6.patch
new file mode 100644
index 0000000..98c33e5
--- /dev/null
+++ b/third-party/pjproject/patches/0006-r5471-svn-backport-Various-fixes-for-DNS-IPv6.patch
@@ -0,0 +1,134 @@
+From 2ab7a9f67caf73be3f2215473f72882cfaef4972 Mon Sep 17 00:00:00 2001
+From: Richard Mudgett <rmudgett at digium.com>
+Date: Fri, 28 Oct 2016 12:11:30 -0500
+Subject: [PATCH 1/3] r5471 svn backport Various fixes for DNS IPv6
+
+Fixed #1974: Various fixes for DNS IPv6
+---
+ pjlib-util/src/pjlib-util/resolver.c     |   11 +++++------
+ pjlib-util/src/pjlib-util/srv_resolver.c |   17 +++++++++++++++--
+ pjsip/src/pjsip/sip_resolve.c            |   14 +++++++-------
+ 3 files changed, 27 insertions(+), 15 deletions(-)
+
+diff --git a/pjlib-util/src/pjlib-util/resolver.c b/pjlib-util/src/pjlib-util/resolver.c
+index e5e1bed..d24ef9d 100644
+--- a/pjlib-util/src/pjlib-util/resolver.c
++++ b/pjlib-util/src/pjlib-util/resolver.c
+@@ -835,7 +835,7 @@ PJ_DEF(pj_status_t) pj_dns_resolver_start_query( pj_dns_resolver *resolver,
+     pj_time_val now;
+     struct res_key key;
+     struct cached_res *cache;
+-    pj_dns_async_query *q;
++    pj_dns_async_query *q, *p_q = NULL;
+     pj_uint32_t hval;
+     pj_status_t status = PJ_SUCCESS;
+ 
+@@ -849,9 +849,6 @@ PJ_DEF(pj_status_t) pj_dns_resolver_start_query( pj_dns_resolver *resolver,
+     /* Check type */
+     PJ_ASSERT_RETURN(type > 0 && type < 0xFFFF, PJ_EINVAL);
+ 
+-    if (p_query)
+-	*p_query = NULL;
+-
+     /* Build resource key for looking up hash tables */
+     init_res_key(&key, type, name);
+ 
+@@ -970,10 +967,12 @@ PJ_DEF(pj_status_t) pj_dns_resolver_start_query( pj_dns_resolver *resolver,
+     pj_hash_set_np(resolver->hquerybyres, &q->key, sizeof(q->key),
+ 		   0, q->hbufkey, q);
+ 
+-    if (p_query)
+-	*p_query = q;
++    p_q = q;
+ 
+ on_return:
++    if (p_query)
++	*p_query = p_q;
++
+     pj_mutex_unlock(resolver->mutex);
+     return status;
+ }
+diff --git a/pjlib-util/src/pjlib-util/srv_resolver.c b/pjlib-util/src/pjlib-util/srv_resolver.c
+index 02672aa..ff9c979 100644
+--- a/pjlib-util/src/pjlib-util/srv_resolver.c
++++ b/pjlib-util/src/pjlib-util/srv_resolver.c
+@@ -187,9 +187,12 @@ PJ_DEF(pj_status_t) pj_dns_srv_cancel_query(pj_dns_srv_async_query *query,
+ 	    has_pending = PJ_TRUE;
+ 	}
+ 	if (srv->q_aaaa) {
+-	    pj_dns_resolver_cancel_query(srv->q_aaaa, PJ_FALSE);
++	    /* Check if it is a dummy query. */
++	    if (srv->q_aaaa != (pj_dns_async_query*)0x1) {
++		pj_dns_resolver_cancel_query(srv->q_aaaa, PJ_FALSE);
++		has_pending = PJ_TRUE;
++	    }
+ 	    srv->q_aaaa = NULL;
+-	    has_pending = PJ_TRUE;
+ 	}
+     }
+ 
+@@ -485,12 +488,22 @@ static pj_status_t resolve_hostnames(pj_dns_srv_async_query *query_job)
+ 	srv->common.type = PJ_DNS_TYPE_A;
+ 	srv->common_aaaa.type = PJ_DNS_TYPE_AAAA;
+ 	srv->parent = query_job;
++	srv->q_a = NULL;
++	srv->q_aaaa = NULL;
+ 
+ 	status = PJ_SUCCESS;
+ 
+ 	/* Start DNA A record query */
+ 	if ((query_job->option & PJ_DNS_SRV_RESOLVE_AAAA_ONLY) == 0)
+ 	{
++	    if ((query_job->option & PJ_DNS_SRV_RESOLVE_AAAA) != 0) {
++		/* If there will be DNS AAAA query too, let's setup
++		 * a dummy one here, otherwise app callback may be called
++		 * immediately (before DNS AAAA query is sent) when
++		 * DNS A record is available in the cache.
++		 */
++		srv->q_aaaa = (pj_dns_async_query*)0x1;
++	    }
+ 	    status = pj_dns_resolver_start_query(query_job->resolver,
+ 						 &srv->target_name,
+ 						 PJ_DNS_TYPE_A, 0,
+diff --git a/pjsip/src/pjsip/sip_resolve.c b/pjsip/src/pjsip/sip_resolve.c
+index ed326ba..3f3654d 100644
+--- a/pjsip/src/pjsip/sip_resolve.c
++++ b/pjsip/src/pjsip/sip_resolve.c
+@@ -452,7 +452,7 @@ PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver,
+ 	}
+ 
+ 	/* Resolve DNS AAAA record if address family is not fixed to IPv4 */
+-	if (af != pj_AF_INET()) {
++	if (af != pj_AF_INET() && status == PJ_SUCCESS) {
+ 	    status = pj_dns_resolver_start_query(resolver->res, 
+ 						 &query->naptr[0].name,
+ 						 PJ_DNS_TYPE_AAAA, 0, 
+@@ -530,9 +530,9 @@ static void dns_a_callback(void *user_data,
+ 
+ 	    ++srv->count;
+ 	}
+-
+-    } else {
+-
++    }
++    
++    if (status != PJ_SUCCESS) {
+ 	char errmsg[PJ_ERR_MSG_SIZE];
+ 
+ 	/* Log error */
+@@ -593,9 +593,9 @@ static void dns_aaaa_callback(void *user_data,
+ 
+ 	    ++srv->count;
+ 	}
+-
+-    } else {
+-
++    }
++    
++    if (status != PJ_SUCCESS) {
+ 	char errmsg[PJ_ERR_MSG_SIZE];
+ 
+ 	/* Log error */
+-- 
+1.7.9.5
+
diff --git a/third-party/pjproject/patches/0006-r5473-svn-backport-Fix-pending-query.patch b/third-party/pjproject/patches/0006-r5473-svn-backport-Fix-pending-query.patch
new file mode 100644
index 0000000..4d11d57
--- /dev/null
+++ b/third-party/pjproject/patches/0006-r5473-svn-backport-Fix-pending-query.patch
@@ -0,0 +1,28 @@
+From 509d4339747f11cfbde3a0acc447ef5d521eea93 Mon Sep 17 00:00:00 2001
+From: Richard Mudgett <rmudgett at digium.com>
+Date: Fri, 28 Oct 2016 12:12:28 -0500
+Subject: [PATCH 2/3] r5473 svn backport Fix pending query
+
+Re #1974:
+If there is a pending query, set the return value to that query (instead of NULL)
+
+Thanks to Richard Mudgett for the patch.
+---
+ pjlib-util/src/pjlib-util/resolver.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/pjlib-util/src/pjlib-util/resolver.c b/pjlib-util/src/pjlib-util/resolver.c
+index d24ef9d..fe687b7 100644
+--- a/pjlib-util/src/pjlib-util/resolver.c
++++ b/pjlib-util/src/pjlib-util/resolver.c
+@@ -940,6 +940,7 @@ PJ_DEF(pj_status_t) pj_dns_resolver_start_query( pj_dns_resolver *resolver,
+ 	/* Done. This child query will be notified once the "parent"
+ 	 * query completes.
+ 	 */
++	p_q = nq;
+ 	status = PJ_SUCCESS;
+ 	goto on_return;
+     } 
+-- 
+1.7.9.5
+
diff --git a/third-party/pjproject/patches/0006-r5475-svn-backport-Remove-DNS-cache-entry.patch b/third-party/pjproject/patches/0006-r5475-svn-backport-Remove-DNS-cache-entry.patch
new file mode 100644
index 0000000..e378c30
--- /dev/null
+++ b/third-party/pjproject/patches/0006-r5475-svn-backport-Remove-DNS-cache-entry.patch
@@ -0,0 +1,70 @@
+From 46e1cfa18853a38b7fcdebad782710c5db676657 Mon Sep 17 00:00:00 2001
+From: Richard Mudgett <rmudgett at digium.com>
+Date: Fri, 28 Oct 2016 12:15:44 -0500
+Subject: [PATCH 3/3] r5475 svn backport Remove DNS cache entry
+
+Re #1974: Remove DNS cache entry from resolver's hash table when app callback has a reference.
+
+Thanks to Richard Mudgett for the patch.
+---
+ pjlib-util/src/pjlib-util/resolver.c |   29 +++++++++++++++--------------
+ 1 file changed, 15 insertions(+), 14 deletions(-)
+
+diff --git a/pjlib-util/src/pjlib-util/resolver.c b/pjlib-util/src/pjlib-util/resolver.c
+index fe687b7..52b7655 100644
+--- a/pjlib-util/src/pjlib-util/resolver.c
++++ b/pjlib-util/src/pjlib-util/resolver.c
+@@ -1444,10 +1444,12 @@ static void update_res_cache(pj_dns_resolver *resolver,
+     if (ttl > resolver->settings.cache_max_ttl)
+ 	ttl = resolver->settings.cache_max_ttl;
+ 
++    /* Get a cache response entry */
++    cache = (struct cached_res *) pj_hash_get(resolver->hrescache, key,
++    					      sizeof(*key), &hval);
++
+     /* If TTL is zero, clear the same entry in the hash table */
+     if (ttl == 0) {
+-	cache = (struct cached_res *) pj_hash_get(resolver->hrescache, key, 
+-						  sizeof(*key), &hval);
+ 	/* Remove the entry before releasing its pool (see ticket #1710) */
+ 	pj_hash_set(NULL, resolver->hrescache, key, sizeof(*key), hval, NULL);
+ 
+@@ -1457,24 +1459,23 @@ static void update_res_cache(pj_dns_resolver *resolver,
+ 	return;
+     }
+ 
+-    /* Get a cache response entry */
+-    cache = (struct cached_res *) pj_hash_get(resolver->hrescache, key, 
+-    					      sizeof(*key), &hval);
+     if (cache == NULL) {
+ 	cache = alloc_entry(resolver);
+-    } else if (cache->ref_cnt > 1) {
+-	/* When cache entry is being used by callback (to app), just decrement
+-	 * ref_cnt so it will be freed after the callback returns and allocate
+-	 * new entry.
+-	 */
+-	cache->ref_cnt--;
+-	cache = alloc_entry(resolver);
+     } else {
+ 	/* Remove the entry before resetting its pool (see ticket #1710) */
+ 	pj_hash_set(NULL, resolver->hrescache, key, sizeof(*key), hval, NULL);
+ 
+-	/* Reset cache to avoid bloated cache pool */
+-	reset_entry(&cache);
++	if (cache->ref_cnt > 1) {
++	    /* When cache entry is being used by callback (to app),
++	     * just decrement ref_cnt so it will be freed after
++	     * the callback returns and allocate new entry.
++	     */
++	    cache->ref_cnt--;
++	    cache = alloc_entry(resolver);
++	} else {
++	    /* Reset cache to avoid bloated cache pool */
++	    reset_entry(&cache);
++	}
+     }
+ 
+     /* Duplicate the packet.
+-- 
+1.7.9.5
+
diff --git a/third-party/pjproject/patches/0006-r5477-svn-backport-Fix-DNS-write-on-freed-memory.patch b/third-party/pjproject/patches/0006-r5477-svn-backport-Fix-DNS-write-on-freed-memory.patch
new file mode 100644
index 0000000..f70dd45
--- /dev/null
+++ b/third-party/pjproject/patches/0006-r5477-svn-backport-Fix-DNS-write-on-freed-memory.patch
@@ -0,0 +1,33 @@
+From 732a997010d60fe93a7453e809672386749b0afc Mon Sep 17 00:00:00 2001
+From: Richard Mudgett <rmudgett at digium.com>
+Date: Tue, 1 Nov 2016 12:55:31 -0500
+Subject: [PATCH] r5477 svn backport Fix DNS write on freed memory.
+
+Re #1974: Fix DNS write on freed memory.
+Thanks to Richard Mudgett for the patch.
+---
+ pjlib-util/src/pjlib-util/resolver.c |    8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/pjlib-util/src/pjlib-util/resolver.c b/pjlib-util/src/pjlib-util/resolver.c
+index 52b7655..365772e 100644
+--- a/pjlib-util/src/pjlib-util/resolver.c
++++ b/pjlib-util/src/pjlib-util/resolver.c
+@@ -908,7 +908,13 @@ PJ_DEF(pj_status_t) pj_dns_resolver_start_query( pj_dns_resolver *resolver,
+ 	    /* Must return PJ_SUCCESS */
+ 	    status = PJ_SUCCESS;
+ 
+-	    goto on_return;
++	    /*
++	     * We cannot write to *p_query after calling cb because what
++	     * p_query points to may have been freed by cb.
++             * Refer to ticket #1974.
++	     */
++	    pj_mutex_unlock(resolver->mutex);
++	    return status;
+ 	}
+ 
+ 	/* At this point, we have a cached entry, but this entry has expired.
+-- 
+1.7.9.5
+
diff --git a/third-party/pjproject/patches/config_site.h b/third-party/pjproject/patches/config_site.h
index 0694f12..1a48695 100644
--- a/third-party/pjproject/patches/config_site.h
+++ b/third-party/pjproject/patches/config_site.h
@@ -56,4 +56,10 @@
 
 /* Defaults too low for WebRTC */
 #define PJ_ICE_MAX_CAND 32
-#define PJ_ICE_MAX_CHECKS (PJ_ICE_MAX_CAND * 2)
+#define PJ_ICE_MAX_CHECKS (PJ_ICE_MAX_CAND * PJ_ICE_MAX_CAND)
+
+/* Increase limits to allow more formats */
+#define	PJMEDIA_MAX_SDP_FMT   64
+#define	PJMEDIA_MAX_SDP_BANDW   4
+#define	PJMEDIA_MAX_SDP_ATTR   (PJMEDIA_MAX_SDP_FMT*2 + 4)
+#define	PJMEDIA_MAX_SDP_MEDIA   16
diff --git a/third-party/pjproject/patches/user.mak b/third-party/pjproject/patches/user.mak
index 31579d1..dafb259 100644
--- a/third-party/pjproject/patches/user.mak
+++ b/third-party/pjproject/patches/user.mak
@@ -1,2 +1,4 @@
 
-CFLAGS += -fPIC -Wno-unused-but-set-variable -Wno-unused-variable -Wno-unused-label -Wno-unused-function -Wno-strict-aliasing
+NUBSV := $(shell gcc -Wno-unused-but-set-variable -o /dev/null -xc -c - </dev/null 2>/dev/null && echo -Wno-unused-but-set-variable)
+
+CFLAGS += -fPIC $(NUBSV) -Wno-unused-variable -Wno-unused-label -Wno-unused-function -Wno-strict-aliasing

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-voip/asterisk.git



More information about the Pkg-voip-commits mailing list