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.

For example, the 4k profile creates 8 different representations in 16x9 aspect ratio, from extremely high quality to audio only, 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.

To view the full list of available predefined streaming profiles and the 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 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"]);
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"]);
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 v1.x (legacy)):
Copy to clipboard
Cloudinary::cloudinary_url("handshake.m3u8", array("streaming_profile"=>"full_hd", "resource_type"=>"video"))
PHP (cloudinary_php v2.x):
Copy to clipboard
(new Video('handshake.m3u8'))
  ->transcode(Transcode::streamingProfile(StreamingProfile::fullHd()));
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:
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
cloudinary.video {
publicId("handshake.m3u8")
transcode(Transcode.streamingProfile(StreamingProfile.fullHd()))
}.generate()

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 above, 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 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"]);
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"]);
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 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"]);
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"]);
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 v1.x (legacy)):
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"]]]);
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

The following tables summarize the possible representations, and the actual representations that are included for each predefined streaming profile.

Possible representations

ID Width Height Video Codec/Profile
(video_codec)
Video Bitrate
(bit_rate)
0 320 240 h264:baseline:3.0 192k
1 480 270
(360 in 4:3 ratio)
h264:baseline:3.0 800k
2 640 360
(480 in 4:3 ratio)
h264:baseline:3.0 2m
3 960 540 h264:main:3.1 3500k
4 1280 720 h264:main:3.1 5500k
5 1920 1080 h264:high:4.0 8500k
6 2560 1440 h264:high:4.0 16m
7 3840 2160 h264:high:4.0 35m

Representations generated for each profile

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

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 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" ]);
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" ]);
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: