Cloudinary Blog

An Open-Source Interface for Editing Videos With Cloudinary

A New, Open-Source Tool for Performing Simple Video-Editing Tasks With Cloudinary

Cloudinary offers powerful APIs for uploading, managing, transforming, and delivering images and videos, enabling developers to treat media as code and to capitalize on media’s full potential. However, performing simple editing tasks with APIs and SDKs might be too overwhelming for nondevelopers, who might prefer UI-based capabilities instead.

Examples of simple video-editing tasks are trimming videos based on time offsets, merging videos, adding text or image overlays to videos, etc. Currently, those capabilities are not part of Cloudinary’s digital asset management (DAM) toolset. To perform those tasks, nondevelopers must construct delivery URLs, such as the one below, which concatenates two clipped videos with an image at the start and another at the end:

Ruby:
Copy to clipboard
cl_video_tag("pramod-vid-demo/telecope_video", :transformation=>[
  {:duration=>"3", :end_offset=>"5", :height=>800, :width=>1200, :crop=>"fill"},
  {:duration=>"3", :flags=>"splice", :height=>800, :overlay=>"pramod-vid-demo:Thank%20you", :width=>1200},
  {:flags=>"layer_apply"},
  {:duration=>"3", :end_offset=>"5", :flags=>"splice", :height=>800, :overlay=>"video:pramod-vid-demo:microscope_video", :width=>1200, :crop=>"fill"},
  {:flags=>"layer_apply", :start_offset=>"0"},
  {:duration=>"3", :flags=>"splice", :height=>800, :overlay=>"pramod-vid-demo:Microscope", :width=>1200},
  {:flags=>"layer_apply", :start_offset=>"0"}
  ])
PHP v1:
Copy to clipboard
cl_video_tag("pramod-vid-demo/telecope_video", array("transformation"=>array(
  array("duration"=>"3", "end_offset"=>"5", "height"=>800, "width"=>1200, "crop"=>"fill"),
  array("duration"=>"3", "flags"=>"splice", "height"=>800, "overlay"=>"pramod-vid-demo:Thank%20you", "width"=>1200),
  array("flags"=>"layer_apply"),
  array("duration"=>"3", "end_offset"=>"5", "flags"=>"splice", "height"=>800, "overlay"=>"video:pramod-vid-demo:microscope_video", "width"=>1200, "crop"=>"fill"),
  array("flags"=>"layer_apply", "start_offset"=>"0"),
  array("duration"=>"3", "flags"=>"splice", "height"=>800, "overlay"=>"pramod-vid-demo:Microscope", "width"=>1200),
  array("flags"=>"layer_apply", "start_offset"=>"0")
  )))
PHP v2:
Copy to clipboard
(new VideoTag('pramod-vid-demo/telecope_video.mp4'))
  ->videoEdit(VideoEdit::trim()->endOffset(5)->duration(3))
  ->resize(Resize::fill()->width(1200)->height(800))
  ->videoEdit(
      VideoEdit::concatenate(Concatenate::imageSource('pramod-vid-demo/Thank you')
        ->transformation((new ImageTransformation())
          ->resize(Resize::scale()->width(1200)->height(800))))
      ->duration(3))
    ->videoEdit(
        VideoEdit::concatenate(Concatenate::videoSource('pramod-vid-demo/microscope_video')
          ->transformation((new VideoTransformation())
            ->videoEdit(VideoEdit::trim()->endOffset(5))
            ->resize(Resize::fill()->width(1200)->height(800))))
        ->prepend()->duration(3))
          ->videoEdit(
              VideoEdit::concatenate(Concatenate::imageSource('pramod-vid-demo/Microscope')
                ->transformation((new ImageTransformation())
                  ->resize(Resize::scale()->width(1200)->height(800))))
              ->prepend()->duration(3)
          
        
          );
Python:
Copy to clipboard
CloudinaryVideo("pramod-vid-demo/telecope_video").video(transformation=[
  {'duration': "3", 'end_offset': "5", 'height': 800, 'width': 1200, 'crop': "fill"},
  {'duration': "3", 'flags': "splice", 'height': 800, 'overlay': "pramod-vid-demo:Thank%20you", 'width': 1200},
  {'flags': "layer_apply"},
  {'duration': "3", 'end_offset': "5", 'flags': "splice", 'height': 800, 'overlay': "video:pramod-vid-demo:microscope_video", 'width': 1200, 'crop': "fill"},
  {'flags': "layer_apply", 'start_offset': "0"},
  {'duration': "3", 'flags': "splice", 'height': 800, 'overlay': "pramod-vid-demo:Microscope", 'width': 1200},
  {'flags': "layer_apply", 'start_offset': "0"}
  ])
