I am working with ffmpeg via C#'s Process class.
I have a script that runs ffmpeg to generate thumbnails from video. Initially it was called manually from command line - .\ffmpeg.exe -i .\input.mp4 -ss 00:00:01.000 -vframes:v 1 output.png, it starts ffmpeg instance, outputs some warnings/errors during the execution:
[image2 # 000001e51095ec80] The specified filename 'output.png' does not contain an image sequence pattern or a pattern is invalid.
[image2 # 000001e51095ec80] Use a pattern such as %03d for an image sequence or use the -update option (with -frames:v 1 if needed) to write a single image.
frame= 1 fps=0.0 q=-0.0 Lsize=N/A time=00:00:00.00 bitrate=N/A speed= 0x
video:73kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
but anyway exits the process and correctly generates thumbnail image at output.png.
I want to execute it from C#.
Let's see the code:
var ffmpegProcess = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = _config.FfmpegExecutablePath,
Arguments = CreateArgumentsForFfmpegProcessToRun(videoTempFilePath, thumbnailTempFilePath),
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
},
EnableRaisingEvents = true
};
ffmpegProcess.Start();
await ffmpegProcess.WaitForExitAsync();
Method CreateArgumentsForFfmpegProcessToRun returns exactly the same arguments as in the script above -i .\input.mp4 -ss 00:00:01.000 -vframes:v 1 output.png.
However, when I run this code, it stucks/blocked at line ffmpegProcess.WaitForExitAsync() infinitely and no output written to output path.
If I omit WaitForExitAsync call and just go to the next line, then it doesn't stuck and writes the output as expected and finish the process with -1 exit code.
I am trying to figure out why block/stuck happens and what is the best way to resolve this situation? As far I know, WaitForExitAsync should return as process ends, no matter how process ends - 0 or another exit code, am I right?
Update #1:
Community advised to search if somewhere up the stack I am blocking my code. I wrote xunit-test and it still stucks.
[Theory]
[InlineData("assets/input.mp4", "assets/ffmpeg.exe")]
public async Task CreateThumbnailFromVideo(string videoFilePath, string ffmpegExePath)
{
var config = new VideoThumbnailServiceConfig
{
FfmpegExecutablePath = ffmpegExePath,
ThumbnailImageExtension = ".png"
};
var sut = new VideoThumbnailService(config);
using var fileStream = File.OpenRead(videoFilePath);
await sut.CreateThumbnailFromVideo(fileStream);
}
Inside sut.CreateThumbnailFromVideom I call process start method and awaits WaitForExitAsync().
I'm using the redmon to redirect de output postcript my c# .exe to process.
The print configuration is the same as if I'd do to configure the ghostscript, but instead of the ghost path I informed my .exe path.
To convert the postscript in PDF I'm using the ghostscript, as the code bellow
Stream content = Console.OpenStandardInput();
using BinaryReader standardInputReader = new BinaryReader(content);
using (FileStream standardInputFile = new FileStream(psFile, FileMode.Create, FileAccess.ReadWrite))
{
standardInputReader.BaseStream.CopyTo(standardInputFile);
}
standardInputReader.Close();
string ghostScriptPath = #"C:\gs\gs9.52\bin\gswin64.exe";
String ars = #"-Ic:\gs\gs925\lib -dNOPAUSE -dBATCH -q -dSAFER -sDEVICE=pdfwrite -sOutputFile=teste.pdf psFile.ps";
Process proc = new Process();
proc.StartInfo.FileName = ghostScriptPath;
proc.StartInfo.Arguments = ars;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
proc.Start();
proc.WaitForExit();
But the ghostscript always throws the error %%[ Error: undefined; OffendingCommand: Redmon ]%%.
If I use the ghostscript instead of my program everything works fine, but if I "intercept", the error always happens. I logged the output on the two cases, but the log is the same, a piece bellow.
The full log can be seen here
%%BeginResource: file Pscript_WinNT_ErrorHandler 5.0 0
/currentpacking where{pop/oldpack currentpacking def/setpacking where{pop false
setpacking}if}if/$brkpage 64 dict def $brkpage begin/prnt{dup type/stringtype
ne{=string cvs}if dup length 6 mul/tx exch def/ty 10 def currentpoint/toy exch
def/tox exch def 1 setgray newpath tox toy 2 sub moveto 0 ty rlineto tx 0
rlineto 0 ty neg rlineto closepath fill tox toy moveto 0 setgray show}bind def
/nl{currentpoint exch pop lmargin exch moveto 0 -10 rmoveto}def/=={/cp 0 def
typeprint nl}def/typeprint{dup type exec}readonly def/lmargin 72 def/rmargin 72
def/tprint{dup length cp add rmargin gt{nl/cp 0 def}if dup length cp add/cp
exch def prnt}readonly def/cvsprint{=string cvs tprint( )tprint}readonly def
/integertype{cvsprint}readonly def/realtype{cvsprint}readonly def/booleantype
{cvsprint}readonly def/operatortype{(--)tprint =string cvs tprint(-- )tprint}
readonly def/marktype{pop(-mark- )tprint}readonly def/dicttype{pop
(-dictionary- )tprint}readonly def/nulltype{pop(-null- )tprint}readonly def
/filetype{pop(-filestream- )tprint}readonly def/savetype{pop(-savelevel- )
tprint}readonly def/fonttype{pop(-fontid- )tprint}readonly def/nametype{dup
xcheck not{(/)tprint}if cvsprint}readonly def/stringtype{dup rcheck{(\()tprint
tprint(\))tprint}{pop(-string- )tprint}ifelse}readonly def/arraytype{dup rcheck
{dup xcheck{({)tprint{typeprint}forall(})tprint}{([)tprint{typeprint}forall(])
tprint}ifelse}{pop(-array- )tprint}ifelse}readonly def/packedarraytype{dup
rcheck{dup xcheck{({)tprint{typeprint}forall(})tprint}{([)tprint{typeprint}
forall(])tprint}ifelse}{pop(-packedarray- )tprint}ifelse}readonly def/courier
/Courier findfont 10 scalefont def end errordict/handleerror{systemdict begin
$error begin $brkpage begin newerror{/newerror false store vmstatus pop pop 0
ne{grestoreall}if errorname(VMerror)ne{showpage}if initgraphics courier setfont
lmargin 720 moveto errorname(VMerror)eq{userdict/ehsave known{clear userdict
/ehsave get restore 2 vmreclaim}if vmstatus exch pop exch pop PrtVMMsg}{
(ERROR: )prnt errorname prnt nl(OFFENDING COMMAND: )prnt/command load prnt
$error/ostack known{nl nl(STACK:)prnt nl nl $error/ostack get aload length{==}
repeat}if}ifelse systemdict/showpage get exec(%%[ Error: )print errorname
=print(; OffendingCommand: )print/command load =print( ]%%)= flush}if end end
end}dup 0 systemdict put dup 4 $brkpage put bind readonly put/currentpacking
where{pop/setpacking where{pop oldpack setpacking}if}if
%%EndResource
obs: I'm using Windows 10
OK well if you look at the file you posted it begins:
RedMon - Redirection Port Monitor
Copyright (C) 1997-2012, Ghostgum Software Pty Ltd. All Rights Reserved.
2012-06-21 Version 1.9
get_filename_as_user sent: C:\Users\Marina\Desktop\teste.pdf
....
...
REDMON WritePort: about to write 4096 bytes to port.
%!PS-Adobe-3.0
Everything up to the %!PS-Adobe is not PostScript and is not being sent by the printer driver. That's all being sent by the RedMon Port Monitor. The output then ends with:
%%EOF
REDMON WritePort: OK count=2489 written=2489
REDMON EndDocPort: starting
REDMON WriteThread: ending
%%[Page: 1]%%
%%[LastPage]%%
REDMON EndDocPort: process finished after 1 second
REDMON EndDocPort: 0 bytes written to printer
REDMON EndDocPort: ending
and there everything following the %%EOF is not PostScript and is coming from the Port Monitor. Since all of that doesn't form a valid PostScript program, sending it to Ghostscript will cause an error of some kind.
You need to work out how to strip that verbiage out and only send the PostScript language program to Ghostscript.
I'm afraid I can't help you with that, I don't know how your application is receiving data from RedMon.
In any event this isn't really a Ghostscript, or PostScript, question.
I'm running a python program from c# using process.start() on a raspberry pi zero w. I've tested it the program is running when called but it will not log to file. But if I just run the program myself python Relays.py 0 0 0 0 0 the python log works. Does anyone know what is causing this problem?
Below are the python code and C# function:
void update()
{
String acommand = status();
Console.WriteLine("LOOKSLIKEWEMADEIT " + acommand);
String location = "/home/pi/Debug/Relays.py " + Convert.ToString(acommand);
//run python
ProcessStartInfo info = new ProcessStartInfo("python", location);
Process.Start(info);
Console.WriteLine("YOUHAVEPASSEd");
}
import RPi.GPIO as G
import time
import sys
import socket
import threading
import logging
G.setwarnings(False)
G.setmode(G.BOARD)
relays = (24,26,32,36,38)
for r in relays:
G.setup(r,G.OUT)
logging.basicConfig(filename='Relaytrigger.log',level=logging.DEBUG,format='%(asctime)s %(message)s$
logging.info('\r\n')
logging.info(sys.argv)
logging.info('\r\n')
#######################heat
if sys.argv[1] == '1':
heater = True
G.output(relays[0],1)
logging.info('heaton\r\n')
else:
heater = False
G.output(relays[0],0)
logging.info('heatoff\r\n')
##########################main
if sys.argv[2] == '1':
mainpump = True
G.output(relays[1],1)
logging.info('mainon\r\n')
else:
mainpump = False
G.output(relays[1],0)
logging.info('mainoff\r\n')
#########################aux
if sys.argv[3] == '1':
auxilarypump = True
G.output(relays[2],1)
logging.info('auxon\r\n')
else:
auxilarypump = False
G.output(relays[2],0)
logging.info('auxoff\r\n')
#########################auxset
if sys.argv[4] == '1':
auxsetting = True
G.output(relays[3],1)
logging.info('mainhigh\r\n')
else:
auxsetting = False
G.output(relays[3],0)
logging.info('heatlow\r\n')
########################light
if sys.argv[5] == '1':
lighting = True
G.output(relays[4],1)
logging.info('lighton\r\n')
else:
lighting = False
G.output(relays[4],0)
logging.info('lightoff\r\n')
I seems to me like problems with your CWD (current working directory).
Depending on from where an python application is started "relative paths" may differ.
Solution #1 : "The python-side solution":
Use absolute paths! Relative paths are more likely to be unsafe.
Solution #2 : "The C#-side solution":
Use the ProcessStartInfo-Property "WorkingDirectory" to define your CWD.
Your CWD is usualy the directory containing your .py file.
Link to ProcessStartInfo-Class
HF
I'm trying to integrate MATLAB 2010b with Visual Studio 2008 Professional.
I have the following MATLAB method.
function varargout = perform( func, varargin )
%% // Set default values
workspaceDirectory = ['Results/MatlabWorkspace_' datestr(now, 'yyyy-mm-dd_HH-MM-SS')];
clear args
args = struct('workspacePath', [ workspaceDirectory '/workspace.mat'], ...
'testArray', [], ...
'k', 50, ...
'rate', 0.0001, ...
'trainingDataPath', 'P2AT_LaserImageVectorList.csv', ...
'network', 'feedforwardnet', ...
'initialWeights', [], ...
'divideFcn', 'dividerand', ...
'trainRatio', 70/100, ...
'testRatio', 15/100, ...
'valRatio', 15/100, ...
'trainInd', [], ...
'testInd', [], ...
'valInd', [], ...
'trainFcn', 'trainlm', ...
'performFcn', 'mse', ...
'biasConnect', [0; 0; 0], ...
'layerSize', [9; 4; 1], ...
'layerTransferFcn', ['tansig '; 'tansig '; 'purelin'], ...
'max_fail', 10, ...
'mu_dec', 0.04, ...
'useInitialWeights', false, ...
'saveResults', true);
% // Returns a modified properties structure
args = getopt(args,varargin);
args.layerTransferFcn = cellstr(args.layerTransferFcn);
if args.saveResults && ~strcmpi(func,'test')
if (exist(workspaceDirectory, 'dir') ~= 7)
mkdir(workspaceDirectory);
end
end
if (strcmpi(func,'test'))
try
load(args.workspacePath, '-regexp', '.*');
catch err
Warning(err.message);
varargin{1,1} = null;
return;
end
data_inputAngle = args.testArray(2501);
data_inputPCA = args.testArray(1:2500);
if size(data_inputPCA,1) == 1
data_inputPCA = data_inputPCA';
end
switch(featureExtractionMethod)
case {'gha','apex'}
% // [W, errvals] = gha(data_inputPCA, k, varargin{1,3});
data_PCs = W' * data_inputPCA;
data_inputNN = [data_PCs; data_inputAngle];
case 'nnmf'
% // [W,H,D] = nnmf(data_inputPCA',k);
data_PCs = H * data_inputPCA;
data_inputNN = [data_PCs; data_inputAngle];
case 'pcaProcess'
otherwise
warning = 'ERROR: no feature extraction method has been defined.';
Warning('ERROR: no feature extraction method has been defined.');
varargout{1,1} = null;
return;
end
% // Just to test to see if it recognizes 'feedforwardnet'.
testnet = feedforwardnet; % // <------------------------------- LINE 81
% // Saving all the workspace variables to see if they are all correctly processed.
save('all');
varargout{1,1} = net(data_inputNN); %// <------------------------- LINE 86
end
end
And this is how I create my DLL file to import in Visual Studio:
%%// Determine path names
workdir = pwd();
outdir = fullfile(workdir, 'Output');
dnetdir = fullfile(workdir, 'dotnet');
%%// Determine file names
mfile = fullfile(workdir, 'perform.m');
dnetdll = fullfile(dnetdir, 'dotnet.dll');
%%// Build .NET Assembly
eval(['mcc -N -d ''' dnetdir ''' -W ''dotnet:dotnet,' ...
'dotnetclass,0.0,private'' -T link:lib ''' mfile '''']);
So everything works perfectly fine when I use MATLAB Engine's COM interface to run the routine inside MATLAB from C#:
/*
* This function calls the routine inside
* MATLAB using the MATLAB Engine's COM interface
*/
static private float MatlabTestDebug(float[] testData, Double targetAngle)
{
Array X = new double[testData.Length + 1];
testData.CopyTo(X, 0);
X.SetValue((double)targetAngle, testData.Length);
Array zerosX = new double[X.GetLength(0)];
MLApp.MLAppClass matlab = new MLApp.MLAppClass();
matlab.PutFullMatrix("testArray", "base", X, zerosX);
matlab.PutWorkspaceData("workspacePath", "base", "workspace.mat");
// Using Engine Interface, execute the ML command
// contained in quotes.
matlab.Execute("cd 'c:\\Users\\H\\Documents\\Project\\Source Code\\MatlabFiles';");
matlab.Execute("open perform.m");
matlab.Execute("dbstop in perform.m");
matlab.Execute("result = perform('test', 'workspacePath', 'workspace.mat', 'testArray', testArray);");
matlab.Execute("com.mathworks.mlservices.MLEditorServices.closeAll");
return (double)matlab.GetVariable("result", "base");
}
But when I use the .NET assembly, it's not recognizing 'feedforwardnet'. I used to get an error on line 86 (net(data_inputNN)). So I added a line to test to see if it at least recognizes 'feedforwardnet', but it didn't.
Note: I'm loading some variables from a file including "net" which is a neural network (load(args.workspacePath, '-regexp', '.*');)
Also in the MATLAB method if I load a saved "network" from file and then save it to see how it processes the network, it will save it as a "struct" instead of a "network".
I'm assuming it's loading it as a struct to begin with.
I also had this problem inside MATLAB 2009b itself. That's the reason I'm using MATLAB 2010b now, because apparently MATLAB 2009b didn't have this particular neural networks toolbox.
Following is the C# code to use the .NET assembly.
/*
* Calls the method from inside a .NET assembly created with MATLAB
* using Builder for .NET.
*/
private float MatlabTest(float[] testData, Double targetAngle)
{
if (testData != null)
{
dotnetclass AClass = new dotnetclass();
Array X = new double[testData.Length + 1];
testData.CopyTo(X, 0);
X.SetValue((double)targetAngle, testData.Length);
MWNumericArray XNumericArray = new MWNumericArray(X);
MWArray[] RetVal = AClass.perform(1, "test",
"workspacePath", "workspace.mat",
"testArray", XNumericArray);
Array result = ((MWNumericArray)RetVal[0]).ToVector(MWArrayComponent.Real);
return (float)result.GetValue(0);
}
else
{
return 0f;
}
}
I'm getting this error in Visual Studio:
... MWMCR::EvaluateFunction error ...
Undefined function or variable 'feedforwardnet'.
Error in => perform.m at line 81.
NOTE: version of my compiler and softwares:
Compiler: Microsoft Visual C++ 2008 SP1 in C:\Program Files (x86)\Microsoft Visual Studio 9.0
MATLAB: R2010b (64-bit)
Visual Studio: MVS 2008 professional (.NET Framework 3.5 SP1)
Microsoft Windows SDK 6.1
Recent Updates:
I've added the path of the neural network toolbox in mcc.
eval(['mcc -N -p ''C:\Program Files\MATLAB\R2010b\toolbox\nnet'' -d ''' dnetdir ''' -W ''dotnet:dotnet,' ...
'dotnetclass,0.0,private'' -T link:lib -v ''' mfile '''']);
Now I get these messages in mccExcludeFiles.log:
C:\Program Files\MATLAB\R2010b\toolbox\nnet\nnet\#network\network.m
called by C:\Program Files\MATLAB\R2010b\toolbox\nnet\nnet\nnnetwork\cascadeforwardnet.m
(because of toolbox compilability rules)
C:\Program Files\MATLAB\R2010b\toolbox\nnet\nnet\#network\network.m
called by C:\Program Files\MATLAB\R2010b\toolbox\nnet\nnet\nnnetwork\feedforwardnet.m
(because of toolbox compilability rules)
The only answer I could come up with (which not a solution to the problem) was from Creating standalone application that contains neural network toolbox functions, stating that:
You will not be able to compile any
function which trains the network
(like ADAPT). Though the link does not
explicitly list these funcions (like
ADAPT), they fall under the 'All other
command line functionality'.
However, you can deploy a M function
code which uses a pre-trained
network. I believe the SIM function
will deploy fine.
The workflow I see is:
In MATLAB, train you network using test input/output
Save the network (MAT file?)
Create a deployable function which then uses the pretrained network for
new data. The network itself would not
change/adapt/train in this function
Compile and deploy the above function
I want to extract first frame of uploaded video and save it as image file.
Possible video formats are mpeg, avi and wmv.
One more thing to consider is that we are creating an ASP.NET website.
You could use FFMPEG as a separate process (simplest way) and let it decode first IDR for you. Here you have a class FFMPEG that has GetThumbnail() method, to it you pass address of video file, address of the JPEG image to be made, and resolution that you want the image to be:
using System.Diagnostics;
using System.Threading;
public class FFMPEG
{
Process ffmpeg;
public void exec(string input, string output, string parametri)
{
ffmpeg = new Process();
ffmpeg.StartInfo.Arguments = " -i " + input+ (parametri != null? " "+parametri:"")+" "+output;
ffmpeg.StartInfo.FileName = "utils/ffmpeg.exe";
ffmpeg.StartInfo.UseShellExecute = false;
ffmpeg.StartInfo.RedirectStandardOutput = true;
ffmpeg.StartInfo.RedirectStandardError = true;
ffmpeg.StartInfo.CreateNoWindow = true;
ffmpeg.Start();
ffmpeg.WaitForExit();
ffmpeg.Close();
}
public void GetThumbnail(string video, string jpg, string velicina)
{
if (velicina == null) velicina = "640x480";
exec(video, jpg, "-s "+velicina);
}
}
Use like this:
FFMPEG f = new FFMPEG();
f.GetThumbnail("videos/myvid.wmv", "images/thumb.jpg", "1200x223");
For this to work, you must have ffmpeg.exe in folder /utils, or change the code to locate ffmpeg.exe.
There are other ways to use FFMPEG in .NET, like .NET wrappers, you could google for them. They basically do the same thing here, only better. So if FFMPEG gets your job done, I'd recomend to use .NET wrapper.
Try to make argument string format like:
ffmpeg.StartInfo.Arguments =" -i c:\MyPath\MyVideo -vframes 1 c:\MyOutputPath\MyImage%d.jpg"
Instead of
ffmpeg.StartInfo.Arguments = " -i " + input+ (parametri != null? " "+parametri:"")+" "+output;
in the answer code provided above.
I don't know what was the reason, but second mentioned argument line is not working on my machine whereas when I changed argument like the first command it works fine.
Probably the best tool for working with videos programatically is FFMpeg. It has support for many formats, even wmv. I suspect there's even a .net wrapper for it.