How to trim silence from audio with ffmpeg in php - php

My hosting does not have ffmpeg's silenceremove filter, so I'm trying to remove the beginning and end silence of an audio file with silencedetect then cut. I'm trying to do this programmatically with php. At this point, I can't even detect the silence. I've tried:
define('UPLOAD_DIR', $_SERVER['DOCUMENT_ROOT'].'/audiofiles/');
$filein = UPLOAD_DIR . "music.ogg";
$fileout = UPLOAD_DIR . "music_no_silence.ogg";
$fileouttxt = UPLOAD_DIR . "music_log.txt";
$ffmpeg = "/usr/local/ffmpeg/bin/ffmpeg";
I first tried to put the ffmpeg command into a variable and then tried to access the output but was unable:
$output = shell_exec("$ffmpeg -i $filein -af silencedetect=n=-50dB:d=1");
echo $output;
I then tried to create a.txt log of the conversion but the .txt file doesn't show anything about the silencedetect. Furthermore, how would I be able to extract the info that I need programmatically from the .txt log in order to use it in php?
shell_exec("$ffmpeg -i $filein -af silencedetect=n=-50dB:d=1 $fileout 2> $fileouttxt");
I'm basically trying to adapt the answer at https://stackoverflow.com/a/25698675 into php
Edit: Here's kind of a hack way to do it:
define('UPLOAD_DIR', $_SERVER['DOCUMENT_ROOT'].'/audiofiles/');
$filein = UPLOAD_DIR . "music.ogg";
$fileout1 = UPLOAD_DIR . "music_out1.ogg";
$fileout2 = UPLOAD_DIR . "music_out2.ogg";
$ffmpeg = "/usr/local/ffmpeg/bin/ffmpeg";
$command = "$ffmpeg -i $filein -filter_complex silencedetect=n=-10dB:d=0.5 -y $fileout1 2>&1";
$output = array();
exec($command,$output);
$searchword1 = 'silence_end';
$endmatches = array_filter($output, function($var) use ($searchword1) {return preg_match("/\b$searchword1\b/i", $var);});
$endmatches = array_values($endmatches);
$parts1 = explode(" ",$endmatches[0]);
$a = $parts1[4];
$a = $a-.5;
$searchword2 = 'silence_start';
$startmatches = array_filter($output, function($var) use ($searchword2) {return preg_match("/\b$searchword2\b/i", $var);});
$startmatches = array_values($startmatches);
$parts2 = explode(" ",end($startmatches));
$b = $parts2[4];
$b = $b+.5;
$c = $b-$a;
exec("$ffmpeg -i $fileout1 -ss $a -t $c -y $fileout2 && rm $fileout1");

PHP's shell_exec returns the output written to stdout and ffmpeg writes the messages to stderr. If you want to use shell_exec you must redirect the stderr output to stdout using 2>&1.
You also need to specify at least one output file for ffmpeg, in this case /dev/null.
set_time_limit(0);
$input_file = 'music.ogg';
$cmd = "ffmpeg -i {$input_file} -af silencedetect=n=-50dB:d=1 -f null /dev/null 2>&1";
$output = shell_exec($cmd);
if (preg_match_all("/^\[silencedetect\s[^]]+\]\s([^\n]+)/m", $output, $matches)) {
var_export($matches[1]);
}
An alternative is to use proc_open and stream_get_contents.

Related

How to get terminal text from gnu screen session?

