tutorial 07: FFMPEG
Welcome to this tutorial on using FFmpeg. In this tutorial, you will learn to process files using FFmpeg. You will also learn how to use various other functions and commands to manipulate strings, display dialog boxes, update the progress bar, check for user cancelation, and more. By the end of this tutorial, you should have a solid understanding of how these functions and commands work and be able to use them to perform similar tasks in your own projects.
Step 1: Implement the cmd_callback() function
To start, create a new recipe file and define the cmd_callback()
function. This function will be called after the cmd()
function is executed and receives the command name and output as arguments. This function calculates the progress of the ffmpeg command and updates the progress bar. It also checks if the process has been canceled and returns an object to terminate the process if needed.
function cmdcallback(cmdname, cmd_output)
{
// Check if the command source is "ffmpeg"
if (cmdname == "ffmpeg" && cmdoutput.length > 0)
{
// Calculate progress in seconds from the current timestamp and update progress bar
var progress = CalculateSeconds(GetStringFromTo(cmd_output, "time=", " bitrate"));
progress = Math.round(progress / totalsec * 100);
setProgress(progress);
}
return {
"terminate": isCanceled()
};
}
Step 2: Implement the GetStringFromTo() function
Next, define the GetStringFromTo()
function. This function searches for a string between a specified starting point (from) and ending point (to) in a source string (source). It uses a regular expression to extract the desired string and returns it.
function GetStringFromTo(source, from, to){
// Create regular expression to match string between 'from' and 'to'
var wildcard = from + "(.*)" + to;
// Search for match in source string
var r = searchRegEx(source, wildcard, "i", 0);
// If a match is found, return the desired string
if (typeof(r[0]) == "string") {
return r[0].substring(from.length, r[0].length - to.length);
} else {
return "";
}
}
Step 3: Implement the CalculateSeconds() function
Now let's implement the CalculateSeconds()
function. This function converts a duration in the format HH:MM:SS to the equivalent number of seconds.
function CalculateSeconds(dur_tc)
{
// Extract hours, minutes, and seconds from duration string
var hours = (dur_tc.substring(0, 2));
var minutes = (dur_tc.substring(3, 3+2));
var seconds = (dur_tc.substring(6, 6+2));
// Remove leading zeros
if (hours.substring(0, 1) == "0") {
hours = hours.substring(1,2);
}
if (minutes.substring(0, 1) == "0") {
minutes = minutes.substring(1,2);
}
if (seconds.substring(0, 1) == "0") {
seconds = seconds.substring(1,2);
}
// Convert strings to integers
hours = parseInt(hours);
minutes = parseInt(minutes);
seconds = parseInt(seconds);
// Calculate total number of seconds
return (hours 60 60) + (minutes * 60) + seconds;
}
Step 4: Implement the getFileSeconds() function
Now let's implement the getFileSeconds()
function. This function runs the ffprobe command on a given file (infile) to get its duration and then uses CalculateSeconds() to convert it to the number of seconds.
function getFileSeconds(infile)
{
// Run ffprobe command on infile
var probeoutput = cmd("ffprobe", [infile]);
// Extract duration string from output
var dur_tc = GetStringFromTo(probeoutput, "Duration: ", ", s");
// Convert duration string to number of seconds
return CalculateSeconds(dur_tc);
}
Step 5: Implement the onConfig() function
Now let's define the onConfig()
function. This function displays a dialog box to the user to select a container format from a list of options. It stores the selection in the items object.
function onConfig()
{
// Define options for combobox
var options = ["mp4","avi","mov"];
// Create form object
var form = {
"extension" : {
"type" : "combobox",
"default" : options[0],
"items" : options,
"label" : "Select container type:"
},
"OK" : {
"type" : "button",
"label" : "OK",
"returns" : 1
},
"cancel" : {
"type" : "button",
"label" : "cancel",
"returns" : 0
}
};
// Display dialog box and store result
var r = dialog(header.title, "Select a container format", "w", form);
// If user cancels, abort process
if (r.cancel) {
abort("cancelled");
}
// Store selection in items object
var items = getItems();
items.extension = r.extension;
setItems(items);
};
Step 6: Implement the main() function
Finally, let's implement the main()
function. This is the main function that coordinates the rest of the code. It first checks if ffmpeg and ffprobe are installed, then gets a list of files from the user, displays the configuration dialog, and processes each file in the list. It uses cmd()
to run ffmpeg commands and cmd_callback()
as a callback function. It also updates the progress bar and checks for user cancelation at various points.
var totalsec=0.; // a global var to track the total length in sec of the current file
function main()
{
// Get current epoch time
var startEpoch = getCurrentEpoch(); // secs since 1 jan 1970
// Check if ffmpeg is installed
if (fileExists(getAllowedApps("ffmpeg")) == false) {
abort("no ffmpeg installed");
}
if (fileExists(getAllowedApps("ffprobe")) == false) {
abort("no ffprobe installed");
}
// Set main message and progress bar
setMainMessage("starting");
setProgress(0);
// Get list of files from user
var files = getFiles();
if (files.length <= 0) {
abort("drop a file in the list to encode");
}
// Display configuration dialog
onConfig();
// Process each file in the list
for (i = 0; i < files.length; i++)
{
setProgress(101);
var infile = files[i].path;
// Get duration of file in seconds
totalsec = getFileSeconds(infile);
if (totalsec <= 0) {
abort("no content found in this file");
}
// Set up output path and filename
var pathinfo = getPathInfo(infile);
var items = getItems();
var outfile = pathinfo.folder + gvar.pss + pathinfo.basename + "-[magnetron.app]." + items.extension;
outfile = saveFileDialog("Select a output path", config.outfile, ".");
if (outfile.length > 0)
{
setMainMessage("busy please wait");
// Set the ffmpeg arguments
// the -y option specifies that the output file should be overwritten if it already exists.
// the -i option is used to specify the input file for the command
// plus the input and output file paths
var args = ["-y", "-i", infile, outfile];
echo(cmd("ffmpeg", args)); // perform cmd
// check output
if (getFileBytes(outfile) <= 0)
setMainMessage("can't encode this file");
if(isCanceled())
abort("cancelled");
}
}
// Set progress bar
setProgress(100);
// Calculate elapsed time in seconds
var endEpoch = getCurrentEpoch();
var elapsed = endEpoch - startEpoch;
// Display elapsed time in a message box
setMainMessage("done, elapsed time: " + elapsed + " secs");
}
That's it! You should now have a complete understanding of using FFmpeg in your own recipes. Check /syntax/ for more details on the functions and arguments.