Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion eng/scripts/Export-API.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ if ($SpellCheckPublicApiSurface) {
if ($LASTEXITCODE) {
Write-Host "##vso[task.LogIssue type=error;]Spelling errors detected. To correct false positives or learn about spell checking see: https://aka.ms/azsdk/engsys/spellcheck"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Release History

## 1.3.0-beta.1 (Unreleased)
## 1.4.0-beta.2 (Unreleased)

### Features Added

Expand All @@ -10,6 +10,54 @@

### Other Changes

## 1.4.0-beta.1 (2024-11-22)
Comment thread
v-dharmarajv marked this conversation as resolved.

### Features Added

- Added support for ConnectAPI to enable streaming and real-time transcription
- Enhanced media streaming with bidirectional capabilities, enabling support for audio formats in both directions. Currently, it supports sample rates of 24kHz and 16kHz

### Other Changes

- Introduced audio streaming and transcription data parsing capabilities.

## 1.3.0 (2024-11-22)

### Features Added

- Support multiple play sources for Play and Recognize
- Support for PlayStarted event in Play/Recognize
- Hold and Unhold the participant
- CallDisconnected now includes more information on why the call has ended
- Support to manage the rooms/servercall/group call using connect API
- Expose original PSTN number target from incoming call event in call connection properties
- Support for VoIP to PSTN transfer scenario

### Other Changes

- Added CreateCallFailed event to signify when create call API fails to establish a call
- Added AnswerFailed event to signify when answer call API fails to answer a call

## 1.3.0-beta.2 (2024-10-28)

### Features Added

- Added CreateCallFailed event to signify when create call API fails to establish a call

## 1.3.0-beta.1 (2024-08-02)

### Features Added

- Support multiple play sources for Play and Recognize
- Support for PlayStarted event in Play/Recognize
- Support for the real time transcription
- Monetization for real-time transcription and audio streaming
- Hold and Unhold the participant
- Support to manage the rooms/servercall/group call using connect API
- Support for the audio streaming
- Expose original PSTN number target from incoming call event in call connection properties
- Support for VoIP to PSTN transfer scenario

## 1.2.0 (2024-04-15)

### Features Added
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "net",
"TagPrefix": "net/communication/Azure.Communication.CallAutomation",
"Tag": "net/communication/Azure.Communication.CallAutomation_a7eb7ffb4a"
"Tag": "net/communication/Azure.Communication.CallAutomation_c4f60a9554"
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
Microsoft Azure Communication Call Automation quickstart - https://learn.microsoft.com/azure/communication-services/quickstarts/voice-video-calling/callflows-for-customer-interactions?pivots=programming-language-csharp
</Description>
<AssemblyTitle>Azure Communication CallAutomation Service</AssemblyTitle>
<Version>1.3.0-beta.1</Version>
<Version>1.4.0-beta.2</Version>
<!--The ApiCompatVersion is managed automatically and should not generally be modified manually.-->
<PackageTags>Microsoft Azure Communication CallAutomation Service;Microsoft;Azure;Azure Communication Service;Azure Communication CallAutomation Service;Calling;Communication;$(PackageCommonTags)</PackageTags>
<TargetFrameworks>$(RequiredTargetFrameworks)</TargetFrameworks>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ public virtual async Task<Response<AnswerCallResult>> AnswerCallAsync(AnswerCall
scope.Start();
try
{
if (options == null) throw new ArgumentNullException(nameof(options));
if (options == null)
throw new ArgumentNullException(nameof(options));

AnswerCallRequestInternal request = CreateAnswerCallRequest(options);

Expand Down Expand Up @@ -226,7 +227,8 @@ public virtual Response<AnswerCallResult> AnswerCall(AnswerCallOptions options,
scope.Start();
try
{
if (options == null) throw new ArgumentNullException(nameof(options));
if (options == null)
throw new ArgumentNullException(nameof(options));

AnswerCallRequestInternal request = CreateAnswerCallRequest(options);

Expand Down Expand Up @@ -259,8 +261,8 @@ private AnswerCallRequestInternal CreateAnswerCallRequest(AnswerCallOptions opti
};
}

request.MediaStreamingConfiguration = CreateMediaStreamingOptionsInternal(options.MediaStreamingOptions);
request.TranscriptionConfiguration = CreateTranscriptionOptionsInternal(options.TranscriptionOptions);
request.MediaStreamingOptions = CreateMediaStreamingOptionsInternal(options.MediaStreamingOptions);
request.TranscriptionOptions = CreateTranscriptionOptionsInternal(options.TranscriptionOptions);
request.AnsweredBy = Source == null ? null : new CommunicationUserIdentifierModel(Source.Id);
request.OperationContext = options.OperationContext;
request.CustomCallingContext = new CustomCallingContextInternal(
Expand Down Expand Up @@ -344,8 +346,8 @@ public virtual Response RedirectCall(RedirectCallOptions options, CancellationTo
RedirectCallRequestInternal request = new RedirectCallRequestInternal(options.IncomingCallContext, CommunicationIdentifierSerializer.Serialize(options.CallInvite.Target));

request.CustomCallingContext = new CustomCallingContextInternal(
options.CallInvite.CustomCallingContext.SipHeaders == null ? new ChangeTrackingDictionary<string, string>() : options.CallInvite.CustomCallingContext.SipHeaders,
options.CallInvite.CustomCallingContext.VoipHeaders == null ? new ChangeTrackingDictionary<string, string>() : options.CallInvite.CustomCallingContext.VoipHeaders);
options.CallInvite.CustomCallingContext.SipHeaders == null ? new ChangeTrackingDictionary<string, string>() : options.CallInvite.CustomCallingContext.SipHeaders,
options.CallInvite.CustomCallingContext.VoipHeaders == null ? new ChangeTrackingDictionary<string, string>() : options.CallInvite.CustomCallingContext.VoipHeaders);

return AzureCommunicationServicesRestClient.RedirectCall(request, cancellationToken);
}
Expand Down Expand Up @@ -519,7 +521,8 @@ public virtual Response<CreateCallResult> CreateCall(CreateCallOptions options,
scope.Start();
try
{
if (options == null) throw new ArgumentNullException(nameof(options));
if (options == null)
throw new ArgumentNullException(nameof(options));

CreateCallRequestInternal request = CreateCallRequest(options);

Expand Down Expand Up @@ -618,6 +621,107 @@ public virtual Response<CreateCallResult> CreateGroupCall(CreateGroupCallOptions
}
}

/// <summary>
/// Create a connect request.
/// </summary>
/// <param name="callLocator"></param>
/// <param name="callbackUri"></param>
/// <param name="cancellationToken"></param>
/// <exception cref="ArgumentNullException"><paramref name="callLocator"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="callbackUri"/> callbackUrl is not formatted correctly or empty. </exception>
/// <exception cref="RequestFailedException">The server returned an error. See <see cref="Exception.Message"/> for details returned from the server.</exception>
/// <returns></returns>
public virtual async Task<Response<ConnectCallResult>> ConnectCallAsync(CallLocator callLocator, Uri callbackUri, CancellationToken cancellationToken = default)
{
ConnectCallOptions options = new ConnectCallOptions(callLocator, callbackUri);

return await ConnectCallAsync(options, cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Create a connect request.
/// </summary>
/// <param name="connectCallOptions">ConnectOptions for connect request.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <exception cref="ArgumentNullException"><paramref name="connectCallOptions"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="connectCallOptions"/> CallbackUrl is not formatted correctly. </exception>
/// <exception cref="RequestFailedException">The server returned an error. See <see cref="Exception.Message"/> for details returned from the server.</exception>
/// <returns></returns>
public virtual async Task<Response<ConnectCallResult>> ConnectCallAsync(ConnectCallOptions connectCallOptions, CancellationToken cancellationToken = default)
{
using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(CallAutomationClient)}.{nameof(ConnectCall)}");
scope.Start();
try
{
if (connectCallOptions == null)
throw new ArgumentNullException(nameof(connectCallOptions));

ConnectRequestInternal connectRequest = ConnectRequest(connectCallOptions);
Response<CallConnectionPropertiesInternal> response = await AzureCommunicationServicesRestClient.ConnectAsync(connectRequest).ConfigureAwait(false);

var callConnection = GetCallConnection(response.Value.CallConnectionId);
ConnectCallResult connectResult = new ConnectCallResult(new CallConnectionProperties(response.Value), callConnection);
connectResult.SetEventProcessor(EventProcessor, response.Value.CallConnectionId);

return Response.FromValue(connectResult, response.GetRawResponse());
}
catch (Exception ex)
{
scope.Failed(ex);
throw;
}
}

/// <summary>
/// Create a connect request.
/// </summary>
/// <param name="callLocator"></param>
/// <param name="callbackUri"></param>
/// <param name="cancellationToken"></param>
/// <exception cref="ArgumentNullException"><paramref name="callLocator"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="callbackUri"/> callbackUrl is not formatted correctly or empty. </exception>
/// <exception cref="RequestFailedException">The server returned an error. See <see cref="Exception.Message"/> for details returned from the server.</exception>
/// <returns></returns>
public virtual Response<ConnectCallResult> ConnectCall(CallLocator callLocator, Uri callbackUri, CancellationToken cancellationToken = default)
{
ConnectCallOptions options = new ConnectCallOptions(callLocator, callbackUri);

return ConnectCall(options, cancellationToken);
}

/// <summary>
/// Create a connect request.
/// </summary>
/// <param name="connectCallOptions">ConnectOptions for connect request.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <exception cref="ArgumentNullException"><paramref name="connectCallOptions"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="connectCallOptions"/> CallbackUrl is not formatted correctly. </exception>
/// <exception cref="RequestFailedException">The server returned an error. See <see cref="Exception.Message"/> for details returned from the server.</exception>
/// <returns></returns>
public virtual Response<ConnectCallResult> ConnectCall(ConnectCallOptions connectCallOptions, CancellationToken cancellationToken = default)
{
using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(CallAutomationClient)}.{nameof(ConnectCall)}");
scope.Start();
try
{
if (connectCallOptions == null)
throw new ArgumentNullException(nameof(connectCallOptions));

ConnectRequestInternal connectRequest = ConnectRequest(connectCallOptions);
Response<CallConnectionPropertiesInternal> response = AzureCommunicationServicesRestClient.Connect(connectRequest);

var callConnection = GetCallConnection(response.Value.CallConnectionId);
ConnectCallResult connectResult = new ConnectCallResult(new CallConnectionProperties(response.Value), callConnection);
connectResult.SetEventProcessor(EventProcessor, response.Value.CallConnectionId);

return Response.FromValue(connectResult, response.GetRawResponse());
}
catch (Exception ex)
{
scope.Failed(ex);
throw;
}
}
private CreateCallRequestInternal CreateCallRequest(CreateCallOptions options)
{
CreateCallRequestInternal request = new(
Expand Down Expand Up @@ -647,8 +751,8 @@ private CreateCallRequestInternal CreateCallRequest(CreateCallOptions options)
}

request.OperationContext = options.OperationContext;
request.MediaStreamingConfiguration = CreateMediaStreamingOptionsInternal(options.MediaStreamingOptions);
request.TranscriptionConfiguration = CreateTranscriptionOptionsInternal(options.TranscriptionOptions);
request.MediaStreamingOptions = CreateMediaStreamingOptionsInternal(options.MediaStreamingOptions);
request.TranscriptionOptions = CreateTranscriptionOptionsInternal(options.TranscriptionOptions);

return request;
}
Expand Down Expand Up @@ -682,18 +786,39 @@ private CreateCallRequestInternal CreateCallRequest(CreateGroupCallOptions optio
}

request.OperationContext = options.OperationContext;
request.MediaStreamingConfiguration = CreateMediaStreamingOptionsInternal(options.MediaStreamingOptions);
request.TranscriptionConfiguration = CreateTranscriptionOptionsInternal(options.TranscriptionOptions);
request.MediaStreamingOptions = CreateMediaStreamingOptionsInternal(options.MediaStreamingOptions);
request.TranscriptionOptions = CreateTranscriptionOptionsInternal(options.TranscriptionOptions);

return request;
}

private ConnectRequestInternal ConnectRequest(ConnectCallOptions options)
{
CallLocatorInternal callLocatorInternal = CallLocatorSerializer.Serialize(options.CallLocator);
ConnectRequestInternal connectRequest = new ConnectRequestInternal(callLocatorInternal, options.CallbackUri?.AbsoluteUri);
connectRequest.OperationContext = options.OperationContext;
connectRequest.MediaStreamingOptions = CreateMediaStreamingOptionsInternal(options.MediaStreamingOptions);
connectRequest.TranscriptionOptions = CreateTranscriptionOptionsInternal(options.TranscriptionOptions);

if (options.CallIntelligenceOptions != null && options.CallIntelligenceOptions.CognitiveServicesEndpoint != null)
{
CallIntelligenceOptionsInternal callIntelligenceOptionsInternal = new CallIntelligenceOptionsInternal
{
CognitiveServicesEndpoint = options.CallIntelligenceOptions?.CognitiveServicesEndpoint?.AbsoluteUri
};
connectRequest.CallIntelligenceOptions = callIntelligenceOptionsInternal;
}

return connectRequest;
}

/// <summary>
/// Validates an Https Uri.
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
private static bool IsValidHttpsUri(Uri uri) {
private static bool IsValidHttpsUri(Uri uri)
{
if (uri == null)
return false;
var uriString = uri.AbsoluteUri;
Expand All @@ -704,15 +829,20 @@ private static MediaStreamingOptionsInternal CreateMediaStreamingOptionsInternal
{
return configuration == default
? default
: new MediaStreamingOptionsInternal(configuration.TransportUri.AbsoluteUri, configuration.MediaStreamingTransport, configuration.MediaStreamingContent,
configuration.MediaStreamingAudioChannel);
: new MediaStreamingOptionsInternal(configuration.TransportUri.AbsoluteUri, configuration.MediaStreamingTransport,
configuration.MediaStreamingContent, configuration.MediaStreamingAudioChannel, configuration.StartMediaStreaming,
configuration.EnableBidirectional, configuration.AudioFormat == null ? AudioFormat.Pcm24KMono : configuration.AudioFormat);
}
private static TranscriptionOptionsInternal CreateTranscriptionOptionsInternal(TranscriptionOptions configuration)
{
return configuration == default
? default
: new TranscriptionOptionsInternal(configuration.TransportUrl.AbsoluteUri, configuration.TranscriptionTransport, configuration.Locale,
configuration.StartTranscription);
configuration.StartTranscription.GetValueOrDefault())
{
EnableIntermediateResults = configuration.EnableIntermediateResults,
SpeechRecognitionModelEndpointId = configuration.SpeechRecognitionModelEndpointId
};
}

/// <summary> Initializes a new instance of CallConnection. <see cref="CallConnection"/>.</summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class CallAutomationClientOptions : ClientOptions
/// <summary>
/// The latest version of the CallAutomation service.
/// </summary>
internal const ServiceVersion LatestVersion = ServiceVersion.V2023_10_03_Preview;
internal const ServiceVersion LatestVersion = ServiceVersion.V2024_09_01_Preview;

internal string ApiVersion { get; }

Expand All @@ -43,6 +43,7 @@ public CallAutomationClientOptions(ServiceVersion version = LatestVersion)
ServiceVersion.V2023_06_15_Preview => "2023-06-15-preview",
ServiceVersion.V2023_10_15 => "2023-10-15",
ServiceVersion.V2023_10_03_Preview => "2023-10-03-preview",
ServiceVersion.V2024_09_01_Preview => "2024-09-01-preview",
_ => throw new ArgumentOutOfRangeException(nameof(version)),
};
}
Expand Down Expand Up @@ -71,7 +72,12 @@ public enum ServiceVersion
/// <summary>
/// Latest ALPHA3 (1.2.0-alpha) preview of the CallAutomation service.
/// </summary>
V2023_10_03_Preview = 4
V2023_10_03_Preview = 4,

/// <summary>
/// Latest ALPHA4 (1.4.0-alpha) preview of the CallAutomation service.
/// </summary>
V2024_09_01_Preview = 5
#pragma warning restore CA1707 // Identifiers should not contain underscores
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class AnswerCallEventResult
public CallConnected SuccessResult { get; }

/// <summary>
/// <see cref="AnswerFailed"/> evnet will be returned when the call was not answered.
/// <see cref="AnswerFailed"/> event will be returned once the call is established with AnswerCall.
/// </summary>
/// <value></value>
public AnswerFailed FailureResult { get; }
Expand Down
Loading