A concept to create a video trim FFmpeg command by using a slider to view the video at a start and end timeframe.
The easy slider integration is with the noUiSlider framework. FFmpeg on the backend to create the thumbnail files.
View the full index.php file here. The source video link.
To do a video trim or snip in FFmpeg you need the start time and then the duration for the end cut.
ffmpeg -ss 00:00:10 -i video.mp4 -t 00:00:25 -c copy trimmed.mp4
The above command will create a 15-second clip called trimmed.mp4 this clip is from the 10 second timeframe to the 25 second timeframe of video.mp4
The code is very similar to the thumbnail creator concept. However there are now 2 thumbnails and the slider time difference is calculated.
Defines the video file and the 2 thumbnail image file names. Next is a check for if new thumbnails need to be created (slider was changed, POST request sent) or if none exists (first time running) create from the 1 second fragment.
<?php
$file = "jellyfish.mkv";
$thumb_start = "thumbnail1.jpg";
$thumb_finish = "thumbnail2.jpg";
if (isset($_POST['time_string'])) {
if ($_POST['type'] === 'start') {
exec("ffmpeg -y -ss {$_POST['time_string']} -i $file -vframes 1 -q:v 1 $thumb_start");
} else {
exec("ffmpeg -y -ss {$_POST['time_string']} -i $file -vframes 1 -q:v 1 $thumb_finish");
}
} elseif (!file_exists("thumbnail1.jpg") || !file_exists("thumbnail2.jpg")) {
exec("ffmpeg -y -ss 00:00:01 -i $file -vframes 1 -q:v 1 $thumb_start");
exec("ffmpeg -y -ss 00:00:01 -i $file -vframes 1 -q:v 1 $thumb_finish");
} A check is done for the length of the video in seconds. This value is used as the max value for the slider.
function getSeconds(string $file): float
{
return exec("ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 $file");
}
$seconds = getSeconds($file); This requires jQuery and the noUiSlider library. Note PHP is used to echo the seconds into the max value.
let slider = document.getElementById("slider");
noUiSlider.create(slider, {
start: [0, <?=$seconds?>],
connect: true,
step: 1,
range: {
min: 0,
max: <?=$seconds?>
}
});
let start_element = document.getElementsByClassName("start");
let finish_element = document.getElementsByClassName("finish");
slider.noUiSlider.on("change", function (values, handle) {
let old_start = start_element[0].innerHTML;
let old_finish = finish_element[0].innerHTML;
let slider_values = slider.noUiSlider.get();
let start_string = new Date(slider_values[0] * 1000).toISOString().substr(11, 8);
let finish_string = new Date(slider_values[1] * 1000).toISOString().substr(11, 8);
let duration = new Date((slider_values[1] - slider_values[0]) * 1000).toISOString().substr(11, 8);
start_element[0].innerHTML = start_string;
start_element[1].innerHTML = start_string;
finish_element[0].innerHTML = finish_string;
finish_element[1].innerHTML = duration;
if (old_start !== start_string) {
createThumb(start_string, 'start');
}
if (old_finish !== finish_string) {
createThumb(finish_string, 'finish');
}
}); The Ajax POST request to create the thumbnail
function createThumb(time, type) {
$.ajax({
type: "POST",
url: "index.php",
data: {"time_string": time, "type": type},
success: function (result) {
let d = new Date();
if (type === 'start') {
$("#thumbnail-start").attr("src", "thumbnail1.jpg?" + d.getTime());
} else {
$("#thumbnail-finish").attr("src", "thumbnail2.jpg?" + d.getTime());
}
}
});
} A Bootstrap card with the noui slider.
<div class="container">
<div class="row">
<div class="col-12">
<div class="card shadow">
<div class="card-header">
<h2 class="text-center">Create video trim command</h2>
</div>
<div class="card-body px-4">
<div class="row">
<div class="col-6">
<img id="thumbnail-start" class="img-fluid" src="<?= $thumb_start ?>" width="100%"
alt="<?= $file ?> video thumbnail">
</div>
<div class="col-6">
<img id="thumbnail-finish" class="img-fluid" src="<?= $thumb_finish ?>" width="100%"
alt="<?= $file ?> video thumbnail">
</div>
</div>
<div class="row text-center mt-4">
<div id="slider"></div>
</div>
<div class="row text-center mt-3">
<div class="col-6">
<p>Start: <code><span class="start">00:00:01</span></code></p>
</div>
<div class="col-6">
<p>Finish: <code><span class="finish"><?= gmdate("H:i:s", $seconds) ?></span></code></p>
</div>
</div>
<div class="row text-center mt-3">
<div class="col-12">
<code>ffmpeg -ss <span class="start">00:00:00</span> -i INPUT.mp4 -t <span class="finish">00:00:00</span>
-c copy OUTPUT.mp4</code>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
A drained and empty Kennington reservoir images from a drone in early July 2024. The…
Merrimu Reservoir from drone. Click images to view larger.
Using FTP and PHP to get an array of file details such as size and…
Creating and using Laravel form requests to create cleaner code, separation and reusability for your…
Improving the default Laravel login and register views in such a simple manner but making…
Laravel validation for checking if a field value exists in the database. The validation rule…