How to trim HLS or MEPG-DASH video using FFmpeg?

I have one HLS video URL and I want to trim the video from 30 seconds to 2 minutes and 30 seconds, how do I do that using FFmpeg?

2 Likes

Hey Annie!

Basically, FFmpeg provides a two-parameter for trimming the video.
• -ss option allows you to seek video at a particular position it takes value in HH:MM: SS format or in absolute seconds.
• -t options allow you to read a specified number of seconds from the video, taking value in the same format as “ -ss “. So with the combination of both -ss and -t you can trim an HLS or MPEG-DASH video.

You can use the following FFmpeg commands to cut the video.

Let’s assume that I want to trim a video to the first 30 seconds and there is only one video and audio stream in the video.
ffmpeg -i <HLS/MPEG-DASH Video URL> -ss 0 -t 30 -c copy -bsf:a aac_adtstoasc -f mp4 output.mp4

This is all to trim videos and this is how you can merge those videos using ffmpeg.
I hope this may resolve your issue!

Thankyou :smiley:

Great! It works for me.

How can FFmpeg be utilized to handle additional tasks, such as adding or replacing metadata, when trimming HLS or MPEG-DASH videos, and what impact might these tasks have on the overall streaming workflow?

Sail, in addition to simple trimming, FFmpeg has flexible capabilities that let users change or replace metadata as the file is being trimmed. For example, users can use the FFmpeg command with the -metadata title=“New Title” option to add metadata to a trimmed movie. This can be especially helpful when it comes to updating data about the video material.

It’s important to remember, too, that adding new jobs, like metadata modification, could have an effect on the streaming workflow as a whole. The jobs selected should be in line with the unique needs of the streaming service and the expectations of the audience.
For instance, upgrading or adding metadata can improve the video’s classification and accessibility, but it may also lengthen processing time.

Beyond trimming, FFmpeg offers several features that help improve the workflow related to managing the video and metadata when streaming. Content providers can improve classification and searchability by updating information related to their videos by adding or removing metadata.

All new tasks added to the FFmpeg process, however, have to be taken into account. For example, adding information may improve the viewing experience but can somewhat increase processing time. The advantages of these extra responsibilities should be weighed against any possible trade-offs in processing efficiency by content authors.

To sum up, FFmpeg’s versatility goes beyond basic editing, allowing users to customize films to meet particular streaming needs. To establish a balance between improved functionality and overall workflow, users should carefully consider the effects of new activities.

For more visit:

