> ## Documentation Index
> Fetch the complete documentation index at: https://cloudinary.com/documentation/llms.txt
> Use this file to discover all available pages before exploring further.

# Microsoft Azure Video Indexer


[Cloudinary](https://cloudinary.com) is a cloud-based service that provides an end-to-end image and video management solution including uploads, storage, transformations, optimizations and delivery. Cloudinary's video solution includes a rich set of video transformation capabilities, including cropping, overlays, optimizations, and a large variety of special effects. 

The Microsoft Azure Video Indexer add-on integrates [Microsoft Azure's](https://learn.microsoft.com/en-us/azure/azure-video-indexer/video-indexer-overview) automatic video indexing capabilities with Cloudinary's complete video management and transformation pipeline. 

Cloudinary has currently integrated the following Microsoft Azure Video Indexing services:

* [Video categorization](#video_categorization): Identifies visual objects, brands and actions displayed. Extends Cloudinary's powerful semantic data extraction and tagging features, so that your videos can be automatically tagged according to the automatically detected categories and tags in each video.
* [Video transcription](#video_transcription): Automatically generate speech-to-text transcripts of videos that you or your users upload to your Cloudinary product environment. The add-on supports transcribing videos in almost any [language](#transcription_languages). You can also use the contents of returned transcript files to display the transcript of your video on your page, making your content more skimmable, accessible, and SEO-friendly.

#### Getting started

Before you can use the Microsoft Azure Video Indexer add-on:

* You must have a Cloudinary account. If you don't already have one, you can [sign up](https://cloudinary.com/users/register_free) for a free account. 

* Register for the add-on: make sure you're logged in to your account and then go to the [Add-ons](https://console.cloudinary.com/app/settings/addons) page. For more information about add-on registrations, see [Registering for add-ons](cloudinary_add_ons#registering_for_add_ons).

* Keep in mind that many of the examples on this page use our SDKs. For SDK installation and configuration details, see the relevant [SDK](cloudinary_sdks) guide.
  
* If you're new to Cloudinary, you may want to take a look at the [Developer Kickstart](dev_kickstart) for a hands-on, step-by-step introduction to a variety of features.

> **NOTE**: As of June 2023, the Microsoft Azure Video Indexer no longer supports categorization based on celebrity faces.

## Video categorization

Take a look at the following video of a dog called `jack`:

![jack.mp4](https://res.cloudinary.com/demo/video/upload/jack.mp4)

```nodejs
cloudinary.video("jack")
```

```react
new CloudinaryVideo("jack.mp4");
```

```vue
new CloudinaryVideo("jack.mp4");
```

```angular
new CloudinaryVideo("jack.mp4");
```

```js
new CloudinaryVideo("jack.mp4");
```

```python
CloudinaryVideo("jack").video()
```

```php
(new VideoTag('jack.mp4'));
```

```java
cloudinary.url().transformation(new Transformation().videoTag("jack");
```

```ruby
cl_video_tag("jack")
```

```csharp
cloudinary.Api.UrlVideoUp.BuildVideoTag("jack")
```

```dart
cloudinary.video('jack.mp4').transformation(Transformation());
```

```swift
cloudinary.createUrl().setResourceType("video").generate("jack.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation().resourceType("video").generate("jack.mp4");
```

```flutter
cloudinary.video('jack.mp4').transformation(Transformation());
```

```kotlin
cloudinary.video {
	publicId("jack.mp4") 
}.generate()
```

```jquery
$.cloudinary.video("jack")
```

```react_native
new CloudinaryVideo("jack.mp4");
```

By setting the `categorization` parameter to `azure_video_indexer` when calling Cloudinary's [upload](image_upload_api_reference#upload) or [update](admin_api#update_details_of_an_existing_resource) method, Microsoft is used to automatically classify the scenes of the uploaded or specified existing video. For example:

```multi
|ruby
Cloudinary::Uploader.upload("jack.mp4", 
  resource_type: "video", 
  categorization: "azure_video_indexer")

|php_2
$cloudinary->uploadApi()->upload("jack.mp4", [
    "resource_type" => "video", 
    "categorization" => "azure_video_indexer"]);

|python
cloudinary.uploader.upload("jack.mp4",
  resource_type = "video", 
  categorization = "azure_video_indexer")

|nodejs
cloudinary.v2.uploader
.upload("jack.mp4", 
  { resource_type: "video", 
    categorization: "azure_video_indexer" })
.then(result=>console.log(result)); 

|java
cloudinary.uploader().upload("jack.mp4", 
  ObjectUtils.asMap(
    "resource_type", "video", 
    "categorization", "azure_video_indexer"));

|csharp
var uploadParams = new VideoUploadParams() 
{
  File = new FileDescription(@"jack.mp4"),
  Categorization = "azure_video_indexer"
};
var uploadResult = cloudinary.Upload(uploadParams);  

|go
resp, err := cld.Upload.Upload(ctx, "jack.mp4", uploader.UploadParams{
		ResourceType:   "video",
		Categorization: "azure_video_indexer"})

|android
MediaManager.get().upload("jack.mp4")
  .option("resource_type", "video")
  .option("categorization", "azure_video_indexer").dispatch();

|swift
let params = CLDUploadRequestParams()
  .setCategorization("azure_video_indexer")
  .setResourceType(.video)
var mySig = MyFunction(params)  // your own function that returns a signature generated on your backend
params.setSignature(CLDSignature(signature: mySig.signature, timestamp: mySig.timestamp))
let request = cloudinary.createUploader().signedUpload(url: "jack.mp4", params: params)

|curl
curl https://api.cloudinary.com/v1_1/demo/video/upload -X POST -F 'file=@/path/to/jack.mp4' -F 'categorization=azure_video_indexer' -F 'timestamp=173719931' -F 'api_key=436464676' -F 'signature=a781d61f86a6f818af'

|cli
cld uploader upload "jack.mp4" resource_type="video" categorization="azure_video_indexer"
```

> **TIP**:
>
> :title=Tips

> * You can use **upload presets** to centrally define a set of upload options including add-on operations to apply, instead of specifying them in each upload call. You can define multiple upload presets, and apply different presets in different upload scenarios. You can create new upload presets in the **Upload Presets** page of the [Console Settings](https://console.cloudinary.com/app/settings/upload/presets) or using the [upload_presets](admin_api#upload_presets) Admin API method. From the **Upload** page of the Console Settings, you can also select default upload presets to use for image, video, and raw API uploads (respectively) as well as default presets for image, video, and raw uploads performed via the Media Library UI. 
> **Learn more**: [Upload presets](upload_presets)* You can run multiple categorization add-ons on the resource. The `categorization` parameter accepts a comma-separated list of all the Cloudinary categorization add-ons to run on the resource.

The video analysis and categorization is performed asynchronously after the method call is completed. 

> **NOTE**: The amount of time it takes for analysis and categorization of the video depends on the size and length of the video file itself. You can include a `notification_url` parameter in your request to get a [notification](notifications) to the requested URL when the categorization is ready.

The response of the upload method indicates that the process is in `pending` status.

```json
{ 
  ...
  "info": { 
    "categorization": { 
      "azure_video_indexer": { 
        "status": "pending" 
      }
    }
  },
  ...
}
```

Once the categorization process completes, the information is returned to Cloudinary and stored with your video. The details of the analysis and categorization are also sent to the `notification_url` if this option was included with your method call. For example:

```json
{
  "info_kind": "azure_video_indexer",
  "info_status": "complete",
  "public_id": "jack",
  "uploaded_at": "2019-02-25T10:35:25Z",
  "version": 1551090925,
  "url": "http://res.cloudinary.com/demo/video/upload/v1551090925/jack.mp4",
  "secure_url": "https://res.cloudinary.com/demo/video/upload/v1551090925/jack.mp4",
  "etag": "58caa750581129cdca6ac4496653e186",
  "notification_type": "info",
  "info_data": {
    "en-US": {
      "labels": [
        {
          "id": 0,
          "name": "brown",
          "language": "en-US",
          "instances": [
            {
              "confidence": 0.9379,
              "adjustedStart": "0:00:00",
              "adjustedEnd": "0:00:13.413",
              "start": "0:00:00",
              "end": "0:00:13.413",
              "duration": "0:00:13.413"
            }
          ]
        },
        {
          "id": 1,
          "name": "dog",
          "language": "en-US",
          "instances": [
            {
              "confidence": 1,
              "adjustedStart": "0:00:00",
              "adjustedEnd": "0:00:13.413",
              "start": "0:00:00",
              "end": "0:00:13.413",
              "duration": "0:00:13.413"
            }
          ]
        },
        {
    ...
}
```

The information includes the automatic tagging and categorization information identified by the Microsoft Azure Video Indexer add-on. As can be seen in the example snippet above, various labels (tags) were automatically detected in the uploaded video. Each label is listed together with other information including the start and end times of the relevant video segment. The `confidence` score is a numerical value that represents the confidence level of the detected label, where 1.0 means 100% confidence. 

[//]: # (REMOVING NOTE & LINK ABOUT JSON RESPONSE UNTIL R&D VALIDATES THE UPDATED ADD-ON VERSION - Slack discussion: h t - t p s: //cloudinary.slackDOTcom/archives/C03RTMS832N/p1724664356399979 -- (> **NOTE**: The entire JSON response from Microsoft is included: for detailed information on the response structure see the [Microsoft Video Indexer output] (h t - t p s://learn.microsoftDOTcom/en-us/azure/azure-video-indexer/video-indexer-output-json-v2). -- )

### Automatically adding tags to videos

Automatically categorizing your videos is a useful way to organize your Cloudinary media assets. By providing the `auto_tagging` parameter in an `upload` or `update` call for any video where `azure_video_indexer` was run, the video is automatically assigned tags based on the detected scene *labels* and *brands*. The `auto_tagging` parameter represents the minimum confidence score required for automatic assignment as a resource tag. Assigning these resource tags allows you to list and search videos using Cloudinary's API or Web interface.

The following code example automatically tags an uploaded video with all detected scene labels and brands that have a confidence score higher than 0.6.

```multi
|ruby
Cloudinary::Uploader.upload("jack.mp4", 
  resource_type: "video", 
  categorization: "azure_video_indexer", 
  auto_tagging: 0.6)

|php_2
$cloudinary->uploadApi()->upload("jack.mp4", [
    "resource_type" => "video",
    "categorization" => "azure_video_indexer", 
    "auto_tagging" => 0.6]);

|python
cloudinary.uploader.upload("jack.mp4",
  resource_type = "video",
  categorization = "azure_video_indexer", 
  auto_tagging = 0.6)

|nodejs
cloudinary.v2.uploader
.upload("jack.mp4", 
  { resource_type: "video", 
    categorization: "azure_video_indexer", 
    auto_tagging: 0.6 })
.then(result=>console.log(result)); 

|java
cloudinary.uploader().upload("jack.mp4", 
  ObjectUtils.asMap(
    "resource_type", "video", 
    "categorization", "azure_video_indexer", 
    "auto_tagging", "0.6"));

|csharp
var uploadParams = new VideoUploadParams() 
{
  File = new FileDescription(@"jack.mp4"),
  Categorization = "azure_video_indexer",
  AutoTagging = 0.6
};
var uploadResult = cloudinary.Upload(uploadParams); 

|go
resp, err := cld.Upload.Upload(ctx, "jack.mp4", uploader.UploadParams{
		ResourceType:   "video",
		Categorization: "azure_video_indexer",
		AutoTagging:    0.6})

|android
MediaManager.get().upload("jack.mp4")
  .option("resource_type", "video")
  .option("categorization", "azure_video_indexer")
  .option("auto_tagging", "0.6").dispatch();

|swift
let params = CLDUploadRequestParams()
  .setCategorization("azure_video_indexer")
  .setResourceType(.video)
  setAutoTagging(0.6)
var mySig = MyFunction(params)  // your own function that returns a signature generated on your backend
params.setSignature(CLDSignature(signature: mySig.signature, timestamp: mySig.timestamp))
let request = cloudinary.createUploader().signedUpload(url: "jack.mp4", params: params)

|curl
curl https://api.cloudinary.com/v1_1/demo/video/upload -X POST -F 'file=@/path/to/jack.mp4' -F 'categorization=azure_video_indexer' -F 'auto_tagging=0.6' -F 'timestamp=173719931' -F 'api_key=436464676' -F 'signature=a781d61f86a6f818af'

|cli
cld uploader upload "jack.mp4" resource_type="video" categorization="azure_video_indexer" auto_tagging=0.6
```

The response of the upload call above returns the detected categorization as well as automatically assigning tags. In this case:

```json
{ 
  ...    
  "tags": [ "brown", "dog", "grass", "looking", "sitting", "animal", "white", "mammal" ]   
  ...  

```

### Tagging uploaded videos

You can also use the `update` method to apply auto-tagging to already uploaded videos, based on their public IDs, and then automatically tag them according to the detected categories.

For example, the following video was uploaded to Cloudinary with the 'horses' public ID:

![horses.mp4](https://res.cloudinary.com/demo/video/upload/horses.mp4)

```nodejs
cloudinary.video("horses")
```

```react
new CloudinaryVideo("horses.mp4");
```

```vue
new CloudinaryVideo("horses.mp4");
```

```angular
new CloudinaryVideo("horses.mp4");
```

```js
new CloudinaryVideo("horses.mp4");
```

```python
CloudinaryVideo("horses").video()
```

```php
(new VideoTag('horses.mp4'));
```

```java
cloudinary.url().transformation(new Transformation().videoTag("horses");
```

```ruby
cl_video_tag("horses")
```

```csharp
cloudinary.Api.UrlVideoUp.BuildVideoTag("horses")
```

```dart
cloudinary.video('horses.mp4').transformation(Transformation());
```

```swift
cloudinary.createUrl().setResourceType("video").generate("horses.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation().resourceType("video").generate("horses.mp4");
```

```flutter
cloudinary.video('horses.mp4').transformation(Transformation());
```

```kotlin
cloudinary.video {
	publicId("horses.mp4") 
}.generate()
```

```jquery
$.cloudinary.video("horses")
```

```react_native
new CloudinaryVideo("horses.mp4");
```

The following code sample uses Cloudinary's `update` method to apply automatic video tagging and categorization to the `sample` uploaded video, and then automatically assign resource tags based on the categories detected with over a 60% confidence level.

```multi
|ruby
Cloudinary::Api.update("sample", 
   resource_type: "video", 
   categorization: "azure_video_indexer", 
   auto_tagging: 0.6)

|php_2
$api->update("sample", [
    "resource_type" => "video",
    "categorization" => "azure_video_indexer", 
    "auto_tagging" => 0.6]);

|python
cloudinary.api.update("sample",
  resource_type = "video",
  categorization = "azure_video_indexer", 
  auto_tagging = 0.6)

|nodejs
cloudinary.v2.api
.update("sample", 
  { resource_type: "video", 
    categorization: "azure_video_indexer", 
    auto_tagging: 0.4 })
.then(result=>console.log(result));

|java
cloudinary.api().update("sample", ObjectUtils.asMap(
  "resource_type": "video",
  "categorization", "azure_video_indexer", 
  "auto_tagging", 0.6));

|csharp
var updateParams = new UpdateParams("sample") 
{
  Categorization = "azure_video_indexer",
  ResourceType = "video",
  AutoTagging = 0.6
};
var updateResult = cloudinary.UpdateResource(updateParams); 

|go
resp, err := cld.Admin.UpdateAsset(ctx, admin.UpdateAssetParams{
		PublicID:       "sample",
		Categorization: "azure_video_indexer",
		AutoTagging:    0.6})

|cli
cld admin update "sample" resource_type="video" categorization="azure_video_indexer" auto_tagging=0.6
```

> **TIP**: Whether or not you included a `notification_url` to get a response om the analysis, you can always use the Admin API's [resource](admin_api#get_details_of_a_single_resource_by_public_id) method to return the details of a resource, including the categorization that you already extracted using the `upload` or `update` methods.

## Video transcription

You can request an auto-generated transcript for your video or audio file in one or more languages. 

To request a transcript (in the default US English language), include the `raw_convert` parameter with the value `azure_video_indexer` in your `upload` or `update` call. 

(To request other languages, see [transcription languages](#transcription_languages) below.)

For example, to request transcription on the introduction to a video tutorial on folder permissions (see the full tutorial [here](https://support.cloudinary.com/hc/en-us/articles/360019830172-Adding-Permissions-to-Your-Cloudinary-Folders)):

```multi
|ruby
Cloudinary::Uploader.upload("folder-permissions-tutorial.mp4", 
  resource_type: "video", 
  raw_convert: "azure_video_indexer")

|php_2
$cloudinary->uploadApi()->upload("folder-permissions-tutorial.mp4", [
    "resource_type" => "video", 
    "raw_convert" => "azure_video_indexer"]);

|python
cloudinary.uploader.upload("folder-permissions-tutorial.mp4",
  resource_type = "video", 
  raw_convert = "azure_video_indexer")

|nodejs
cloudinary.v2.uploader
.upload("folder-permissions-tutorial.mp4", 
  { resource_type: "video", 
    raw_convert: "azure_video_indexer" })
.then(result=>console.log(result)); 

|java
cloudinary.uploader().upload("folder-permissions-tutorial.mp4", 
  ObjectUtils.asMap(
    "resource_type", "video", 
    "raw_convert", "azure_video_indexer"));

|csharp
var uploadParams = new VideoUploadParams()
{
  File = new FileDescription(@"folder-permissions-tutorial.mp4"),
  RawConvert = "azure_video_indexer"
};
var uploadResult = cloudinary.Upload(uploadParams);  

|go
resp, err := cld.Upload.Upload(ctx, "folder-permissions-tutorial.mp4", uploader.UploadParams{
		ResourceType: "video",
		RawConvert:   "azure_video_indexer"})

|android
MediaManager.get().upload("folder-permissions-tutorial.mp4")
  .option("resource_type", "video")
  .option("raw_convert", "azure_video_indexer").dispatch();

|swift
let params = CLDUploadRequestParams()
  .setRawConvert("azure_video_indexer")
  .setResourceType(.video)
var mySig = MyFunction(params)  // your own function that returns a signature generated on your backend
params.setSignature(CLDSignature(signature: mySig.signature, timestamp: mySig.timestamp))
let request = cloudinary.createUploader().signedUpload(url: "folder-permissions-tutorial.mp4", params: params)

|curl
curl https://api.cloudinary.com/v1_1/demo/video/upload -X POST -F 'file=@/path/to/folder-permissions-tutorial.mp4' -F 'categorization=azure_video_indexer' -F 'timestamp=173719931' -F 'api_key=436464676' -F 'signature=a781d61f86a6f818af'

|cli
cld uploader upload "folder-permissions-tutorial.mp4" resource_type="video" raw_convert="azure_video_indexer"
```

> **TIP**:
>
> You can use **upload presets** to centrally define a set of upload options including add-on operations to apply, instead of specifying them in each upload call. You can define multiple upload presets, and apply different presets in different upload scenarios. You can create new upload presets in the **Upload Presets** page of the [Console Settings](https://console.cloudinary.com/app/settings/upload/presets) or using the [upload_presets](admin_api#upload_presets) Admin API method. From the **Upload** page of the Console Settings, you can also select default upload presets to use for image, video, and raw API uploads (respectively) as well as default presets for image, video, and raw uploads performed via the Media Library UI. 
> **Learn more**: [Upload presets](upload_presets)

The `azure_video_indexer` parameter value activates a call to the Microsoft Azure Video Indexer API, which is performed asynchronously after your original method call is completed. Thus your original method call response displays a `pending` status:

```json
...
"info": {   
   "raw_convert": {
      "azure_video_indexer": {
        "status": "pending"
      }
    }
 }
...
```

When the `azure_video_indexer` asynchronous request is complete (depending on the length of the video), a new `raw` file is created in your product environment with the same public ID as your video or audio file and with the `en-us.azure.transcript` file extension. You can additionally [request a standard subtitle format](#generating_standard_subtitle_formats) such as 'vtt' or 'srt'. For details on the contents of the generated file, see [Cloudinary transcript files](#cloudinary_transcript_files) below.

If you also provided a `notification_url` in your method call or have defined a [global notification URL](notifications#global_notification_urls) in the Console, the specified URL receives a [notification](notifications) when the indexing process completes:

```json
{
  "info_kind":"azure_video_indexer",
  "info_status":"complete",
  "public_id":"folder-permissions-tutorial",
  ...
}
```

### Cloudinary transcript files

The created `.transcript` file includes details of the audio transcription, for example:

```json 
[
  {
    "confidence":0.9312,
    "transcript":"Many organizations that use cloudinary have thousands if not millions of digital assets stored in their system.",
    "start_time":7.11,
    "end_time":15.1
  },
  {
    "confidence":0.9312,
    "transcript":"However, certain users on the account may not need to access all the images videos and raw files. Because of this cloudinary offers the ability to add a layer of",
    "start_time":15.1,
    "end_time":26.59
  },
 ...
]
```

Each excerpt of text has a `confidence` value, and a breakdown of specific start and end times. 

### Generating standard srt or vtt transcripts

If you want to include the transcript as a separate track for a video player, you can also request that cloudinary create an [SRT](https://en.wikipedia.org/wiki/SubRip) and/or [WebVTT](https://en.wikipedia.org/wiki/WebVTT) raw file by including the `srt` and/or `vtt` qualifiers (separated by a colon) with the `azure_video_indexer` value. For example, to upload a video and also request both `srt` and `vtt` files with the transcript:

```multi
|ruby
Cloudinary::Uploader.upload("folder-permissions-tutorial.mp4", 
  resource_type: "video", 
  raw_convert: "azure_video_indexer:srt:vtt")

|php_2
$cloudinary->uploadApi()->upload("folder-permissions-tutorial.mp4", [
    "resource_type" => "video", 
    "raw_convert" => "azure_video_indexer:srt:vtt"]);

|python
cloudinary.uploader.upload("folder-permissions-tutorial.mp4",
  resource_type = "video", 
  raw_convert = "azure_video_indexer:srt:vtt")

|nodejs
cloudinary.v2.uploader
.upload("folder-permissions-tutorial.mp4",
  { resource_type: "video", 
    raw_convert: "azure_video_indexer:srt:vtt" })
.then(result=>console.log(result)); 

|java
cloudinary.uploader().upload("folder-permissions-tutorial.mp4", 
  ObjectUtils.asMap(
    "resource_type", "video", 
    "raw_convert", "azure_video_indexer:srt:vtt"));

|csharp
var uploadParams = new VideoUploadParams()
{
  File = new FileDescription(@"folder-permissions-tutorial.mp4"),
  RawConvert = "azure_video_indexer:srt:vtt"
};
var uploadResult = cloudinary.Upload(uploadParams);

|go
resp, err := cld.Upload.Upload(ctx, "folder-permissions-tutorial.mp4", uploader.UploadParams{
		ResourceType: "video",
		RawConvert:   "azure_video_indexer:srt:vtt"})

|android
MediaManager.get().upload("folder-permissions-tutorial.mp4")
  .option("resource_type", "video")
  .option("raw_convert", "azure_video_indexer:srt:vtt").dispatch();
  
|swift
let params = CLDUploadRequestParams()
  .setRawConvert("azure_video_indexer:srt:vtt")
  .setResourceType(.video)
var mySig = MyFunction(params)  // your own function that returns a signature generated on your backend
params.setSignature(CLDSignature(signature: mySig.signature, timestamp: mySig.timestamp))
let request = cloudinary.createUploader().signedUpload(url: "folder-permissions-tutorial.mp4", params: params)

|curl
curl https://api.cloudinary.com/v1_1/demo/video/upload -X POST -F 'file=@/path/to/folder-permissions-tutorial.mp4' -F 'categorization=azure_video_indexer:srt:vtt' -F 'timestamp=173719931' -F 'api_key=436464676' -F 'signature=a781d61f86a6f818af'

|cli
cld uploader upload "folder-permissions-tutorial.mp4" resource_type="video" raw_convert="azure_video_indexer:srt:vtt"
```

When the request completes, there will be 4 files associated with the uploaded video in your product environment:

```
.../video/upload/folder-permissions-tutorial.mp4    // the source video
.../raw/upload/folder-permissions-tutorial.en-US.azure.transcript
.../raw/upload/folder-permissions-tutorial.en-US.azure.srt
.../raw/upload/folder-permissions-tutorial.en-US.azure.vtt
```

> **NOTES**:
>
> * If you also specify one or more [languages](#transcript_languages) in the `azure_video_indexer` transcript request:- the request for subtitle formats must be specified before the languages (e.g., `azure_video_indexer:srt:vtt:fr-FR`)- the generated files will include the language and region code in the generated filename (e.g., `folder-permissions-tutorial.fr-FR.azure.transcript.vtt`). 

> * No speech recognition tool is 100% accurate. If exact accuracy is important for your video, you can download the generated `.transcript`, `.srt` or `.vtt` file, edit them manually, and re-upload them (overwriting the original files).

### Transcription languages

By default, the Azure Video Indexer add-on assumes the audio in your audio/video source file is in US English. If the audio is in any other language, you must specify a source language in your request.

You can also optionally request to get [additional translated transcripts](#requesting_additional_transcript_languages) in other languages.

#### Specifying the source language

To indicate the **source** language, add both the language and region code to the `raw_convert` value (e.g., `azure_video_indexer:fr-FR`). The resulting transcript file will always be generated in the same language, and the generated index file will include the language code in the name (`{public_id}.{lang-code}.azure.transcript`).

For a full list of supported source languages and region codes, scroll to the **language** section of the [Azure Video Indexer upload options](https://api-portal.videoindexer.ai/api-details#api=Operations&operation=Upload-Video).

For example, to request a video transcript in French when uploading the video `Paris.mp4`:

```multi
|ruby
Cloudinary::Uploader.upload("Paris.mp4", 
  resource_type: "video", 
  raw_convert: "azure_video_indexer:fr-FR")

|php_2
$cloudinary->uploadApi()->upload("Paris.mp4", [
    "resource_type" => "video", 
    "raw_convert" => "azure_video_indexer:fr-FR"]);

|python
cloudinary.uploader.upload("Paris.mp4",
  resource_type = "video", 
  raw_convert = "azure_video_indexer:fr-FR")

|nodejs
cloudinary.v2.uploader
.upload("Paris.mp4", 
  { resource_type: "video", 
    raw_convert: "azure_video_indexer:fr-FR" })
.then(result=>console.log(result)); 

|java
cloudinary.uploader().upload("Paris.mp4", 
  ObjectUtils.asMap(
    "resource_type", "video", 
    "raw_convert", "azure_video_indexer:fr-FR"));

|csharp
var uploadParams = new VideoUploadParams()
{
  File = new FileDescription(@"Paris.mp4"),
  RawConvert = "azure_video_indexer:fr-FR"
};
var uploadResult = cloudinary.Upload(uploadParams); 

|go
resp, err := cld.Upload.Upload(ctx, "Paris.mp4", uploader.UploadParams{
		ResourceType: "video",
		RawConvert:   "azure_video_indexer:fr-FR"})

|android
MediaManager.get().upload("Paris.mp4")
  .option("resource_type", "video")
  .option("raw_convert", "azure_video_indexer:fr-FR").dispatch();

|swift
let params = CLDUploadRequestParams()
  .setRawConvert("azure_video_indexer:fr-FR")
  .setResourceType(.video)
var mySig = MyFunction(params)  // your own function that returns a signature generated on your backend
params.setSignature(CLDSignature(signature: mySig.signature, timestamp: mySig.timestamp))
let request = cloudinary.createUploader().signedUpload(url: "Paris.mp4", params: params)

|curl
curl https://api.cloudinary.com/v1_1/demo/video/upload -X POST -F 'file=@/path/to/Paris.mp4' -F 'categorization=azure_video_indexer:fr-FR' -F 'timestamp=173719931' -F 'api_key=436464676' -F 'signature=a781d61f86a6f818af'

|cli
cld uploader upload "Paris.mp4" resource_type="video" raw_convert="azure_video_indexer:fr-FR"
```
#### Auto-detecting the source language
If you're not sure which language video or audio files might have been recorded in, you can pass `auto` as the source language, This instructs the Azure Video Indexer add-on to auto-detect the language based on the first few spoken words in the file. As with specified languages, the resulting transcript filename will include the language and region code.

#### Requesting additional transcript languages

The Azure Video Indexer add-on will always generate the transcript into the language requested as the source language.  But you can optionally request additional transcripts in other languages, specifying the target languages immediately after the source language, separated by colons.  

For example, if you specify: `raw_convert: "azure_video_indexer:fr-FR:pl-PL:he-IL:et-EE"` 
The add-on first indexes the audio in your audio or video file based on French as the source language and generates the French transcript file.  It then generates translated transcripts in Polish, Hebrew, and Estonian.

> **NOTE**:
>
> :title=Important guidelines for requesting multiple languages

> * The first file you specify must be the language and region of the audio in the source file. The supported languages are those listed in the **language** section of the [Azure Video Indexer upload options](https://api-portal.videoindexer.ai/api-details#api=Operations&operation=Upload-Video)

> * For the additional translated transcript files, you can specify any language-region code listed in the **language** section of the [Azure Video Indexer Index options](https://api-portal.videoindexer.ai/api-details#api=Operations&operation=Get-Video-Index). This list contains all languages supported for the source file, as well as some additional options.

> * When specifying multiple languages, [auto](#auto_detecting_the_source_language) is not supported for the source language.

> * You can specify a maximum of 5 languages (including the source language). 

> * Even if US English is the source language, make sure to specify it as the source language.

> * If also requesting [srt or vtt transcript formats](#generating_standard_srt_or_vtt_transcripts), those must be specified before the languages (e.g., `azure_video_indexer:srt:vtt:en-US:fr-FR`)- the generated files will include the language and region code in the generated filename (e.g., `folder-permissions-tutorial.fr-FR.azure.transcript.vtt`). 

> * When requesting multiple languages, you'll be charged units from your add-on quota for each indexing language specified according to the length of the audio in the source file.

### Displaying transcripts as subtitle overlays

Cloudinary can automatically generate subtitles from the returned transcripts. To automatically embed subtitles with your video, add the `subtitles` property of the `overlay` parameter (`l_subtitles` in URLs), followed by the public ID to the raw transcript file (including the extension).

For example, the following URL delivers the video with automatically generated subtitles:

![Display automatically generated subtitles on the video using the Microsoft transcription add-on](https://res.cloudinary.com/demo/video/upload/l_subtitles:folder-permissions-tutorial.en-us.azure.transcript/fl_layer_apply/folder-permissions-tutorial.mp4 "width:800")

```nodejs
cloudinary.video("folder-permissions-tutorial", {transformation: [
  {overlay: {resource_type: "subtitles", public_id: "folder-permissions-tutorial.en-us.azure.transcript"}},
  {flags: "layer_apply"}
  ]})
```

```react
new CloudinaryVideo("folder-permissions-tutorial.mp4").overlay(
  source(subtitles("folder-permissions-tutorial.en-us.azure.transcript"))
);
```

```vue
new CloudinaryVideo("folder-permissions-tutorial.mp4").overlay(
  source(subtitles("folder-permissions-tutorial.en-us.azure.transcript"))
);
```

```angular
new CloudinaryVideo("folder-permissions-tutorial.mp4").overlay(
  source(subtitles("folder-permissions-tutorial.en-us.azure.transcript"))
);
```

```js
new CloudinaryVideo("folder-permissions-tutorial.mp4").overlay(
  source(subtitles("folder-permissions-tutorial.en-us.azure.transcript"))
);
```

```python
CloudinaryVideo("folder-permissions-tutorial").video(transformation=[
  {'overlay': {'resource_type': "subtitles", 'public_id': "folder-permissions-tutorial.en-us.azure.transcript"}},
  {'flags': "layer_apply"}
  ])
```

```php
(new VideoTag('folder-permissions-tutorial.mp4'))
	->overlay(Overlay::source(
	Source::subtitles("folder-permissions-tutorial.en-us.azure.transcript")));
```

```java
cloudinary.url().transformation(new Transformation()
  .overlay(new SubtitlesLayer().publicId("folder-permissions-tutorial.en-us.azure.transcript")).chain()
  .flags("layer_apply")).videoTag("folder-permissions-tutorial");
```

```ruby
cl_video_tag("folder-permissions-tutorial", transformation: [
  {overlay: {resource_type: "subtitles", public_id: "folder-permissions-tutorial.en-us.azure.transcript"}},
  {flags: "layer_apply"}
  ])
```

```csharp
cloudinary.Api.UrlVideoUp.Transform(new Transformation()
  .Overlay(new SubtitlesLayer().PublicId("folder-permissions-tutorial.en-us.azure.transcript")).Chain()
  .Flags("layer_apply")).BuildVideoTag("folder-permissions-tutorial")
```

```dart
cloudinary.video('folder-permissions-tutorial.mp4').transformation(Transformation()
	.overlay(Overlay.source(
	Source.subtitles("folder-permissions-tutorial.en-us.azure.transcript"))));
```

```swift
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation()
  .setOverlay("subtitles:folder-permissions-tutorial.en-us.azure.transcript").chain()
  .setFlags("layer_apply")).generate("folder-permissions-tutorial.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation()
  .overlay(new SubtitlesLayer().publicId("folder-permissions-tutorial.en-us.azure.transcript")).chain()
  .flags("layer_apply")).resourceType("video").generate("folder-permissions-tutorial.mp4");
```

```flutter
cloudinary.video('folder-permissions-tutorial.mp4').transformation(Transformation()
	.overlay(Overlay.source(
	Source.subtitles("folder-permissions-tutorial.en-us.azure.transcript"))));
```

```kotlin
cloudinary.video {
	publicId("folder-permissions-tutorial.mp4")
	 overlay(Overlay.source(
	Source.subtitles("folder-permissions-tutorial.en-us.azure.transcript"))) 
}.generate()
```

```jquery
$.cloudinary.video("folder-permissions-tutorial", {transformation: [
  {overlay: new cloudinary.SubtitlesLayer().publicId("folder-permissions-tutorial.en-us.azure.transcript")},
  {flags: "layer_apply"}
  ]})
```

```react_native
new CloudinaryVideo("folder-permissions-tutorial.mp4").overlay(
  source(subtitles("folder-permissions-tutorial.en-us.azure.transcript"))
);
```

As with any [subtitle overlay](video_layers#subtitles), you can use transformation parameters to make a variety of formatting adjustments when you overlay an automatically generated transcript file, including choice of font, font size, fill, outline color, and gravity.

For example, these subtitles are displayed using the Times font, size 20, in a blue color, and located on the top of the screen (north):

![Transformed transcription](https://res.cloudinary.com/demo/video/upload/co_blue,l_subtitles:times_20:folder-permissions-tutorial.en-us.azure.transcript/fl_layer_apply,g_north/folder-permissions-tutorial.mp4 "width:800")

```nodejs
cloudinary.video("folder-permissions-tutorial", {transformation: [
  {color: "blue", overlay: {font_family: "times", font_size: 20, resource_type: "subtitles", public_id: "folder-permissions-tutorial.en-us.azure.transcript"}},
  {flags: "layer_apply", gravity: "north"}
  ]})
```

```react
new CloudinaryVideo("folder-permissions-tutorial.mp4").overlay(
  source(
    subtitles("folder-permissions-tutorial.en-us.azure.transcript")
      .textStyle(new TextStyle("times", 20))
      .textColor("blue")
  ).position(new Position().gravity(compass("north")))
);
```

```vue
new CloudinaryVideo("folder-permissions-tutorial.mp4").overlay(
  source(
    subtitles("folder-permissions-tutorial.en-us.azure.transcript")
      .textStyle(new TextStyle("times", 20))
      .textColor("blue")
  ).position(new Position().gravity(compass("north")))
);
```

```angular
new CloudinaryVideo("folder-permissions-tutorial.mp4").overlay(
  source(
    subtitles("folder-permissions-tutorial.en-us.azure.transcript")
      .textStyle(new TextStyle("times", 20))
      .textColor("blue")
  ).position(new Position().gravity(compass("north")))
);
```

```js
new CloudinaryVideo("folder-permissions-tutorial.mp4").overlay(
  source(
    subtitles("folder-permissions-tutorial.en-us.azure.transcript")
      .textStyle(new TextStyle("times", 20))
      .textColor("blue")
  ).position(new Position().gravity(compass("north")))
);
```

```python
CloudinaryVideo("folder-permissions-tutorial").video(transformation=[
  {'color': "blue", 'overlay': {'font_family': "times", 'font_size': 20, 'resource_type': "subtitles", 'public_id': "folder-permissions-tutorial.en-us.azure.transcript"}},
  {'flags': "layer_apply", 'gravity': "north"}
  ])
```

```php
(new VideoTag('folder-permissions-tutorial.mp4'))
	->overlay(Overlay::source(
	Source::subtitles("folder-permissions-tutorial.en-us.azure.transcript")
	->textStyle((new TextStyle("times",20)))
	->textColor(Color::BLUE)
	)
	->position((new Position())
	->gravity(
	Gravity::compass(
	Compass::north()))
	)
	);
```

```java
cloudinary.url().transformation(new Transformation()
  .color("blue").overlay(new SubtitlesLayer().fontFamily("times").fontSize(20).publicId("folder-permissions-tutorial.en-us.azure.transcript")).chain()
  .flags("layer_apply").gravity("north")).videoTag("folder-permissions-tutorial");
```

```ruby
cl_video_tag("folder-permissions-tutorial", transformation: [
  {color: "blue", overlay: {font_family: "times", font_size: 20, resource_type: "subtitles", public_id: "folder-permissions-tutorial.en-us.azure.transcript"}},
  {flags: "layer_apply", gravity: "north"}
  ])
```

```csharp
cloudinary.Api.UrlVideoUp.Transform(new Transformation()
  .Color("blue").Overlay(new SubtitlesLayer().FontFamily("times").FontSize(20).PublicId("folder-permissions-tutorial.en-us.azure.transcript")).Chain()
  .Flags("layer_apply").Gravity("north")).BuildVideoTag("folder-permissions-tutorial")
```

```dart
cloudinary.video('folder-permissions-tutorial.mp4').transformation(Transformation()
	.overlay(Overlay.source(
	Source.subtitles("folder-permissions-tutorial.en-us.azure.transcript")
	.textStyle(TextStyle("times",20))
	.textColor(Color.BLUE)
	)
	.position(Position()
	.gravity(
	Gravity.compass(
	Compass.north()))
	)
	));
```

```swift
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation()
  .setColor("blue").setOverlay("subtitles:times_20:folder-permissions-tutorial.en-us.azure.transcript").chain()
  .setFlags("layer_apply").setGravity("north")).generate("folder-permissions-tutorial.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation()
  .color("blue").overlay(new SubtitlesLayer().fontFamily("times").fontSize(20).publicId("folder-permissions-tutorial.en-us.azure.transcript")).chain()
  .flags("layer_apply").gravity("north")).resourceType("video").generate("folder-permissions-tutorial.mp4");
```

```flutter
cloudinary.video('folder-permissions-tutorial.mp4').transformation(Transformation()
	.overlay(Overlay.source(
	Source.subtitles("folder-permissions-tutorial.en-us.azure.transcript")
	.textStyle(TextStyle("times",20))
	.textColor(Color.BLUE)
	)
	.position(Position()
	.gravity(
	Gravity.compass(
	Compass.north()))
	)
	));
```

```kotlin
cloudinary.video {
	publicId("folder-permissions-tutorial.mp4")
	 overlay(Overlay.source(
	Source.subtitles("folder-permissions-tutorial.en-us.azure.transcript") {
	 textStyle(TextStyle("times",20))
	 textColor(Color.BLUE)
	 }) {
	 position(Position() {
	 gravity(
	Gravity.compass(
	Compass.north()))
	 })
	 }) 
}.generate()
```

```jquery
$.cloudinary.video("folder-permissions-tutorial", {transformation: [
  {color: "blue", overlay: new cloudinary.SubtitlesLayer().fontFamily("times").fontSize(20).publicId("folder-permissions-tutorial.en-us.azure.transcript")},
  {flags: "layer_apply", gravity: "north"}
  ]})
```

```react_native
new CloudinaryVideo("folder-permissions-tutorial.mp4").overlay(
  source(
    subtitles("folder-permissions-tutorial.en-us.azure.transcript")
      .textStyle(new TextStyle("times", 20))
      .textColor("blue")
  ).position(new Position().gravity(compass("north")))
);
```

#### Subtitle length and confidence levels

Microsoft returns transcript excerpts of varying lengths. When displaying subtitles, long excerpts are automatically divided into 20 word entities and displayed on two lines.

You can also optionally set a minimum confidence level for your subtitles, for example: `l_subtitles:my-video-id.en-us.azure.transcript:90`. In this case, any excerpt that Microsoft returns with a lower confidence value will be omitted from the subtitles. Keep in mind that in some cases, this may exclude several sentences at once.

### Displaying transcripts as a separate track

Instead of embedding a transcript in your video as an overlay, you can alternatively add [returned vtt or srt transcript files](#generating_standard_srt_or_vtt_transcripts) as a separate track for a video player. This way, the subtitles can be controlled (toggled on/off) separately from the video itself. For example, to add the video and transcript sources for an HTML5 video player:

```html
<video crossorigin autobuffer controls muted 
  poster="https://res.cloudinary.com/demo/video/upload/w_800/folder-permissions-tutorial.jpg" >
     <source id="mp4" src="https://res.cloudinary.com/demo/video/upload/w_800/folder-permissions-tutorial.mp4" type="video/mp4">
     <track label="English" kind="subtitles" srclang="en" src="https://res.cloudinary.com/demo/raw/upload/folder-permissions-tutorial.en-US.azure.vtt" default>
</video>
```

     
     

> **NOTE**: If you're using the Cloudinary video player, you can [add subtitles and captions](video_player_customization#subtitles_and_captions) as a separate text track by using the `textTracks` parameter.