Adaptive bitrate streaming

Overview

Adaptive bitrate streaming is a video delivery technique that adjusts the quality of a video stream in real time according to detected bandwidth and CPU capacity. This enables videos to start quicker, with fewer buffering interruptions, and at the best possible quality for the current device and network connection, to maximize user experience.

For example, as a video begins to download, the client can play the first few seconds of the movie at a low quality to get started quickly. When the user’s video buffer is full and assuming CPU utilization is low, the client player may switch to a higher quality stream to enhance the viewing experience. If the buffer later drops below certain levels, or CPU utilization jumps above certain thresholds, the client may switch to a lower quality stream.

At the start of the streaming session, the client software downloads a master playlist file containing the metadata for the various sub-streams that are available. The client software then decides what to download from the available media files, based on predefined factors such as device type, resolution, current bandwidth, size, etc.

To deliver videos using adaptive streaming, you need multiple copies of your video prepared at different resolutions, qualities, and data rates. These are called video representations (also sometimes known as variants). You also need index and streaming files for each representation. The master file references the available representations, provides information required for the client to choose between them, and includes other required metadata, depending on the delivery protocol.

Cloudinary can automatically generate and deliver all of these files from a single original video, transcoded to either or both of the following protocols:

  • HTTP Live Streaming (HLS)
  • Dynamic Adaptive Streaming over HTTP (MPEG-DASH)

See also: HTTP Live Streaming feature overview.

To deliver videos using HLS or MPEG-DASH Adaptive Streaming:

  1. Select a predefined streaming profile
  2. Upload your video with an eager transformation
  3. Deliver the video

Note
If you have a special use-case that does not enable you to use the built-in streaming profiles to automatically generate the master playlist file and all the relevant representations, you can still have Cloudinary create the index and streaming files for each streaming transformation (representation) that you define, and then you can create the master playlist file manually. For details, see Manually creating representations and master files.

Step 1. Select a streaming profile

Cloudinary provides a collection of predefined streaming profiles, where each profile defines a set of representations according to suggested best practices. Select a predefined profile based on your performance and codec requirements.

For example, the 4k profile creates 9 different representations in 16:9 aspect ratio, from extremely high quality to low quality, while the sd profile creates only 3 representations, all in 4:3 aspect ratio. Other commonly used profiles include the hd and full_hd profiles. You can also select a profile that uses a more advanced codec such as h265 or vp9, these profiles create the same representations but with slightly different settings.

Below is the list of predefined streaming profiles for the h264, h265 and vp9 codecs:

Codec Profiles Supported for
h264 4k, full_hd, full_hd_wifi, full_hd_lean, hd, hd_lean, sd HLS, DASH
h265 4k_h265, full_hd_h265, full_hd_wifi_h265, full_hd_lean_h265, hd_h265, hd_lean_h265, sd_h265 HLS, DASH
vp9 4k_vp9, full_hd_vp9, full_hd_wifi_vp9, full_hd_lean_vp9, hd_vp9, hd_lean_vp9, sd_vp9 DASH

To view a detailed list of settings for each representation, see Predefined streaming profiles.

If none of the predefined profiles exactly answers your needs, you can also optionally define custom streaming profiles or even fine-tune the predefined options. You might want a different number of representations, different divisions of quality, different codecs or a different aspect ratio. Or, you might want to apply special transformations for different representations within the profile.

For example, if you want to make use of the more advanced video encoding provided by the vp9 and h265 codecs, or audio encoding provided by the opus codec, you can create your own streaming profiles (or update the predefined ones). You can use the h265 codec with both HLS and MPEG-DASH, however the vp9 and opus codecs can only be used with MPEG-DASH. You can combine codecs when creating your streaming profiles to ensure the widest support across different browsers and devices.

Use the streaming_profiles method of the Admin API to create, update, list, delete, or get details of streaming profiles.

Step 2. Upload your video with an eager transformation

A single streaming profile is comprised of many derived files, so it can take a while for Cloudinary to generate them all. Therefore, when you upload your video (or later, explicitly), you should include eager, asynchronous transformations with the required streaming profile and video format.

You can even eagerly prepare your videos for streaming in both formats and you can include other video transformations as well. However, make sure the streaming_profile is provided as a separate component of chained transformations.

For example, this upload command encodes the handshake.mp4 video to HLS format using the full_hd streaming profile:

