How to control mid-call media actions with Call Automation
Call Automation uses a REST API interface to receive requests for actions and provide responses to notify whether the request was successfully submitted or not. Due to the asynchronous nature of calling, most actions have corresponding events that are triggered when the action completes successfully or fails. This guide covers the actions available to developers during calls, like Send DTMF and Continuous DTMF Recognition. Actions are accompanied with sample code on how to invoke the said action.
Call Automation supports various other actions to manage calls and recording that aren't included in this guide.
Note
Call Automation currently doesn't interoperate with Microsoft Teams. Actions like making, redirecting a call to a Teams user or playing audio to a Teams user using Call Automation isn't supported.
As a prerequisite, we recommend you to read the below articles to make the most of this guide:
- Call Automation concepts guide that describes the action-event programming model and event callbacks.
- Learn about user identifiers like CommunicationUserIdentifier and PhoneNumberIdentifier used in this guide.
- Learn more about how to control and steer calls with Call Automation, which teaches you about dealing with the basics of dealing with a call.
For all the code samples, client
is CallAutomationClient object that can be created as shown and callConnection
is the CallConnection object obtained from Answer or CreateCall response. You can also obtain it from callback events received by your application.
var callAutomationClient = new CallAutomationClient("<Azure Communication Services connection string>");
Send DTMF
You can send DTMF tones to an external participant, which may be useful when you’re already on a call and need to invite another participant who has an extension number or an IVR menu to navigate.
Note
This is only supported for external PSTN participants and supports sending a maximum of 18 tones at a time.
SendDtmfAsync Method
Send a list of DTMF tones to an external participant.
var tones = new DtmfTone[] { DtmfTone.One, DtmfTone.Two, DtmfTone.Three, DtmfTone.Pound };
var sendDtmfTonesOptions = new SendDtmfTonesOptions(tones, new PhoneNumberIdentifier(calleePhonenumber))
{
OperationContext = "dtmfs-to-ivr"
};
var sendDtmfAsyncResult = await callAutomationClient.GetCallConnection(callConnectionId)
.GetCallMedia()
.SendDtmfTonesAsync(sendDtmfTonesOptions);
When your application sends these DTMF tones, you receive event updates. You can use the SendDtmfTonesCompleted
and SendDtmfTonesFailed
events to create business logic in your application to determine the next steps.
Example of SendDtmfTonesCompleted event
if (acsEvent is SendDtmfTonesCompleted sendDtmfCompleted)
{
logger.LogInformation("Send DTMF succeeded, context={context}", sendDtmfCompleted.OperationContext);
}
Example of SendDtmfTonesFailed
if (acsEvent is SendDtmfTonesFailed sendDtmfFailed)
{
logger.LogInformation("Send dtmf failed: result={result}, context={context}",
sendDtmfFailed.ResultInformation?.Message, sendDtmfFailed.OperationContext);
}
Continuous DTMF Recognition
You can subscribe to receive continuous DTMF tones throughout the call. Your application receives DTMF tones as the targeted participant presses on a key on their keypad. These tones are sent to your application one by one as the participant is pressing them.
StartContinuousDtmfRecognitionAsync Method
Start detecting DTMF tones sent by a participant.
await callAutomationClient.GetCallConnection(callConnectionId)
.GetCallMedia()
.StartContinuousDtmfRecognitionAsync(new PhoneNumberIdentifier(c2Target), "dtmf-reco-on-c2");
When your application no longer wishes to receive DTMF tones from the participant anymore, you can use the StopContinuousDtmfRecognitionAsync
method to let Azure Communication Services know to stop detecting DTMF tones.
StopContinuousDtmfRecognitionAsync
Stop detecting DTMF tones sent by participant.
var continuousDtmfRecognitionOptions = new ContinuousDtmfRecognitionOptions(new PhoneNumberIdentifier(callerPhonenumber))
{
OperationContext = "dtmf-reco-on-c2"
};
var startContinuousDtmfRecognitionAsyncResult = await callAutomationClient.GetCallConnection(callConnectionId)
.GetCallMedia()
.StartContinuousDtmfRecognitionAsync(continuousDtmfRecognitionOptions);
Your application receives event updates when these actions either succeed or fail. You can use these events to build custom business logic to configure the next step your application needs to take when it receives these event updates.
ContinuousDtmfRecognitionToneReceived Event
Example of how you can handle a DTMF tone successfully detected.
if (acsEvent is ContinuousDtmfRecognitionToneReceived continuousDtmfRecognitionToneReceived)
{
logger.LogInformation("Tone detected: sequenceId={sequenceId}, tone={tone}",
continuousDtmfRecognitionToneReceived.SequenceId,
continuousDtmfRecognitionToneReceived.Tone);
}
Azure Communication Services provides you with a SequenceId
as part of the ContinuousDtmfRecognitionToneReceived
event, which your application can use to reconstruct the order in which the participant entered the DTMF tones.
ContinuousDtmfRecognitionFailed Event
Example of how you can handle when DTMF tone detection fails.
if (acsEvent is ContinuousDtmfRecognitionToneFailed continuousDtmfRecognitionToneFailed)
{
logger.LogInformation("Start continuous DTMF recognition failed, result={result}, context={context}",
continuousDtmfRecognitionToneFailed.ResultInformation?.Message,
continuousDtmfRecognitionToneFailed.OperationContext);
}
ContinuousDtmfRecogntionStopped Event
Example of how to handle when continuous DTMF recognition has stopped, this could be because your application invoked the StopContinuousDtmfRecognitionAsync
event or because the call has ended.
if (acsEvent is ContinuousDtmfRecognitionStopped continuousDtmfRecognitionStopped)
{
logger.LogInformation("Continuous DTMF recognition stopped, context={context}", continuousDtmfRecognitionStopped.OperationContext);
}
Hold
The hold action allows developers to temporarily pause a conversation between a participant and a system or agent. This can be useful in scenarios where the participant needs to be transferred to another agent or department or when the agent needs to consult a supervisor in the background before continuing the conversation. During this time you can choose to play audio to the participant that is on hold.
// Option 1: Hold without additional options
await callAutomationClient.GetCallConnection(callConnectionId)
.GetCallMedia().HoldAsync(c2Target);
/*
// Option 2: Hold with play source
PlaySource playSource = /* initialize playSource */;
await callAutomationClient.GetCallConnection(callConnectionId)
.GetCallMedia().HoldAsync(c2Target, playSource);
// Option 3: Hold with options
var holdOptions = new HoldOptions(target)
{
OperationCallbackUri = new Uri(""),
OperationContext = "holdcontext"
};
await callMedia.HoldAsync(holdOptions);
*/
Unhold
The unhold action allows developers to resume a conversation between a participant and a system or agent that was previously paused. When the participant is taken off hold they will be able to hear the system or agent again.
var unHoldOptions = new UnholdOptions(target)
{
OperationContext = "UnHoldPstnParticipant"
};
// Option 1
var UnHoldParticipant = await callMedia.UnholdAsync(unHoldOptions);
/*
// Option 2
var UnHoldParticipant = await callMedia.UnholdAsync(target);
*/
Audio streaming (public preview)
Audio streaming allows you to subscribe to real-time audio streams from an ongoing call. For more detailed guidance on how to get started with audio streaming and information about audio streaming callback events, see this page.
Real-time transcription (public preview)
Real-time transcription allows you to access live transcriptions for the audio of an ongoing call. For more detailed guidance on how to get started with real-time transcription and information about real-time transcription callback events, see this page.
Media Action Compatibility Table
The following table illustrates the what media operations are allowed to run/queue if a previous operation is still running/queued.
Existing Operation | Call Leg | Allowed | Disallowed |
---|---|---|---|
PlayToAll | Main | PlayToAll, Recognize(Non-Group Call), PlayTo, Recognize(Group Call), SendDTMF, StartContinuousDtmfRecognition | None |
Recognize(Non-Group Call) | Main | PlayToAll, Recognize(Non-Group Call), PlayTo, Recognize(Group Call), SendDTMF, StartContinuousDtmfRecognition | None |
PlayTo | Sub | PlayToAll, Recognize(Non-Group Call) | PlayTo, Recognize(Group Call), SendDTMF, StartContinuousDtmfRecognition |
Recognize(Group Call) | Sub | PlayToAll, Recognize(Non-Group Call) | PlayTo, Recognize(Group Call), SendDTMF, StartContinuousDtmfRecognition |
SendDTMF | Sub | PlayToAll, Recognize(Non-Group Call) | PlayTo, Recognize(Group Call), SendDTMF, StartContinuousDtmfRecognition |
StartContinuousDtmfRecognition | Sub | PlayToAll, Recognize(Non-Group Call),PlayTo, Recognize(Group Call), SendDTMF, StartContinuousDtmfRecognition | None |