Making Adhearsion Compatible with Asterisk 13

Asterisk version 12 introduced a number of changes both in its internals and the various control APIs. While these changes were important, they also were not backward compatible (and this is a good thing). Since Asterisk 13, the Long Term Support release, was made in October of last year, we’ve been looking at what it would take to get Adhearsion to support Asterisk 13, while retaining support for Asterisk 11. This post addresses the changes we made to reach that goal.

The most relevant changes as far as Adhearsion is involved are the revamped bridging core and the AMI v2 specification, which for the most part removes sub-event types from AMI messages.

Other important new parts include a PJSIP channel type and the ARI REST interface, based on the new StasisCore.

You can find more information on the changes in the following documents:
New in 12 Asterisk wiki page
AMI v2 Specification
CHANGES for Asterisk 12
UPGRADE.txt for Asterisk 12

Asterisk 13 is the long term support version, incorporating changes from 12 with more fixes and upgrades.

We based our Adhearsion upgrade on Asterisk 13 to ensure long-term compatibility.

Adhearsion and Asterisk 13

Asterisk logoAdhearsion interacts with Asterisk through the Punchblock translator, so most changes that we will discuss were actually done at that level. Punchblock uses AsyncAGI to grab control of a call using the Asterisk Manager Interface (AMI), which sends Events and accepts Commands. The problem was that the SubEvent change mentioned above caused our AMI parser to not recognize certain Asterisk 13 events.

How we solved it

The AMI v2 specifications has three major areas which had to be addressed to have basic translator functionality work on Asterisk 13:
AsyncAGI events, which used to have a subtype, now are separate (AsyncAGIStart, AsyncAGIExec, etc.)
DTMF events went through the same change, and were replaced by DTMFStart and DTMFEnd events
– Bridging was entirely redone and warrants its own discussion

We wanted to make sure Punchblock supported both Asterisk 11 and 13, and were helped by the extensive test coverage that we built over the course of the years. I cannot stress enough how having a comprehensive suite of tests is essential to ensuring quality code that can be trusted as a foundation to build complex applications. It gives us much more confidence when making these types of changes that what was working before, still works after we finish the upgrade.

Events have no subtype any more

The AsyncAGI family of events is used by Punchblock to acquire control of a call and manage the lifecycle of commands it sends.

The compatibility work here mostly involved using has_guarded_handlers‘s’ excellent support for specifying complex conditions on an event handler.

Since the events themselves are not different, that is all that is needed to grab a call on AsyncAGIStart or receive confirmation for a command from AsyncAGIExec.

register_tmp_handler :ami, [{name: 'AsyncAGI', [:[], 'SubEvent'] => 'Exec'}, {name: 'AsyncAGIExec'}], [{[:[], 'CommandID'] => agi.id}, {[:[], 'CommandId'] => agi.id}] # ...

The same changes allow us to handle DTMFEnd events that replaced DTMF with a End subtype.

Wait, that did not work.

The first iteration through the above changes caught us by surprise, since everything looked to be in its place but command confirmations were not being received correctly.

After a short investigation, we found a minor but very important difference that is worth noting: the CommandID field in an AMI event used to be spelled with a final capital D, and that syntax had a few inconsistencies too. From Asterisk 12 onwards, the field is consistently spelled as CommandId.

The new bridging model

AsyncAGI and DTMF events only required a different handler syntax but still have the same payload.

Asterisk 13 handles bridging in an entirely different way than previous versions.

The new bridge lifecycle involves the following events:
BridgeCreate when channels are about to enter a bridge
BridgeEnter when a channel is put in a bridge
BridgeLeave when a channel leaves a bridge
BridgeDestroy when a bridge ceases existing

All events are always emitted and Asterisk 12 makes sure they are in the correct order. A lot of work has been put into making the bridging interface deterministic and simple to parse (and thanks to the Asterisk development team for that!).

That said, we ran into one major problem.

The old bridging event model uses entirely different messages, BridgeExec to signal that a Bridge command has been acknowledged, and the Bridge event subtypes to indicate that channels have been joined or unjoined.

The Asterisk 11 model is in general a bit cumbersome, but it does have an advantage, which is that it passes in the names of both channels that have participated in a join.

On Asterisk 12 and up, each event only relates to its own channel. That results in us having to track the bridge creation and destruction to figure out which call got joined to another.
That is done by considering the first BridgeEnter and BridgeLeave for a specific BridgeId as the creation event, then using the second to count the latch up or down and sending the correct Joined or Unjoined events to calls.

After solving that, though, the new events provide a more reliable way to follow calls and make sure they are all kept in the correct state.

More to follow!

The above concludes the first batch of work on Asterisk 13 compatibility, without introducing new functionality. However, Asterisk 13 does expose some new and interesting AMI commands that we plan to use to build a better translator layer in Punchblock.

Some of those functions are:
MixMonitor now has an option to automatically play a beep before starting to record. It does also not need flags to be set to record both legs of a bridge.
ControlPlayback is a new command that allows for stopping, rewinding and seeking playback of an audio file. Adhearsion currently relies on stopping output by redirecting a channel to a special context that is added programmatically.
BridgeWait is similar in function to call parking, but it is cleaner to use for general channel handling and initial call setup.

Conclusions

Adhearsion and Punchblock are ready now to be used with Asterisk 13, and soon there will be more and better functionality available.

Stay tuned for more updates soon!

Subscribe to our mailing list

* indicates required
I want to read about...
Email Format

What do you think?