Ruby:
Copy to clipboard
Cloudinary::Uploader.upload("handshake.mp4", :resource_type => :video, 
  :eager => [{:streaming_profile => "full_hd", :format => "m3u8"}], 
  :eager_async => true,
  :eager_notification_url => "https://mysite.example.com/notify_endpoint",
  :public_id => "handshake")
PHP (cloudinary_php v2.x):
Copy to clipboard
$cloudinary->uploadApi()->upload("handshake.mp4", [
  "resource_type" => "video", 
  "eager" => ["streaming_profile" => "full_hd", "format" => "m3u8"],
  "eager_async" => true,
  "eager_notification_url" => "https://mysite.example.com/notify_endpoint"
  "public_id" => "handshake"]);
PHP (cloudinary_php v1.x (legacy)):
Copy to clipboard
\Cloudinary\Uploader::upload("handshake.mp4", [
  "resource_type" => "video", 
  "eager" => ["streaming_profile" => "full_hd", "format" => "m3u8"],
  "eager_async" => true,
  "eager_notification_url" => "https://mysite.example.com/notify_endpoint"
  "public_id" => "handshake"]);
Python:
Copy to clipboard
cloudinary.uploader.upload("handshake.mp4", resource_type = "video",
  eager = [
    {"streaming_profile": "full_hd", "format": "m3u8"}]
  eager_async = true,
  eager_notification_url = "https://mysite.example.com/notify_endpoint")
  public_id = "handshake")
Node.js:
Copy to clipboard
var up_options = 
 {resource_type: "video", 
  eager: [
    { streaming_profile: "full_hd", format: "m3u8" }],                                   
  eager_async: true,
  eager_notification_url: "https://mysite.example.com/notify_endpoint",
  public_id: "handshake"};
cloudinary.v2.uploader.upload("handshake.mp4", up_options, function(result) {console.log(result); };
Java:
Copy to clipboard
cloudinary.uploader().upload("handshake.mp4", 
  ObjectUtils.asMap("resource_type", "video",
  "eager", Arrays.asList(
    new EagerTransformation().streamingProfile("full_hd").format("m3u8")),
  "eager_async", true,
  "eager_notification_url", "https://mysite.example.com/notify_endpoint",
  "public_id", "handshake"));
cURL:
Copy to clipboard
curl https://api.cloudinary.com/v1_1/demo/video/upload -X POST --data 'file=handshake.mp4&eager=sp_full_hd,f_m3u8&public_id=handshake&eager_notification_url=https://mysite.example.com/notify_endpoint&eager_async=true&&timestamp=173719931&api_key=436464676&signature=a781d61f86a6f818af'
CLI:
Copy to clipboard
cld uploader upload "handshake.mp4" resource_type="video" eager='[{"streaming_profile": "full_hd", "format": "m3u8"}]' eager_async=true eager_notification_url="https://mysite.example.com/notify_endpoint" public_id="handshake"

The eager transformation response includes the delivery URLs for each requested encoding.

See more adaptive streaming upload examples.

Derived adaptive streaming files

When the encoding process is complete for all representations, the derived files will include:

HLS

  • A fragmented video streaming file (.ts) 1 for each representation
  • An index file (.m3u8) of references to the fragments for each representation
  • A single master playlist file (.m3u8) containing references to the representation files above and other required metadata

DASH

  • A fragmented video streaming file (.mp4dv) for each representation
  • An audio stream file (.mp4da) for each representation
  • An index file (.mpd) containing references to the fragments of each streaming file as well as a master playlist containing references to the representation files above and other required metadata

1 If working with HLS v.3, a separate video streaming file (.ts) is derived for each fragment of each representation. To work with HLS v.3, you need a private CDN configuration and you need to add a special hlsv3 flag to your video transformations.

Step 3. Deliver the video

After the eager transformation is complete, deliver your video using the .m3u8 (HLS) or .mpd (MPEG-DASH) file format (extension) and include the streaming_profile (sp_<profilename>) and other transformations exactly matching those you provided in your eager transformation (as per the URL that was returned in the upload response).

For example, the delivery code for the video that was uploaded in Step 2 above is:

Ruby:
Copy to clipboard
cloudinary_url("handshake.m3u8", :streaming_profile=>"full_hd", :resource_type=>"video")
PHP (cloudinary_php v2.x):
Copy to clipboard
(new VideoTag('handshake.m3u8'))
  ->transcode(Transcode::streamingProfile(
  StreamingProfile::fullHd()));
PHP (cloudinary_php v1.x (legacy)):
Copy to clipboard
Cloudinary::cloudinary_url("handshake.m3u8", array("streaming_profile"=>"full_hd", "resource_type"=>"video"))
Python:
Copy to clipboard
cloudinary.utils.cloudinary_url("handshake.m3u8", streaming_profile="full_hd", resource_type="video")
Node.js:
Copy to clipboard
cloudinary.url("handshake.m3u8", {streaming_profile: "full_hd", resource_type: "video"})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation().streamingProfile("full_hd")).resourceType("video").generate("handshake.m3u8")
JS (js-url-gen v1.x):
Copy to clipboard
// This code example is not currently available.
JS (cloudinary-core v2.x (legacy)):
Copy to clipboard
cloudinary.url('handshake.m3u8', {streamingProfile: "full_hd"}, {resource_type: 'video'});
jQuery:
Copy to clipboard
$.cloudinary.url("handshake.m3u8", {streaming_profile: "full_hd", resource_type: "video"})
React:
Copy to clipboard
cloudinary.url('handshake.m3u8', {streamingProfile: "full_hd"}, {resource_type: 'video'});
Vue.js:
Copy to clipboard
cloudinary.url('handshake.m3u8', {streamingProfile: "full_hd"}, {resource_type: 'video'});
Angular:
Copy to clipboard
cloudinary.url('handshake.m3u8', {streamingProfile: "full_hd"}, {resource_type: 'video'});
.NET:
Copy to clipboard
cloudinary.Api.UrlVideoUp.Transform(new Transformation().StreamingProfile("full_hd")).BuildUrl("handshake.m3u8")
iOS:
Copy to clipboard
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation().setStreamingProfile("full_hd")).generate("handshake.m3u8")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation().streamingProfile("full_hd")).resourceType("video").generate("handshake.m3u8");
Kotlin:
Copy to clipboard
// This code example is not currently available.

