PDA

View Full Version : x264 Progress Bar


Metroidn1f
21st August 2010, 01:29
hey, I was just wondering if anyone could help me make a progress bar for x264 with vb10? I am sort of new to programing, but I still know some things. I am making a DVD and Blu Ray Ripper that encodes the videos to be compatible to be streamed to the 360. I have that all done, but I just want to add a progress bar to everything, The Demuxing, Audio Encoding, Video Encoding, and Muxing. Right now I just have a marquee progress bar that will keep active till a file is created after one of the procedures are done.

So does anyone know how to help me make a progress bar for these things? First on the list the x264 procedure.

LoRd_MuldeR
21st August 2010, 01:43
What exactly is your problem/question ???

linyx
21st August 2010, 01:57
Add a ProgressBar to your form, then you will have to capture the stdout of the program(s), parse it and update the progressbar.
For x264, it should be pretty easy (i.e., just round it's percentage to an integer and set ProgressBar1.Value = x264Percentage)

Metroidn1f
21st August 2010, 02:35
Add a ProgressBar to your form, then you will have to capture the stdout of the program(s), parse it and update the progressbar.
For x264, it should be pretty easy (i.e., just round it's percentage to an integer and set ProgressBar1.Value = x264Percentage)

But exactly how do I do that? I have been searching on how to do this for along time. I posted a forum on MSDN, but no one could help me, they kept on telling me to do stuff like do it per byte or something like that, but I did not know how to do that. How do I get the persentage of x264? I tried outputting the encode results to a text file with this command in the command prompt, > "Output.txt" but that does not work. I thenfound something else that was really weird, it was like, tee | 2>1 "Output.txt" That did to the trick a little, but even if I changed the --progress to something else, it would output and make that whole file after the encode was done.

So could you tell me how I could do this?

Thanks.
Metroidn1f

Sharktooth
21st August 2010, 03:50
http://msdn.microsoft.com/en-us/library/3x292kth.aspx
every program outputs console info to stdout and stderr (IIRC x264 outputs the progress percentage to stderr). you should parse them and get the info (percentage text) you need from that output, convert that into an int and set your progressbar.value property to that value.
obviously you need a continous or timed capture of the stdout/stderr.

linyx
21st August 2010, 04:10
I honestly don't know how to capture stdout in realtime, my best suggestion would be to look into LoRd_MuldeR's Logger program (http://mulder.dummwiedeutsch.de/home/?page=projects#logger).

Sharktooth
21st August 2010, 04:14
look at ProcessStartInfo class and ProcessStartInfo.RedirectStandardOutput and ProcessStartInfo.RedirectStandardError properties
start here: http://msdn.microsoft.com/en-us/library/bfbyhds5(v=VS.100).aspx then here: http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandarderror.aspx and here: http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandardoutput.aspx

Metroidn1f
21st August 2010, 06:31
Sorry for my lack of knoledge, but I for the first link, I don't see what a progress bar has to do with Internet Explorer.

If possible, could someone give me an example of how to do it with x264, I am making batch files with VB then starting them, will that interfeer with anything?

I learn better from examples no looking at some code that has nothing to do with or nothing to do with the same subject that I want to deal with. I know that might not make sence, but I am not good at expaining my self. To tell you the truth, this is my first application that I have attempted to make. If you need to know anything about my code, just ask me and I can provide it for you, or I can even give you the source to see how I have it programed. If you see it you will probably laugh on how I have it programed and set up.

Well here is the link to my source. It is just a little under
12MB, it is a rar, compressed at stored/Fastest. The only thing that will work is the media info. I did not inckude the tools, the file would have been over 100MB. If you want me to include the tools, just ask and I will.

http://download898.mediafire.com/5nt78zp6v0fg/0jqvpg84qddb0a9/Metroidn1f+Encoder+-+Source.rar

LoRd_MuldeR
21st August 2010, 13:04
Well, it's not written in VisualBasic but in Pascal/Delphi, however here is a class that I use to redirect the stdout/stderr in several of my projects:
http://code.google.com/p/mulder/source/browse/trunk/LameXP/src/Unit_RunProcess.pas

This should be relatively easy to adapt for C/C++ or other languages (I often did it the other way around), as long as you are on the Windows platform (the code heavily uses Win32 API calls).

Another project of mine related to stdout/stderr redirection that might be interesting in this context:
http://code.google.com/p/mulder/source/browse/trunk/Utils/stdout_logger/logger.dpr

(Please remember to release your software under a GPL license, in case you decide to re-use any of the code from the above mentioned projects ^^)

See also:
http://support.microsoft.com/kb/190351

stax76
21st August 2010, 13:24
Why not use StaxRip, RipBot264, HDConvertToX, Handbrake etc.?

Answer to your question:

http://forum.doom9.org/showthread.php?t=153580

Sharktooth
21st August 2010, 14:51
Sorry for my lack of knoledge, but I for the first link, I don't see what a progress bar has to do with Internet Explorer.

If possible, could someone give me an example of how to do it with x264, I am making batch files with VB then starting them, will that interfeer with anything?

I learn better from examples no looking at some code that has nothing to do with or nothing to do with the same subject that I want to deal with. I know that might not make sence, but I am not good at expaining my self. To tell you the truth, this is my first application that I have attempted to make. If you need to know anything about my code, just ask me and I can provide it for you, or I can even give you the source to see how I have it programed. If you see it you will probably laugh on how I have it programed and set up.

Well here is the link to my source. It is just a little under
12MB, it is a rar, compressed at stored/Fastest. The only thing that will work is the media info. I did not inckude the tools, the file would have been over 100MB. If you want me to include the tools, just ask and I will.

http://download898.mediafire.com/5nt78zp6v0fg/0jqvpg84qddb0a9/Metroidn1f+Encoder+-+Source.rar

The internet explorer code is an example on how to start a process. You need to start a process before capturing its standard streams...

JonE
21st August 2010, 15:48
@Metroidn1f

Heres a c# example - translation to vb should be straight forward.
First launch your process - x264 in your case


Process process = new Process();
process.StartInfo.FileName = <path to your x264.exe>;
process.StartInfo.Arguments = <command line args you want to pass>;
process.EnableRaisingEvents = false;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = false;
process.StartInfo.RedirectStandardOutput = false;
process.StartInfo.RedirectStandardError = true;
bool ok = process.Start();


Note you have to set process' RedirectStandardOutput/Error as appropriate (if only I had a quid for each time I forgot that one :o).

Now attach to the stream you want to read


using (StreamReader reader = process.StandardError)
{
string line = reader.ReadLine();
if( line == null )
break;

// Interpret the line as you see fit
}


Job done (give or take error handling!).

TTFN,
Jon

Metroidn1f
21st August 2010, 23:13
Why not use StaxRip, RipBot264, HDConvertToX, Handbrake etc.?

Answer to your question:

http://forum.doom9.org/showthread.php?t=153580

I don't know. I got inspired from the program Megui, so I just wanted to make my own. I like having my own programs that I can consider mine. It makes me feel good, lol.

@JonE
What are the arguments though? Are the arguments my command line?

This is what I have so far.

Dim process = New Process()
process.StartInfo.FileName = "C:\Users\Metroidn1f\Desktop\Metroidn1f Encoder v4.2.38.3\Tools\x264\x264.exe"
process.StartInfo.Arguments = "x264 --help> ""help.txt"""
process.EnableRaisingEvents = False
Process.StartInfo.UseShellExecute = False
process.StartInfo.CreateNoWindow = False
process.StartInfo.RedirectStandardOutput = False
process.StartInfo.RedirectStandardError = True
process.Start()

Using IO.StreamReader(process.StandardError)

String.line = reader.ReadLine()
If (line = null) Then
break()
End If


End Using

But the Using IO.StreamReader(process.StandardError) has an error "StreamReader' is a type in 'IO' and cannot be used as an expression." What do I do?

JonE
22nd August 2010, 00:14
Yep, it is the command line args.

I've not used x264 in this scenario yet (used it with ffmpeg - nightmare!) but I guess the c# code would b something like

process.StartInfo.Arguments = "x264 --help \"help.txt\""

assuming x264 is expecting to see help.txt encased in quotes.

The "using" statement looks wrong. It should be along the lines of "using <type> name = ...." but you do not seem to be setting a name for the variable. Its not a "using" statement like a using statement at the head of a file - you aren't specifying a namespace that is being used - rather, the "using" statement is a way of wrapping certain types of variable such that it is released when it goes out of scope, be that normal loop exit, via a break statement or even an exception.

Try removing the "using" block and replacing it with



StreamReader reader = process.StandardError;

while( true)
{
string line = reader.Readline();
if( line == null )
break;

// Do stuff with 'line'
}


I need to go look up if you need to explicitly close "reader" once finished with.

TTFN,
Jon

Metroidn1f
31st August 2010, 23:32
Alright, I have the progress bar working now, but I have 2 problems now.

The first on is I can't get my form 3 to do anything. As soon as I click the encode button it will start encoding, but it will not display anything in the form 3. I think I might have to use a background worker, but I don't know how.

This is part of the code that I am using for the encode button.

If VideoMode = "ConstantQuality" Then
Dim s = Sub()
Form3.Show()
Using p As New Process
AddHandler p.ErrorDataReceived,
Sub(sendingProcess As Object,
outLine As DataReceivedEventArgs)
If outLine.Data <> "" Then BeginInvoke(Sub() Form3.Label1.Text = outLine.Data.Substring(1).Split("]")(0))
If ProgressETACheck < 1 Then
If outLine.Data <> "" Then BeginInvoke(Sub() ProgressETA = outLine.Data.Split(" ")(6))
If ProgressETA = "kb/s," Then
ProgressETACheck = ProgressETACheck + 1
End If
End If
If ProgressETACheck = 1 Then
If outLine.Data <> "" Then BeginInvoke(Sub() Form3.Label5.Text = "ETA:" & outLine.Data.Split(" ")(8))
If outLine.Data <> "" Then BeginInvoke(Sub() Form3.Label4.Text = outLine.Data.Split(" ")(5) & " KB/s")
If outLine.Data <> "" Then BeginInvoke(Sub() Form3.Label2.Text = outLine.Data.Split(" ")(1) & " Frames")
If outLine.Data <> "" Then BeginInvoke(Sub() Form3.Label3.Text = outLine.Data.Split(" ")(3) & " fps")
End If
End Sub

p.StartInfo.FileName = WorkingDirectory & "\Tools\x264\x264.exe"
p.StartInfo.Arguments = "--quiet --profile " & VideoProfileDropDown.Invoke(New GetTextDelegate(AddressOf GetText), VideoProfileDropDown).ToString _
& " --level " & VideoLevelDropDown.Invoke(New GetTextDelegate(AddressOf GetText), VideoLevelDropDown).ToString & _
" --preset medium --no-dct-decimate --no-fast-pskip --crf " & _
VideoCrfUpDown.Invoke(New GetTextDelegate(AddressOf GetText), VideoCrfUpDown).ToString & " " & Trellis & " " & SubME & " " & MeAlgorhithim & " " & _
BAdapt & " --merange " & VideoMERangeUpDown.Text & " --fps " & fps & " --ref " _
& VideoReferenceFramesUpDown.Invoke(New GetTextDelegate(AddressOf GetText), VideoReferenceFramesUpDown).ToString & " --bframes " _
& VideoBFramesUpDown.Invoke(New GetTextDelegate(AddressOf GetText), VideoBFramesUpDown).ToString & " --deblock " _
& VideoDeblockStrengthUpDown.Invoke(New GetTextDelegate(AddressOf GetText), VideoDeblockStrengthUpDown).ToString & ":" _
& VideoDeblockThresholdUpDown.Invoke(New GetTextDelegate(AddressOf GetText), VideoDeblockThresholdUpDown).ToString & " --scenecut " _
& VideoIFramesUpDown.Invoke(New GetTextDelegate(AddressOf GetText), VideoIFramesUpDown).ToString & " --slices " _
& VideoSlicesUpDown.Invoke(New GetTextDelegate(AddressOf GetText), VideoSlicesUpDown).ToString & " --rc-lookahead " _
& VideoLookaheadUpDown.Invoke(New GetTextDelegate(AddressOf GetText), VideoLookaheadUpDown).ToString & " --qpmin " _
& VideoQuantinizersMinUpDown.Invoke(New GetTextDelegate(AddressOf GetText), VideoQuantinizersMinUpDown).ToString & " --qpmax " _
& VideoQuantinizersMaxUpDown.Invoke(New GetTextDelegate(AddressOf GetText), VideoQuantinizersMaxUpDown).ToString & " --qpstep " _
& VideoQuantinizersStepUpDown.Invoke(New GetTextDelegate(AddressOf GetText), VideoQuantinizersStepUpDown).ToString _
& " --output """ & VideoOutputBox.Invoke(New GetTextDelegate(AddressOf GetText), VideoOutputBox).ToString & """ """ _
& VideoInputBox.Invoke(New GetTextDelegate(AddressOf GetText), VideoInputBox).ToString & """"
p.StartInfo.UseShellExecute = False
p.StartInfo.RedirectStandardError = True
p.StartInfo.CreateNoWindow = False
p.Start()
p.BeginErrorReadLine()
p.WaitForExit()
End Using
End Sub
s.BeginInvoke(Nothing, Nothing)
End If
End If

In the form 3 I just have the basic stuff that just converts the stdout to a percentage.

My second problem is I don't know how to end the process if one of the windows are closed. I tried using on form closing but it will not let me cancel or kill the task with the variable. I tried making it a public variable but it still did not work.

Can anyone help me out?

Snowknight26
1st September 2010, 07:01
You're right, your form won't be repainted unless you use a background worker or a thread. If you go the thread route, you could try something like this:

Imports System.Threading

Public Class Form1

Public t As Thread = New Thread(AddressOf doWork)
Public p As Process = New Process()

Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
If Not p.HasExited Then
p.Close() 'or maybe p.Kill()
End If
If t.ThreadState = ThreadState.Running Then
t.Abort()
End If
End Sub

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
t.Start()
End Sub

Private Sub doWork()
'code that interacts with x264
'p.StartInfo = ...
'p.Start()
'...
End Sub

End Class


If you don't go the thread route, well.. no example code from me. :P

Metroidn1f
1st September 2010, 17:53
What ever I try in the formclosing, the form will never close. I try e.Cancel = False, Me.Close(), End but the form will never close. It will only close if I do not have the p.Kill() there. I also tried what said to do if not p.hasexited. I did not bother putting the thread part because I got it to work. I don't know why, I had this problem before that I could not get the form to close if I had anything else in the formclosing but the end or stuff like that. I even tried closing the form 3 first then closing the main form, but it will not close.

JonE
1st September 2010, 22:05
I'm concerned that an attempt is being made to kill a process on which a subsequent thread may currently be working. Not necessarily wrong, but does ring alarm bells.

So the first step I'd take is to introduce two bools (there are better ways but this illustrates the process).

Initially set bool's A and B to false.

When you want to exit via form closing, you set A and then whilst B is false call System.Threading.Thread.Sleep(100). [Its good practice to include a timeout - say, try 10 times then give up and exit anyway - setting the thread's IsBackground to true is also a good idea as a backup plan)

Meanwhile, inside the thread itself, after processing each line, check if A is set and if so stop reading the process (exit the "while" loop), dispose of the reader, and only then try and kill the process. Then, regardless of how you get to the end of the thread function, set B to true.

This means that the process is terminated only after you stop trying to read it and is closed from within the thread that launched it (good practice if nothing else). Also there is no timing problems. for example

If t.ThreadState = ThreadState.Running Then
t.Abort()

think what happens if thread just so happens to exit between the first line and the second line (albeit very unlikely, but typical of the every-now-an-again / only on certain machines / etc type errors that you see apps generate).

Another thing is that if I read correctly you are using some of form3's controls to supply parameters for the encoding process ? If so, note that Form3.Show() is modeless so returns immediately, so you'll only ever be using the default values of form3. Try Form3.ShowDialog(). Or, is Form3 showing the progress too?

Anyhow, if you are still learning then trying to use threads may be a bit of a big learning curve.

So, what you can do instead, is forget threads and maybe instead on your "encode" button run the inline code to launch process and read lines, but call DoEvents() for each line read as this will keep your UI updated (if a bit sluggish - depends on how frequently x264 outputs a new line of text).

Don't get me wrong, Snowknight26's suggestion to use threads is absolutely the right design choice (DoEvents() is the spawn of Satan), however at this stage to just get something working give DoEvents() a whirl, you should also then not have any cross-thread issues to worry about.

TTFN,
Jon

Metroidn1f
8th September 2010, 22:42
Well I can't figure out a way to do it. I have another problem on not getting the stdout from mkvextract and ffmpeg. I have not tried mp4box yet though. I am using the same code as I did before with the dim s = sub() but I am not getting anything. Do you know the cause of this? Also, is there a way that I can take out subtitles with avisynth without cropping the video to a smaller clip?

JonE
10th September 2010, 11:16
Well, attached is an example of a form that launches a command line (ffmsindex.exe in this instance) and retrieves progress information which it uses to update a progress bar. It also implements a cancel.

For reasons of file size, I've omitted ffms2.dll and ffmsindex.exe which are part of the avisynth add-in for FfmpegSource, but hopefully you wont need to run the code to see how it works. (If you want to run the code then add them them to the project directory.)

Example code to run the form :-

var form = new OpenFileDialog();
form.RestoreDirectory = true;
form.Filter = "Video file|*.avi;*.flv;*.mpg;*.vob;*.mp4|All Files|*.*";

if (form.ShowDialog() == DialogResult.OK)
{
var form2 = new Synthedit.FfmpegSource.FormFfmpegSourceImport();
form2.VideoFilename = form.FileName;

MessageBox.Show(form2.ShowDialog().ToString(), "Result :", MessageBoxButtons.OK, MessageBoxIcon.None);
}


Some apps output text to StandardOutput. Some output to StandardError. You need to select the appropriate method when setting up the Process :-


...
proc.StartInfo.RedirectStandardError = false;
proc.StartInfo.RedirectStandardOutput = true;
...
...
using (StreamReader reader = proc.StandardOutput)
or if you reverse the above true/false pair :-
using (StreamReader reader = proc.StandardError)
...


Some apps may write to both - such as Ffmpeg. However IIRC ffmpeg uses StandardError to output progress info so thats the one you need to attach to. Warning : Ffmpeg can output all sorts of spurious info during file conversions - writing a general purpose interpreter for ffmpeg's output text is a major challenge ! That's why I used ffmsindex as an example instead. However it might be worth experimenting with ffmpeg's logging options, maybe you can remove / reduce the spurious bits.

Hope this helps.

TTFN,
Jon

PS : Ignore any bit where e.Result is set to something at the same time that e.Cancel is set to true. Micro$oft thought it a jolly good wheeze to make accessing e.Result impossible when a cancel occurs. Thanks Bill :-(

Metroidn1f
12th September 2010, 16:34
Alright thanks. I knew it would be hard to make code for ffmpeg. But then what is a good cli that can demux avi and m2ts? I know there is tsmuxer but that uses xml and I still have to learn that. I think I could just use stream writer for that.

Is there a way that I can remove subtitles with avisynth without having to crop the video?

JonE
12th September 2010, 20:52
Dont be put off by my comments regarding ffmpeg - its only corner cases where it gets a bit of a PITA to interpret the output.

The code I posted is fairly general - launch any command line app and then just change the loop that reads each line to suit the particular app.

TTFN,
Jon

PS : Re avisynth / logos, the only method I've used is DeLogo by Karel Suhajda - I get it set up in VirtualDub, then export the settings to an avisynth script. Some very good results (especially with B&W!?), some not so good. Anyhow, to discuss this further need to start a new thread in the Avisynth Usage section :)