Node.js:
Copy to clipboard
cloudinary.video("pramod-vid-demo/telecope_video", {transformation: [
  {duration: "3", end_offset: "5", height: 800, width: 1200, crop: "fill"},
  {duration: "3", flags: "splice", height: 800, overlay: "pramod-vid-demo:Thank%20you", width: 1200},
  {flags: "layer_apply"},
  {duration: "3", end_offset: "5", flags: "splice", height: 800, overlay: "video:pramod-vid-demo:microscope_video", width: 1200, crop: "fill"},
  {flags: "layer_apply", start_offset: "0"},
  {duration: "3", flags: "splice", height: 800, overlay: "pramod-vid-demo:Microscope", width: 1200},
  {flags: "layer_apply", start_offset: "0"}
  ]})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation()
  .duration("3").endOffset("5").height(800).width(1200).crop("fill").chain()
  .duration("3").flags("splice").height(800).overlay(new Layer().publicId("pramod-vid-demo:Thank%20you")).width(1200).chain()
  .flags("layer_apply").chain()
  .duration("3").endOffset("5").flags("splice").height(800).overlay(new Layer().publicId("video:pramod-vid-demo:microscope_video")).width(1200).crop("fill").chain()
  .flags("layer_apply").startOffset("0").chain()
  .duration("3").flags("splice").height(800).overlay(new Layer().publicId("pramod-vid-demo:Microscope")).width(1200).chain()
  .flags("layer_apply").startOffset("0")).videoTag("pramod-vid-demo/telecope_video");