Important
Not all browsers and mobile devices natively support MPEG-DASH and HLS. For example, Chrome does not natively support DASH or HLS. Both HLS and DASH are supported in Microsoft Edge and some SmartTVs. HLS is natively supported on all Apple devices, Safari, and the latest versions of the Chrome for Android browsers.

To ensure that your encoded video can adaptively stream in all environments, you can embed the Cloudinary Video Player in your application. The Cloudinary video player supports video delivery in both HLS and MPEG-DASH.

Delivering HLS version 3

By default, HLS transcoding uses HLS v4 If you need to deliver HLS v3, add the hlsv3 flag parameter (fl_hlsv3 in URLs) when setting the video file format (extension) to .m3u8. Cloudinary will then automatically create the fragmented video files (all with a .ts extension), each with a duration of 10 seconds, as well as the required .m3u8 index files for each of the fragmented files.

Important
Supporting HLS v3 requires a private CDN configuration. CDN distributions are available for Cloudinary's Advanced plan and higher, and require a small setup on Cloudinary's side. For details, submit a support request.

Notes and guidelines

  • For small videos, where all of the derived files combined are less than 60 MB, you can deliver the transformation URL on-the-fly (instead of as an eager transformation), but this should be used only for demo or debugging purposes. In a production environment, we recommend to always eagerly transform your video representations.

  • The adaptive streaming files for small videos (where all of the derived files combined are less than 60 MB) are derived synchronously if you do not set the asynch parameter. Larger videos are prepared asynchronously. Notification (webhook) of completion is sent to the eager_notification_url parameter.

  • If you set the format to an adaptive manifest file (m3u8 or mpd) using the format parameter, you must either set the file extension to match, or omit the extension completely. If you provide a different file extension, your video will not play correctly.

  • By default, video sampling is per 2 seconds. You can define a custom sampling rate by specifying it as a chained transformation prior to the streaming profile. For example (in Ruby):

Copy to clipboard
   Cloudinary::Uploader.upload("handshake.mp4", :resource_type => :video,
     :eager => [{:format => "mpd", :transformation => 
      [{:video_sampling => 3}, {:streaming_profile => "hd"}] }]
     :public_id => "handshake")
  • All the generated files are considered derived files of the original video. For example, when performing invalidate on a video, the corresponding derived adaptive streaming files are also invalidated.

  • If the profile you select includes any representations larger resolution than the original, those representations will not be created. However, at minimum, one representation will be created.

  • If the aspect ratio of the original video does not match the aspect ratio of the selected streaming profile, then the c_limit cropping transformation is applied to make the video fit the required size. If you don’t want to use this cropping method, use a base transformation to crop the video to the relevant aspect ratio.

  • If you prepare streaming for a lower-level profile, and then in the future, you want to prepare the same video with a higher-level profile, the new high-quality representations will be created, while any already existing representations that match will be reused.