How do i get the terminal text from a GNU Screen session in PHP? I am puzzled because GitHub CoPilot suggest i can do
function getTerminalTextFromGnuScreen(string $screenName): string
{
$screenText = shell_exec("screen -S " . escapeshellarg($screenName) . " -X hardcopy -h");
return $screenText;
}
but that does not actually get me anything, just emptystring.
I came up with a much more complex, convoluted, and dodgy implementation, which works, but i hope, as CoPilot suggest, that there is a much easier way to do this, hence asking here: how should it be done? my dodgy implementation:
function getTerminalTextFromGnuScreenDodgy(string $screenName, bool $skipTerminalSizeControlCharacters = true): string
{
// the double escapeshellarg is intentional.
$cmd = 'script --command ' . escapeshellarg('screen -x -r ' . escapeshellarg($screenName)) . ' /dev/null ';
$proc = proc_open($cmd, [['pipe', 'rb'], ['pipe', 'wb'], ['pipe', 'wb']], $pipes);
stream_set_blocking($pipes[1], true);
fgets($pipes[1]); // skip "Script started, output log file is '/dev/null'." line
$ret = '';
if ($skipTerminalSizeControlCharacters) {
$ret = fgets($pipes[1]); // contains terminal size characters
$lastControlCharacter = strrpos($ret, "\x1b"); // THIS IS NOT 100% RELIABLE, and i don't know how to make it reliable either :(
$ret = substr($ret, $lastControlCharacter + strlen("\x1b[2J")); // skip terminal size characters
}
stream_set_blocking($pipes[1], false);
for (;;) {
$r = [$pipes[1]];
$w = $e = null;
$sel = stream_select($r, $w, $e, 0, 10000);
$tmp = stream_get_contents($pipes[1]);
if ($tmp === false || $tmp === '') {
break;
}
$ret .= $tmp;
}
fclose($pipes[1]);
// send ctrl+AD... why is \x01 equivalent to ctrl+AD? i have no idea! but it seems to work
fwrite($pipes[0], "\x01");
fclose($pipes[0]);
fclose($pipes[2]); // contains a "script: write error" message.. no idea why.
proc_close($proc);
return $ret;
}
so much wasted effort.. the answer is indeed -X hardcopy, but the the code refuse to writes to both stdout and /dev/stdout, and if you tell it to write to the filename - which traditionally means stdout, it will write to ./-, and if no file is given it default to the filename "hardcopy.n" -
Why? I have no idea, but regardless, use tmpfile() to get a output file for -X hardcopy:
function getTerminalTextFromGnuScreen(string $screenName)
{
$tmph = tmpfile();
$tmpf = stream_get_meta_data($tmph)['uri'];
shell_exec("screen -S " . escapeshellarg($screenName) . " -X hardcopy " . escapeshellarg($tmpf));
$screenText = file_get_contents($tmpf);
fclose($tmph);
return $screenText;
}
don't know why CoPilot added the -h argument, but the documentation over at https://www.gnu.org/software/screen/manual/screen.html says
‘-h num’
Set the history scrollback buffer to be num lines high. Equivalent to the defscrollback command (see Copy).

Why exec() is not working in foreach loop?

See, for testing purpose if I am trying to run PHP exec() statically with only one video file, then its compressing video perfectly(please see first line line).
Later when I am compressing dynamically in the loop, then exec function is not working. Please tell me why it's not working in the loop?
// echo shell_exec("/usr/local/bin/ffmpeg -i /home4/machine/public_html/riyaz/check_video/video_1494453415.mp4 -strict -2 -crf 20 /home4/machine/public_html/riyaz/zcompress/video_1494453415.mp4");
#include_once("start.php");
$directory_path = FILEPATH.'/';
$video_mp4 = glob("/home4/machine/public_html/riyaz/check_video/*.mp4");
/*video size will show in mb*/
foreach ($video_mp4 as $video_mp4_list){
$video_size = filesize($video_mp4_list);
$video_size_in_mb = round(($video_size/1048576), 2);
$get_file_name = explode('/',$video_mp4_list);
$get_file_name_for_destination = $get_file_name[6];
$getSourceFileNamePath = '/home4/machine/public_html/riyaz/check_video/'.$get_file_name_for_destination;
$getDestFileNamePath = '/home4/machine/public_html/riyaz/zcompress/'.$get_file_name_for_destination;
if ($video_size_in_mb >= 1000 ){
echo exec("/usr/local/bin/ffmpeg -i ". $getSourceFileNamePath ." -strict -2 -crf 20 ".$getDestFileNamePath);
}
}
shell_exec() returns the full output: PHP shell_exec() however
exec() returns only the last line from the outptut that might be empty.. So you have to provide second $output and third parameter $return_var to get more useful data: PHP exec()
if ($video_size_in_mb >= 1000 ){
$command = "/usr/local/bin/ffmpeg -y -i ". $getSourceFileNamePath ." -strict -2 -crf 20 ".$getDestFileNamePath . ' 2>&1';
echo PHP_EOL ."Executing command: ". $command;
if(file_exists($getDestFileNamePath)) echo "File already exists!";
$output = null;
$return_var = null;
exec($command, $output, $return_var);
print_r($command);
print_r($return_var);
print_r($output);
echo PHP_EOL;
} else {
echo "video too small to proceed';
}
php shell_exec() vs exec()
EDIT: The problem is that the destination file exists and ffmpeg exits without any action. One solution is to use attribute -y to overwrite the output file 5.4 Main options -y (global) Overwrite output files without asking.

Powershell output to PHP variable using shell_exec