JS:
Copy to clipboard
cloudinary.videoTag('pramod-vid-demo/telecope_video', {transformation: [
  {duration: "3", endOffset: "5", height: 800, width: 1200, crop: "fill"},
  {duration: "3", flags: "splice", height: 800, overlay: new cloudinary.Layer().publicId("pramod-vid-demo:Thank%20you"), width: 1200},
  {flags: "layer_apply"},
  {duration: "3", endOffset: "5", flags: "splice", height: 800, overlay: new cloudinary.Layer().publicId("video:pramod-vid-demo:microscope_video"), width: 1200, crop: "fill"},
  {flags: "layer_apply", startOffset: "0"},
  {duration: "3", flags: "splice", height: 800, overlay: new cloudinary.Layer().publicId("pramod-vid-demo:Microscope"), width: 1200},
  {flags: "layer_apply", startOffset: "0"}
  ]}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.video("pramod-vid-demo/telecope_video", {transformation: [
  {duration: "3", end_offset: "5", height: 800, width: 1200, crop: "fill"},
  {duration: "3", flags: "splice", height: 800, overlay: new cloudinary.Layer().publicId("pramod-vid-demo:Thank%20you"), width: 1200},
  {flags: "layer_apply"},
  {duration: "3", end_offset: "5", flags: "splice", height: 800, overlay: new cloudinary.Layer().publicId("video:pramod-vid-demo:microscope_video"), width: 1200, crop: "fill"},
  {flags: "layer_apply", start_offset: "0"},
  {duration: "3", flags: "splice", height: 800, overlay: new cloudinary.Layer().publicId("pramod-vid-demo:Microscope"), width: 1200},
  {flags: "layer_apply", start_offset: "0"}
  ]})
React:
Copy to clipboard
<Video publicId="pramod-vid-demo/telecope_video" >
  <Transformation duration="3" endOffset="5" height="800" width="1200" crop="fill" />
  <Transformation duration="3" flags="splice" height="800" overlay="pramod-vid-demo:Thank%20you" width="1200" />
  <Transformation flags="layer_apply" />
  <Transformation duration="3" endOffset="5" flags="splice" height="800" overlay="video:pramod-vid-demo:microscope_video" width="1200" crop="fill" />
  <Transformation flags="layer_apply" startOffset="0" />
  <Transformation duration="3" flags="splice" height="800" overlay="pramod-vid-demo:Microscope" width="1200" />
  <Transformation flags="layer_apply" startOffset="0" />
</Video>
Vue.js:
Copy to clipboard
<cld-video publicId="pramod-vid-demo/telecope_video" >
  <cld-transformation duration="3" endOffset="5" height="800" width="1200" crop="fill" />
  <cld-transformation duration="3" flags="splice" height="800" :overlay="pramod-vid-demo:Thank%20you" width="1200" />
  <cld-transformation flags="layer_apply" />
  <cld-transformation duration="3" endOffset="5" flags="splice" height="800" :overlay="video:pramod-vid-demo:microscope_video" width="1200" crop="fill" />
  <cld-transformation flags="layer_apply" startOffset="0" />
  <cld-transformation duration="3" flags="splice" height="800" :overlay="pramod-vid-demo:Microscope" width="1200" />
  <cld-transformation flags="layer_apply" startOffset="0" />
</cld-video>
Angular:
Copy to clipboard
<cl-video public-id="pramod-vid-demo/telecope_video" >
  <cl-transformation duration="3" end-offset="5" height="800" width="1200" crop="fill">
  </cl-transformation>
  <cl-transformation duration="3" flags="splice" height="800" overlay="pramod-vid-demo:Thank%20you" width="1200">
  </cl-transformation>
  <cl-transformation flags="layer_apply">
  </cl-transformation>
  <cl-transformation duration="3" end-offset="5" flags="splice" height="800" overlay="video:pramod-vid-demo:microscope_video" width="1200" crop="fill">
  </cl-transformation>
  <cl-transformation flags="layer_apply" start-offset="0">
  </cl-transformation>
  <cl-transformation duration="3" flags="splice" height="800" overlay="pramod-vid-demo:Microscope" width="1200">
  </cl-transformation>
  <cl-transformation flags="layer_apply" start-offset="0">
  </cl-transformation>
</cl-video>
.NET:
Copy to clipboard
cloudinary.Api.UrlVideoUp.Transform(new Transformation()
  .Duration("3").EndOffset("5").Height(800).Width(1200).Crop("fill").Chain()
  .Duration("3").Flags("splice").Height(800).Overlay(new Layer().PublicId("pramod-vid-demo:Thank%20you")).Width(1200).Chain()
  .Flags("layer_apply").Chain()
  .Duration("3").EndOffset("5").Flags("splice").Height(800).Overlay(new Layer().PublicId("video:pramod-vid-demo:microscope_video")).Width(1200).Crop("fill").Chain()
  .Flags("layer_apply").StartOffset("0").Chain()
  .Duration("3").Flags("splice").Height(800).Overlay(new Layer().PublicId("pramod-vid-demo:Microscope")).Width(1200).Chain()
  .Flags("layer_apply").StartOffset("0")).BuildVideoTag("pramod-vid-demo/telecope_video")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation()
  .duration("3").endOffset("5").height(800).width(1200).crop("fill").chain()
  .duration("3").flags("splice").height(800).overlay(new Layer().publicId("pramod-vid-demo:Thank%20you")).width(1200).chain()
  .flags("layer_apply").chain()
  .duration("3").endOffset("5").flags("splice").height(800).overlay(new Layer().publicId("video:pramod-vid-demo:microscope_video")).width(1200).crop("fill").chain()
  .flags("layer_apply").startOffset("0").chain()
  .duration("3").flags("splice").height(800).overlay(new Layer().publicId("pramod-vid-demo:Microscope")).width(1200).chain()
  .flags("layer_apply").startOffset("0")).resourceType("video").generate("pramod-vid-demo/telecope_video.mp4");
iOS:
Copy to clipboard
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation()
  .setDuration("3").setEndOffset("5").setHeight(800).setWidth(1200).setCrop("fill").chain()
  .setDuration("3").setFlags("splice").setHeight(800).setOverlay("pramod-vid-demo:Thank%20you").setWidth(1200).chain()
  .setFlags("layer_apply").chain()
  .setDuration("3").setEndOffset("5").setFlags("splice").setHeight(800).setOverlay("video:pramod-vid-demo:microscope_video").setWidth(1200).setCrop("fill").chain()
  .setFlags("layer_apply").setStartOffset("0").chain()
  .setDuration("3").setFlags("splice").setHeight(800).setOverlay("pramod-vid-demo:Microscope").setWidth(1200).chain()
  .setFlags("layer_apply").setStartOffset("0")).generate("pramod-vid-demo/telecope_video.mp4")