Examples

Step 2 above includes a simple example for uploading a video and using a streaming profile to prepare the required files for delivering the video in adaptive streaming format. This section provides some additional examples.

Request several encodings and formats at once

Ruby:
Copy to clipboard
Cloudinary::Uploader.upload("handshake.mp4", :resource_type => :video, 
  :eager => [
     {:streaming_profile => "full_hd", :format => "m3u8"},
     {:streaming_profile => "sd", :format => "m3u8"},
     {:streaming_profile => "hd", :format => "mpd"}], 
  :eager_async => true
  :eager_notification_url => "https://mysite.example.com/notify_endpoint",
  :public_id => "handshake")
PHP (cloudinary_php v2.x):
Copy to clipboard
$cloudinary->uploadApi()->upload("handshake.mp4", [
  "resource_type" => "video", 
  "eager" => [
    ["streaming_profile" => "full_hd", "format" => "m3u8"],
    ["streaming_profile" => "sd", "format" => "m3u8"],
    ["streaming_profile" => "hd", "format" => "mpd"]],
  "eager_async" => true,
  "eager_notification_url" => "https://mysite.example.com/notify_endpoint",
  "public_id => "handshake"]);
PHP (cloudinary_php v1.x (legacy)):
Copy to clipboard
\Cloudinary\Uploader::upload("handshake.mp4", [
  "resource_type" => "video", 
  "eager" => [
    ["streaming_profile" => "full_hd", "format" => "m3u8"],
    ["streaming_profile" => "sd", "format" => "m3u8"],
    ["streaming_profile" => "hd", "format" => "mpd"]],
  "eager_async" => true,
  "eager_notification_url" => "https://mysite.example.com/notify_endpoint",
  "public_id => "handshake"]);
Python:
Copy to clipboard
cloudinary.uploader.upload("handshake.mp4", resource_type = "video",
  eager = [
    {"streaming_profile": "full_hd", "format": "m3u8"},
    {"streaming_profile": "sd", "format": "m3u8"},
    {"streaming_profile": "hd", "format": "mpd"}],
  eager_async = true,
  eager_notification_url = "https://mysite.example.com/notify_endpoint",
  public_id = "handshake")
Node.js:
Copy to clipboard
var up_options = 
{ resource_type: "video", 
  eager: [
    {streaming_profile: "full_hd", format: "m3u8"}, 
    {streaming_profile: "sd", format: "m3u8"}, 
    {streaming_profile: "hd", format: "mpd"} ],                                   
  eager_async: true,
  eager_notification_url: "https://mysite.example.com/notify_endpoint",
  public_id: "handshake" };
cloudinary.v2.uploader.upload("handshake.mp4", up_options, function(result) {console.log(result); });
Java:
Copy to clipboard
cloudinary.uploader().upload("handshake.mp4", 
  ObjectUtils.asMap("resource_type", "video",
  "eager", Arrays.asList(
    new EagerTransformation().streamingProfile("full_hd").format("m3u8"),
    new EagerTransformation().streamingProfile("sd").format("m3u8"),
    new EagerTransformation().streamingProfile("hd").format("mpd")),
  "eager_async", true,
  "eager_notification_url", "https://mysite.example.com/notify_endpoint,
  "public_id", "handshake"));
.NET:
Copy to clipboard
var uploadParams = new VideoUploadParams()
{
    File = new FileDescription(@"handshake.mp4"),
  PublicId = "handshake",           
    EagerTransforms = new List<Transformation>()
    {
        new EagerTransformation().SetFormat("m3u8").StreamingProfile("full_hd"),
        new EagerTransformation().SetFormat("m3u8").StreamingProfile("sd"),
        new EagerTransformation().SetFormat("mpd").StreamingProfile("hd"),
    },
    EagerAsync = true,
    EagerNotificationUrl = "https://mysite.example.com/notify_endpoint"
};
var uploadResult = cloudinary.Upload(uploadParams);
cURL:
Copy to clipboard
curl https://api.cloudinary.com/v1_1/demo/video/upload -X POST --data 'file=handshake.mp4&eager=f_m3u8,sp_full_hd|f_m3u8,sp_sd|f_m3u8,sp_hd&public_id=handshake&eager_notification_url=https://mysite.example.com/notify_endpoint&eager_async=true&timestamp=173719931&api_key=436464676&signature=a781d61f86a6f818af'
CLI:
Copy to clipboard
cld uploader upload "handshake.mp4" resource_type="video" eager='[{"streaming_profile": "full_hd", "format": "m3u8"},{"streaming_profile": "sd", "format": "m3u8"},{"streaming_profile": "hd", "format": "mpd"}]' eager_async=true eager_notification_url="https://mysite.example.com/notify_endpoint" public_id="handshake"

Use chained transformations with a streaming profile

Ruby:
Copy to clipboard
Cloudinary::Uploader.upload("handshake.mp4", 
  :resource_type => :video,
  :eager => 
    [{:format => "m3u8", :transformation => 
      [  
        {:crop => "crop", :aspect_ratio => "16:9"},
        {:streaming_profile => "hd"},
        {:overlay => "logowatermark", :width => 100}
      ] }],
  :eager_async => true, 
  :eager_notification_url => "https://mysite.example.com/notify_endpoint",
  :public_id => "handshake")
PHP (cloudinary_php v2.x):
Copy to clipboard
$cloudinary->uploadApi()->upload("big_buck_bunny.mp4", [
  "resource_type" => "video", 
  "eager" => [
    "format" => "m3u8",
    "transformation" => [
      ["crop" => "crop", "aspect_ratio" => "16:9"],
      ["streaming_profile" => "hd"],
      ["overlay" => "logowatermark", "width" => 100]]]),
  "eager_async" => true,
  "eager_notification_url" => "https://mysite.example.com/notify_endpoint",
  "public_id => "bb_bunny"]);
PHP (cloudinary_php v1.x (legacy)):
Copy to clipboard
\Cloudinary\Uploader::upload("handshake.mp4", [
  "resource_type" => "video", 
  "eager" => [
    "format" => "m3u8",
    "transformation" => [
      ["crop" => "crop", "aspect_ratio" => "16:9"],
      ["streaming_profile" => "hd"],
      ["overlay" => "logowatermark", "width" => 100]]],
  "eager_async" => true,
  "eager_notification_url" => "https://mysite.example.com/notify_endpoint",
  "public_id => "handshake"]);
Python:
Copy to clipboard
cloudinary.uploader.upload("handshake.mp4", 
  resource_type = "video",
  eager = [
    {"format": "m3u8", 
    "transformation": [
      {"crop":"crop", "aspect_ratio":"16:9"},
      {"streaming_profile": "hd"},
      {"overlay": "logowatermark", "width": 100}]
     }],
  eager_async = true,
  eager_notification_url = "https://mysite.example.com/notify_endpoint",
  public_id = "handshake")
Node.js:
Copy to clipboard
var up_options = 
{ resource_type: "video", 
  eager: [
    {format: "m3u8", 
     transformation: [
       {crop: "crop", aspect_ratio: "16:9"},
       {streaming_profile: "hd"},
       {:overlay: "logowatermark", width: 100}
   ]}, 
  ],                                   
  eager_async: true,
  eager_notification_url: "https://mysite.example.com/notify_endpoint",
  public_id: "handshake" };
cloudinary.v2.uploader.upload("handshake.mp4", up_options, function(result) {console.log(result); });
Java:
Copy to clipboard
cloudinary.uploader().upload("handshake.mp4", 
  ObjectUtils.asMap("resource_type", "video",
  "eager", Arrays.asList(
    new EagerTransformation()
      .format("m3u8").chain()
      .crop("crop").aspectRatio("16:9").chain()
      .streamingProfile("hd").chain()
      .overlay("logowatermark").width(100))        
  "eager_async", true,
  "eager_notification_url", "https://mysite.example.com/notify_endpoint,
  "public_id", "handshake"));
.NET:
Copy to clipboard
var uploadParams = new VideoUploadParams()
{
  File = new FileDescription(@"handshake.mp4"),
  PublicId = "handshake",
  EagerTransforms = new List<Transformation>()
  {
            new EagerTransformation().SetFormat("m3u8").StreamingProfile("hd").Chain()
            .Crop("crop").AspectRatio("16:9").Chain()
      .Overlay("logowatermark").Width(100)
  },
  EagerAsync = true, EagerNotificationUrl = "https://mysite.example.com/notify_endpoint"
};
var uploadResult = cloudinary.Upload(uploadParams);
cURL:
Copy to clipboard
curl https://api.cloudinary.com/v1_1/demo/video/upload -X POST --data 'file=handshake.mp4&eager=f_m3u8,sp_hd/c_crop,ar_16:9/l_logowatermark,w_100&public_id=handshake&eager_notification_url=https://mysite.example.com/notify_endpoint&eager_async=true&timestamp=173719931&api_key=436464676&signature=a781d61f86a6f818af'
CLI:
Copy to clipboard
cld uploader upload "handshake.mp4" resource_type="video" eager='[{"format": "m3u8", "transformation": [{"crop": "crop", "aspect_ratio": "16:9"},{"streaming_profile": "hd"},{"overlay": "logowatermark", "width": 100}]}]' eager_async=true eager_notification_url="https://mysite.example.com/notify_endpoint" public_id="handshake"

Encode an already uploaded video using Explicit

Ruby:
Copy to clipboard
Cloudinary::Uploader.explicit("handshake", 
  :resource_type => :video, :type => "upload",
  :eager => [
    {:streaming_profile => "hd", :format => "m3u8"}])
PHP (cloudinary_php v2.x):
Copy to clipboard
$cloudinary->uploadApi()->explicit("bb_bunny", [
  "resource_type" => "video", "type" => "upload"
  "eager" => [
    ["streaming_profile" => "hd", "format" => "m3u8"]]]);
PHP (cloudinary_php v1.x (legacy)):
Copy to clipboard
\Cloudinary\Uploader::explicit("handshake", [
  "resource_type" => "video", "type" => "upload"
  "eager" => [
    ["streaming_profile" => "hd", "format" => "m3u8"]]]);
Python:
Copy to clipboard
cloudinary.uploader.explicit("handshake", 
  resource_type = "video", type = "upload", 
  eager = 
   [
     {"streaming_profile": "hd", "format": "m3u8"},
   ])
Node.js:
Copy to clipboard
var up_options = 
  {resource_type: "video", type: "upload",
   eager: [
    { streaming_profile: "hd", format: "m3u8" }, 
   ] };
cloudinary.v2.uploader.explicit("handshake", up_options, function(result) {console.log(result); } );
Java:
Copy to clipboard
cloudinary.uploader().explicit("handshake", ObjectUtils.asMap(
  "resource_type", "video", "type", "upload", 
  "eager", Arrays.asList(
    new EagerTransformation().streaming_profile("hd").format("m3u8"),
    )));
.NET:
Copy to clipboard
var explicitParams = new ExplicitParams("handshake")
{
  Type = "upload",
  ResourceType = ResourceType.Video,
  EagerTransforms = new List<Transformation>()
  {
    new EagerTransformation().SetFormat("m3u8").StreamingProfile("hd")
  },
  EagerAsync = true,
  EagerNotificationUrl = "https://mysite.example.com/notify_endpoint"
};
var explicitResult = cloudinary.Explicit(explicitParams);
cURL:
Copy to clipboard
curl https://api.cloudinary.com/v1_1/demo/video/explicit -X POST --data 'type=upload&public_id=handshake&eager=sp_hd,f_m3u8&eager_notification_url=https://mysite.example.com/notify_endpoint&eager_async=true&timestamp=173719931&api_key=436464676&signature=a781d61f86a6f818af'
CLI:
Copy to clipboard
cld uploader explicit "handshake" resource_type="video" type="upload" eager='[{"streaming_profile": "hd", "format": "m3u8"}]'

Eager response example

Copy to clipboard
{
  "public_id": "handshake",
  "version": 1466426766,
  "signature": "245679585758d518703ab50142b1c88685e17777",
  "width": 640,
  "height": 360,
  "format": "mp4",
  "resource_type": "video",
  "created_at": "2017-06-20T12:46:06Z",
  "bytes": 5510872,
  "type": "upload",
  "url": "http://res.cloudinary.com/demo/video/upload/v1466426766/handshake.mp4",
  "secure_url": "https://res.cloudinary.com/demo/video/upload/v1466426766/handshake.mp4",
  "eager": [
    {
      "transformation": "sp_hd/m3u8",
      "url": "http://res.cloudinary.com/demo/video/upload/sp_hd/v1466426766/handshake.m3u8",
      "secure_url": "https://res.cloudinary.com/demo/video/upload/sp_hd/v1466426766/handshake.m3u8"
    }
  ]
}

Predefined streaming profiles

Each of the predefined streaming profiles will generate a number of different representations. The following sections summarize the representations that are generated for each predefined streaming profile and the settings for each representation.

Note
The representations that are generated for each of the streaming profiles have been updated recently for all new customers and most existing customers. If you're still using the older representations, contact support for further details.

Representations generated for each profile

The table below lists the available streaming profiles, the aspect ratio, and which of the representations are generated for each. The IDs in the table map to the IDs defined in the section below.

Profile Name Aspect Ratio 1 2 3 4 5 6 7 8 9
4k 16:9 + + + + + + + + +
full_hd 16:9 + + + + + + +
full_hd_wifi 16:9 + + +
full_hd_lean 16:9 + + +
hd_lean 16:9 + + +
hd 16:9 + + + + + +
sd 4:3 + + +

Note
The profiles in the table above also apply to the codec specific profiles such as 4k_h265 or 4k_vp9.

Possible representations

ID Aspect Ratio Resolution Audio H264 Bitrate H265 Bitrate VP9 Bitrate
9 16:9 3840x2160 AAC stereo 128kbps 18000 12000 (30fps)
15000 (60fps)
12000 (30fps)
15000 (60fps)
8 16:9 2560x1440 AAC stereo 128kbps 12000 6000 (30fps)
9000 (60fps)
6000 (30fps)
9000 (60fps)
7 16:9 1920x1080 AAC stereo 128kbps 5500 3800 3800
6 16:9 1280x720 AAC stereo 128kbps 3300 2300 2300
5 16:9 960x540 AAC stereo 96kbps 1500 1100 1100
4 16:9 768x432 AAC stereo 96kbps 800 550 550
3 16:9 640x360 AAC stereo 96kbps 500 350 350
3(SD) 4:3 640x480 AAC stereo 128kbps 800 550 550
2 16:9 480x270 AAC stereo 64kbps 270 200 200
2(SD) 4:3 480x360 AAC stereo 64kbps 400 300 300
1 16:9 320x180 AAC stereo 64kbps 140 100 100
1(SD) 4:3 320x240 AAC stereo 64kbps 200 150 150

Manually creating representations and master files

If you have a special use-case that does not enable you to use the built-in streaming profiles to automatically generate master playlist file and all the relevant representations, you can still have Cloudinary create the index and streaming files for each streaming transformation you define, and then you can create the master playlist file manually.

Step 1. Upload with eager transformations per representation

When you upload your video, provide eager transformations for each representation (resolution, quality, data rate) that you want to create. Also make sure to define the format for each transformation as .m3u8 (HLS) or .mpd (DASH) in a separate component of the chain.

When you define the format of your transformation as .m3u8 or .mpd, Cloudinary automatically generates the following derived files:

  • A fragmented video streaming file (.ts1 or .mp4dv)
  • An index file (.m3u8 or .mpd) of references to the fragments
  • For DASH only: an audio stream file (.mp4da)

For example:

Ruby:
Copy to clipboard
Cloudinary::Uploader.upload("handshake.mp4", 
  :resource_type => :video,
  :eager => 
   [{:format => "m3u8", :transformation => 
      [
        {:width => 960, :height => 540, :crop => :limit, :video_codec => "h264:main:3.1", :bit_rate =>"3500k"},
        {:overlay => "watermark", :width => 100}
      ] 
    }], 
   :eager_async => true, 
   :eager_notification_url => "https://mysite.example.com/notify_endpoint")
   :public_id => "handshake"
PHP (cloudinary_php v2.x):
Copy to clipboard
$cloudinary->uploadApi()->upload("handshake.mp4", [
  "resource_type" => "video", 
  "eager" => [
    ["format" => "m3u8", "width" => 960, "height" => 540, "crop" => "limit", "video_codec" => "h264:main:3.1", "bit_rate" =>"3500k"]],
  "eager_async" => true,
  "eager_notification_url" => "https://mysite.example.com/notify_endpoint"
  "public_id => "handshake" ]);
PHP (cloudinary_php v1.x (legacy)):
Copy to clipboard
\Cloudinary\Uploader::upload("handshake.mp4", [
  "resource_type" => "video", 
  "eager" => [
    ["format" => "m3u8", "width" => 960, "height" => 540, "crop" => "limit", "video_codec" => "h264:main:3.1", "bit_rate" =>"3500k"]],
  "eager_async" => true,
  "eager_notification_url" => "https://mysite.example.com/notify_endpoint"
  "public_id => "handshake" ]);
Python:
Copy to clipboard
cloudinary.uploader.upload("handshake.mp4", 
  resource_type = "video",
  eager = 
    [
      {"format": "m3u8", "width": 960, "height": 540, "crop": "limit", "video_codec": "h264:main:3.1", "bit_rate": "3500k"}
    ],
  eager_async = true,
  eager_notification_url = "https://mysite.example.com/notify_endpoint"
  public_id = "handshake" )
Node.js:
Copy to clipboard
var up_options = 
{ 
  resource_type: "video", 
  eager: 
  [
    { format: "m3u8", width: 960, height: 540, crop: "limit", video_codec: "h264:main:3.1", bit_rate: "3500k" }
  ],                                   
  eager_async: true,
  eager_notification_url: "https://mysite.example.com/notify_endpoint", 
  public_id: "handshake"    
 };
cloudinary.v2.uploader.upload("handshake.mp4", up_options, function(result) {console.log(result); });
Java:
Copy to clipboard
cloudinary.uploader().upload("handshake.mp4", 
  ObjectUtils.asMap("resource_type", "video",
  "eager", Arrays.asList(
    new EagerTransformation().format("m3u8").width(960).height(540).crop("limit").videoCodec("h264:main:3.1").bitRate("3500k")),
  "eager_async", true,
  "eager_notification_url", "https://mysite.example.com/notify_endpoint"
  "public_id", "handshake"));
.NET:
Copy to clipboard
var uploadParams = new VideoUploadParams()
{
  File = new FileDescription(@"handshake.mp4"),
  EagerTransforms = new List<Transformation>()
  {
            new EagerTransformation().SetFormat("m3u8").Width(960).Height(540).Crop("limit").VideoCodec("h264:main:3.1").BitRate("3500k")
  },
  EagerAsync = true, EagerNotificationUrl = "https://mysite.example.com/notify_endpoint"
};
var uploadResult = cloudinary.Upload(uploadParams);
cURL:
Copy to clipboard
curl https://api.cloudinary.com/v1_1/demo/video/upload -X POST --data 'file=handshake.mp4&eager=f_m3u8,w_960,h_540,c_limit,vc_h264:main:3.1,br_3500k&eager_notification_url=https://mysite.example.com/notify_endpoint&eager_async=true&timestamp=173719931&api_key=436464676&signature=a781d61f86a6f818af'
CLI:
Copy to clipboard
cld uploader upload "handshake.mp4" resource_type="video" eager='[{"format": "m3u8", "width": 960, "height": 540, "crop": "limit", "video_codec": "h264:main:3.1", "bit_rate": "3500k"}]' eager_async=true eager_notification_url="https://mysite.example.com/notify_endpoint" public_id="handshake"

Note
It is possible to create adaptive streaming transformations on the fly, but recommended to you create all the required transformations eagerly when uploading the original video file or explicitly, so that there is no delay when first accessed by your users.

1If working with HLS v.3, a separate video streaming file (.ts) is derived for each fragment of each representation. To work with HLS v.3, you need a private CDN configuration and you need to add a special hlsv3 flag to your video transformations.

Step 2. Manually create the master playlist file

If you are not using streaming profiles, you need to create your master playlist file manually.

HLS Master Playlist (m3u8)

Each item in the m3u8 master index file points to a Cloudinary dynamic transformation URL, which represents the pair of auto-generated m3u8 index and ts video files that were created in the preceding step.

For example, the following m3u8 master playlist file contains information on 2 versions of the dog.mp4 video, one high resolution suitable for iPhone 4s and the other lower resolution suitable for iPhone 1s:

Copy to clipboard
#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=480x320,NAME="Nice Dog - iPhone 4"    https://res.cloudinary.com/demo/video/upload/vc_h264:main:3.1/c_lpad,h_640,w_960/br_5m/vs_2/q_70/dog.m3u8

#EXT-X-STREAM-INF:BANDWIDTH=1500000,RESOLUTION=480x320,NAME="Nice Dog - iPhone 1"    https://res.cloudinary.com/demo/video/upload/vc_h264:baseline:3.0/c_lpad,h_320,w_480/br_1400k/vs_2/q_70/dog.m3u8

MPEG-DASH Master Playlist

The .mpd master file is an XML document. For details on creating the .mpd master file, see: The Structure of an MPEG-DASH MPD.

Advanced transformations

In addition to the commonly used video transformation options described on this page, the following pages describe a variety of additional video transformation options:

The Transformation URL API Reference details all transformation parameters available for both images and videos. Icons indicate which parameters are supported for each asset type.

✔️ Feedback sent!

Rate this page: