Search the Asterisk Blog

Asterisk 14 ARI: Media Playlists

By Matt Jordan

Several improvements to ARI’s media manipulation are coming in Asterisk 14. In this post, we’ll explore media playlists, how they are used, and the problem they solve.

A Simple Application

It’s fairly common in Asterisk to construct complex sound prompts from multiple simpler sound files. Say, for example, we wanted to emulate the simple IVR menu from this dialplan example on the Asterisk wiki:

To emulate this in ARI, we’d need to do the following:

  1. Play back a sequence of media URIs, starting each of the subsequent URIs when each of the previous URIs finishes.
  2. When a DTMF key is pressed during any of the sound files, cancel the current media being played and break out of the sequence.

Using node-ari-client, we could write this in JavaScript in NodeJS. Before we complicate our lives with a list of media URIs, let’s start with a simple skeleton application that plays back a single media file ( demo-congrats ) and that stops the playback if 1  or 2  is pressed:

So what does this look like if we wanted to replace demo-congrats  with the sound files press-1 , or , and press-2 ?

Building a Media State Machine

Unlike the dialplan, ARI in versions of Asterisk prior to 14 do not expose a mechanism to tell Asterisk to ‘chain’ the sound files together. This means that we have to create the chain of sound files ourselves. Starting with the sample application above, we can replace demo-congrats  with a list of medias to play back:

Inside our forEach loop, we can instruct ARI to play back the current media item on the incoming channel:

Easy, right?

Not so fast!

When we issue play  on incoming , the call does not block until the sound file has completed. Instead, the above code will loop and immediately call play  for each media  in medias  in succession on the incoming  channel. ARI does a “nice” thing here and queues up the sound files for us. However, consider our DTMF handler:

When someone presses 1  and fires our ChannelDtmfReceived  handler, what Playback  object does playback  reference? The answer is: all three! This is a subtle bug in the code above that is hard to see or recognize unless you’re very familiar with the node-ari  client. Because we are registering for the ChannelDtmfReceived  event in a loop, we are actually placing three event handlers onto the incoming  channel! When someone presses a digit, all three event handlers will fire, each of which references one of the Playback  objects ( press-1 , or , and press-2 ). We will then attempt to stop all three, where only one out of the three will be in the correct state. This can result in a lot of errors being fired in addition to the sound file not actually being stopped correctly! Clearly we need to be a bit more careful than what we’ve written above.

First, let’s pull that event handler out of the loop. Because the ChannelDtmfReceived  event handler will need to use the current playback  object, we’ll pull that out as well, leaving it as undefined  for now:

Now we have to make it so that playback  always references the current playing sound. To do that, we’ll need some extra events:

This will mostly work (which is almost as good as working correctly, but not quite). It will cause us to have a single DTMF event handler, which will stop the correct playback  object when a DTMF is pressed. Unfortunately, it will also leave all of the other playback  objects still queued up to be played within ARI. So, if a user presses 1  or 2  during press-1 , it will absolutely stop the playback of press-1  – but will still playback or  and press-2 . Nuts!

At this point, we really need to abandon our forEach  loop. Instead of looping immediately over all of the medias  entries, we need to just issue a play  for the current media item, and only start the next media item when the previous is finished. We’ll also need to instruct our state changes to stop processing sounds in the medias  list if a DTMF key has been pressed.

An implementation that meets these requirements might look like the following:

All of this is a bit messy, and – as you can tell from the above process – very easy to get wrong! Since Asterisk already has the ability internally to maintain the state in a list of media files during channel playback, why not just use that?

Media Playlists

Coming soon in Asterisk 14, we can bypass all of the state machine work we just went through and simply instruct Asterisk to play a list of media URIs in a single play  command. The media URIs will be played back sequentially in order; if a stop command is issued, the entire playlist is stopped. If we wanted to know more about the media URIs being played back, a new event – PlaybackContinuing  – is issued as the media URIs transition during the list playback. That allows us to write much simpler code in our external application:

The above code is much cleaner, and allows us to more easily handle a very common use case for Asterisk application developers.

Be sure to check out what else is coming in Asterisk 14 – and happy coding!

Note: When I wrote this blog post, I ran into that great error that so many developers stumble into. While I had written several tests for this feature, I had not written a test for what is probably the most obvious use case – the one described in this blog post, wherein a list is stopped mid playback. And, as is so often the case with code that doesn’t have a test, there was a bug. Yikes. Luckily writing this helped to catch the problem, which will be fixed before the first release candidate of Asterisk 14.0.0. Always write tests!

There Are 2 Comments

Add to the Discussion

Your email address will not be published. Required fields are marked *

About the Author

Matt Jordan

Matt Jordan is CTO at Digium. Matt joined Digium in 2011 as a software developer on the Asterisk project. During that time, he was involved in the development of both Asterisk and the Asterisk Test Suite. If there is a failing voicemail test in your Test Suite, it is highly likely to be his fault. From 2012 to 2015, Matt was lead of the Asterisk project. During this time, a major re-architecture of Asterisk was performed (Asterisk 12), culminating in a new SIP stack based on PJSIP and new APIs for building communication applications. If you like these changes, Matt is happy for you, and would love to celebrate your success with you over beer at AstriCon. If you don't like these changes, Matt will be happy to listen to your complaints over beer at AstriCon. In 2015, Matt was named an Engineering Director at Digium. Apparently he didn't mess this up too badly, as in 2016 Matt was named CTO of Digium. Prior to Digium, one could describe Matt's background in software development as “eclectic”, having worked in a variety of industries. Uniting the various experiences, however, is a firm belief in good software development practices and methodologies and the effect they have on producing quality software. In his free time, Matt runs (hopefully), reads, enjoys art, and - along with his exceedingly awesome wife - tolerates their slightly neurotic dog.

See All of Matt's Articles