Project Overview¶
Media playback in ARI today queues multiple play operations to a resource, such as a bridge
or channel
. That is, performing the following will result in three different Playback
objects:
POST /channels/12345/play?media=sound:tt-monkeys
POST /channels/12345/play?media=sound:tt-weasels
POST /channels/12345/play?media=number:555
This works well enough when you know that a user needs to listen to all of the prompts, and you don't need to cancel them. If, however, you need to let the prompts be interruptible or allow a user to manipulate the playback, you have to keep a reference to the current Playback
object for the resource, as well as any 'future' Playback
objects (so you can delete
them). Alternatively, of course, you don't have to initiate all of the media operations at once - you can ostensibly play each when the previous has finished. However, that negates the benefit of the media queueing, and entails writing a state machine.
Either way, however, the client is required to maintain state. That state is cumbersome - having to 'remember' which media operation the resource is currently in and/or future media operations to be performed/are enqueued is extra work that a developer using ARI shouldn't have to perform.
Ideally, an ARI user should be able to issue a list of media that they would like played, and Asterisk should play it back as a list. Operations should be performed (wherever possible) on the list itself.
Requirements Specification¶
Channels/Bridges Resource Operations¶
ARI will allow a user to specify multiple media resources to play on a supported resource (currently, channels
and bridges
).
Example¶
Other Media Parameters¶
Other existing media parameters will be supported as follows:
lang
- specified only once, and applies to allsound:
resources.offsetms
- specified only once, and only applies to the first specified resource. If the first specified resource is not asound:
resource, the parameter has no effect.skipms
- specified only once, and applies to allsound:
resources. If the process of issuing a reverse/forward takes the user past the beginning/end of a media resource, then the previous media resource/next media resource should be started at the approximate offset.
Playbacks Resource Operations¶
A Playback
resource will support two new operations:
next
- move to the next media resource in the listprev
- move to the previous media resource in the list
An attempt to move past the end of the last media resource will return a 2xx response and will result in a PlaybackFinished
event. An attempt to move prior to the beginning of the first media resource will return a 2xx response and will result in a PlaybackStarted
event.
On this Page
Example¶
Existing Operations¶
The existing operations will work as follows:
restart
- restarts the current media in the list.pause
- pauses the current media in the list.unpause
- unpauses the current media in the list.reverse
- reverses the current media in the list. If that causes the media to reach the beginning of a sound file, it will do one of the two following actions:- If the previous media resource was a sound file, it will start playback of the previous media resource at an offset equal to the remaining jump not displaced in the existing sound file.
- If the previous media resource was not a sound file (and cannot be indexed), it will start playback at the start of that resource.
forward
- fast-forwards the current media in the list. if that causes the media to reach the end of a sound file, the sound file will complete and the next media resource will be started.- If the next media resource is a sound file, it will start playback of the next media resource at an offset equal to the remaining jump not displaced in the existing sound file.
- If the next media resource was not a sound file (and cannot be indexed), it will start playback at the start of that resource. Configuration =============
No configuration should be needed.
Design/Implementation¶
Swagger Model updates¶
The channels
and bridges
resources will have their respective play
operations updated to allow multiple media
query parameters. The list is constructed in the order that the query parameters are presented.
[channels|bridges]/play
js {
"name": "media",
"description": "Media URIs to play.",
"paramType": "query",
"required": true,
"allowMultiple": true,
"dataType": "string"
},
The playbacks
resource will have its control
operation updated with new options for the operation
query parameter.
playbacks/control
js {
"name": "operation",
"description": "Operation to perform on the playback.",
"paramType": "query",
"required": true,
"allowMultiple": false,
"dataType": "string",
"allowableValues": {
"valueType": "LIST",
"values": [
"restart",
"pause",
"unpause",
"reverse",
"forward",
"next",
"prev"
]
}
}
Core updates¶
- New control frames for
next
andprev
should be added:
frame.h
AST_CONTROL_STREAM_STOP = 1000, /*!< Indicate to a channel in playback to stop the strea */
AST_CONTROL_STREAM_SUSPEND = 1001, /*!< Indicate to a channel in playback to suspend the strea */
AST_CONTROL_STREAM_RESTART = 1002, /*!< Indicate to a channel in playback to restart the strea */
AST_CONTROL_STREAM_REVERSE = 1003, /*!< Indicate to a channel in playback to rewin */
AST_CONTROL_STREAM_FORWARD = 1004, /*!< Indicate to a channel in playback to fast forwar */
AST_CONTROL_STREAM_PREV = 1005, /*!< Indicate to a channel in playback to play the previous strea */
AST_CONTROL_STREAM_NEXT = 1006, /*!< Indicate to a channel in playback to play the next strea */
- Update
channel.c
to consume and ignore the two new frame types - Have
file.c
waitstream_core
handle the two new frame types. These should break out of the control loop and return the frame value. - Add a new function to
app.h
(because we don't have enough yet) calledast_control_streamfile_lang_w_cb
. Its usage should be somewhat obvious.
res_stasis_playback¶
In addition model and parameter changes that will ripple through the auto-generated HTTP server/resource bindings, the control of the playback will have to be modified in the following ways in res_stasis_playback
:
- The
stasis_app_playback
struct will have to specify a list of media resources to play, as well as the current/next file to be played. play_on_channel
will have to iterate over that list. Ifast_control_streamfile_lang
returns anext
orprev
result, it will have to handle it accordingly.- Use
ast_control_streamfile_lang_w_cb
instead ofast_control_streamfile_lang
. Pass thestasis_app_playback
object as thevoid *
. If therewind
orfast forward
operation indicates that we are at the beginning or end of a file, alter the next file to be played accordingly. - Implement handling for the control frame values corresponding to
PREV
andNEXT
.
- Use
- Add two new function handlers for
playback_opreation_cb
-playback_next
andplayback_prev
. Queue the control frames appropriately.
app_controlplayback¶
Currently, the ControlPlayback
dialplan application does not support playback of multiple files. As such, handling of the new feature should be defensive in nature only.
controlplayback_exec
should be modified to handle the return ofAST_CONTROL_STREAM_PREV
andAST_CONTROL_STREAM_NEXT
gracefully. Since the application does not currently handle multiple playbacks, it should simply return with the correctCPLAYBACKSTATUS
asPREVIOUS
orNEXT
, respectively.controlplayback_manager
should be updated to understand "prev" and "next" as valid control signals (as, who knows, you may want to use AMI to control a channel in ARI, if you're that crazy)
res_agi¶
The control stream file
AGI command does not support playback of multiple files. As such, handling of the new feature should be defensive in nature only.
handle_controlstreamfile
should be modified to handle the return ofAST_CONTROL_STREAM_PREV
andAST_CONTROL_STREAM_NEXT
gracefully. The result ofCPLAYBACKSTATUS
should mirror that ofapp_controlplayback
.
chan_iax2¶
- Explicitly specify that the two new control frames should not be passed over the wire.
func_frame_trace¶
- Do the frame trace dance Testing =======
Test | Path | Purpose |
---|---|---|
control_prev | tests/apps/control_playback | Verify that sending a remote initiated prev command via AMI results in CPLAYBACKSTATUS being set to PREVIOUS . |
control_next | tests/apps/control_playback | Verify that sending a remote initiated next command via AMI results in CPLAYBACKSTATUS being set to NEXT . |
remote_prev | tests/apps/playback | Verify that sending a remote initiated prev command via AMI results in a test event indicating that the playback was broken |
remote_next | tests/apps/playback | Verify that sending a remote initiated next command via AMI results in a test event indicating that the playback was broken |
channels/playback/list | tests/rest_api | Verify that nominal playback of a list - with no user control - occurs in the expected fashion, and that each sound file in the list is played in the correct sequence |
channels/playback/list_forward | tests/rest_api | Verify that playback of a list where a user fast forwards plays through each sound file in sequence. Verify that unsupported skippable media is handled correctly. |
channels/playback/list_reverse | tests/rest_api | Verify that playback of a list where a user reverses through each sound file is handled correctly, including restarting already played media. Verify that media that cannot be reversed through is restarted as well. |
channels/playback/next | tests/rest_api | Verify that playback of a list supports skipping to the next media in the list, for multiple media resource types |
channels/playback/prev | tests/rest_api | Verify that playback of a list supports skipping to the previous media in the list, for multiple media resource types |
bridges/playback/list | tests/rest_api | Verify that nominal playback of a list - with no user control - occurs in the expected fashion, and that each sound file in the list is played in the correct sequence |
bridges/playback/list_forward | tests/rest_api | Verify that playback of a list where a user fast forwards plays through each sound file in sequence. Verify that unsupported skippable media is handled correctly. |
bridges/playback/list_reverse | tests/rest_api | Verify that playback of a list where a user reverses through each sound file is handled correctly, including restarting already played media. Verify that media that cannot be reversed through is restarted as well. |
bridges/playback/next | tests/rest_api | Verify that playback of a list supports skipping to the next media in the list, for multiple media resource types |
bridges/playback/prev | tests/rest_api | Verify that playback of a list supports skipping to the previous media in the list, for multiple media resource types |
Project Planning¶
The following are rough tasks that need to be done in order to complete this feature. These are meant to be guidelines, and should not necessarily be followed verbatim. Note that many of these are actually independent of each other, and can be worked out simultaneously. If you're interested in helping out with any of these tasks, please speak up on the asterisk-dev
mailing list!
The various phases are meant to be implemented as separately as possible to ease the process of peer review.
Phase One - Write Tests That Fail¶
In both of these cases, it is expected that the existing tests can be used as a basis.
Task | Description | Status |
---|---|---|
Write the dialplan application tests | Write tests that exercise the ControlPlayback and Playback applications with the new options. | |
Write the REST API tests | Write tests that update the existing options, as well as the new options. |
Phase Two - Update the core/non-ARI portions¶
Task | Description | Status |
---|---|---|
Add the control frame handling | * Add the new control frame types | |
* Update the various modules that simply have to be aware of the new frame's existence | ||
* Update waitstream_core to handle the new frames and pass the value back up to the caller | ||
* Add the new function and update the signature of the callback and its corresponding functions. | ||
Update non-ARI consumers | Update:* Playback | |
* ControlPlayback (dialplan application and AMI action) | ||
* control stream file AGI command | ||
Phase Three - Update ARI¶
Task | Description | Status |
---|---|---|
Update the swagger model; generate bindings | What the task says. | |
Add operation handlers | Handle prev and next , queueing up control frames on the channel | |
Update play_on_channel to play multiple files | * Iterate over the files in the list, playing them back | |
* If the NEXT or PREV control frame is returned, skip to the next/previous media resource | ||
Update play_on_channel to handle reversing/forwarding between files in a list | * Add a callback when initiating the control stream file operation in the core, passing the playback object along | |
* If the callback is called and the file is at the end/beginning of the stream, shift to the next/previous file appropriately and store the offset | ||
* In the main loop, start the playback at the appropriate offset | ||