import React, { useState, useEffect, useRef } from “react”;
import Hls from “hls.js”;
import { createFFmpeg, fetchFile } from “@ffmpeg/ffmpeg”;
const VideoTrimmer = () => {
const [video, setVideo] = useState(null);
const [ffmpeg, setFfmpeg] = useState(null);
const [isReady, setIsReady] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [progress, setProgress] = useState(0);
const [startTime, setStartTime] = useState(0);
const [endTime, setEndTime] = useState(0);
const [downloadLink, setDownloadLink] = useState(“”);
const [duration, setDuration] = useState(0);
const [videoChunks, setVideoChunks] = useState();
const videoRef = useRef(null);
const hlsUrl = “https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8”;
// Initialize FFmpeg and HLS
useEffect(() => {
const initializeFFmpeg = async () => {
const ffmpegInstance = createFFmpeg({
log: true,
progress: ({ ratio }) => {
setProgress(Math.round(ratio * 100));
},
});
try {
await ffmpegInstance.load();
setFfmpeg(ffmpegInstance);
setIsReady(true);
} catch (error) {
console.error(“Failed to load FFmpeg:”, error);
}
};
initializeFFmpeg();
setupHls();
}, );
const setupHls = () => {
if (Hls.isSupported()) {
const hls = new Hls({
xhrSetup: function (xhr) {
xhr.responseType = “arraybuffer”;
},
});
hls.on(Hls.Events.MEDIA_ATTACHED, () => {
console.log(“HLS media attached”);
});
hls.on(Hls.Events.MANIFEST_PARSED, (event, data) => {
console.log(
“Manifest parsed, found " + data.levels.length + " quality levels”
);
videoRef.current.play();
});
hls.on(Hls.Events.FRAG_LOADED, (event, data) => {
const fragment = data.frag;
const payload = data.payload;
// Store video chunks for later processing
setVideoChunks((prev) => [
…prev,
{
sequence: fragment.sn,
startTime: fragment.start,
duration: fragment.duration,
data: new Uint8Array(payload.byteLength),
},
]);
// Copy payload data to our chunk object
new Uint8Array(payload).forEach((byte, i) => {
videoChunks[videoChunks.length - 1].data[i] = byte;
});
});
hls.loadSource(hlsUrl);
hls.attachMedia(videoRef.current);
setVideo(hls);
} else if (videoRef.current.canPlayType(“application/vnd.apple.mpegurl”)) {
// For Safari browsers
videoRef.current.src = hlsUrl;
}
};
const handleLoadedMetadata = () => {
setDuration(videoRef.current.duration);
setEndTime(videoRef.current.duration);
};
const handleTrim = async () => {
if (!isReady || !ffmpeg) {
alert(“FFmpeg is not ready yet”);
return;
}
setIsLoading(true);
setProgress(0);
try {
// We need to download the HLS segments that cover our trim range
const relevantChunks = videoChunks.filter(
(chunk) =>
(chunk.startTime >= startTime && chunk.startTime <= endTime) ||
(chunk.startTime + chunk.duration >= startTime &&
chunk.startTime + chunk.duration <= endTime)
);
if (relevantChunks.length === 0) {
alert(“No video data available for the selected range”);
setIsLoading(false);
return;
}
// Combine chunks into a temporary video file
const tempFileName = “temp_video.mp4”;
ffmpeg.FS(
“writeFile”,
tempFileName,
await fetchFile(new Blob(relevantChunks.map((chunk) => chunk.data)))
);
// Trim the video using FFmpeg
await ffmpeg.run(
“-i”,
tempFileName,
“-ss”,
startTime.toString(),
“-to”,
endTime.toString(),
“-c:v”,
“copy”,
“-c:a”,
“copy”,
“output.mp4”
);
// Read the output file
const data = ffmpeg.FS(“readFile”, “output.mp4”);
// Create a download link
const url = URL.createObjectURL(
new Blob([data.buffer], { type: “video/mp4” })
);
setDownloadLink(url);
// Clean up
ffmpeg.FS(“unlink”, tempFileName);
ffmpeg.FS(“unlink”, “output.mp4”);
} catch (error) {
console.error(“Error during trimming:”, error);
alert(“Error during video trimming”);
}
setIsLoading(false);
};
const updateVideoPosition = (time) => {
if (videoRef.current) {
videoRef.current.currentTime = time;
}
};
return (


HLS Video Trimmer






Start Time: {startTime.toFixed(2)}s
End Time: {endTime.toFixed(2)}s



<input
type=“range”
min=“0”
max={duration}
step=“0.1”
value={startTime}
onChange={(e) => {
const value = parseFloat(e.target.value);
if (value < endTime) {
setStartTime(value);
updateVideoPosition(value);
}
}}
className=“w-full”
/>


<input
type=“range”
min=“0”
max={duration}
step=“0.1”
value={endTime}
onChange={(e) => {
const value = parseFloat(e.target.value);
if (value > startTime) {
setEndTime(value);
updateVideoPosition(value);
}
}}
className=“w-full”
/>




<button
onClick={() => updateVideoPosition(startTime)}
className=“px-4 py-2 bg-blue-500 text-white rounded”
disabled={isLoading}
>
Preview Start

<button
onClick={() => updateVideoPosition(endTime)}
className=“px-4 py-2 bg-blue-500 text-white rounded”
disabled={isLoading}
>
Preview End

<button
onClick={handleTrim}
className=“px-4 py-2 bg-green-500 text-white rounded”
disabled={isLoading || !isReady}
>
{isLoading ? Processing (${progress}%) : “Trim Video”}


{downloadLink && (

)}
{!isReady &&

Loading FFmpeg…

}

);
};
export default VideoTrimmer;

video trimming is not working here getting error messgae

[fferr] [mov,mp4,m4a,3gp,3g2,mj2 @ 0x1f65fc0] Format mov,mp4,m4a,3gp,3g2,mj2 detected only with low score of 1, misdetection possible!
bf288058-73f8-4a6a-a608-ffabac80179d:49 [fferr] [mov,mp4,m4a,3gp,3g2,mj2 @ 0x1f65fc0] moov atom not found
bf288058-73f8-4a6a-a608-ffabac80179d:49 [fferr] temp_video.mp4: Invalid data found when processing input
bf288058-73f8-4a6a-a608-ffabac80179d:49 [ffout] FFMPEG_END
videotrimmer.js:125 [info] run FS.readFile output.mp4

@Deepak_k, welcome to the community.

[fferr] [mov,mp4,m4a,3gp,3g2,mj2 @ 0x1f65fc0] Format mov,mp4,m4a,3gp,3g2,mj2 detected only with low score of 1, misdetection possible! → this means the file may be corrupted of truncated.

bf288058-73f8-4a6a-a608-ffabac80179d:49 [fferr] [mov,mp4,m4a,3gp,3g2,mj2 @ 0x1f65fc0] moov atom not found → this contains the metadata of an MP4 file, if this is missing, processing is not possible

Note: Try this command ffmpeg -v error -i temp_video.mp4 -f null - and if this throws error, the file is damaged.

Solution:
You can try this ffmpeg -i temp_video.mp4 -c copy -movflags +faststart fixed_video.mp4 to fix the errors. If not, try to source the file again. It may be not completely downloaded or still in writing mode.