-
Notifications
You must be signed in to change notification settings - Fork 5
/
Form1.cs
2702 lines (2590 loc) · 147 KB
/
Form1.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
using System;
using System.ComponentModel;
using System.IO;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Data;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.CSharp.RuntimeBinder;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace VTC
{
public partial class Form1 : Form
{
//VCT is wrapper for FFmpeg, which is used as process started within this GUI.
//The code below allow user to create encoding tasks, calls FFmpeg and informs user of progress.
//As any GUI it tries to be fool proof, but since it is a first version, there may be bugs.
//It is probably more complicated than needed, but it tries to allow user to change encoding options,
//by clicking radio buttons, check boxes and drop down boxes, and displaying effect change immediately.
//Although it is not in the spirit of OO programming, I defined most of the variables as static.
//Since this app is not large, it makes sense to reserve memory at the beginning and keep it until it runs.
//Also, it is neccessary when using threads so than values can be exchanged between threads.
//There are 3 threads used when encoding is started:
//1. Main thread is GUI control, it starts Background worker to start
//2. encoding task list - each task in the list starts
//3. process that is command which executes ffmpeg with options
//define variables
Stopwatch stopwatch = new Stopwatch(); //measure execution time for each job
static string statustekst = "", std_out = ""; //text to be displayed as info; store part from ffmpeg standard output
bool duration_found = false; //check if duration of the video or audio is found in ffmpeg output - measures percentage of executed job
static string percent = ""; //shows perventage of executed task - used for progress bar
static float total_sec, encoded_sec; //stores total length of video or audio; currently number of encoded seconds
static string input_stream, input_file, out_file, out_path = "", str_extension, orig_ext, audio_ext, stream_file = ""; //stores names of input, output files, output path and temp. varr
static string subtitle_stream, audio_stream = "1";
static int number_of_rows = 0; //stores number of rows for batch list
static int ffmpeg_process_id; //process id of started ffmpeg process - used to close it if user cancels or pauses
static bool canceled = false, video_only = false, audio_only = false; //flags to mark cancel job; if video or audio only is encoded
static bool add_sub_stream = false, error_in_file = false, use_out_path = false, paused = false, started = false; // paused or started encoding
static string preset = "veryfast", crf = "23", audio = "libmp3lame", container = "mkv", audiobitrate = "128k"; //options values used as ffmpeg encodin parameters
static string video = "", audio_part = "", task = "", video_size=""; //video;audio part of parameters string; ffmpeg command string
static string time_duration = ""; //used if only specific part of file is needed
static string vf = ""; //video filter part, used currently to rotate video
static bool h265 = false; //use H.264 codec or not, controlled by checkBoxH265
static bool set_fps = false; //set if different target FPS is to be used
static bool slow_motion = false; //check if video is converted to slow motion from e.g. high FPS video source
static double fps = 0.00; //initial value for video file fps
List<String> task_list = new List<String>(); //all tasks put in a batch list
string json = "", ffplay_output=""; //ffprobe shows JSON style info about file properties
string time_position = "2"; //position from which to extract image from video
Process proc = new System.Diagnostics.Process(); //process that call cmd.exe to execute ffmpeg task
static string pass_video_info, pass_audio_info, pass_subtitle_info, temp_path; //vars to pass to other infoForm
static string pass_labelFileName2, pass_labelFormat2, pass_labelDuration2, pass_labelSize2, pass_labelvideobitrate;
static bool log = false; //output from ffmpeg visible or not
static string output_log, out_fps = "";
Form logForm = new Form();
// Create the ToolTips and associate with the Form container.
ToolTip toolTip1 = new ToolTip();
ToolTip toolTip2 = new ToolTip();
ToolTip toolTip3 = new ToolTip();
ToolTip toolTip4 = new ToolTip();
ToolTip toolTip5 = new ToolTip();
ToolTip toolTip6 = new ToolTip();
ToolTip toolTip7 = new ToolTip();
ToolTip toolTip8 = new ToolTip();
ToolTip toolTip9 = new ToolTip();
ToolTip toolTip10 = new ToolTip();
ToolTip toolTip11 = new ToolTip();
ToolTip toolTip12 = new ToolTip();
ToolTip toolTip13 = new ToolTip();
ToolTip toolTip14 = new ToolTip();
ToolTip toolTip15 = new ToolTip();
ToolTip toolTip16 = new ToolTip();
ToolTip toolTip17 = new ToolTip();
ToolTip toolTip18 = new ToolTip();
ToolTip toolTip19 = new ToolTip();
ToolTip toolTip20 = new ToolTip();
ToolTip toolTip21 = new ToolTip();
ToolTip toolTip22 = new ToolTip();
ToolTip toolTip23 = new ToolTip();
ToolTip toolTip24 = new ToolTip();
ToolTip toolTip25 = new ToolTip();
ToolTip toolTip26 = new ToolTip();
ToolTip toolTip27 = new ToolTip();
ToolTip toolTip28 = new ToolTip();
ToolTip toolTip29 = new ToolTip();
ToolTip toolTip30 = new ToolTip();
ToolTip toolTip31 = new ToolTip();
ToolTip toolTip32 = new ToolTip();
ToolTip toolTip33 = new ToolTip();
ToolTip toolTip34 = new ToolTip();
ToolTip toolTip35 = new ToolTip();
ToolTip toolTip36 = new ToolTip();
ToolTip toolTip37 = new ToolTip();
ToolTip toolTip38 = new ToolTip();
ToolTip toolTip39 = new ToolTip();
ToolTip toolTip40 = new ToolTip();
ToolTip toolTip41 = new ToolTip();
ToolTip toolTip42 = new ToolTip();
ToolTip toolTip43 = new ToolTip();
ToolTip toolTip44 = new ToolTip();
ToolTip toolTip45 = new ToolTip();
public Form1()
{
try
{
// uncomment next line to build for specifig language UI - useful if you have Windows installed in english but you want specific language for app UI
//Thread.CurrentThread.CurrentUICulture = new CultureInfo("nb");
//Thread.CurrentThread.CurrentUICulture = new CultureInfo("sr-Cyrl");
InitializeComponent();
comboBoxPreset.SelectedIndex = 5;
comboBoxQuality.SelectedIndex = 6;
comboBoxAudioBitRate.SelectedIndex = 2;
comboBoxAudioStreamNo.SelectedIndex = 0;
DateTime datum = DateTime.Now;
if (datum.Month == 1 || datum.Month == 12)
{
this.buttonAbout.BackgroundImage = VTC.Properties.Resources.santahead_512;
this.buttonAbout.BackgroundImageLayout = ImageLayout.Stretch;
this.buttonAbout.Text = "";
this.buttonAbout.FlatAppearance.BorderSize = 0;
}
else
{
this.buttonAbout.BackgroundImage = null;
this.buttonAbout.Text = "About";
}
// Use 'Transcode' tab to repack h.264 containers (MP4 vs MKV), or 'Convert' tab for full range of conversion options.
// Handler for capturing output data from external process ffmpeg
proc.OutputDataReceived += (sender, args) => DisplayOutput(args.Data);
proc.ErrorDataReceived += (sender, args) => DisplayOutput(args.Data); //same method used for error data
}
catch { }
}
public static int IsLinux
{ // check if OS is Linux (4, 128) or Mac (6)
get
{
int p = (int)Environment.OSVersion.Platform;
//return (p == 4) || (p == 6) || (p == 128); // 4 or 128 = Linux, 6 = MacOS
if (p == 4 || p == 128)
return 1; // 1 for Linux
else if (p == 6)
return 2; // 2 for MacOS
else
return 0; // 0 for Windows
}
}
public Process GetProcByID(int id)
{ //handy way to get process without raising exception
Process[] processlist = Process.GetProcesses();
return processlist.FirstOrDefault(pr => pr.Id == id);
}
private void buttonOutTransFile_Click(object sender, EventArgs e)
{ //raised when user clicks to select output path for transcoding tasks
#pragma warning disable CS0168 // Variable is declared but never used
try
{
FolderBrowserDialog savePath = new FolderBrowserDialog();
savePath.SelectedPath = out_path; //stores selected path for both transcoding and converting tasks
savePath.Description = "Choose output file path";
if (savePath.ShowDialog() == DialogResult.OK)
{
if (IsLinux == 0)
out_path = savePath.SelectedPath + "\\"; //adds it so correct folder is chosen later when file name is appended to the string
else
out_path = savePath.SelectedPath + "/";
labelOutTransFile.Text = out_path; //displays selected path so user is aware of the change
labelOutConvFile.Text = out_path;
buttonMultiTransFile.Enabled = true; //now, it is safe to allow user to click this button to select input files
buttonRemoveTransPath.Enabled = true;
buttonRemoveTransPath.Visible = true;
buttonRemoveOutPath.Enabled = true;
buttonRemoveOutPath.Visible = true;
use_out_path = true;
EnableConvButtons();
}
}
catch { }
}
private void buttonMultiTransFile_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
foreach (string filePath in files)
{
input_file = filePath;
MultiTransRow(); //process each file to add appropriate command to the list
EnableTransButtons(); //enable user to start, stop, select, delete
input_file = "";
}
}
}
private void buttonMultiTransFile_Click(object sender, EventArgs e)
{ //raised when user clicks to select input files
string[] input_list = new string[16383]; //defines max number of files selected from the same folder
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.InitialDirectory = "C://";
openFileDialog.Filter = "H.264 or H.265 files|*.m4v;*.mp4;*mkv;*.avi;*.mpg;*.divx;*.mov;*.wmv|All files|*.*"; //sets filter of displayed files
openFileDialog.Multiselect = true; //allows to select more files at once
openFileDialog.Title = "Choose video files to transcode";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
input_list = openFileDialog.FileNames; //populate list with selected file names
foreach (string value in input_list)
{ //process each file name string to automatically define output names
input_file = value;
MultiTransRow(); //process each file to add appropriate command to the list
EnableTransButtons(); //enable user to start, stop, select, delete
input_file = "";
}
buttonInfo.Visible = false;
buttonPlay.Visible = false;
}
}
private void MultiTransRow()
{
#pragma warning disable CS0168 // Variable is declared but never used
try
{
string from_duration = "";
int str_position = input_file.LastIndexOf('.') + 1; //find position of '.' to determine file extension
str_extension = input_file.Substring(str_position); //store found file extension
if (!checkBoxKeepExtension.Checked) //Change extansion only if box unchecked
{
switch (str_extension)
{ //decide what to do for each extension
case "mp4": //if extension is MP4, output will be MKV
str_extension = "1.mkv";
break;
case "m4v": //if extension is MvV, output will be MKV
str_extension = "1.mkv";
break;
case "mkv": //if extension is MKV, output will be MP4
str_extension = "1.mp4";
break;
default: //default extension is MKV, altough more correct would be to skip!!!!
str_extension = "1.mkv";
break;
}
}
else
str_extension = "2." + str_extension;
if (IsLinux == 0)
str_position = input_file.LastIndexOf("\\") + 1; //find where is the last folder mark
else
str_position = input_file.LastIndexOf("/") + 1;
string in_file = input_file.Substring(str_position);//after that there is a file name
if (!use_out_path) //if out path not set, use the same as input file
{
out_path = input_file.Substring(0, str_position);
//labelOutConvFile.Text = out_path;
//labelOutTransFile.Text = out_path;
}
else
out_path = labelOutTransFile.Text;
str_position = in_file.LastIndexOf('.') + 1; //get position just before extension
in_file = in_file.Substring(0, str_position); //set temp var in_file with input file name
out_file = out_path + in_file; //set temp var out_file as selected path + input file name
string _subs = " -map 0:s? "; //include all subs streams
if (checkBoxTransRemoveSubtitle.Checked)
_subs = " -map -0:s? "; //remove all subs streams
string _copy_all_streams = " -map 0:v? -map 0:a? "; //include all v&a streams
if (!checkBoxTranscodeAllStreams.Checked)
{
_copy_all_streams = " -map 0:v:" + numericUpDownVideoNr.Value + "? -map 0:a:" + numericUpDownAudioNr.Value + "? "; //include only 1st v&a streams
}
if (checkBoxCopyDuration.Checked)
from_duration = " -ss " + textBoxFromTime.Text + " -t " + textBoxCopyDuration.Text + " ";
string command = "";
if (IsLinux == 0)
command = "ffmpeg -y -i \"" + input_file + "\" " + from_duration + _copy_all_streams + _subs + " -c copy \"" + out_file + str_extension + "\"";//define ffmpeg command Windows
else
command = " -y -i \"" + input_file + "\" " + from_duration + _copy_all_streams + _subs + " -c copy \"" + out_file + str_extension + "\""; //Linux or Mac mono
number_of_rows++; //increase counter so we know how many files in the list are
DataGridViewRow tempRow = new DataGridViewRow();//define row that will store command
DataGridViewCell check_cell = new DataGridViewCheckBoxCell(false);//define each column i a row -cell
DataGridViewCell No_cell = new DataGridViewTextBoxCell(); //number of a row
DataGridViewCell task_cell = new DataGridViewTextBoxCell(); //task string column
check_cell.Value = false; //set that row is not checked for deletion
No_cell.Value = number_of_rows; //number of a row = current number from list
task_cell.Value = command; //put ffmpeg command string
tempRow.Cells.Add(check_cell); //add all column values to the temp row
tempRow.Cells.Add(No_cell);
tempRow.Cells.Add(task_cell);
dataGridViewBatch.Rows.Add(tempRow); //add new temp row to the batch list
}
catch { }
}
private int ffplay_test(string input)
{ //start ffplay to test the Internet stream
try
{
System.Diagnostics.ProcessStartInfo procffplay;
if (IsLinux==1)
procffplay = new System.Diagnostics.ProcessStartInfo("ffplay", " \"" + input + "\""); // Linux with mono
else if (IsLinux==2)
procffplay = new System.Diagnostics.ProcessStartInfo("./ffplay", " \"" + input + "\""); // for MacOS with mono
else
procffplay = new System.Diagnostics.ProcessStartInfo("cmd", "/c " + "ffplay \"" + input + "\""); // Windows: define Process Info to assing to the process
// The following commands are needed to redirect the standard output and standard error.
ffplay_output = ""; // reset ffplay log from cmd output
log = false;
buttonLog.Enabled = true;
buttonLog2.Enabled = true;
buttonLogRec.PerformClick();
procffplay.RedirectStandardError = false;
procffplay.RedirectStandardOutput = false;
procffplay.RedirectStandardInput = false;
procffplay.UseShellExecute = false;
procffplay.CreateNoWindow = true;
Process ffproc = new Process();
ffproc.StartInfo = procffplay;
//ffproc.ErrorDataReceived += (sender, args) => ffplayOutput(args.Data);
ffproc.Start(); //start the ffplay
//ffproc.BeginErrorReadLine();
//ffproc.WaitForExit(); //since it is started as separate thread, GUI will continue separately, but we wait here before starting next task
//ffproc.CancelErrorRead();
/*if (ffplay_output.Contains("Invalid") || ffplay_output.Contains("error"))
{
richTextBox3.Text = ffplay_output;
return -1;
}
else
{
richTextBox3.Text = ffplay_output;
return 0;
}*/
return 0;
}
catch (Exception ex)
{
statustekst = ex.Message;
richTextBox3.Text += statustekst;
return -1;
}
}
void ffplayOutput(string output)
{ //read output sent from ffplay
try
{
ffplay_output += output; //put it in string to be parsed to get stream info
}
catch {}
}
private int ffprobe(string input)
{
try
{
System.Diagnostics.ProcessStartInfo procffprobe;
if (IsLinux==1)
procffprobe = new System.Diagnostics.ProcessStartInfo("ffprobe", " -v quiet -print_format json -show_format -show_streams \"" + input + "\"");// Linux with mono
else if (IsLinux==2)
procffprobe = new System.Diagnostics.ProcessStartInfo("./ffprobe", " -v quiet -print_format json -show_format -show_streams \"" + input + "\""); // MacOS with mono
else
procffprobe = new System.Diagnostics.ProcessStartInfo("cmd", "/c " + " ffprobe -v quiet -print_format json -show_format -show_streams \"" + input + "\"");// Windows: define Process Info to assing to the process
// The following commands are needed to redirect the standard output and standard error.
// This means that it will be redirected to the Process.StandardOutput StreamReader.
procffprobe.RedirectStandardOutput = true;
procffprobe.RedirectStandardInput = true;
procffprobe.RedirectStandardError = true;
procffprobe.UseShellExecute = false;
procffprobe.CreateNoWindow = true; // Do not create the black window.
procffprobe.WorkingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);//set path of vtc.exe same as ffmpeg.exe
Process ffprobeproc = new Process();
ffprobeproc.StartInfo = procffprobe;
ffprobeproc.OutputDataReceived += (sender, args) => ffprobeOutput(args.Data);
ffprobeproc.ErrorDataReceived += (sender, args) => ffprobeOutput(args.Data); //same method used for error data
ffprobeproc.Start(); //start the ffprobe
ffprobeproc.BeginOutputReadLine(); // Set our event handler to asynchronously read the sort output.
ffprobeproc.BeginErrorReadLine();
ffprobeproc.WaitForExit(); //since it is started as separate thread, GUI will continue separately, but we wait here before starting next task
temp_path = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".jpg";
ffprobeproc.CancelOutputRead(); //stop reading redirected standard output
ffprobeproc.CancelErrorRead();
//Thread.Sleep(500);
return 0; //0 means OK, not used so far
}
catch (Exception ex)
{
statustekst = ex.Message;
return -1; //-1 means NOT OK, not used so far
}
}
void ffprobeOutput(string output)
{ //read output sent from ffprobe
try
{
json += output; //put it in string to be parsed to get file info
}
catch { }
}
private void ffmpeg_extract_jpeg(string tstamp)
{ //start ffmpeg process in separate thread to extract image from video file at specified position
try
{
System.Diagnostics.ProcessStartInfo procff;
if (IsLinux==1)
procff = new System.Diagnostics.ProcessStartInfo("ffmpeg", " -ss " + tstamp + " -i \"" + input_file + "\" -y -qscale:v 2 -vframes 1 " + temp_path);// Linux with mono
else if (IsLinux==2)
procff = new System.Diagnostics.ProcessStartInfo("./ffmpeg", " -ss " + tstamp + " -i \"" + input_file + "\" -y -qscale:v 2 -vframes 1 " +temp_path); // MacOS with mono
else
procff = new System.Diagnostics.ProcessStartInfo("cmd", "/c " + " ffmpeg -ss " + tstamp + " -i \"" + input_file + "\" -y -qscale:v 2 -vframes 1 \"" + temp_path);// Windows: define Process Info to assing to the process
// The following commands are needed to redirect the standard output and standard error.
// This means that it will be redirected to the Process.StandardOutput StreamReader.
procff.RedirectStandardError = false;
procff.RedirectStandardOutput = false;
procff.RedirectStandardInput = false; ;
procff.UseShellExecute = false;
procff.CreateNoWindow = true; // Do not create the black window.
procff.WorkingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);//set path of vtc.exe same as ffmpeg.exe
Process ffproc = new Process();
ffproc.StartInfo = procff;
ffproc.Start(); //start the ffprobe
ffproc.WaitForExit();
}
catch (Exception ex)
{
statustekst = ex.Message;
}
}
private void extract_jpeg()
{
try
{
BackgroundWorker jpeg = new BackgroundWorker(); //new instance of Background worker
jpeg.WorkerReportsProgress = true;
jpeg.DoWork += jpeg_DoWork; //handler for starting thread
jpeg.RunWorkerCompleted += jpeg_RunWorkerCompleted; //handler for finishing thread
jpeg.RunWorkerAsync(); //start job as separate thread
}
catch {}
}
private void jpeg_DoWork(object sender, DoWorkEventArgs e)
{
try
{ //new thread started, here we define process start, etc.
Thread.Sleep(900);
ffmpeg_extract_jpeg(time_position); //extract image screenshot in this func
}
catch (Exception x)
{
statustekst = x.Message;
}
}
private void jpeg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
private void timerBatch_Tick(object sender, EventArgs e)
{ //timer ticks every 1 sec to display progress, messages, etc. when encoding
try
{
if (canceled)
{ //cancel if flag set by Cancel method
afterCancelOrFinish();
toolStripStatusLabel1.Text = statustekst;
richTextBox3.Text = output_log;
}
else
{ //display elapsed time, output from ffmpeg
toolStripStatusLabel1.Text = "Time: " + stopwatch.Elapsed.ToString(@"hh\:mm\:ss") + "s | " + statustekst;
toolStripProgressBar1.Value = (int)((encoded_sec / total_sec) * 100);//set percentage for displaying progress of current task
richTextBox3.Text = output_log;
}
}
catch (Exception ex)
{
statustekst = ex.Message;
}
}
private int batchTask(string current_task)
{ //called when starting each ffmpeg encoding task, passed task string as parameter
try
{
System.Diagnostics.ProcessStartInfo procStartffmpeg;
if (IsLinux==1)
procStartffmpeg = new System.Diagnostics.ProcessStartInfo("ffmpeg", current_task);// Linux with mono
else if (IsLinux==2)
procStartffmpeg = new System.Diagnostics.ProcessStartInfo("./ffmpeg", current_task); // MacOS with mono
else
procStartffmpeg = new System.Diagnostics.ProcessStartInfo("cmd", "/c " + current_task);// Windows: define Process Info to assing to the process
// The following commands are needed to redirect the standard output and standard error.
// This means that it will be redirected to the Process.StandardOutput StreamReader.
procStartffmpeg.RedirectStandardOutput = true;
procStartffmpeg.RedirectStandardInput = true;
procStartffmpeg.RedirectStandardError = true;
procStartffmpeg.UseShellExecute = false;
procStartffmpeg.CreateNoWindow = true; // Do not create the black window.
procStartffmpeg.WorkingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);//set path of vtc.exe same as ffmpeg.exe
proc.StartInfo = procStartffmpeg; // Now we assign process its ProcessStartInfo and start it
proc.Start(); //start the ffmpeg
ffmpeg_process_id = proc.Id;//remember process id so that it can be closed if user cancels
Thread.Sleep(500); //wait a little bit - prevent glitches for concurrent threads
proc.BeginOutputReadLine(); // Set our event handler to asynchronously read the sort output.
proc.BeginErrorReadLine();
proc.WaitForExit(); //since it is started as separate thread, GUI will continue separately, but we wait here before starting next task
proc.CancelOutputRead(); //stop reading redirected standard output
proc.CancelErrorRead();
return 0; //0 means OK, not used so far
}
catch (Exception ex)
{
statustekst = ex.Message;
return -1; //-1 means NOT OK, not used so far
}
}
void DisplayOutput(string output)
{ //called by task thread to read std out from process and set values to be displayed when timer event fires
try
{
if (!duration_found) //read line by line std out until duration of the video or audio is found
{
if (output.Contains("Duration:"))
{ //ok, here is duration, remember it as total time
std_out = output.Substring(output.IndexOf("Duration:") + 10, 8); //always at same position within std out line
total_sec = TimeSpan.ParseExact(std_out, "hh\\:mm\\:ss", null).Ticks; //set format of duration as TimeSpan
duration_found = true; //flag that duration is found, we may skip it for the rest of the current std out
}
}
else if (output != null)
{ //after duration found, read progress for each std out line
if (output.Contains("time="))
{ //in each line we search for occurence of time value
std_out = output.Substring(output.IndexOf("time=") + 5, 8); //time value is at this position
encoded_sec = TimeSpan.ParseExact(std_out, "hh\\:mm\\:ss", null).Ticks; //set format of current time
percent = ((encoded_sec / total_sec) * 100).ToString("0.00"); //calculate percentage
statustekst = " " + percent + "% | " + output; //define text to display with calculated values
}
else if (output.Contains("Conversion failed!"))
{
error_in_file = true; //set to warn that file failed to convert
statustekst = output;
}
else if (!output.Contains("To ignore this"))
statustekst = output; //catch any error message
}
output_log += output + "\n";
}
catch { }
}
private void buttonStartQueue_Click(object sender, EventArgs e)
{ //handler for user clicking to start encoding of batch list
try
{
// first start of task list (not before started or paused)
// populate task list from Grid and start execution
if (!started && !paused)
{
started = true;
buttonStartQueue.Text = "Pause ||";
richTextBox3.Text = "";
output_log = "";
buttonLog.Visible = true;
buttonLog.Enabled = true;
buttonLog2.Visible = true;
buttonLog2.Enabled = true;
DataGridViewCell check_cell = new DataGridViewCheckBoxCell(true);//new instance of check cell
DataGridViewRow row = new DataGridViewRow(); //new temp row
BackgroundWorker bg = new BackgroundWorker(); //new instance of Background worker
bg.WorkerReportsProgress = true;
bg.DoWork += bg_DoWork; //handler for starting thread
bg.RunWorkerCompleted += bg_RunWorkerCompleted; //handler for finishing thread
canceled = false; //our new job is not yet canceled
statustekst = "Job started";
toolStripProgressBar1.Value = 0; //initial progress
toolStripProgressBar1.Maximum = 100; //max progress possible - 100%
stopwatch.Reset(); //reset measured time
stopwatch.Start(); //start measuring time
timerBatch.Interval = 1000; //display progress every second
timerBatch.Enabled = true; //start displaying progress
DisableButtonsWhenEncoding(); //prevent user to add, delete, etc. while encoding is running
task_list.Clear(); // clear then fill the task list
for (int i = 0; i < number_of_rows; i++)
{ //for each task in the list, read command string value
task_list.Add(dataGridViewBatch.Rows[i].Cells[2].Value.ToString());
}
bg.RunWorkerAsync(); //start job as separate thread
}
else if (started && !paused) // task list is started but not paused, set it to paused, change button caption and send Pause to ffmpeg proces
{
paused = true;
buttonStartQueue.Text = "Resume >";
buttonStartQueue.BackColor = System.Drawing.Color.SteelBlue;
proc.StandardInput.Write((char) 13);
Thread.Sleep(300);
}
else // task list is started and paused, set it to resumed, change button caption and send Resume code to ffmpeg proces
{
paused = false;
buttonStartQueue.Text = "Pause ||";
buttonStartQueue.BackColor = System.Drawing.Color.Transparent;
proc.StandardInput.Write((char) 10);
Thread.Sleep(300);
}
}
catch (Exception ex)
{ //in case something goes wrong, stop timers and enable user control
toolStripStatusLabel1.Text = "ERROR: " + ex.ToString() + statustekst;
afterCancelOrFinish();
}
}
private void bg_DoWork(object sender, DoWorkEventArgs e)
{
try
{ //new thread started, here we define process start, etc.
DataGridViewCell check_cell = new DataGridViewCheckBoxCell(true);//needed to mark task checked when each job finishes
DataGridViewRow row = new DataGridViewRow();
statustekst = ""; //empty messages at the beginning
for (int i = 0; i < number_of_rows; i++)
{
if (!canceled)
{ //if not by any chance user marked as canceled
task = task_list[i]; //read next command string from task list
dataGridViewBatch.Rows[i].Selected = true; //mark row in GUI so user knows which one is processed
duration_found = false; //reset flag from previous task
ffmpeg_process_id = batchTask(task); //start encoding, process has already finished so this var can be reused
check_cell.Value = true; //set check in GUI that this job is finished
row = dataGridViewBatch.Rows[i];
row.Cells["check_cell"].Value = check_cell.Value;
row.Selected = false; //unselect this row in the list
stopwatch.Restart(); //restart measuring time for next job
}
}
//finished = true;
}
catch (Exception x)
{
ffmpeg_process_id = -1;
statustekst = x.Message;
afterCancelOrFinish(); //after error occured, call this function to clean-up and reset encoding status
}
}
private void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (statustekst.Contains("kb/s") || statustekst.Contains("mux") || statustekst.Contains("global headers:") || statustekst.Contains("Qavg:"))
{
statustekst = "Encoding finished! " + statustekst; //message when thread finishes with all jobs
canceled = true;
}
else
{
statustekst = "Encoding aborted! " + statustekst;
canceled = true;
}
if (error_in_file)
{
MessageBox.Show("At least one file failed to convert. Check output file sizes: usually the very small one (only few kB) is not converted for some reason, e.g. missing header, etc.");
error_in_file = false;
}
afterCancelOrFinish(); //after error occured, call this function to clean-up and reset encoding status
}
private void recStreamFFmpeg(string input)
{ //start ffmpeg to test stream
try
{
System.Diagnostics.ProcessStartInfo recffmpeg;
if (IsLinux==1)
recffmpeg = new System.Diagnostics.ProcessStartInfo("ffmpeg", input); // Linux with mono
else if (IsLinux==2)
recffmpeg = new System.Diagnostics.ProcessStartInfo("./ffmpeg", input); // MacOS with mono
else
recffmpeg = new System.Diagnostics.ProcessStartInfo("cmd", "/c " + input); // Windows: define Process Info to assing to the process
// The following commands are needed to redirect the standard output and standard error.
recffmpeg.RedirectStandardError = false;
recffmpeg.RedirectStandardOutput = false;
recffmpeg.RedirectStandardInput = false;
recffmpeg.UseShellExecute = false;
recffmpeg.CreateNoWindow = false;
Process recff = new Process();
recff.StartInfo = recffmpeg;
recff.Start(); //start the ffmpeg to record stream to file
Thread.Sleep(500);
}
catch (Exception ex)
{
statustekst = ex.Message;
richTextBox3.Text += statustekst;
}
}
private void afterCancelOrFinish()
{
try
{
stopwatch.Stop();
timerBatch.Enabled = false;
buttonStartQueue.Text = "Start";
EnableButtonsAfterEncoding();//enable buttons so user can edit tasks
toolStripProgressBar1.Value = 0;
toolStripStatusLabel1.Text = statustekst;
canceled = false;
paused = false;
started = false;
}
catch (Exception x)
{
statustekst = x.Message;
paused = false;
started = false;
}
}
private void buttonCancelBatch_Click(object sender, EventArgs e)
{
try
{ //user clicks to cancel encoding
DialogResult result = MessageBox.Show("All your jobs will be canceled!\nHowever, your queue will remain available for editing.\nDo you want to proceed?", "Queued jobs cancel", MessageBoxButtons.OKCancel);
if (result == DialogResult.OK)
{
canceled = true;
afterCancelOrFinish(); //if confirms cancel call method to stop all jobs
toolStripStatusLabel1.Text = "Encoding canceled after " + stopwatch.Elapsed.ToString(@"hh\:mm\:ss") + "s ";
proc.StandardInput.Write('q'); //sending 'q' to ffmpeg gracefully stops encoding and closes parent cmd window (hidden)
Thread.Sleep(900);
proc.CancelErrorRead(); //stop reading std out
proc.CancelOutputRead(); //stop reading std error
Process p = Process.GetProcessesByName("ffmpeg").FirstOrDefault(); // find current ffmpeg process
p.Kill(); // and kill it
Thread.Sleep(900);
}
}
catch (Exception ex)
{
statustekst = ex.Message;
}
}
private void DisableButtonsWhenEncoding()
{ //disable user interaction with controls while encoding in progress
try
{
panelConvert.Enabled = false;
panelTranscode.Enabled = false;
groupBoxOptions.Enabled = false;
buttonCancelBatch.Enabled = true;
//buttonStartQueue.Enabled = false;
buttonDeleteQueue.Enabled = false;
buttonAddBatchConv.Enabled = false;
buttonInputConvFile.Enabled = false;
buttonMultiConvFiles.Enabled = false;
//buttonOutConvFile.Enabled = false;
buttonOutTransFile.Enabled = false;
buttonMultiTransFile.Enabled = false;
groupBoxAudio.Enabled = false;
groupBoxContainer.Enabled = false;
groupBoxVideoOrAudio.Enabled = false;
buttonAddSubtitle.Enabled = false;
buttonInfo.Visible = false;
buttonPlay.Visible = false;
groupBoxVideoSize.Enabled = false;
groupBoxSlow.Enabled = false;
groupBoxRotate.Enabled = false;
groupBoxCPU.Enabled = false;
comboBoxAudioStreamNo.Enabled = false;
}
catch { }
}
private void EnableButtonsAfterEncoding()
{ //enable user interaction
try
{
panelConvert.Enabled = true;
panelTranscode.Enabled = true;
groupBoxOptions.Enabled = true;
buttonCancelBatch.Enabled = false;
buttonStartQueue.Enabled = true;
buttonDeleteQueue.Enabled = true;
buttonAddBatchConv.Enabled = true;
buttonInputConvFile.Enabled = true;
buttonMultiConvFiles.Enabled = true;
//buttonOutConvFile.Enabled = true;
buttonOutTransFile.Enabled = true;
buttonMultiTransFile.Enabled = true;
groupBoxAudio.Enabled = true;
groupBoxContainer.Enabled = true;
groupBoxVideoOrAudio.Enabled = true;
buttonAddSubtitle.Enabled = true;
buttonStartQueue.BackColor = System.Drawing.Color.Transparent;
groupBoxVideoSize.Enabled = true;
groupBoxSlow.Enabled = true;
groupBoxRotate.Enabled = true;
groupBoxCPU.Enabled = true;
comboBoxAudioStreamNo.Enabled = true;
}
catch { }
}
private void ReadParametersFromGUI()
{
try
{ //called whenever user clicks options controls to store new values in variables
//This function is the most important to update preprocessed ffmpeg command from GUI
if (checkBoxCopyDuration.Checked)
time_duration = " -ss " + textBoxFromTime.Text + " -t " + textBoxCopyDuration.Text;
else time_duration = "";
if (checkBoxVideoOnly.Checked)
{
video_only = true;
audio_only = false;
groupBoxAudio.Enabled = false;
comboBoxAudioStreamNo.Enabled = false;
str_extension = orig_ext;
}
else
{
video_only = false;
groupBoxAudio.Enabled = true;
comboBoxAudioStreamNo.Enabled = true;
}
if (checkBoxAudioOnly.Checked)
{
audio_only = true;
video_only = false;
groupBoxContainer.Enabled = false;
groupBoxRotate.Enabled = false;
groupBoxCPU.Enabled = false;
groupBoxVideoSize.Enabled = false;
groupBoxSlow.Enabled = false;
}
else
{
audio_only = false;
groupBoxContainer.Enabled = true;
groupBoxRotate.Enabled = true;
groupBoxCPU.Enabled = true;
groupBoxVideoSize.Enabled = true;
groupBoxSlow.Enabled = true;
str_extension = orig_ext;
}
if (checkBoxAudioOnly.Checked)
buttonAddSubtitle.Enabled = false;
else
buttonAddSubtitle.Enabled = true;
//
if (radioButtonMP3.Checked)
{
audio = "libmp3lame";
audio_ext = "mp3";
}
else if (radioButtonAAC.Checked)
{
audio = "aac";
audio_ext = "aac";
}
else audio = " copy";
//
if (radioButtonMKV.Checked)
{
container = "mkv";
}
else if (radioButtonMP4.Checked)
{
container = "mp4";
}
else container = " copy";
//
if (checkBox180.Checked)
{
if (vf != "")
vf += ",\"rotate=PI\"";
else
vf = " -vf \"rotate=PI\"";
}
if (checkBox90clockwise.Checked)
{
if (vf != "")
vf += "\"rotate=PI/2\"";
else
vf = " -vf \"rotate=PI/2\"";
}
if (checkBox90counterclockwise.Checked)
{
if (vf != "")
vf += "\"rotate=-PI/2\"";
else
vf = " -vf \"rotate=-PI/2\"";
}
//
preset = comboBoxPreset.SelectedItem.ToString();
//
crf = comboBoxQuality.SelectedItem.ToString();
//
audiobitrate = comboBoxAudioBitRate.SelectedItem.ToString();
// check if user wants to resize video and apply VF filter value
if (radioButton_1080p.Checked)
{
if (vf != "")
vf += ",scale=1920:-2";//value -2 means if because some filters want to have multiplicator of 2 (some even 4)
else
vf = " -vf scale=1920:-2";
}
else if (radioButton_720p.Checked)
{
if (vf != "")
vf += ",scale=1280:-2";
else
vf = " -vf scale=1280:-2";
}
else if (radioButton_480p.Checked)
{
if (vf != "")
vf += ",scale=720:-2";
else
vf = " -vf scale=720:-2";
}
else if (radioButton_No_Video_Resize.Checked)
{
video_size = "";
}
else video_size = "";
}
catch {}
}
private string SetupConversionOptions()
{
try
{ //the most important part of preparing values passed as ffmpeg arguments
//depending on options selected in GUI and files selected for encoding
NumberFormatInfo nfi = new NumberFormatInfo();
nfi.NumberDecimalSeparator = "."; //use . as number separator as ffmpeg requests it
string ext = ""; //initial file extension
string input_srt = "";
string srt_options = "";
string stream_option = " -map 0:0 -map 0:" + audio_stream + "?"; //used when user selects audio stream and/or subtitle stream
vf = "";
string input_fps = ""; //if option to set input FPS is used
out_fps = ""; //if option to set output FPS is used. i.e. creation of slow motion video
ReadParametersFromGUI();//read options set by user on GUI
// Test if target FPS is to be set differently from source video
if (set_fps)
{
out_fps = " -r " + textBoxFPSout.Text;
}
// Test if input FPS rate is high speed and needs to be normalized, used in conjunction with target FPS
if (slow_motion)
{
out_fps = " -r " + textBoxFPSout.Text;
if (vf != "")
vf += ",setpts=" + Convert.ToDouble(textBoxSlowFPS.Text).ToString(nfi) + "*PTS";
else
vf = " -vf setpts=" + Convert.ToDouble(textBoxSlowFPS.Text).ToString(nfi) + "*PTS";
}
if (str_extension == "mp3" || str_extension == "aac" || str_extension == "ac3" || str_extension == "wma" || str_extension == "wav" || str_extension == "flac") //if input file is audio file, then set options so the only audio ouput is produced
{
audio_only = true;
video_only = false;
}
string ff; //temp var to store ffmpeg command string
if (audio_only) //audio only file is produced
{
container = " -vn";//put option to exclude video stream
input_fps = "";
out_fps = "";
vf = "";
}
if (video_only)
audio = " -an"; //set audio option to exclude audio stream
if (audio != " copy" && audio != " -an") //if audio not excluded or copied
audio_part = " -c:a " + audio + " -b:a " + audiobitrate; //define audio options as read from GUI
else
{
if (audio == " copy")
audio_part = " -c:a" + audio; //set option to copy audio stream
else
audio_part = audio; //otherwise complete audio part as excluded, maybe this is redundant?!
}
if (container != " copy" && container != " -vn")
{ //if video not excluded or copied set video codec