Hi guys,
Was trying to create a sort of batch process to convert some AVI files to MP4 through FFMPEG.
I am able to create a .bat which I want to see run directly after creation. I have tried launch() and exec() too but to no avail. Clicking directly onto the bat file runs the ffmpeg code and the conversion happens. Just not an auto way of doing it. Would be really grateful for any suggestions. Thanks!
//Processing ver 4.0b2
String fn="myfile.bat";
void setup () {
size(500, 500);
}
void draw() {
}
void mousePressed() {
println("running...");
createBat();
delay(3000); //not sure if this wait is really needed
runBat();
println("finished");
}
//creates the .bat file with the FFMPEG code
//running this directly by double clicking it converts the file from AVI to MP4
//would like the code to do this automatically instead of manually
void createBat() {
PrintWriter output=null;
output = createWriter(fn);
output.println("ffmpeg -i shot01.avi -c:v copy -c:a copy -y shot01.mp4");
output.flush();
output.close();
output=null;
//REFERENCE: //http:// stackoverflow.com/questions/33974730/processing-3-0-launch-function-doesnt-launch-my-exe
}
//runs the created .bat file but doesn't really do anything when run
void runBat() {
launch(sketchPath("") + fn);
}
I had issues getting FFMPEG to run from processing when I was trying to capture film frames. Hamoid suggested that I should use Process Builder in this post. In that he shared a really useful link to his code for the video export library.
Here’s a relevant excerpt of the code (probably with some rubbish you don’t need) that I ended up creating, hope this helps put you in the right direction:
void captureScaledFrames() {
String[] commands = {
ffmpegPath,
"-i",
path,
"-vf",
"\"scale=320:-1,fps=1/3\"",
"-vsync",
"0",
"-q:v",
"2",
sketchPath("Films/" + fileName + "/Frames/Scaled/out%07d.jpg")
};
captureProcess(commands);
}
void captureProcess(String[] cmds) {
ProcessBuilder processBuilder = new ProcessBuilder(cmds);
Process process;
File ffmpegOutputLog;
processBuilder.redirectErrorStream(true);
ffmpegOutputLog = new File(dataPath(fileName+"_ffmpegLog.txt"));
processBuilder.redirectOutput(ffmpegOutputLog);
processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE);
try {
process = processBuilder.start();
}
catch (Exception e) {
e.printStackTrace();
println(ffmpegOutputLog);
}
tmr.start();
capturing = true;
//getCaptureProgress();
}
Thank you so much for pointing me in the right direction.
Here is the completed code that works on a single file.
Have a few queries in the comments though.
Would be great if you could take a look to help understand them a lot better.
Will re-comment and post the final code for anyone else in need. Thanks a ton!
//Simple conversion of .avi to .mp4 through FFMPEG
//hardcoded
//replace with your own ffmpeg path
String ffmpegPath = "C:\\FFMPEG\\bin\\ffmpeg.exe";
//allow user input and output selection
//output selection requires file extension as .mp4 for this version
String input_filename = "";
String output_filename = "";
Process process;
void setup () {
size(500, 500);
//not sure why output has to come before input. The reverse order has errors (any thoughts?)
selectOutput("Select a file to write to:", "outputFileSelected");
selectInput("Select a file to process:", "inputFileSelected");
}
// input selection method
void inputFileSelected(File selection) {
if (selection == null) {
println("Window was closed or the user hit cancel.");
} else {
input_filename = selection.getAbsolutePath();
println("User selected input path: " + selection.getAbsolutePath());
}
}
// output selection method
void outputFileSelected(File selection) {
if (selection == null) {
println("Window was closed or the user hit cancel.");
} else {
output_filename = selection.getAbsolutePath();
println("User selected output path: " + selection.getAbsolutePath());
}
}
void draw() {
}
//mouse click in processing output window to trigger the conversion
void mousePressed() {
converter();
}
void converter() {
String[] commands = {
ffmpegPath,
"-i",
input_filename,
"-c:v",
"copy",
"-c:a",
"copy",
"-y",
output_filename
};
ProcessBuilder processBuilder = new ProcessBuilder(commands);
File ffmpegOutputLog;
processBuilder.redirectErrorStream(true);
ffmpegOutputLog = new File(dataPath(output_filename+"_ffmpegLog.txt"));
processBuilder.redirectOutput(ffmpegOutputLog);
// would like to understand what this section really does.
// any errors or incorrect file format eg: instead of mp4 we do .mp45?
try {
process = processBuilder.start();
}
catch (Exception e) {
e.printStackTrace();
println(ffmpegOutputLog);
}
}
Hi Deanomite,
-
I have no idea why the file selection dialogs need to be backwards, could you set a boolean that is true once the user has selected the file and then from there call the output selection?
-
Why not also have the conversion happen automatically once the output has been selected? no more need for a mouse click to trigger it.
-
The redirection of the error log is allowing me (you) to print the stderror stream from ffmpeg to a text file. I did this in my program so that I could get hold of the progress of the capture i was doing and then read it back in to processing as a progress bar.
-
When you convert a file you will see that in the selected folder there is also the ffmpeglog.txt file, that’s what the redirecterrorstream is doing.
Have a look at the thread I linked to last time and you can see hamoid and jeremy making just this suggestion.
Glad you got it to work, and I hope this has helped to clarify things. I am by no means an expert in this, Hamoid and JeremyDouglass were the reason I go this working in the first place.
edit:
I’d also point you toward the fileFilter class from java.
goToLoop has an example here..
This will make it easier when using the select file dialog as you can filter for only .avi files.
Hi tribblesontoast,
Thanks for all the inputs. Means a lot!
I’m still working on this but stuck in a few places. Solved the backwards output before input dialog issue by using selectOutput() as a nested method.
// input selection method
void inputFileSelected(File selection) {
if (selection == null) {
println("Window was closed or the user hit cancel.");
} else {
input_filename = selection.getAbsolutePath();
println("User selected input path: " + selection.getAbsolutePath());
//selectOutput method now runs only if input selection is not equal to null
//takes care of error where output dialog was running before input (reason not yet known)
selectOutput("Select a file to write to:", "outputFileSelected");
}
}
Tried to get converter()
out from mousePressed
but even if I put it in setup() it doesn’t work. Tried putting it in draw() with a noLoop() but that didn’t work. So am stuck there Running directly… haven’t yet cracked that
Tried out the fileFilter class but didn’t get a visual dropdown as you would see in a program like Photoshop, etc. It all ran under the hood. Have made a new thread for that idea to solve separately under a new title.
Thanks once again. If you do have any inputs based on your experience would love to hear. Tc. Cheers!
Hi deano,
A couple of changes:
-
I moved the convertor()
call to the else in the outputFileSelected method. This works without issue for me.
-
I’ve also added an exit()
in both the input and outputFileSelected. This is to stop from needing to close the program manually.
-
I’ve changed the ffmpeg commands you were using as they didn’t work at all for me, went for something simpler. I’ve hardcoded the extension.
As for the fileFilter, my intention was that you could use it to filter for only avi files - it won’t provide an interface - it will just filter what is shown in the file selection dialog.
//Simple conversion of .avi to .mp4 through FFMPEG
//hardcoded
//replace with your own ffmpeg path
String ffmpegPath = "G:\\ffmpeg\\bin\\ffmpeg.exe";
//allow user input and output selection
//output selection requires file extension as .mp4 for this version
String input_filename = "";
String output_filename = "";
Process process;
void setup () {
size(500, 500);
selectInput("Select a file to process:", "inputFileSelected");
}
// input selection method
void inputFileSelected(File selection) {
if (selection == null) {
println("Window was closed or the user hit cancel.");
exit();
} else {
input_filename = selection.getAbsolutePath();
println("User selected input path: " + selection.getAbsolutePath());
//selectOutput method now runs only if input selection is not equal to null
//takes care of error where output dialog was running before input (reason not yet known)
selectOutput("Select a file to write to:", "outputFileSelected");
}
}
// output selection method
void outputFileSelected(File selection) {
if (selection == null) {
println("Window was closed or the user hit cancel.");
exit();
} else {
output_filename = selection.getAbsolutePath();
println("User selected output path: " + selection.getAbsolutePath());
converter();
}
}
void draw() {
}
void converter() {
String[] commands = {
ffmpegPath,
"-i",
input_filename,
output_filename + ".mp4"
};
ProcessBuilder processBuilder = new ProcessBuilder(commands);
File ffmpegOutputLog;
processBuilder.redirectErrorStream(true);
ffmpegOutputLog = new File(dataPath(output_filename+"_ffmpegLog.txt"));
processBuilder.redirectOutput(ffmpegOutputLog);
try {
process = processBuilder.start();
}
catch (Exception e) {
e.printStackTrace();
println(ffmpegOutputLog);
}
}
1 Like
Thanks a ton Tribblesontoast! This solves the challenge. Didn’t strike me to push the converter() call to the ‘else.’ Aarrgghh!!
Will close this for now. Meanwhile a little background to the whole conversion bit.
A few days ago, a friend called to inform me that her CCTV/security cam footage was the only footage in an area which captured some criminals beating another neighbor up pretty badly. The police were called in but unfortunately, the DAV files from the cameras’ recorder only allowed them to save to a sluggish ‘AVI’ format.
FFMPEG was a quick solution. Although I did some of it manually, it was cumbersome and processing helped make life a bit easier for us. She was able to share the footage with law enforcement and the criminals were apprehended. The neighborhood is safer now.
Will be converting this to an .exe and will share with her so that she can convert future files easily should the need arise.
Although my Processing work is mostly creative/art , this was the first time I was able to use Processing to help the police! (not directly though but still…)
Thank you for your help on this. Wish you the very best!