Simple media playback
Almost all media is played to a channel using the POST /channels/{channel_id}/play
operation. This will do the following:
- Create a new
Playback
object for the channel. If a media operation is currently in progress on the channel, the newPlayback
object will be queued up for the channel. The
media
URI passed to theplay
operation will be inspected, and Asterisk will attempt to find the media requested. Currently, the following media schemes are supported:URI Scheme Description sound
A sound file located on the Asterisk system. You can use the /sounds
resource to query for available sounds on the system. You can also use specify a media file which is consumed via HTTP (e.g sound:http://foo.com/sound.wav)recording
A StoredRecording
stored on the Asterisk system. You can use the/recordings/stored
resource to query for availableStoredRecording
s on the system.number
Play back the specified number. This uses the same mechanism as Asterisk's Say
family of applications.digits
Play back the specified digits. This uses the same mechanism as Asterisk's Say
family of applications.characters
Play back the specified characters. This uses the same mechanism as Asterisk's Say
family of applications.tone
Play a particular tone sequence until stopped. This can be used to play locale specific ringing, stutter, busy, congestion, or other tones to a device. - Once the media operation is started or enqueued, the
Playback
object will be returned to the caller in theHTTP
response to the request. The caller can use that playback object to manipulate the media operation.
Early Media
Generally, before a channel has been answered and transitioned to the Up state, media cannot pass between Asterisk and a device. For example, if Asterisk is placing an outbound call to a device, the device may be ringing but no one has picked up a handset yet! In such circumstances, media cannot be successfully played to the ringing device - after all, who could listen to it?
However, with inbound calls, Asterisk is the entity that decides when the path of communication between itself and the device is answered - not the user on the other end. This can be useful when answering the channel may trigger billing times or other mechanisms that we don't want to fire yet. This is called "early media". For the channel technologies that support this, ARI and Asterisk will automatically handle sending the correct indications to the ringing phone before sending it media. The same play
operation can be used both for "regular" playback of media, as well as for "early media" scenarios.
Example: Playing back tones
This example ARI application will do the following:
- When a channel enters into the Stasis application, it will start a playback of a French ringing tone.
- After 8 seconds, the channel will be answered.
- After 1 second, the channel will be rudely hung up on - we didn't want to talk to them anyway!
Dialplan
For this example, we need to just drop the channel into Stasis, specifying our application:
Python
This example will use a very similar structure as the channel-state.py
example. Instead of performing a ring
operation in our StasisStart
handler, we'll instead initiate a playback using the playWithId
operation on the channel. Note that our URI uses the tone
scheme, which supports an optional tonezone
parameter. We specify our tonezone
as fr
, so that we get an elegant French ringing tone. Much like the channel-state.py
example, we then use a Python timer to schedule a callback that will answer the channel. Since we care about both the channel
and the playback
initiated on it, we pass both parameters as *args
parameters to the callback function.
Since this is a media operation and not technically a ringing indication, when we answer
the channel, the tone playback will not stop! To stop playing back our French ringing tone, we issue a stop
operation on the playback
object. This actually maps to a DELETE /playbacks/{playback_id}
operation.
Once answered, we'll schedule another Python timer that will do the actual hanging up of the channel.
channel-tones.py
The full source code for channel-tones.py
is shown below:
channel-tones.py in action
The following shows the output of the channel-tones.js
script when a PJSIP
channel for alice
enters the application:
JavaScript (Node.js)
This example will use a very similar structure as the channel-state.js
example. Instead of performing a ring
operation in our StasisStart
handler, we'll instead initiate a playback using the play
operation on the channel. Note that our URI uses the tone
scheme, which supports an optional tonezone
parameter. We specify our tonezone
as fr
, so that we get an elegant French ringing tone. Much like the channel-state.js
example, we then use a JavaScript timeout to schedule a callback that will answer the channel.
Since this is a media operation and not technically a ringing indication, when we answer
the channel, the tone playback will not stop! To stop playing back our French ringing tone, we issue a stop
operation on the playback
object. This actually maps to a DELETE /playbacks/{playback_id}
operation. Notice that we use the fact that the answer callback closes on the original channel and playback variables to access them from the callback.
Once answered, we'll schedule another timeout that will do the actual hanging up of the channel.
channel-tones.js
The full source code for channel-tones.js
is shown below:
channel-tones.js in action
The following shows the output of the channel-tones.js
script when a PJSIP
channel for alice
enters the application:
Channel PJSIP/alice-00000000 has entered the application Answering channel PJSIP/alice-00000000 Hanging up channel PJSIP/alice-00000000 Channel PJSIP/alice-00000000 just left our application
Example: Playing back a sound file
This example ARI application will do the following:
- When a channel enters the Stasis application, initiate a playback of howler monkeys on the channel. Fly my pretties, FLY!
- If the user has not hung up their phone in panic, it will hang up the channel when the howler monkeys return victorious - or rather, when ARI notifies the application that the playback has finished via the
PlaybackFinished
event.
Dialplan
For this example, we need to just drop the channel into Stasis, specifying our application:
Python
Much like the channel-tones.py
example, we'll start off by initiating a playback on the channel. Instead of specifying a tone
scheme, however, we'll specify a scheme of sound
with a resource of tt-monkeys
. Unlike the tones, this media does have a well defined ending - the end of the sound file! So we'll subscribe for the PlaybackFinished
event and tell ari-py
to call playback_finished
when our monkeys are done attacking.
Unfortunately, ari-py
doesn't let us pass arbitrary data to a callback function in the same fashion as a Python timer. Nuts. Luckily, the Playback
object has a property, target_uri
, that tells us which object it just finished playing to. Using that, we can get the channel
object back from Asterisk so we can hang it up.
Note that unlike the channel-tones.py
example, this application eschews the use of Python timers and simply responds to ARI events as they happen. This means we don't have to do much in our StasisEnd
event, and we have to track less state.
channel-playback-monkeys.py
The full source code for channel-playback-monkeys.py
is shown below:
channel-playback-monkeys.py in action
The following shows the output of the channel-playback-monkeys
.py script when a PJSIP
channel for alice
enters the application:
JavaScript (Node.js)
Much like the channel-tones.js
example, we'll start off by initiating a playback on the channel. Instead of specifying a tone
scheme, however, we'll specify a scheme of sound
with a resource of tt-monkeys
. Unlike the tones, this media does have a well defined ending - the end of the sound file! So we'll subscribe for the PlaybackFinished
event and tell ari-client
to call playbackFinished
when our monkeys are done attacking. Notice that we use client.Playback()
to generate a playback object with a pre-existing Id so we can scope the PlaybackFinished event to the playback we just created.
Notice that we use the fact that the playbackFinished callback closes over the original channel variable to perform a hangup operation using that object directly.
Note that unlike the channel-tones.js
example, this application eschews the use of JavaScript timeouts and simply responds to ARI events as they happen. This means we don't have to do much in our StasisEnd
event, and we have to track less state.
channel-playback-monkeys.js
The full source code for channel-playback-monkeys.js
is shown below:
channel-playback-monkeys.js in action
The following shows the output of the channel-playback-monkeys
.js script when a PJSIP
channel for alice
enters the application:
2 Comments
Yuriy
Hi!
I couldn't find in the wiki the following:
Is it possible to stream audio from channel to external programm using ARI?
I want to implement IVR with streaming speech recognition service with Asterisk. I don't want to first record the file, and then send it for recognition. Instead, I want to send streaming audio to make the script faster.
Thanks.
Bigyan Adhikary
In channel-playback-monkeys.py, I'm get an exception when I hangup the call while the monkeys sound file is playing.
Output from channel-playback-monkeys.py:
And here's the output from asterisk:
How exactly can I handle this? A code example would be very helpful.
Thanks.