Cloudinary Blog

Cloudinary Video Player: “Drinking Our Own Champagne”

How Cloudinary Set Up Its Podcast Webpage for ABR With the Cloudinary Video Player

Using a company’s software in-house was made famous by Microsoft’s adoption of the term “eating our own dog food” to refer to the request to employees to use the alpha and beta versions of their products. This story, “Drinking Our Own Champagne” refers to our serving videos created by our Customer Training team with the Cloudinary Video Player to implement a business initiative and enhance site performance.

The Project: Cloudinary Podcasts

A Cloudinary cross-functional team with employees from Customer Training and Marketing are working on the initiative of releasing video podcasts biweekly. The subject matter is split between two podcast brands:

  • Dev Jams, which showcases projects created by our customers in an effort to get to know them better.
  • MX Matters, which serves as a platform for our product team to share information about our evolving product line.

The video podcasts are about an hour in length, access to which (both video and audio) are through YouTube, Spotify, Apple Podcasts, Google Podcasts, Overcast, and Stitcher. In addition, you can view the videos on a webpage.

For a superior user experience, we did the following:

  1. Offered smooth-playing, no-buffering streaming with minimal configuration through Adaptive Bitrate Streaming (ABR).
  2. Presented the video collections as a grid with modal popups for easy full-screen viewing.

Definition of ABR

ABR is a video-streaming technique that can be run in the application layer of the internet: HTTP. Essentially, the video server chunks up the video and the client browser reads them in with HTTP GET requests. ABR works by detecting the user's bandwidth on the client side and adjusting the streaming quality accordingly. If the client's network supports a heavier load, the video’s quality is higher. For clients with a lower bandwidth network, ABR lowers the quality of the chunks, resulting in smaller file sizes. The net result is a smooth playback and less or no buffering. Not only does the Cloudinary Video Player help with ABR, but it can also process the various renditions, segmenting them into chunks and creating manifests that describe the chunks.

ABR contains two protocols: Dynamic Adaptive Streaming Over HTTP (MPEG-DASH) and Apple’s HTTP Live Streaming (HLS), both of which are operationally and functionally equivalent. The differences lie in device support, e.g., HLS is natively supported in Safari but not in Chrome.

A strength of Cloudinary is its ability to create multi-codec ABR, which means that you can use the Cloudinary Video Player with HLS in all major browsers, e.g., you can build profiles for HLS/h.264, HLS/h.265, and DASH/VP9. Each player would then choose the best codec based on the browser or device it’s running on. Cloudinary’s podcast webpage runs HLS/h.264.

It’s a good idea to become familiar with both MPEG-DASH and HLS, which offer different browser and video codec compatibilities. Browsers support specific video formats and codecs, which are different algorithms for compression. Also, some advanced codecs require specific formats. Here are the details.

Both MPEG-DASH and HLS define a manifest that lists all the chunks to be requested.

The HLS manifest, which has an extension of m3u8, contains two levels of manifest data:

  • The master manifest (see the image below for an example), which lists the secondary manifests with the details of the bitrate and video size for the various bitrates.
    master manifest
  • The secondary manifests, which detail the chunks for a given bitrate. The example below shows two climbing.ts chunks.
    secondary manifests

Your browser’s network tab shows the .ts files being loaded and even preloaded. Mousing over one of those files displays a transformation for both resizing and bitrate, as in this example:

Network tab
Network tab .ts files

The height and width might vary with the bandwidth based on the ABR algorithm. The transformations shown above were derived from the original MP4 format of the file uploaded to Cloudinary.


Workflows hinge on the organization’s people and their skill sets. In the case of our podcast videos, the workflow contains two major steps:

  1. The Customer Training team creates, edits, uploads, and transforms the videos, and then hands them off to Marketing.
  2. Marketing works with an agency to code and build the webpages for publication.

Podcast Production and Delivery Workflow
Podcast Production and Delivery Workflow

Cloudinary has the tools for the various workflows. Modularizing the above workflow enables different people to work on different aspects of the product, as in the six-step process below:

  1. Record the video with Zoom.
  2. Process the video with Descript, a tool that cleans up and transcribes the language. Cloudinary’s add-ons from Google and Microsoft can create video transcription files for closed captioning during video upload.
  3. Annotate and enhance the video with visuals.
  4. Upload the video as an MP4 file to Cloudinary.
  5. Execute a script to create the transformations (chunking) that prepare the video for ABR in the Cloudinary Video Player. If feasible in the workflow, you can turn the script into an upload preset for use during upload.
  6. Hand off the public ID of the derived video to Marketing for publication on the website.

Code Development for ABR and Cloudinary Video Player

This section delves into the back-end and front-end code that creates the derived video chunks and the code for rendering the Cloudinary Video Player.

Back End: Script for Creating the Derived Video for ABR