I have a powershell script which outputs a video file duration. Running this script gives me the expected result.
$Folder = 'C:\my\path\to\folder'
$File = 'sample1_1280_720.mp4'
$LengthColumn = 27
$objShell = New-Object -ComObject Shell.Application
$objFolder = $objShell.Namespace($Folder)
$objFile = $objFolder.ParseName($File)
$Length = $objFolder.GetDetailsOf($objFile, $LengthColumn)
Write-Output $Length
In a php file, I'm trying to save this output to a variable.
<?php
$var = shell_exec("powershell -File C:\my\path\to\psFile.ps1 2>&1");
echo "<pre>$var</pre>";
?>
The string output I get from shell_exec is the text you see when you start powershell from cmd. Windows PowerShell
Copyright (C) 2016 Microsoft Corporation. All rights reserved. Any suggestions on how to extract the video duration?
Using your PS code
$Folder = 'C:\my\path\to\folder'
$File = 'sample1_1280_720.mp4'
$LengthColumn = 27
$objShell = New-Object -ComObject Shell.Application
$objFolder = $objShell.Namespace($Folder)
$objFile = $objFolder.ParseName($File)
$Length = $objFolder.GetDetailsOf($objFile, $LengthColumn)
$Length
I'm able to get the file length using PS -File and -Command. I added a few other flags you may want or need. You shouldn't need to use redirection 2>&1 to get your variable from PS to PHP. It is most likely the reason you are getting the logo.
function PowerShellCommand($Command)
{
$unsanitized = sprintf('powershell.exe -NonInteractive -NoProfile -ExecutionPolicy Bypass -Command "%s"', $Command);
return shell_exec($unsanitized);
}
function PowerShellFile($File)
{
$unsanitized = sprintf('powershell.exe -NonInteractive -NoProfile -ExecutionPolicy Bypass -File "%s"', $File);
return shell_exec($unsanitized);
}
// Can use relative paths
echo PowerShellCommand("./psFile.ps1");
// Be sure to escape Windows paths if needed
echo PowerShellFile("C:\\my\\path\\to\\folder\\psFile.ps1");
Returning $Length in all three ways work for me
$Length
return $Length
Write-Output $length

PHP: Redirect output of a bash command into default logger

I have a class executing commands in my console. This class is defined with a LoggerInterface defined from #logger.handler.
I want to execute commands in my bash but:
I dont want to display result in default stream
I want to put result in the default log file (defined by $logger), e.g. app/logs/application.log
The main method of this class looks like:
$command = $command . " > 2>&1";
$returnVal = null;
$output = [];
exec($command, $output, $returnVal);
$this->logger->debug($output);
I added > 2>&1 to display nothing in the current stream, and because I don't know how to get app/log/application.log from the $output...
The issue is that I have nothing in my app/config/application.log!
Do you know what I've missed ?
You are not capturing anything in the output because of > 2>&1
//$command = $command . " > 2>&1";
$returnVal = null;
$output = [];
exec($command, $output, $returnVal);
for ($i=0;$i<count($output);$i++)
$this->logger->debug($output[$i]);
if the debugger creates several timestamps for each one and it is not the desired output then use this:
$returnVal = null;
$output = [];
exec($command, $output, $returnVal);
$result=""
for ($i=0;$i<count($output);$i++)
$result.=$output[i]."\n";
$this->logger->debug($result);

using exec(); in PHP with variables

I'm trying to execute a command on a local Debian server. The code is as following;
if (isset($INPUTdifference)) {
$counter = count($INPUTdifference);
for ($i = 0; $i < $counter; $i++) {
$pkts = $INPUTdifference[$i]["pkts"];
$bytes = $INPUTdifference[$i]["bytes"];
$target = $INPUTdifference[$i]["target"]; // = -j
$prot = $INPUTdifference[$i]["prot"]; // = -p
$opt = $INPUTdifference[$i]["opt"];
$in = $INPUTdifference[$i]["in"]; // = -i
$out = $INPUTdifference[$i]["out"]; // = -o
$source = $INPUTdifference[$i]["source"]; // = -s
$destination = $INPUTdifference[$i]["destination"]; // -d
//Filter results
$badOpt;
if (strcmp($opt, $badOpt)) {
$opt = "all";
return $opt;
}
// Execute command with parameters acquired from array
exec("sudo /sbin/iptables -A INPUT -j $target -p $prot -i $in -o $out -s $source -d $destination ");
echo 'complete yo';
}
}
As you can see, I'm trying to use values from the Array into my exec call. The values are all tested and get parsed correctly.
I try to execute the command, and it doesn't give me any feedback/results on the server. At first it was cussing about how I didn't get a parameter right, but I fixed that. So it -is- getting executed on the server. That, I guess, narrows it down to the syntax itself... am I missing something here?
You might want to put direct /path/to/sudo instead of just sudo. exec don't appreciate sometimes to find out the path by itself.

Categories