Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 47 additions & 34 deletions ServerRecording/README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -6,83 +6,96 @@ products:
- azure
- azure-communication-services
---

[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure-Samples%2Fcommunication-services-web-calling-hero%2Fmain%2Fdeploy%2Fazuredeploy.json)

# Recording APIs Sample

This is a sample application to show how the Azure Communication Services server calling SDK can be used to build a call recording feature.

Its a Java web application powered by Spring boot to connect this application with Azure Communication Services.
It's a Java web application powered by Spring Boot to connect this application with Azure Communication Services.

## Prerequisites

- Create an Azure account with an active subscription. For details, see [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F)
- [Visual Studio Code](https://code.visualstudio.com/download)
- [Java Development Kit (JDK) version 11 or above](https://docs.microsoft.com/azure/developer/java/fundamentals/java-jdk-install)
- [Apache Maven](https://maven.apache.org/download.cgi)
- [Visual Studio code (2019 and above)](https://code.visualstudio.com/download)
- [Spring boot framework v- 2.5.0](https://spring.io/projects/spring-boot)
- Create an Azure Communication Services resource. For details, see [Create an Azure Communication Resource](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). You'll need to record your resource **connection string** for this quickstart.
- Create a webhook and subscribe to the recording events [Create webhook](https://docs.microsoft.com/azure/communication-services/quickstarts/voice-video-calling/download-recording-file-sample)
- [Spring Boot framework v- 2.5.0](https://spring.io/projects/spring-boot)
- An Azure account with an active subscription. For details, see here to [create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F).
- [Create container registry](https://docs.microsoft.com/en-us/azure/developer/java/spring-framework/deploy-spring-boot-java-app-on-linux#create-an-azure-container-registry-to-use-as-a-private-docker-registry)
- [Create an Azure Communication Resource](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). You'll need to record your resource\'s **connection string** for this quickstart.
- [Create a webhook](https://docs.microsoft.com/azure/communication-services/quickstarts/voice-video-calling/download-recording-file-sample) and subscribe to the recording events.

## Code structure

- ./ServerRecording/src/main/java/com/acsrecording/api/controllers : Server app core logics to make Api calls that connects with the Azure Communication Services Web Calling SDK
- ./ServerRecording/pom.xml : XML file which contains project and package configurations
- ./ServerRecording/src/main/resources/config.properties : config file which contains user level configurations
- ./src/main/java/com/acsrecording/api/Controller : Server app core logic to make Api calls that connect with the Azure Communication Services Web Calling SDK
- ./pom.xml : XML file which contains project and package configurations
- ./src/main/resources/config.properties : config file which contains user level configurations

## Before running the sample for the first time
1. Get the `Connection String` from the Azure portal. For more information on connection strings, see [Create an Azure Communication Resources](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource)
2. Once you get the config keys add the keys to the **resources/config.properties** file found under the ./ServerRecording/src/main/resources folder.
- Input your connection string in the variable: `Connectionstring`
- Input your blob storage connectionString string in the variable: `BlobStorageConnectionString`
2. Once you get the config keys, add the keys to the **resources/config.properties** file found under the ./src/main/resources folder.
- Input your ACS connection string in the variable: `ACSConnectionString`
- Input your blob storage connection string in the variable: `BlobStorageConnectionString`
- Input blob container name for recorded media in the variable `ContainerName`
- Input recording callback url for start recording api in the variable `CallbackUri`

## Locally deploying the sample app

1. Build java code : mvn clean install
1. Build java code : `mvn clean install`

2. Run app locally : mvn spring-boot:run
2. Run app locally : `mvn spring-boot:run`

3. Use postman or any debugging tool and open url - http://localhost:8080
3. Use [Postman](https://www.postman.com/) or any debugging tool and open url - http://localhost:8080
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ravithanneeru - I just went through this as well.
I think that advising to 'open url - http://localhost:8080' is insufficient instructions.
Firstly, that just returns a 404 as there is no default route.
Secondly, all of the controller endpoints require a parameter - e.g. serverCallId in .....

@GetMapping("/startRecording")
public StartCallRecordingResult startRecording(String serverCallId)

I tried a few different ways to reference serverCallId, but since it is not directly stated here, I wasn't sure how. When I went to reference Spring Boot, it seems that serverCallId would need to be specified in the GetMapping via something like @GetMapping("/startRecording/{serverCallId}")

I fixed similar things to what I suggested and what was fixed in the C# quickstart, but can you please continue this based on my comments above?

cc @bharat-kalyan-namburi


### Troubleshooting
## Troubleshooting

1. Solution doesn\'t build, it throws errors during NPM installation/build
1. Solution doesn\'t build / It throws errors during MVN installation/build

- Check if all the config keys are present
- Check if all the config keys are present, and rebuild with `mvn package`, then `mvn clean install`

- Run mvn package, then mvn clean install
- After installing the JDK and building, if you see "invalid target release: 11", verify that your JAVA_HOME variable does in fact point to your Java 11 installation (as opposed to a previous installation).

## Publish to Azure

1. Build java code using : mvn clean install
1. Build java code :

2. Run app locally using : mvn spring-boot:run
```
mvn clean install
```

1. Run app locally using :

3. Create container registry - follow steps in link - https://docs.microsoft.com/azure/developer/java/spring-framework/deploy-spring-boot-java-app-on-linux
```
mvn spring-boot:run
```

4. Login to Azure :
1. Login to Azure :

```azurecli
using az login
az login
```

5. Login to Azure Container Registry :
1. Login to your Azure Container Registry :

```azurecli
az acr login --name <registryName>
```

6. Build docker image using : mvn compile jib:dockerBuild
1. Build docker image using :

```
mvn compile jib:dockerBuild
```

7. Run image locally to validate using : docker run -it --rm -p 8080:8080 <registryName>.azurecr.io/<projectNameAndVersion>
1. Run image locally to validate using :
```
docker run -it --rm -p 8080:8080 {registryName}.azurecr.io/{projectNameAndVersion}
```

8. Push docker image to ACR using : docker push <registryName>.azurecr.io/<projectNameAndVersion>
1. Push docker image to ACR using :
```
docker push {registryName}.azurecr.io/{projectNameAndVersion}
```

9.Create web app by following steps in link : https://docs.microsoft.com/azure/developer/java/spring-framework/deploy-spring-boot-java-app-on-linux
1. Create web app by following steps in link : https://docs.microsoft.com/en-us/azure/developer/java/spring-framework/deploy-spring-boot-java-app-on-linux#create-a-web-app-on-linux-on-azure-app-service-using-your-container-image

## Additional Reading

- [Azure Communication Calling SDK](https://docs.microsoft.com/azure/communication-services/concepts/voice-video-calling/calling-sdk-features) - To learn more about the calling web sdk
- [Azure Communication Calling SDK](https://docs.microsoft.com/azure/communication-services/concepts/voice-video-calling/calling-sdk-features) - To learn more about the calling web sdk.
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@ public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public class CallRecordingController {

CallRecordingController() {
ConfigurationManager configurationManager = ConfigurationManager.getInstance();
String connectionString = configurationManager.getAppSettings("Connectionstring");
String connectionString = configurationManager.getAppSettings("ACSConnectionString");
container = configurationManager.getAppSettings("ContainerName");
recordingStateCallbackUrl = configurationManager.getAppSettings("CallbackUri");
blobStorageConnectionString = configurationManager.getAppSettings("BlobStorageConnectionString");
Expand All @@ -73,7 +73,7 @@ public StartCallRecordingResult startRecording(String serverCallId) {
var output = response.getValue();

logger.log(Level.INFO, "Start Recording response --> " + getResponse(response) + "\n recording ID: " + response.getValue().getRecordingId());
if(!recordingDataMap.containsKey(serverCallId)){
if (!recordingDataMap.containsKey(serverCallId)) {
recordingDataMap.put(serverCallId, "");
}
recordingDataMap.replace(serverCallId, output.getRecordingId());
Expand All @@ -86,8 +86,8 @@ public StartCallRecordingResult startRecording(String serverCallId) {
}

@GetMapping("/pauseRecording")
public void pauseRecording(String serverCallId, String recordingId){
if(!Strings.isNullOrEmpty(serverCallId)){
public void pauseRecording(String serverCallId, String recordingId) {
if (!Strings.isNullOrEmpty(serverCallId)) {
if (Strings.isNullOrEmpty(recordingId))
{
recordingId = recordingDataMap.get(serverCallId);
Expand All @@ -106,8 +106,8 @@ public void pauseRecording(String serverCallId, String recordingId){
}

@GetMapping("/resumeRecording")
public void resumeRecording(String serverCallId, String recordingId){
if(!Strings.isNullOrEmpty(serverCallId)){
public void resumeRecording(String serverCallId, String recordingId) {
if (!Strings.isNullOrEmpty(serverCallId)) {
if (Strings.isNullOrEmpty(recordingId))
{
recordingId = recordingDataMap.get(serverCallId);
Expand All @@ -126,8 +126,8 @@ public void resumeRecording(String serverCallId, String recordingId){
}

@GetMapping("/stopRecording")
public void stopRecording(String serverCallId, String recordingId){
if(!Strings.isNullOrEmpty(serverCallId)){
public void stopRecording(String serverCallId, String recordingId) {
if (!Strings.isNullOrEmpty(serverCallId)) {
if (Strings.isNullOrEmpty(recordingId))
{
recordingId = recordingDataMap.get(serverCallId);
Expand Down Expand Up @@ -174,13 +174,13 @@ public CallRecordingState getRecordingState(String serverCallId, String recordin
}

@PostMapping(value = "/getRecordingFile", consumes = "application/json", produces = "application/json")
public ResponseEntity<?> getRecordingFile (@RequestBody String eventGridEventJsonData){
public ResponseEntity<?> getRecordingFile (@RequestBody String eventGridEventJsonData) {

logger.log(Level.INFO, "Entered getRecordingFile API");

List<EventGridEvent> eventGridEvents = EventGridEvent.fromString(eventGridEventJsonData);

if(eventGridEvents.stream().count() > 0)
if (eventGridEvents.stream().count() > 0)
{
EventGridEvent eventGridEvent = eventGridEvents.get(0);
logger.log(Level.INFO, "Event type is --> " + eventGridEvent.getEventType());
Expand All @@ -196,13 +196,13 @@ public ResponseEntity<?> getRecordingFile (@RequestBody String eventGridEventJso
responseData.setValidationResponse(subscriptionValidationEvent.getValidationCode());

return new ResponseEntity<>(responseData, HttpStatus.OK);
} catch (Exception e){
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}

if(eventGridEvent.getEventType().equals(SystemEventNames.COMMUNICATION_RECORDING_FILE_STATUS_UPDATED)){
if (eventGridEvent.getEventType().equals(SystemEventNames.COMMUNICATION_RECORDING_FILE_STATUS_UPDATED)) {
try {
AcsRecordingFileStatusUpdatedEventData acsRecordingFileStatusUpdatedEventData = eventData.toObject(AcsRecordingFileStatusUpdatedEventData.class);
AcsRecordingChunkInfoProperties recordingChunk = acsRecordingFileStatusUpdatedEventData
Expand All @@ -228,13 +228,14 @@ public ResponseEntity<?> getRecordingFile (@RequestBody String eventGridEventJso
);

return new ResponseEntity<>(true, HttpStatus.OK);
} catch (Exception e){
} catch (Exception e) {
e.printStackTrace();
logger.log(Level.SEVERE, e.getMessage());
return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
else{
else
{
return new ResponseEntity<>(eventGridEvent.getEventType() + " is not handled.", HttpStatus.BAD_REQUEST);
}
}
Expand Down
2 changes: 1 addition & 1 deletion ServerRecording/src/main/resources/config.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Connectionstring=endpoint=https://REDACTED.communication.azure.com/;accesskey=QWNjZXNzS2V5
ACSConnectionString=endpoint=https://REDACTED.communication.azure.com/;accesskey=QWNjZXNzS2V5
BlobStorageConnectionString=DefaultEndpointsProtocol=https;AccountName=REDACTED;AccountKey=QWNjZXNzS2V5;EndpointSuffix=core.windows.net
ContainerName=acs-recording-container-name
CallbackUri=http://localhost:3000/