The script below creates the derived videos that can be requested by the Cloudinary Video Player to enable ABR:

Copy to clipboard
const cloudinary = require("cloudinary").v2;

// use explicit because upload takes place at a different
// time and is performed by a different person
function explictHDProfile(publicId) {
  const options = {
    resource_type: "video",
    eager: [
      { streaming_profile: "hd", format: "m3u8" },
        format: "mp4",
        transformation: [{ quality: "auto" }],
    eager_async: true,
    type: "upload",
    invalidate: true,
  cloudinary.uploader.explicit(publicId, options, function (error, result) {
    if (error) console.log("error", error);
    else console.log(result);

The code is set up as a function that accepts the Cloudinary public ID for the original, uploaded video. By way of explanation:

  • The function specifies an explicit transformation, a Cloudinary term that mandates a transformation to be applied to an uploaded asset as opposed to an on-the-fly transformation that is applied to the original asset during upload. Creating ABR is CPU intensive and done in an asynchronous mode, called eager in Cloudinary lingo.
  • In order for us to be notified that the entire process is complete, we set the eager_notification_url key with a website that can capture and report such notifications, in this case, which is free to use. The site accords users a unique URL for notifications.
  • A built-in streaming profile created by Cloudinary and a named hd profile specify the transformations. A streaming profile defines the transformations.
  • This code leverages Cloudinary’s Node.js SDK. The function cloudinary.uploader.explicit calls upon the Upload API’s explicit method to act on a previously uploaded video with the public ID, a unique identifier assigned to an asset as a parameter for the explictHDProfile function.
  • The options, highlighted in the code above, specify that the asset is a video. The eager key lists an array of transformations, including a streaming profile that results in multiple transformations. Another transformation is a fallback to MP4, which enables browsers and devices to choose the optimal codec. As mentioned above, the Cloudinary Video Player can accept multiple codecs for ABR. By creating multiple streaming profiles based on different codecs, you can let the device choose the optimal profile. Here, an optimized MP4 is specified as a fallback. In addition:
    • You must set resource_type to video for transformations that are video specific.
    • Because the workflow dictates that this script be run after the upload, the eager key defines the streaming profile and transformations.
    • eager_notification_url monitors the progress of the derived videos.
    • The type option upload is the default that renders the derived videos public.
    • The invalidate option, set to true, invalidates previously cached versions of the derived files, if any, so that they can be replaced.

Front End: fancyBox for the Webpage

The website offers Lightbox features with a jQuery library called fancyBox. Sean Massa, our front-end developer who implemented this capability, characterized the requirements and solution like this:

“The challenge for this project was to build a single modal and video player in which the content would dynamically change based on the thumbnail clicked. There were many approaches that seemed to work when we were dealing with just a single video implementation, but when it came to reloading the player with a new video on the fly, we ran into a few walls along the way and had to step back and rethink our approach. The result was to set up an instance of the player, including all the parameters and transformations, explicitly through JavaScript, because adding a mix of JS and inline to the player created issues. We then used data attributes to set the poster and source on click of the desired item. We also had to set the poster before the video source or it would not work.”

As shown in the final code for the front end, we created multiple modal video players on a single page by rendering a single <video> tag in the fancyBox popup and then attaching the Cloudinary Video Player code and options to that tag when the user clicks a video link.

For the stripped-down implementation, see the demo.


The Cloudinary Video Player interprets the classes that we added to the <video> tag, such as cld-fluid, which enable the player to fill its container. Located in a single <div>, which is rendered but not displayed, the <video> tag is specified by sample-video-id with which the Cloudinary Video Player locates the tag. You’ll find the tag rendered in your inspector tool but not on the webpage until a fancyBox link has been clicked.

Copy to clipboard
 <div id="video-wrapper" style="display: none">
        class="cloudinary-video cld-video-player cld-video-player-skin-dark cld-fluid"

Supporting Libraries

You can access the Cloudinary Video Player code with NPM and incorporate it into JavaScript frameworks. For the podcast webpage, we leverage jQuery, fetching all external libraries through a content delivery network (CDN). To use the Cloudinary Video Player, you must import cloudinary-core-shrinkwrap, cloudinary-video-player, JavaScript, and CSS. For the fancyBox modal, we imported both jQuery and fancyBox JavaScript and CSS.

Copy to clipboard
    <script src=""></script>
    <script src=""></script>

JavaScript: Clickable Link for Each and Every Video

Each video that we want to open with the Cloudinary Video Player in the modal is set up with data that contains the Cloudinary public ID and poster link. Even though the Cloudinary Video Player can create a poster from a midpoint frame of the video itself, we elected to link to an image on the podcast webpage. The public ID and poster link are the only data handed off to the JavaScript that opens the Cloudinary Video Player. See the code below.

Copy to clipboard
    class="fancybox video-trigger"
    Watch climbing here

Note the following:

  • The video-trigger class attaches a click event.
  • data-video-id contains the Cloudinary public ID.
  • data-video-poster contains a link to an optimized image on Cloudinary.
  • href links to the hidden <video> tag.
  • Clicking the anchor tag below hands off the data to the Cloudinary Video Player.

The JavaScript shown below calls the fancybox() function, which takes as input all the HTML, JavaScript, and CSS that the videoPlayer function attaches to the <video> tag. Also note the following:

  • To call videoPlayer, we added the <video> tag selector sample-video-id**.
  • We added options to instruct the Cloudinary Video Player to use the hd streaming profile or the mp4 fallback.
  • Users can control the speeds at which they stream the video with playbackRates.
  • We added a click event handler with jQuery. On a user click, we pull data-video-id and data-video-poster out of the anchor tag.
  • Finally, we call the Cloudinary Video Player’s source function to activate the Video Player.
Copy to clipboard
jQuery(document).ready(function () {
  // Initialize player
  var cloudinaryCld ={
    cloud_name: "cloudinary-training",
  var options = {
    sourceTypes: ["hls", "mp4"],
    bigPlayButton: "init",
    muted: false,
    sourceTransformation: {
      "hls": [{ streaming_profile: "hd" }],
      "mp4": [{ quality: "auto" }],
    playbackRates: [0.5, 1, 1.5, 2],
  var media = cloudinaryCld.videoPlayer("sample-video-id", options);
  jQuery(document).on("click", "", function () {
    var videoID = jQuery(this).data("video-id");
    var videoPoster = jQuery(this).data("video-poster");
    media.posterOptions({ publicId: videoPoster });
    media.source({ publicId: videoID });

The options on the front end depend on the uploaded and derived assets on the backend. If you are specifying hls, ensure that you’ve created the derived m3u8 and ts files in Cloudinary.

If you are implementing this code for your own videos, replace the cloud_name variable with your Cloudinary account’s cloud name.

CSS max-width Property

Page styling is very important for the setup of this multi-video player webpage. The sample code in the shared GitHub repo is not styled to the extent that the Cloudinary podcast webpage is. In particular, be careful while setting the CSS max-width property on the <video> tag because that configuration can interfere with full-screen display.


There is always a next step in webpage development, but it's nice to reach a milestone where you feel confident about a workflow and can then focus on the content. As more websites embrace video content, we look forward to learning and sharing with you techniques that enhance the developer experience.

Recent Blog Posts

Create Lightweight Sites With Low-Code and No-Code Technology

Consumers expect modern websites to be mainly visual. But, the more compelling and complex the related media is, the more data is involved, compounding the site’s weight. In today’s content-craving world, delivering unoptimized media can cost you because it leads to sluggish page loads, resulting in visitors abandoning your site in search of a faster alternative. In fact, a page load that takes more than three seconds can cause as many as 40% of your visitors to bounce. Given this competitive, digital-first environment, you can’t afford to lose page views, for time is of the essence.

Read more
A Blueprint for AWS-Secured Webhook Listeners for Cloudinary

tl;dr: An AWS-secured and optimized Cloudinary webhook listener for extending the Cloudinary service

Code: Github

A webhook is a communication medium for sending notifications from one platform to another about events that occurred. In place are user-defined HTTP callbacks that are triggered by specific events. When a triggered event takes place on the source site, the webhook listens to the event, collects the data, and sends it to the URL you specified in the form of an HTTP request.

Read more
New Accessibility Features for Cloudinary’s Product Gallery Widget

Cloudinary’s Product Gallery widget, which launched in 2019, has enabled many brands to effectively and efficiently showcase their products in a sleek and captivating manner, saving countless hours of development time and accelerating release cycles. By adding Cloudinary’s Product Gallery widget with its customizable UI to their product page, retailers reap numerous benefits, often turning visitors into customers in short order.

Read more
Why Successful Businesses Engage With and Convert Audiences With Visual Media

Most business buyers prefer to research purchase options online, as do many shoppers. No wonder online retail sales in the U.S. rose by 32.4% in 2020—an impressive gain of $105 billion.

For B2B and B2C businesses, text-heavy websites are no longer adequate in attracting shoppers. Instead, engaging visual media—spin images, videos, 3D models, augmented reality—are becoming a must for conveying eye-catching details and differentiators about products or services.

Read more
Making User-Generated Content (UGC) Shoppable With Cloudinary

User-generated content (UGC) is a powerful marketing tool. Not only does video complement marketing efforts for e-commerce by enabling customers to explore products in greater detail, but UGC also adds an element of trust. As a bonus, user-generated video is an exceptional opportunity for e-businesses to attract website traffic without their marketing team having to create promotional videos from scratch. User-generated content drives conversions and brand loyalty as a direct result of authentic interaction.

Read more