As the number of video clips for trimming or merging increases, the complexity involved in manually building delivery URLs also intensifies. As a remedy, we recently built a simple tool that uses Cloudinary’s transformation APIs behind the scenes via our JavaScript SDK to help you quickly perform these two editing tasks:

  • Trim videos by specifying the start time and the end time.
  • Join multiple videos.

You can also do the following with this new tool:

  • Rearrange the order of the videos.
  • Add images to a video for display for x seconds.
  • Add a global text overlay or have it overridden for specific video clips.
  • Choose the font, color, and size of the text overlay.
  • Specify the gravity, i.e., the position of the text in the video.
  • Add an image and specify its size as an overlay.
  • Specify the image’s gravity, i.e., its position in the video.
  • Adjust the image’s brightness, contrast, saturation, gamma, and vignette.
  • Make the video transparent or untransparent (if supported by the browser).

The sections below describe how to install and use the tool.

Installing the Tool

This tool is a front-end-only app written in HTML and JS with no server required. Download the code to your local machine. For testing, you can use the hosted demo.

Using the Tool

Let’s take an example of a student’s science homework. Say, the required images and videos are already in DAM storage and tagged as ‘my_homework’. Check out the entire collection here.

Select the Media

The student can search for videos manually from his Cloudinary account’s Media Library or under the Search by Tag option. He can add images to a video under Search by Tag, but not from the Media Library.

Note
Feel free to fork the source code and add capabilities, e.g., one that enables selection of images from the Media Library also.

The video below shows you how to search for videos and images based on the tag my_homework.

Trim Videos and Add Images for Video Stitching

To trim videos and add images for video stitching, the student would take these steps:

  1. Specify the start and end offset to clip the video that is active in the video player.
  2. Select images from the image-search results and add them for video stitching.
  3. Drag and drop the videos to arrange them in the correct order.

See the video below for a demo.

Enable Text Overlay

The student can either enable a global text overlay for the entire video or add text overlays to individual videos or images. He can also configure the text font, size, color, and gravity, i.e., the position of the text overlay in the video, such as northwest, center, etc., and adjust the position by specifying x-y offsets.

The video below shows you how to add a date as the text overlay at the bottom-right corner of a video.

Enable Image Overlay

You can add a logo or any image to video output by choosing the image from prior image-search results or from the Media Library. Additionally, you can configure the image size and gravity, i.e., the position of the image overlay in the video.

The demo below shows you how to add the Cloudinary logo as an overlay to our concatenated video, resize the logo, and add it as a transparent image.

Here are more examples:

  • Demo 1: Merge an image and two trimmed videos, displaying the image in the first two seconds.
  • Demo 2: Merge two clipped videos, rearrange their order, add a text overlay, and adjust the brightness.
  • Demo 3: Merge three clipped videos, add two images in between the videos, and then add an image overlay.

Summing It Up

The features in this simple video-editing tool are a subset of the robust capabilities that Cloudinary offers for video editing. We plan to add more features to the Cloudinary DAM UI in the near future so that you can transform videos within that UI. Until then, we’ll incorporate more common video-transformation features as part of the above tool.

Again, you are welcome to clone and modify the code, create personalized versions for your use cases and apps, and send us pull requests if your additions are worth sharing. Above all, feel free to suggest enhancements for the tool. We’d appreciate your insight.

Recent Blog Posts

Partner news: Cloudinary-Getty Images Integration

Supported by intelligent automation, Cloudinary serves as an effective conduit between media asset management and delivery so you can take maximum advantage of assets, compress workflows, and build and coordinate engaging and inspiring customer experiences. Through Cloudinary’s Digital Asset Management (DAM) solution, which employs the company’s innovative image and video APIs, creative and marketing teams can benefit from them, as well as from many AI-powered and automated capabilities. As a result, you can transform, optimize, and deliver media at scale on an intuitive UI.

Read more
Why Audio in Video Matters

Many content creators and consumers tend to regard video as visuals, but that’s only part of the experience. Immersive video content includes strong audio. Just like in a movie, the audio for video content comprises many components: the narrator or subjects, the background music that sets the mood and draws viewers in, sound effects, and so forth.

Read more

For Developers: the HTML <picture> Element Explained

By Amarachi Amaechi
For Developers: the HTML <picture> Element Explained

We all know the good ol', tireless <img> element, which has been a long-time go-to for inserting graphics into webpages. Time doesn’t stop, however, and neither do technological advancements. So, let’s get you up to speed with the element’s modern alternative: the <picture> element.

Read more