> ## Documentation Index
> Fetch the complete documentation index at: https://cloudinary.com/documentation/llms.txt
> Use this file to discover all available pages before exploring further.

# Video concatenation


Use Cloudinary's video concatenation options to join videos together, combine videos with images, or concatenate private and authenticated assets. For transition effects between concatenated assets, see [Video transition effects](video_transition_effects).

## Before you start

Whether [concatenating videos together](#concatenate_videos_together), [concatenating videos with images](#concatenate_videos_with_images), with or without [transitions](video_transition_effects), there are a few important points to note:

* Assets spliced together or concatenated with a [custom transition](video_transition_effects#custom_transitions) must be the **same width and height**. You can use [size transformation parameters](video_resizing_and_cropping) to ensure that both assets match in size (`w` and `h`). If using a [transition video](video_transition_effects#custom_transitions), this is automatically scaled to the same size.
* Replace any forward slashes with colons in the public ID of the concatenated asset or the transition video.
* There is slightly different syntax for [concatenating authenticated or private assets](#concatenating_authenticated_or_private_assets).
* You can't use [object aware cropping](cloudinary_ai_content_analysis_addon#object_aware_cropping) in layers (assets concatenated with the overlay parameter). This includes when specifying an object as a gravity option for the [zoompan effect](transformation_reference#e_zoompan).

## Concatenate videos together

To concatenate videos, use the overlay video parameter (`l_video:` in URLs) to specify the name of another uploaded video and then add the `splice` flag (`fl_splice` in URLs), which instructs Cloudinary to concatenate the video with the base video rather than treating it as a layer over it.

You can also [concatenate videos with custom transitions](video_transition_effects#custom_transitions) using the `transition` effect (`e_transition` in URLs).

> **TIP**: Make sure you've read the [important notes](#before_you_start) regarding concatenating media.

For example, to concatenate the video named `fashion_walk` onto the end of the video named `meadow_walk`, with both videos set to a width of 300 pixels and a height of 200 pixels:

![fashion_walk.mp4 spliced onto the end of meadow_walk.mp4](https://res.cloudinary.com/demo/video/upload/c_fill,h_200,w_300/fl_splice,l_video:fashion_walk/c_fill,h_200,w_300/fl_layer_apply/meadow_walk.mp4 "width:300")

```nodejs
cloudinary.video("meadow_walk", {transformation: [
  {height: 200, width: 300, crop: "fill"},
  {flags: "splice", overlay: "video:fashion_walk"},
  {height: 200, width: 300, crop: "fill"},
  {flags: "layer_apply"}
  ]})
```

```react
import { fill } from "@cloudinary/url-gen/actions/resize";
import { concatenate } from "@cloudinary/url-gen/actions/videoEdit";
import { videoSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(fill().width(300).height(200))
  .videoEdit(
    concatenate(
      videoSource("fashion_walk").transformation(
        new Transformation().resize(fill().width(300).height(200))
      )
    )
  );
```

```vue
import { fill } from "@cloudinary/url-gen/actions/resize";
import { concatenate } from "@cloudinary/url-gen/actions/videoEdit";
import { videoSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(fill().width(300).height(200))
  .videoEdit(
    concatenate(
      videoSource("fashion_walk").transformation(
        new Transformation().resize(fill().width(300).height(200))
      )
    )
  );
```

```angular
import { fill } from "@cloudinary/url-gen/actions/resize";
import { concatenate } from "@cloudinary/url-gen/actions/videoEdit";
import { videoSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(fill().width(300).height(200))
  .videoEdit(
    concatenate(
      videoSource("fashion_walk").transformation(
        new Transformation().resize(fill().width(300).height(200))
      )
    )
  );
```

```js
import { fill } from "@cloudinary/url-gen/actions/resize";
import { concatenate } from "@cloudinary/url-gen/actions/videoEdit";
import { videoSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(fill().width(300).height(200))
  .videoEdit(
    concatenate(
      videoSource("fashion_walk").transformation(
        new Transformation().resize(fill().width(300).height(200))
      )
    )
  );
```

```python
CloudinaryVideo("meadow_walk").video(transformation=[
  {'height': 200, 'width': 300, 'crop': "fill"},
  {'flags': "splice", 'overlay': "video:fashion_walk"},
  {'height': 200, 'width': 300, 'crop': "fill"},
  {'flags': "layer_apply"}
  ])
```

```php
use Cloudinary\Transformation\Resize;
use Cloudinary\Transformation\VideoEdit;
use Cloudinary\Transformation\Concatenate;

(new VideoTag('meadow_walk.mp4'))
	->resize(Resize::fill()->width(300)
->height(200))
	->videoEdit(VideoEdit::concatenate(
	Concatenate::videoSource("fashion_walk")
	->transformation((new Transformation())
	->resize(Resize::fill()->width(300)
->height(200)))
	));
```

```java
cloudinary.url().transformation(new Transformation()
  .height(200).width(300).crop("fill").chain()
  .flags("splice").overlay(new Layer().publicId("video:fashion_walk")).chain()
  .height(200).width(300).crop("fill").chain()
  .flags("layer_apply")).videoTag("meadow_walk");
```

```ruby
cl_video_tag("meadow_walk", transformation: [
  {height: 200, width: 300, crop: "fill"},
  {flags: "splice", overlay: "video:fashion_walk"},
  {height: 200, width: 300, crop: "fill"},
  {flags: "layer_apply"}
  ])
```

```csharp
cloudinary.Api.UrlVideoUp.Transform(new Transformation()
  .Height(200).Width(300).Crop("fill").Chain()
  .Flags("splice").Overlay(new Layer().PublicId("video:fashion_walk")).Chain()
  .Height(200).Width(300).Crop("fill").Chain()
  .Flags("layer_apply")).BuildVideoTag("meadow_walk")
```

```dart
cloudinary.video('meadow_walk.mp4').transformation(Transformation()
	.resize(Resize.fill().width(300)
.height(200))
	.videoEdit(VideoEdit.concatenate(
	Concatenate.videoSource("fashion_walk")
	.transformation(new Transformation()
	.resize(Resize.fill().width(300)
.height(200)))
	)));
```

```swift
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation()
  .setHeight(200).setWidth(300).setCrop("fill").chain()
  .setFlags("splice").setOverlay("video:fashion_walk").chain()
  .setHeight(200).setWidth(300).setCrop("fill").chain()
  .setFlags("layer_apply")).generate("meadow_walk.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation()
  .height(200).width(300).crop("fill").chain()
  .flags("splice").overlay(new Layer().publicId("video:fashion_walk")).chain()
  .height(200).width(300).crop("fill").chain()
  .flags("layer_apply")).resourceType("video").generate("meadow_walk.mp4");
```

```flutter
cloudinary.video('meadow_walk.mp4').transformation(Transformation()
	.resize(Resize.fill().width(300)
.height(200))
	.videoEdit(VideoEdit.concatenate(
	Concatenate.videoSource("fashion_walk")
	.transformation(new Transformation()
	.resize(Resize.fill().width(300)
.height(200)))
	)));
```

```kotlin
cloudinary.video {
	publicId("meadow_walk.mp4")
	 resize(Resize.fill() { width(300)
 height(200) })
	 videoEdit(VideoEdit.concatenate(
	Concatenate.videoSource("fashion_walk") {
	 transformation(Transformation {
	 resize(Resize.fill() { width(300)
 height(200) }) })
	 })) 
}.generate()
```

```jquery
$.cloudinary.video("meadow_walk", {transformation: [
  {height: 200, width: 300, crop: "fill"},
  {flags: "splice", overlay: new cloudinary.Layer().publicId("video:fashion_walk")},
  {height: 200, width: 300, crop: "fill"},
  {flags: "layer_apply"}
  ]})
```

```react_native
import { fill } from "@cloudinary/url-gen/actions/resize";
import { concatenate } from "@cloudinary/url-gen/actions/videoEdit";
import { videoSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(fill().width(300).height(200))
  .videoEdit(
    concatenate(
      videoSource("fashion_walk").transformation(
        new Transformation().resize(fill().width(300).height(200))
      )
    )
  );
```

By default, Cloudinary splices the video onto the end of the base video. To splice the video onto the beginning, add the `start_offset` parameter and set it to 0 (`so_0` in URLs). For example, to concatenate the video named `fashion_walk` onto the beginning of the video named `meadow_walk`, with both videos set to a width of 300 pixels and a height of 200 pixels:

![fashion_walk.mp4 spliced to the beginning of meadow_walk.mp4](https://res.cloudinary.com/demo/video/upload/c_fill,h_200,w_300/fl_splice,l_video:fashion_walk/c_fill,h_200,w_300/fl_layer_apply,so_0/meadow_walk.mp4 "width:300")

```nodejs
cloudinary.video("meadow_walk", {transformation: [
  {height: 200, width: 300, crop: "fill"},
  {flags: "splice", overlay: "video:fashion_walk"},
  {height: 200, width: 300, crop: "fill"},
  {flags: "layer_apply", start_offset: "0"}
  ]})
```

```react
import { fill } from "@cloudinary/url-gen/actions/resize";
import { concatenate } from "@cloudinary/url-gen/actions/videoEdit";
import { videoSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(fill().width(300).height(200))
  .videoEdit(
    concatenate(
      videoSource("fashion_walk").transformation(
        new Transformation().resize(fill().width(300).height(200))
      )
    ).prepend()
  );
```

```vue
import { fill } from "@cloudinary/url-gen/actions/resize";
import { concatenate } from "@cloudinary/url-gen/actions/videoEdit";
import { videoSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(fill().width(300).height(200))
  .videoEdit(
    concatenate(
      videoSource("fashion_walk").transformation(
        new Transformation().resize(fill().width(300).height(200))
      )
    ).prepend()
  );
```

```angular
import { fill } from "@cloudinary/url-gen/actions/resize";
import { concatenate } from "@cloudinary/url-gen/actions/videoEdit";
import { videoSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(fill().width(300).height(200))
  .videoEdit(
    concatenate(
      videoSource("fashion_walk").transformation(
        new Transformation().resize(fill().width(300).height(200))
      )
    ).prepend()
  );
```

```js
import { fill } from "@cloudinary/url-gen/actions/resize";
import { concatenate } from "@cloudinary/url-gen/actions/videoEdit";
import { videoSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(fill().width(300).height(200))
  .videoEdit(
    concatenate(
      videoSource("fashion_walk").transformation(
        new Transformation().resize(fill().width(300).height(200))
      )
    ).prepend()
  );
```

```python
CloudinaryVideo("meadow_walk").video(transformation=[
  {'height': 200, 'width': 300, 'crop': "fill"},
  {'flags': "splice", 'overlay': "video:fashion_walk"},
  {'height': 200, 'width': 300, 'crop': "fill"},
  {'flags': "layer_apply", 'start_offset': "0"}
  ])
```

```php
use Cloudinary\Transformation\Resize;
use Cloudinary\Transformation\VideoEdit;
use Cloudinary\Transformation\Concatenate;

(new VideoTag('meadow_walk.mp4'))
	->resize(Resize::fill()->width(300)
->height(200))
	->videoEdit(VideoEdit::concatenate(
	Concatenate::videoSource("fashion_walk")
	->transformation((new Transformation())
	->resize(Resize::fill()->width(300)
->height(200)))
	)
	->prepend()
	);
```

```java
cloudinary.url().transformation(new Transformation()
  .height(200).width(300).crop("fill").chain()
  .flags("splice").overlay(new Layer().publicId("video:fashion_walk")).chain()
  .height(200).width(300).crop("fill").chain()
  .flags("layer_apply").startOffset("0")).videoTag("meadow_walk");
```

```ruby
cl_video_tag("meadow_walk", transformation: [
  {height: 200, width: 300, crop: "fill"},
  {flags: "splice", overlay: "video:fashion_walk"},
  {height: 200, width: 300, crop: "fill"},
  {flags: "layer_apply", start_offset: "0"}
  ])
```

```csharp
cloudinary.Api.UrlVideoUp.Transform(new Transformation()
  .Height(200).Width(300).Crop("fill").Chain()
  .Flags("splice").Overlay(new Layer().PublicId("video:fashion_walk")).Chain()
  .Height(200).Width(300).Crop("fill").Chain()
  .Flags("layer_apply").StartOffset("0")).BuildVideoTag("meadow_walk")
```

```dart
cloudinary.video('meadow_walk.mp4').transformation(Transformation()
	.resize(Resize.fill().width(300)
.height(200))
	.videoEdit(VideoEdit.concatenate(
	Concatenate.videoSource("fashion_walk")
	.transformation(new Transformation()
	.resize(Resize.fill().width(300)
.height(200)))
	)
	.prepend()
	));
```

```swift
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation()
  .setHeight(200).setWidth(300).setCrop("fill").chain()
  .setFlags("splice").setOverlay("video:fashion_walk").chain()
  .setHeight(200).setWidth(300).setCrop("fill").chain()
  .setFlags("layer_apply").setStartOffset("0")).generate("meadow_walk.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation()
  .height(200).width(300).crop("fill").chain()
  .flags("splice").overlay(new Layer().publicId("video:fashion_walk")).chain()
  .height(200).width(300).crop("fill").chain()
  .flags("layer_apply").startOffset("0")).resourceType("video").generate("meadow_walk.mp4");
```

```flutter
cloudinary.video('meadow_walk.mp4').transformation(Transformation()
	.resize(Resize.fill().width(300)
.height(200))
	.videoEdit(VideoEdit.concatenate(
	Concatenate.videoSource("fashion_walk")
	.transformation(new Transformation()
	.resize(Resize.fill().width(300)
.height(200)))
	)
	.prepend()
	));
```

```kotlin
cloudinary.video {
	publicId("meadow_walk.mp4")
	 resize(Resize.fill() { width(300)
 height(200) })
	 videoEdit(VideoEdit.concatenate(
	Concatenate.videoSource("fashion_walk") {
	 transformation(Transformation {
	 resize(Resize.fill() { width(300)
 height(200) }) })
	 }) {
	 prepend()
	 }) 
}.generate()
```

```jquery
$.cloudinary.video("meadow_walk", {transformation: [
  {height: 200, width: 300, crop: "fill"},
  {flags: "splice", overlay: new cloudinary.Layer().publicId("video:fashion_walk")},
  {height: 200, width: 300, crop: "fill"},
  {flags: "layer_apply", start_offset: "0"}
  ]})
```

```react_native
import { fill } from "@cloudinary/url-gen/actions/resize";
import { concatenate } from "@cloudinary/url-gen/actions/videoEdit";
import { videoSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(fill().width(300).height(200))
  .videoEdit(
    concatenate(
      videoSource("fashion_walk").transformation(
        new Transformation().resize(fill().width(300).height(200))
      )
    ).prepend()
  );
```

To concatenate only a section of a video to the end of another video, use the offset parameters (see [Trimming videos](video_trimming#trimming_videos) for more information on the parameters and their possible values) together with the `layer_apply` flag (`fl_layer_apply` in URLs). For example, to splice the first 5 seconds of the video named `fashion_walk` to the end of the video named `meadow_walk`, with both videos set to a width of 300 pixels and a height of 200 pixels.

![The first 5 seconds of fashion_walk.mp4 spliced onto the end of meadow_walk.mp4 rotated by 20 degrees](https://res.cloudinary.com/demo/video/upload/c_fill,h_200,w_300/du_5,fl_splice,l_video:fashion_walk,so_0/c_fill,h_200,w_300/fl_layer_apply/meadow_walk.mp4 "width:300")

```nodejs
cloudinary.video("meadow_walk", {transformation: [
  {height: 200, width: 300, crop: "fill"},
  {duration: "5", flags: "splice", overlay: "video:fashion_walk", start_offset: "0"},
  {height: 200, width: 300, crop: "fill"},
  {flags: "layer_apply"}
  ]})
```

```react
import { fill } from "@cloudinary/url-gen/actions/resize";
import { concatenate, trim } from "@cloudinary/url-gen/actions/videoEdit";
import { videoSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(fill().width(300).height(200))
  .videoEdit(
    concatenate(
      videoSource("fashion_walk").transformation(
        new Transformation()
          .videoEdit(trim().startOffset("0.0").duration("5.0"))
          .resize(fill().width(300).height(200))
      )
    )
  );
```

```vue
import { fill } from "@cloudinary/url-gen/actions/resize";
import { concatenate, trim } from "@cloudinary/url-gen/actions/videoEdit";
import { videoSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(fill().width(300).height(200))
  .videoEdit(
    concatenate(
      videoSource("fashion_walk").transformation(
        new Transformation()
          .videoEdit(trim().startOffset("0.0").duration("5.0"))
          .resize(fill().width(300).height(200))
      )
    )
  );
```

```angular
import { fill } from "@cloudinary/url-gen/actions/resize";
import { concatenate, trim } from "@cloudinary/url-gen/actions/videoEdit";
import { videoSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(fill().width(300).height(200))
  .videoEdit(
    concatenate(
      videoSource("fashion_walk").transformation(
        new Transformation()
          .videoEdit(trim().startOffset("0.0").duration("5.0"))
          .resize(fill().width(300).height(200))
      )
    )
  );
```

```js
import { fill } from "@cloudinary/url-gen/actions/resize";
import { concatenate, trim } from "@cloudinary/url-gen/actions/videoEdit";
import { videoSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(fill().width(300).height(200))
  .videoEdit(
    concatenate(
      videoSource("fashion_walk").transformation(
        new Transformation()
          .videoEdit(trim().startOffset("0.0").duration("5.0"))
          .resize(fill().width(300).height(200))
      )
    )
  );
```

```python
CloudinaryVideo("meadow_walk").video(transformation=[
  {'height': 200, 'width': 300, 'crop': "fill"},
  {'duration': "5", 'flags': "splice", 'overlay': "video:fashion_walk", 'start_offset': "0"},
  {'height': 200, 'width': 300, 'crop': "fill"},
  {'flags': "layer_apply"}
  ])
```

```php
use Cloudinary\Transformation\Resize;
use Cloudinary\Transformation\VideoEdit;
use Cloudinary\Transformation\Concatenate;

(new VideoTag('meadow_walk.mp4'))
	->resize(Resize::fill()->width(300)
->height(200))
	->videoEdit(VideoEdit::concatenate(
	Concatenate::videoSource("fashion_walk")
	->transformation((new Transformation())
	->videoEdit(VideoEdit::trim()->startOffset(0.0)
->duration(5.0))
	->resize(Resize::fill()->width(300)
->height(200)))
	));
```

```java
cloudinary.url().transformation(new Transformation()
  .height(200).width(300).crop("fill").chain()
  .duration("5").flags("splice").overlay(new Layer().publicId("video:fashion_walk")).startOffset("0").chain()
  .height(200).width(300).crop("fill").chain()
  .flags("layer_apply")).videoTag("meadow_walk");
```

```ruby
cl_video_tag("meadow_walk", transformation: [
  {height: 200, width: 300, crop: "fill"},
  {duration: "5", flags: "splice", overlay: "video:fashion_walk", start_offset: "0"},
  {height: 200, width: 300, crop: "fill"},
  {flags: "layer_apply"}
  ])
```

```csharp
cloudinary.Api.UrlVideoUp.Transform(new Transformation()
  .Height(200).Width(300).Crop("fill").Chain()
  .Duration("5").Flags("splice").Overlay(new Layer().PublicId("video:fashion_walk")).StartOffset("0").Chain()
  .Height(200).Width(300).Crop("fill").Chain()
  .Flags("layer_apply")).BuildVideoTag("meadow_walk")
```

```dart
cloudinary.video('meadow_walk.mp4').transformation(Transformation()
	.resize(Resize.fill().width(300)
.height(200))
	.videoEdit(VideoEdit.concatenate(
	Concatenate.videoSource("fashion_walk")
	.transformation(new Transformation()
	.videoEdit(VideoEdit.trim().startOffset('0.0')
.duration('5.0'))
	.resize(Resize.fill().width(300)
.height(200)))
	)));
```

```swift
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation()
  .setHeight(200).setWidth(300).setCrop("fill").chain()
  .setDuration("5").setFlags("splice").setOverlay("video:fashion_walk").setStartOffset("0").chain()
  .setHeight(200).setWidth(300).setCrop("fill").chain()
  .setFlags("layer_apply")).generate("meadow_walk.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation()
  .height(200).width(300).crop("fill").chain()
  .duration("5").flags("splice").overlay(new Layer().publicId("video:fashion_walk")).startOffset("0").chain()
  .height(200).width(300).crop("fill").chain()
  .flags("layer_apply")).resourceType("video").generate("meadow_walk.mp4");
```

```flutter
cloudinary.video('meadow_walk.mp4').transformation(Transformation()
	.resize(Resize.fill().width(300)
.height(200))
	.videoEdit(VideoEdit.concatenate(
	Concatenate.videoSource("fashion_walk")
	.transformation(new Transformation()
	.videoEdit(VideoEdit.trim().startOffset('0.0')
.duration('5.0'))
	.resize(Resize.fill().width(300)
.height(200)))
	)));
```

```kotlin
cloudinary.video {
	publicId("meadow_walk.mp4")
	 resize(Resize.fill() { width(300)
 height(200) })
	 videoEdit(VideoEdit.concatenate(
	Concatenate.videoSource("fashion_walk") {
	 transformation(Transformation {
	 videoEdit(VideoEdit.trim() { startOffset(0.0F)
 duration(5.0F) })
	 resize(Resize.fill() { width(300)
 height(200) }) })
	 })) 
}.generate()
```

```jquery
$.cloudinary.video("meadow_walk", {transformation: [
  {height: 200, width: 300, crop: "fill"},
  {duration: "5", flags: "splice", overlay: new cloudinary.Layer().publicId("video:fashion_walk"), start_offset: "0"},
  {height: 200, width: 300, crop: "fill"},
  {flags: "layer_apply"}
  ]})
```

```react_native
import { fill } from "@cloudinary/url-gen/actions/resize";
import { concatenate, trim } from "@cloudinary/url-gen/actions/videoEdit";
import { videoSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(fill().width(300).height(200))
  .videoEdit(
    concatenate(
      videoSource("fashion_walk").transformation(
        new Transformation()
          .videoEdit(trim().startOffset("0.0").duration("5.0"))
          .resize(fill().width(300).height(200))
      )
    )
  );
```

The `layer_apply` flag in the above example instructs Cloudinary to apply the `so_0` parameter in the layer component as a regular start offset transformation parameter applied to the layer, and not in its special concatenate-to-beginning usage for `fl_splice`. To use `so_0` with `fl_splice` for concatenating to the beginning, in a case where `fl_layer_apply` is also used, specify `so_0` in the `fl_layer_apply` component, for example: `/l_...../fl_layer_apply,so_0`.

## Concatenate videos with images

Cloudinary also supports the concatenation of videos with images by using the following combination of parameters:

* `overlay` (`l`: in URLs) to specify the name of an uploaded image.
* `splice` flag (`fl_splice` in URLs) to indicate that Cloudinary should concatenate the image onto the base video and not add it as an overlay.
* `duration` parameter (`du` in URLs) to specify the amount of time in seconds to display the image.
* `start_offset` (optional) set to 0 (`so_0` in URLs) to concatenate the image at the beginning of the video instead of at the end.
* `layer_apply` flag (`fl_layer_apply`) to indicate that Cloudinary should apply the above parameters to the image (with the overlay parameter) and not the base video.

> **TIP**: Make sure you've read the [important notes](#before_you_start) regarding concatenating media.

For example, to concatenate the image named `red-sweater` to the start of the video named `meadow_walk` for a duration of 3 seconds (Cloudinary scales the image to a width of 300 pixels and a height of 200 pixels, resizes the video to those dimensions, and pads the video for the extra space):

![meadow_walk.mp4 with red_sweater.jpg concatenated at the start for 3 seconds](https://res.cloudinary.com/demo/video/upload/c_pad,h_200,w_300/du_3,fl_splice,h_200,l_red_sweater,w_300/fl_layer_apply,so_0/meadow_walk.mp4 "width:300")

```nodejs
cloudinary.video("meadow_walk", {transformation: [
  {height: 200, width: 300, crop: "pad"},
  {duration: "3", flags: "splice", height: 200, overlay: "red_sweater", width: 300},
  {flags: "layer_apply", start_offset: "0"}
  ]})
```

```react
import { pad, scale } from "@cloudinary/url-gen/actions/resize";
import { concatenate, trim } from "@cloudinary/url-gen/actions/videoEdit";
import { imageSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(pad().width(300).height(200))
  .videoEdit(
    concatenate(
      imageSource("red_sweater").transformation(
        new Transformation()
          .videoEdit(trim().duration("3.0"))
          .resize(scale().width(300).height(200))
      )
    ).prepend()
  );
```

```vue
import { pad, scale } from "@cloudinary/url-gen/actions/resize";
import { concatenate, trim } from "@cloudinary/url-gen/actions/videoEdit";
import { imageSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(pad().width(300).height(200))
  .videoEdit(
    concatenate(
      imageSource("red_sweater").transformation(
        new Transformation()
          .videoEdit(trim().duration("3.0"))
          .resize(scale().width(300).height(200))
      )
    ).prepend()
  );
```

```angular
import { pad, scale } from "@cloudinary/url-gen/actions/resize";
import { concatenate, trim } from "@cloudinary/url-gen/actions/videoEdit";
import { imageSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(pad().width(300).height(200))
  .videoEdit(
    concatenate(
      imageSource("red_sweater").transformation(
        new Transformation()
          .videoEdit(trim().duration("3.0"))
          .resize(scale().width(300).height(200))
      )
    ).prepend()
  );
```

```js
import { pad, scale } from "@cloudinary/url-gen/actions/resize";
import { concatenate, trim } from "@cloudinary/url-gen/actions/videoEdit";
import { imageSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(pad().width(300).height(200))
  .videoEdit(
    concatenate(
      imageSource("red_sweater").transformation(
        new Transformation()
          .videoEdit(trim().duration("3.0"))
          .resize(scale().width(300).height(200))
      )
    ).prepend()
  );
```

```python
CloudinaryVideo("meadow_walk").video(transformation=[
  {'height': 200, 'width': 300, 'crop': "pad"},
  {'duration': "3", 'flags': "splice", 'height': 200, 'overlay': "red_sweater", 'width': 300},
  {'flags': "layer_apply", 'start_offset': "0"}
  ])
```

```php
use Cloudinary\Transformation\Resize;
use Cloudinary\Transformation\VideoEdit;
use Cloudinary\Transformation\Concatenate;

(new VideoTag('meadow_walk.mp4'))
	->resize(Resize::pad()->width(300)
->height(200))
	->videoEdit(VideoEdit::concatenate(
	Concatenate::imageSource("red_sweater")
	->transformation((new Transformation())
	->videoEdit(VideoEdit::trim()->duration(3.0))
	->resize(Resize::scale()->width(300)
->height(200)))
	)
	->prepend()
	);
```

```java
cloudinary.url().transformation(new Transformation()
  .height(200).width(300).crop("pad").chain()
  .duration("3").flags("splice").height(200).overlay(new Layer().publicId("red_sweater")).width(300).chain()
  .flags("layer_apply").startOffset("0")).videoTag("meadow_walk");
```

```ruby
cl_video_tag("meadow_walk", transformation: [
  {height: 200, width: 300, crop: "pad"},
  {duration: "3", flags: "splice", height: 200, overlay: "red_sweater", width: 300},
  {flags: "layer_apply", start_offset: "0"}
  ])
```

```csharp
cloudinary.Api.UrlVideoUp.Transform(new Transformation()
  .Height(200).Width(300).Crop("pad").Chain()
  .Duration("3").Flags("splice").Height(200).Overlay(new Layer().PublicId("red_sweater")).Width(300).Chain()
  .Flags("layer_apply").StartOffset("0")).BuildVideoTag("meadow_walk")
```

```dart
cloudinary.video('meadow_walk.mp4').transformation(Transformation()
	.resize(Resize.pad().width(300)
.height(200))
	.videoEdit(VideoEdit.concatenate(
	Concatenate.imageSource("red_sweater")
	.transformation(new Transformation()
	.videoEdit(VideoEdit.trim().duration('3.0'))
	.resize(Resize.scale().width(300)
.height(200)))
	)
	.prepend()
	));
```

```swift
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation()
  .setHeight(200).setWidth(300).setCrop("pad").chain()
  .setDuration("3").setFlags("splice").setHeight(200).setOverlay("red_sweater").setWidth(300).chain()
  .setFlags("layer_apply").setStartOffset("0")).generate("meadow_walk.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation()
  .height(200).width(300).crop("pad").chain()
  .duration("3").flags("splice").height(200).overlay(new Layer().publicId("red_sweater")).width(300).chain()
  .flags("layer_apply").startOffset("0")).resourceType("video").generate("meadow_walk.mp4");
```

```flutter
cloudinary.video('meadow_walk.mp4').transformation(Transformation()
	.resize(Resize.pad().width(300)
.height(200))
	.videoEdit(VideoEdit.concatenate(
	Concatenate.imageSource("red_sweater")
	.transformation(new Transformation()
	.videoEdit(VideoEdit.trim().duration('3.0'))
	.resize(Resize.scale().width(300)
.height(200)))
	)
	.prepend()
	));
```

```kotlin
cloudinary.video {
	publicId("meadow_walk.mp4")
	 resize(Resize.pad() { width(300)
 height(200) })
	 videoEdit(VideoEdit.concatenate(
	Concatenate.imageSource("red_sweater") {
	 transformation(Transformation {
	 videoEdit(VideoEdit.trim() { duration(3.0F) })
	 resize(Resize.scale() { width(300)
 height(200) }) })
	 }) {
	 prepend()
	 }) 
}.generate()
```

```jquery
$.cloudinary.video("meadow_walk", {transformation: [
  {height: 200, width: 300, crop: "pad"},
  {duration: "3", flags: "splice", height: 200, overlay: new cloudinary.Layer().publicId("red_sweater"), width: 300},
  {flags: "layer_apply", start_offset: "0"}
  ]})
```

```react_native
import { pad, scale } from "@cloudinary/url-gen/actions/resize";
import { concatenate, trim } from "@cloudinary/url-gen/actions/videoEdit";
import { imageSource } from "@cloudinary/url-gen/qualifiers/concatenate";

new CloudinaryVideo("meadow_walk.mp4")
  .resize(pad().width(300).height(200))
  .videoEdit(
    concatenate(
      imageSource("red_sweater").transformation(
        new Transformation()
          .videoEdit(trim().duration("3.0"))
          .resize(scale().width(300).height(200))
      )
    ).prepend()
  );
```


## Video tutorial: Concatenate videos using the Node.js SDK

Watch this video tutorial to learn how to concatenate two videos using the [Node.js SDK](node_integration):

  This video is brought to you by Cloudinary's video player - embed your own!Use the controls to set the playback speed, navigate to chapters of interest and select subtitles in your preferred language.
{videoTranscript:publicId=training/Splicing_Videos_Node}

### Tutorial contents This tutorial presents the following topics. Click a timestamp to jump to that part of the video.
### Introduction
{table:class=tutorial-bullets}|  | 
| --- | --- |
|{videotime:id=media1 :min=0 :sec=00 :player=cld} | To stitch two videos together, you can use Cloudinary's [video concatenation options](video_concatenation).
|

### Install and configure the Node.js SDK
{table:class=tutorial-bullets}|  | 
| --- | --- |
|{videotime:id=media1 :min=0 :sec=20 :player=cld} | First, [install and configure the Cloudinary Node.js SDK](node_configuration_tutorial) in your Node.js app.
|

### Create the URL to concatenate the videos
{table:class=tutorial-bullets}|  | 
| --- | --- |
|{videotime:id=media1 :min=0 :sec=27 :player=cld} | Start with both of your videos [uploaded](upload_images) to Cloudinary. Then you can specify their public IDs in the code that creates the URL for the spliced videos.  For example, to splice the `samples/elephants` video with the `samples/sea-turtle` video:
|

```nodejs
const videoUrl = cloudinary.url('samples/elephants.mp4', {
  resource_type: 'video',
  transformation: [
    {
      flags: 'splice',
      overlay: 'video:samples:sea-turtle',
    },
    { flags: 'layer_apply' },
  ],
});
```

See the full code example in [GitHub](https://github.com/cloudinary-community/cloudinary-examples/blob/main/examples/node-transformations-effects/video-splice.js).

### Check the resulting URL
{table:class=tutorial-bullets}|  | 
| --- | --- |
|{videotime:id=media1 :min=1 :sec=51 :player=cld} | Open the resulting URL in a browser to see the sea-turtle video following straight on from the elephants video.
|

## Concatenating authenticated or private assets

Similar to [specifying authenticated or private assets in overlays](video_layers#authenticated_or_private_layers), when concatenating authenticated or private assets, you need to modify the syntax accordingly:

**Images**:

* For private images: `l_private:<public_id of image>`
* For authenticated images: `l_authenticated:<public_id of image>`

**Videos**:

* For private videos: `l_video:private:<public_id of video>`
* For authenticated videos: `l_video:authenticated:<public_id of video>`

**Audio assets**:

* For private audio assets: `l_audio:private:<public_id of audio asset>`
* For authenticated audio assets: `l_audio:authenticated:<public_id of audio asset>`

## Related topics

* [Video trimming](video_trimming)
* [Video transition effects](video_transition_effects)
* [Video resizing and cropping](video_resizing_and_cropping)
* [Video layers](video_layers)

