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

Overlaying Glasses Or Masks on Avatars With Vue.js and Cloudinary

Deep learning, a subset of machine learning, breaks down tasks in ways that make all kinds of machine assists possible. With deep learning, you can identify images by machine, i.e., instead of hand-coding software with specific instructions for a task, you train the machine with large amounts of data and algorithms that enable it to learn how to perform the task.

Read more
On-Demand Viewing of Live Video Presents New Opportunities

In early 2020, Cloudinary was planning its fourth annual ImageCon conference, a two-day event in the heart of San Francisco, where we’d congregate with curious digital-media minds to brainstorm best practices for media management. Instead, the COVID-19 pandemic forced the entirety of ImageCon 2020 online. As with all other events being planned, we had to overhaul the content to be communicated on video. Gratifyingly, we found the right partner—the event platform Bizzabo—to turn that into a reality.

Read more
Why the Future of E-commerce Is Live

In a previous post, I discussed how “going live” is gaining popularity across industries and verticals. What began as a way for gamers to jam together has evolved into a medium for broader entertainment and business purposes. To continue the conversation, this post unpacks the current trends of shoppable live streams to shine a light on how brands are leveraging “lives” to connect with shoppers in new ways.

Read more
An Overview of Live-Streaming Video Trends

“Let’s go live.” For decades, that’s what newscasters say as they cut to real-time footage of a colleague reporting in the field. The live-video feed adds visual interest and perspective to a story beyond what can be communicated by someone sitting behind the news desk. In the same way, live-streaming video nowadays adds context to other consumer environments. From gaming and events to shopping and social media, “going live” enhances everyday experiences, and it’s something anyone can do with relative ease.

Read more