This question already has answers here:
True Parallel Downloads
(2 answers)
Closed 8 years ago.
I am trying to download one file at a time. But when I add my value2 into the await Task.Delay(value2); It still downloads both files at the same instead of one at a time. Mind you that I will be putting checkboxes to the ones that I want to download and there are going to be about 20 to 50 downloads to which I would be able to choose which to download. But the main thing is how to download one at a time instead of all at the same time.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Diagnostics;
namespace DownloadFileCSharp8
{
public partial class Form1 : Form
{
private Stopwatch workerTimeElaspsed;
public Form1()
{
InitializeComponent();
}
private async void btnGetDownload_Click(object sender, EventArgs e)
{
string text = label5.Text;
int value2;
//value2 = value2 + 5;
int.TryParse(text, out value2);
InitiateDownload("http://stie.text1.txt", #"E:\Files\text1.txt", wc_DownloadFileCompleted, "text1.txt");
await Task.Delay(value2);
InitiateDownload("http://site.text.docx", #"E:\Files\text2.docx", wc_DownloadFileCompleted, "text2.docx");
}
void InitiateDownload(string RemoteAddress, string LocalFile, AsyncCompletedEventHandler CompleteCallBack, object userToken)
{
WebClient wc = new WebClient();
wc.DownloadProgressChanged += wc_DownloadProgressChanged;
wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadTimer);
wc.DownloadFileCompleted += wc_DownloadFileCompleted;
wc.DownloadFileAsync(new Uri(RemoteAddress), LocalFile, userToken);
workerTimeElaspsed = new Stopwatch();
workerTimeElaspsed.Start();
}
private void DownloadTimer(object sender, DownloadProgressChangedEventArgs e)
{
progressBar2.Value = e.ProgressPercentage;
if (e.ProgressPercentage > 0)
{
double totalTime = (100d / (double)e.ProgressPercentage) * workerTimeElaspsed.Elapsed.TotalSeconds;
double remaining = totalTime - workerTimeElaspsed.Elapsed.TotalSeconds;
label5.Text = Math.Round(remaining).ToString();
}
}
void wc_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
if (e.Error != null)
{
lblInfo1.Visible = true;
lblInfo1.ForeColor = Color.Red;
lblInfo1.Text = "Error Downloading ";
//throw e.Error;
}
else if (e.Cancelled)
{
lblInfo1.Visible = true;
lblInfo1.ForeColor = Color.Red;
lblInfo1.Text = "Download Cancelled " + e.UserState + e.Error;
}
else
{
lblInfo1.Visible = true;
lblInfo1.ForeColor = Color.Red;
lblInfo1.Text = e.UserState + " Download Complete!! ";
}
//throw new NotImplementedException();
}
void wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
double bytesIn = double.Parse(e.BytesReceived.ToString());
double totalBytes = double.Parse(e.TotalBytesToReceive.ToString());
double percentage = bytesIn / totalBytes * 100;
progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString());
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void progressBar1_Click(object sender, EventArgs e)
{
}
}
}
You call InitiateDownload to start the download, then wait for a fixed period of time (which appears to be very short), then you call InitiateDownload again to start another download *regardless of whether or not the first download has finished.
What you want to do is re-write InitiateDownload so that it returns a Task that indicates when the download is complete. You can then await that task and start the next download when it is done.
The easiest way to do that is to simply use the DownloadDataTaskAsync method instead of DownloadFileAsync.
You need to use your DownloadFileCompleted event to trigger the next InitiateDownload if you want them to happen serially.
Related
I'm currently working on an interface between C# and an arduino project. I'm using the serial port to send and receive messages from the arduino and so far everything looks fine.
However I came to a point, where I want to send a string to the arduino and wait for a response until I'm going to send over the next string.
I figured that I cannot simply timeout here since my RichTextField is also going to freeze and the "OK*" message cannot be read from there.
I've read about some other solutions online but since I'm very new to C# I hope that somebody can point me in the right direction.
Best regards
//Edited the post with a full code example:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.IO.Ports;
namespace interface_test
{
public partial class Form1 : Form
{
string serialDataIn;
public Form1()
{
InitializeComponent();
Enable_Console();
comboBox_baudRate.Text = "115200";
comboBox_comPort.Text = "COM3";
string[] portLists = SerialPort.GetPortNames();
comboBox_comPort.Items.AddRange(portLists);
}
private void Form1_Load(object sender, EventArgs e)
{
}
/* erase textbox contents */
private void button_clearLog_Click(object sender, EventArgs e)
{
richTextBox_receiveMsg.Text = "";
}
/* listening to the serial port */
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
serialDataIn = serialPort1.ReadExisting();
this.Invoke(new EventHandler(ShowData));
}
/* process the serial data */
public void ShowData(object sender, EventArgs e)
{
string trimmedMsg = serialDataIn;
richTextBox_receiveMsg.Text += trimmedMsg;
if (trimmedMsg.Contains("Initialization done."))
{
button_detectCEM.Enabled = false;
}
}
/* open COM-port to arduino */
private void button_openCom_Click(object sender, EventArgs e)
{
try
{
serialPort1.PortName = comboBox_comPort.Text;
serialPort1.BaudRate = Convert.ToInt32(comboBox_baudRate.Text);
serialPort1.Open();
Enable_Console();
button_detectCEM.Enabled = true;
}
catch (Exception error)
{
MessageBox.Show(error.Message);
}
}
/* close COM-port to arduino */
private void button_closeCom_Click(object sender, EventArgs e)
{
if (serialPort1.IsOpen)
{
try
{
serialPort1.Close();
Enable_Console();
}
catch (Exception error)
{
MessageBox.Show(error.Message);
}
}
}
private void button_fileBrowser_Click(object sender, EventArgs e)
{
openAndWrite();
}
public void openAndWrite()
{
/* load a .txt file */
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.InitialDirectory = "c:\\";
openFileDialog1.Filter = "TXT files (*.txt)|*.txt";
openFileDialog1.FilterIndex = 0;
openFileDialog1.RestoreDirectory = true;
string selectedFile = "";
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
selectedFile = openFileDialog1.FileName;
}
/* parse file and store it in data[] */
byte[] data;
using (FileStream fsSource = new FileStream(selectedFile, FileMode.Open, FileAccess.Read))
{
data = new byte[fsSource.Length];
int numBytesToRead = (int)fsSource.Length;
int numBytesRead = 0;
while (numBytesToRead > 0)
{
// Read may return anything from 0 to numBytesToRead.
int n = fsSource.Read(data, numBytesRead, numBytesToRead);
// Break when the end of the file is reached.
if (n == 0)
break;
numBytesRead += n;
numBytesToRead -= n;
}
numBytesToRead = data.Length;
}
for (int i = 0; i < BitConverter.ToInt32(blockLength, 0); i++)
{
data[i] = data[offset + i];
}
/* write data block */
offset = 0;
while (offset < data.Length)
{
byte[] dataString = new byte[6];
string blockMsg = "FFFFFF";
int start = offset;
offset += 6;
if (offset > data.Length) offset = data.Length;
for (int i = 0; i < 6; i++)
{
if ((start + i) < data.Length) dataString[i] = data[start + i];
}
blockMsg += BitConverter.ToString(dataString).Replace("-", string.Empty);
blockMsg += "*";
serialPort1.Write(blockMsg);
//Wait for "OK*" before next Message...
}
}
}
}
}
There's a few ways that you could solve this. My approach would be to make use of the TaskCompletionSource class and a lambda function. Here's a rough example, you'll need to make the method containing this code async, and potentially have it return a Task to await it all the way up the chain.
var tcs = new TaskCompletionSource<string>();
serialPort1.DataReceived += (s, e) =>
{
SerialPort sp = (SerialPort)s;
string newData = sp.ReadExisting();
tcs.SetResult(newData);
}
serialPort1.Write(Msg);
var dataFromPort = await tcs.Task;
I'm away from an IDE, nor do I have access to all your code, so without the wider context it's hard to know exactly what else you might need. But I've done some Arduino work in the past, and this is how I solved the problem.
EDIT :: Thinking about it, you might (test it first) run into a problem because that event isn't being unsubscribed from and the code is running in a loop. IF that turns out to be a problem, you could make use of C#'s Local Functions to handle the event subscription, like this:
var tcs = new TaskCompletionSource<string>();
void localHandler(object s, SerialDataReceivedEventArgs e)
{
serialPort1.DataReceived -= localHandler;
SerialPort sp = (SerialPort)s;
string newData = sp.ReadExisting();
tcs.SetResult(newData);
};
serialPort1.DataReceived += localHandler
serialPort1.Write(Msg);
var dataFromPort = await tcs.Task;
It might look a little strange, but it's certainly cleaner (IMO) then needing to keep track of instance variables and manually manage threads.
EDIT2 :: Tested when I was sat at a computer, if I understand your requirement correctly, this should do everything you need.
This may take you in the right direction.
It might be useful in this case to separate the data received from the display update thread. Since the data received is an event, it will be coming in on its own thread, likely from some component - in this case the serial port.
The form, and all the display objects on it, run on a single thread, owned by the form.
You can use a timer as a background thread to make the leap between what was received and what you want to do/display.
For example, one way is to use a concurrent queue and a timer to poll the status of the queue.
using System.Collections.Concurrent;
private ConcurrentQueue<string> dataIn = new ConcurrentQueue<string>();
private System.Threading.Timer timer = new System.Threading.Timer(CheckQue,null,TimeSpan.FromSeconds(1),TimeSpan.FromSeconds(1));
private void CheckQue(object state)
{
while(dataIn.TryDequeue(out var data))
{
ShowData(data);
if (data.Contains("OK*"))
{
//Do Something
//Better yet raise an event that does something
Msg += BitConverter.ToString(dataString).Replace("-", string.Empty);
Msg += "*";
serialPort1.Write(Msg);
}
}
}
Then modify your above code to add to the queue when data is received
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
dataIn.Enqueue(SerialPort1.ReadExisting());
}
And since your using a windows form, you need to make sure you don't get a cross-threaded operation when updating it, by marshaling the update onto the form's thread like so.
public static void ShowData(string data)
{
if (this.InvokeRequired)
{
var del = new MethodInvoker(() => ShowData(data));
try
{
this.Invoke(del);
return;
}
catch
{ }
}
richTextBox_receiveMsg.Text += data;
}
EDIT based on your revised question:
To modify for sending blocks of a file you could added another Que
private ConcurrentQueue<string> dataOut = new ConcurrentQueue<string>();
Then when you read the file, do whatever processing you need and put the final output in the queue and finish by sending the first block.
public void openAndWrite()
{
/* load a .txt file */
.
.
.
while (offset < data.Length)
{
.
.
blockMsg += BitConverter.ToString(dataString).Replace("-", string.Empty);
blockMsg += "*";
dataOut.Enqueue(blockMsg);
}
SendBlock();
}
Where SendBlock looks like this
private void SendBlock()
{
if(dataOut.TryDequeue(out var data))
{
serialPort1.Write(data);
}
}
Then just modify the timer handler to send blocks as needed
private void CheckQue(object state)
{
while (dataIn.TryDequeue(out var data))
{
ShowData(data);
if (data.Contains("OK*"))
{
SendBlock();
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.Xml.Linq;
using System.Diagnostics;
using System.Management;
using System.Runtime.InteropServices;
namespace DownloadFiles
{
public partial class Form1 : Form
{
Stopwatch sw = new Stopwatch();
Stopwatch stopwatch = new Stopwatch();
string filesdirectory = "Downloaded_Files";
string mainurl = "http://www.usgodae.org/ftp/outgoing/fnmoc/models/navgem_0.5/latest_data/";
List<string> parsedlinks = new List<string>();
string path_exe = Path.GetDirectoryName(Application.LocalUserAppDataPath);
List<string> results = new List<string>();
//StreamWriter w = new StreamWriter(#"e:\monitordetector.txt");
public Form1()
{
InitializeComponent();
//DetectScreenName();
label3.Text = "";
label4.Text = "";
label5.Text = "";
label7.Text = "";
button2.Enabled = false;
button3.Enabled = false;
filesdirectory = Path.Combine(path_exe, filesdirectory);
if (!Directory.Exists(filesdirectory))
{
Directory.CreateDirectory(filesdirectory);
}
else
{
if (IsDirectoryEmpty(filesdirectory) == false)
{
button3.Enabled = true;
}
}
}
public bool IsDirectoryEmpty(string path)
{
return !Directory.EnumerateFileSystemEntries(path).Any();
}
private string downloadhtml(string url)
{
backgroundWorker1.ReportProgress(0, "Downloading Main Url");
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
request.Proxy = null;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader sr = new StreamReader(response.GetResponseStream());
string html = sr.ReadToEnd();
sr.Close();
response.Close();
StreamWriter w = new StreamWriter(path_exe + "\\page.html");
w.Write(html);
w.Close();
return html;
}
int Counter = 0;
int percentage = 0;
int total = 0;
int countfiletodownload = 0;
bool processStatus = false;
private void Parseanddownloadfiles()
{
downloadhtml(mainurl);
if (bgw.CancellationPending == false)
{
backgroundWorker1.ReportProgress(0, "Parsing Links");
HtmlAgilityPack.HtmlWeb hw = new HtmlAgilityPack.HtmlWeb();
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
doc = hw.Load(path_exe + "\\page.html");
foreach (HtmlAgilityPack.HtmlNode link in doc.DocumentNode.SelectNodes("//a[#href]"))
{
string hrefValue = link.GetAttributeValue("href", string.Empty);
if (hrefValue.Contains("US"))
{
string url = "http://www.usgodae.org/ftp/outgoing/fnmoc/models/navgem_0.5/latest_data/" + hrefValue;
parsedlinks.Add(url);
if (bgw.CancellationPending == true)
return;
}
}
countfiletodownload = parsedlinks.Count;
total = parsedlinks.Count;
backgroundWorker1.ReportProgress(0, "Downloading Files");
processStatus = true;
for (int i = 0; i < parsedlinks.Count && bgw.CancellationPending == false; i++)
{
try
{
using (WebClient client = new WebClient())
{
sw.Start();
Uri uri = new Uri(parsedlinks[i]);
string filename = parsedlinks[i].Substring(71);
//client.DownloadFile(parsedlinks[i], filesdirectory + "\\" + filename);
client.DownloadFileAsync(uri, filesdirectory + "\\" + filename);
Counter += 1;
percentage = Counter * 100 / total;
string filenametoreport = filename.Substring(1);
countfiletodownload--;
backgroundWorker1.ReportProgress(percentage, filenametoreport);//countfiletodownload, filenametoreport);
}
}
catch (Exception err)
{
string error = err.ToString();
}
}
}
}
/*void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
// Calculate download speed and output it to labelSpeed.
if (label12.InvokeRequired)
{
label12.Invoke(new MethodInvoker(delegate
{
label12.Text = string.Format("{0} kb/s", (e.BytesReceived / 1024d / sw.Elapsed.TotalSeconds).ToString("0.00"));
}));
}
// Update the progressbar percentage only when the value is not the same.
if (progressBar1.InvokeRequired)
{
progressBar1.Invoke(new MethodInvoker(delegate
{
progressBar1.Value = e.ProgressPercentage;
}));
}
// Show the percentage on our label.
if (label13.InvokeRequired)
{
label13.Invoke(new MethodInvoker(delegate
{
label13.Text = e.ProgressPercentage.ToString() + "%";
}));
}
// Update the label with how much data have been downloaded so far and the total size of the file we are currently downloading
if (label14.InvokeRequired)
{
label14.Invoke(new MethodInvoker(delegate
{
label14.Text = string.Format("{0} MB's / {1} MB's",
(e.BytesReceived / 1024d / 1024d).ToString("0.00"),
(e.TotalBytesToReceive / 1024d / 1024d).ToString("0.00"));
}));
}
}*/
/*void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
// Reset the stopwatch.
sw.Reset();
if (e.Cancelled == true)
{
MessageBox.Show("Download has been canceled.");
}
else
{
//MessageBox.Show("Download completed!");
}
}*/
private void Form1_Load(object sender, EventArgs e)
{
}
BackgroundWorker bgw;
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
bgw = (BackgroundWorker)sender;
if (bgw.CancellationPending == true)
{
return;
}
else
{
Parseanddownloadfiles();
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.UserState.ToString() == "Downloading Main Url")
{
label3.Text = e.UserState.ToString();
}
if (e.UserState.ToString() == "Parsing Links")
{
label3.Text = e.UserState.ToString();
}
if (e.UserState.ToString() == "Downloading Files")
{
label7.Text = countfiletodownload.ToString();//parsedlinks.Count.ToString();
label3.Text = e.UserState.ToString();
}
if (processStatus == true)
{
if (e.UserState.ToString() != "Downloading Files")
{
label4.Text = e.UserState.ToString();
label7.Text = countfiletodownload.ToString();
progressBar1.Value = e.ProgressPercentage;
/*using (var bitmap = new Bitmap(this.Width, this.Height))
{
this.DrawToBitmap(bitmap, new Rectangle(0, 0, bitmap.Width, bitmap.Height));
bitmap.Save(#"e:\screens\ss.gif" + countscreenshots, System.Drawing.Imaging.ImageFormat.Gif);
countscreenshots += 1;
}*/
}
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
string fff = null;
}
label3.Text = "Operation Cancelled";
button1.Enabled = true;
}
private void button2_Click(object sender, EventArgs e)
{
label3.Text = "Cancelling Operation";
backgroundWorker1.CancelAsync();
button2.Enabled = false;
timer1.Stop();
stopwatch.Stop();
stopwatch.Reset();
}
private void button1_Click(object sender, EventArgs e)
{
label3.Text = "";
label4.Text = "";
label7.Text = "";
backgroundWorker1.RunWorkerAsync();
timer1.Start();
stopwatch.Start();
button1.Enabled = false;
button2.Enabled = true;
}
private void button3_Click(object sender, EventArgs e)
{
Process.Start(filesdirectory);
}
private void timer1_Tick(object sender, EventArgs e)
{
label5.Text = string.Format("{0:hh\\:mm\\:ss}", stopwatch.Elapsed);
}
}
}
for some reason the code inside the event make the whole application to not to be async. If I comment the code not to use it then it's async working fine.
I also want to use the client_DownloadProgressChanged that now is not in use to show and display the downloading infos.
Like speed time using progressBar info per current file download and info for overall progress download.
No*, you do not need to Invoke in a BackgroundWorker's ProgressChanged event. DoWork is run on a background thread but the thread that executes the code inside ProgressChanged (and the other event handlers on a backgroundworker) is done by the thread that created the backgroundworker, which should be the same as the thread that created the other ui controls and hence no Invokation required
*Having said this, pay close attention to the part where I said that BGW will run the ProgressChanged event using the thread that created the BGW. In most cases this will be the UI thread.. if you've used a thread other than the UI thread to create the BGW then YES, invokation would be required. Create the BGW on the UI thread along with all your other controls, if you want a simple life. For the rest of my advice I'll assume this is what you've done.
I wasn't able to understand exactly your problem, but remember that it's the UI thread that runs the event handler. If you send that thread off doing some long task or blocking operation as part of your efforts to update the labels in the UI then it will make the application seem hung. You must let the UI thread complete the code in the event handler as soon as possible. If it will need to access data that is from a long or blocking operation, either have the DoWork calculate the data before it raises its progress, or use another method to avoid blocking the UI thread, like async task pattern
Note that Invoke and blocking the ui are completely different things. Windows controls may only be accessed by the thread they were created with. If another thread wants to access the control, it should use Invoke to cause the ui thread to do the work instead. This is a very different thing to the notion of not jamming up your ui by using the UI thread to read 50 gigabytes from a slow server and not using something that lets it quickly get back to its job of processing window messages and keeping the app responsive
I used Accord.net (AForge) for connect to the webcam and record video
But stored videos is slow motion ...
this my project :
using AForge.Video;
using AForge.Video.DirectShow;
using AForge.Video.FFMPEG;
using System;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;
namespace CaptureWebcam
{
public partial class Form1 : Form
{
private VideoCaptureDeviceForm captureDevice;
private string path = Path.GetDirectoryName(Application.ExecutablePath) + #"\Videos\";
private FilterInfoCollection videoDevice;
private VideoCaptureDevice videoSource;
private VideoFileWriter FileWriter = new VideoFileWriter();
private Bitmap video;
bool isRecord = false;
public Form1()
{
InitializeComponent();
}
private void videoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
video = (Bitmap)eventArgs.Frame.Clone();
pictureBox1.Image = (Bitmap)eventArgs.Frame.Clone();
}
private void btnStartCam_Click(object sender, EventArgs e)
{
videoDevice = new FilterInfoCollection(FilterCategory.VideoInputDevice);
captureDevice = new VideoCaptureDeviceForm();
if (captureDevice.ShowDialog(this) == DialogResult.OK)
{
videoSource = captureDevice.VideoDevice;
videoSource = captureDevice.VideoDevice;
videoSource.NewFrame += new NewFrameEventHandler(videoSource_NewFrame);
videoSource.Start();
timer1.Enabled = true;
}
//videoSource.DisplayPropertyPage(IntPtr.Zero);
}
private Thread workerThread = null;
private bool stopProcess = false;
private void recordLiveCam()
{
if (!stopProcess)
{
while (isRecord)
{
FileWriter.WriteVideoFrame(video);
}
FileWriter.Close();
}
else
{
workerThread.Abort();
}
}
private void btnRecord_Click(object sender, EventArgs e)
{
//try
//{
isRecord = true;
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
int h = captureDevice.VideoDevice.VideoResolution.FrameSize.Height;
int w = captureDevice.VideoDevice.VideoResolution.FrameSize.Width;
FileWriter.Open(path + "recorded at " + DateTime.Now.ToString("HH-mm-ss") + ".mp4", w, h, 25, VideoCodec.MPEG4);
stopProcess = false;
workerThread = new Thread(new ThreadStart(recordLiveCam));
workerThread.Start();
//}
//catch (Exception ex)
//{
// MessageBox.Show(ex.Message);
//}
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void btnStopRecord_Click(object sender, EventArgs e)
{
stopProcess = true;
isRecord = false;
FileWriter.Close();
workerThread.Abort();
video = null;
}
private void btnStopCam_Click(object sender, EventArgs e)
{
try
{
if (videoSource.IsRunning)
{
videoSource.SignalToStop();
videoSource.Stop();
pictureBox1.Image = null;
pictureBox1.Invalidate();
if (FileWriter.IsOpen)
{
FileWriter.Close();
video = null;
}
}
else
return;
}
catch
{
videoSource.Stop();
FileWriter.Close();
video = null;
}
}
float fts = 0.0f;
private void timer1_Tick(object sender, EventArgs e)
{
fts = videoSource.FramesReceived;
label1.Text = "Frame Rate : " + fts.ToString("F2") + " fps";
}
}
}
And when click the btnStopRecord following error :
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
"Slow motion" can have multiple reasons. First you need to know if the actual framerate, at which the NewFrames get produces is so slow (how many HZ then?) or if the CPU is too busy. Or the graphics card maybe.
For everything DirectShow related I strongly suggest using GraphEdit and AMCap to see what the real capabilites of the device are. AForge was prone to oversampling and I would not wonder if it's similar in Accord.
Also, you can always rely on good old processexplorer to see if the load (if any) is caused by SYSTEM or INTERRUPRS or is actually produced by your executable.
Another thing that happened to me was PictureBox. VideoSourcePlayer is far better and I wound up doing my own blitting.
It is worth mentioning these three optimizations:
DO NOT USE Bitmap.GetPixel!
Use videoSource.DisplayPinPropertyPage(IntPtr.Zero)
Check the color space of the videostream
Bitmap.GetPixel
The problem with GetPixel is, that it's really slow as it has to unmanage/manage a lot for every single pixel. It is fine as along as you only get a small a mount of calls and use it on a loaded bitmap. It is definately the wrong approach if you want to use it on all pixelx every couple of milliseconds. The CPU load from this phenomenon is associated with your application and will show as such in process explorer.
In the end I wrote my own pixel routines starting here: Work with bitmap faster with Csharp
If you just want a kernel or something more usual than I needed, implement a
custom Filter.
VideoSource.DisplayPinPropertyPage
If you use the AForge-buit-in mechanism for choosing the resolution and initiating the video stream, you cannot set the Framerate (I do not consider this a bug in AForge). So AForge always chooses the highest framerate. If you circumvent videoSource.VideoCapabilities and directly show the native device configuration dialog (it's formally called "Video Capture Pin Properties Dialog"). There you can set all the parameters manually and the dialog will fill in the appropriate framerate. This way, you will get the "real" (hardware) refresh rate in your callbacks. The CPU load of this oversampling happens in the SYSTEM process.
Color space conversion
In my case, the camera supports three output formats: YUY2, MJPG and RGB24. Depending on what format you use, the image is transformed by AForge into RGB (I think actually it's ARGB32). The rendered DirectShow graphs for all three formats are different and RGB obviously uses much less CPU than the others, as the color conversion is quite trivial. The load from this conversion also shows up in the SYSTEM process and not in your applications exe.
You can try to put some timers to understand which operation is slowing down the process.(Stopwatch class will be perfect)
I don't know the format of your Frame but usually the conversion to Bitmap is the bottle neck, AForge is quite fast. Furthermore you can pass a time stamp to WriteVideoFrame method, then the frame rate indicated in Open is just for replaying the video. Aforge will ordinate the frames in the right order with the right time.
That's according to my experience. Hope it can help.
Try this
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using AForge;
using AForge.Video.DirectShow;
using Accord.Video.FFMPEG;
using System.IO;
using AForge.Video;
using System.Threading;
namespace New_Project_2
{
public partial class Form1 : Form
{
private VideoCaptureDeviceForm captureDevice;
private FilterInfoCollection videoDevice;
private VideoCaptureDevice videoSource;
private VideoFileWriter FileWriter = new VideoFileWriter();
bool isRecord = false;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void videoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
try
{
if (isRecord == true)
{
pictureBox1.Image = (Bitmap)eventArgs.Frame.Clone();
FileWriter.WriteVideoFrame((Bitmap)eventArgs.Frame.Clone());
}
}
catch (Exception)
{
throw;
}
}
private void button1_Click(object sender, EventArgs e)
{
videoDevice = new FilterInfoCollection(FilterCategory.VideoInputDevice);
captureDevice = new VideoCaptureDeviceForm();
if (captureDevice.ShowDialog(this) == DialogResult.OK)
{
isRecord = true;
int h = captureDevice.VideoDevice.VideoResolution.FrameSize.Height;
int w = captureDevice.VideoDevice.VideoResolution.FrameSize.Width;
FileWriter.Open("d:\\" + "recorded at " + DateTime.Now.ToString("HH-mm-ss") + ".mp4", w, h, 25, VideoCodec.MPEG4);
videoSource = captureDevice.VideoDevice;
videoSource.NewFrame += new NewFrameEventHandler(videoSource_NewFrame);
videoSource.Start();
}
//videoSource.DisplayPropertyPage(IntPtr.Zero)
}
private void button2_Click(object sender, EventArgs e)
{
}
private void button3_Click(object sender, EventArgs e)
{
isRecord = false;
FileWriter.Close();
}
private void button4_Click(object sender, EventArgs e)
{
}
}
}
You can use emguCV to record videos.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Emgu;
using Emgu.CV;
using Emgu.CV.Structure;
namespace Load_Images
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
double TotalFrame;
double Fps;
int captureFrameNo;
VideoCapture capture;
VideoWriter writer;
bool isCapturing = false;
private void button1_Click(object sender, EventArgs e)
{
if (isCapturing == false)
{
capture = new VideoCapture(0);
TotalFrame = capture.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameCount);
Fps = capture.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.Fps);
isCapturing = true;
int fourcc = fourcc = VideoWriter.Fourcc('H', '2', '6', '4');
capture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameWidth, 2048);
capture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameHeight, 1024);
//int fourcc = Convert.ToInt32(capture.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.FourCC));
int width = Convert.ToInt32(capture.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameWidth));
int height = Convert.ToInt32(capture.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameHeight));
string destin_path = "D:\\out.mp4";
writer = new VideoWriter(destin_path, fourcc, Fps, new Size(width, height), true);
capture.ImageGrabbed += Capture_ImageGrabbed;
capture.Start();
}
}
private void Capture_ImageGrabbed(object sender, EventArgs e)
{
try
{
if (isCapturing == true)
{
if (capture == null)
{
return;
}
Mat m = new Mat();
capture.Retrieve(m);
writer.Write(m);
pictureBox1.Image = m.ToImage<Bgr, byte>().Bitmap;
}
}
catch (Exception)
{
throw;
}
}
private void button2_Click(object sender, EventArgs e)
{
if (isCapturing == true) {
capture.Stop();
isCapturing = false;
}
}
private void button3_Click(object sender, EventArgs e)
{
if (capture != null) {
capture.Pause();
}
}
private void button4_Click(object sender, EventArgs e)
{
if (writer.IsOpened)
{
isCapturing = false;
writer.Dispose();
}
MessageBox.Show("Completed");
}
private void Form2_Load(object sender, EventArgs e)
{
}
}
}
if you write file on newFrame handler Func will cause fastplaqy/slow record on some codec VP8-VP9 etc. (mpeg4 not effect) but if you start different timer on some time to write you solve your issues
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.IO;
using HtmlAgilityPack;
using mshtml;
using System.Text.RegularExpressions;
namespace Extract_Images
{
public partial class Form1 : Form
{
private string[] linkstoextract;
private int numberoflinks;
private int currentLinkNumber = 0;
private string mainlink;
private WebClient client;
private WebBrowser webBrowser1;
private string htmlCode;
private bool pagesorimages = false;
public Form1()
{
InitializeComponent();
webBrowser1 = new WebBrowser();
webBrowser1.ScriptErrorsSuppressed = true;
webBrowser1.DocumentCompleted += webBrowser1_DocumentCompleted;
label1.Text = "Number of links: ";
mainlink = "http://www.test.com/";
numberoflinks = 13;
backgroundWorker1.RunWorkerAsync();
}
private void ProcessNextLink()
{
if (currentLinkNumber < numberoflinks)
{
currentLinkNumber++;
string linktonav = mainlink + "index"+currentLinkNumber.ToString() + ".html";
pagesorimages = false;
backgroundWorker1.ReportProgress(0,currentLinkNumber);
webBrowser1.Navigate(linktonav);
}
}
int count = 0;
void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
mshtml.HTMLDocument objHtmlDoc = (mshtml.HTMLDocument)webBrowser1.Document.DomDocument;
string pageSource = objHtmlDoc.documentElement.innerHTML;
List<string> links = new List<string>();
string[] hrefs = this.webBrowser1.Document.Links.Cast<HtmlElement>()
.Select(a => a.GetAttribute("href")).Where(h => h.Contains(".jpg")).ToArray();
foreach(string a in hrefs)
{
using (WebClient client = new WebClient())
{
client.DownloadFile(a, #"C:\Images\file" + count + ".jpg");
}
count ++;
pagesorimages = true;
backgroundWorker1.ReportProgress(0, count);
}
//ProcessNextLink();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
ProcessNextLink();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (pagesorimages == false)
{
label1.Text = e.UserState.ToString();
}
if (pagesorimages == true)
{
label2.Text = e.UserState.ToString();
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
}
}
The exception is on the second ReportProgress:
backgroundWorker1.ReportProgress(0, count);
This operation has already had OperationCompleted called on it and further calls are illegal
What i want to do is to report first the current page number in this case it's 1 to label1.
And then to report the number of downloaded images in this page to label2.
Then to move to the next page with the method ProcessNextLink(); and again report the page number it should be 2 and then to report the number of images downloaded in page 2.
But i'm getting this exception already on the first page.
It was working fine without the backgroundworker in the event webBrowser1_DocumentCompleted i called ProcessNextLink(); in the bottom and it was working fine. But with the backgroundworker it's not working.
Im making a program, which is working nicely, except i started to create an update system and somehow its not working. The splash screen checks for updates, using a webClient, which now seems to error as well. I set it up so the splash screen downloads a text file. If the versions mismatch, a separate form starts and uses a webClient to download the updater. I've created this form twice. It has 1 progressBar, 1 webClient, an image background and 1 label. The update is supposed to download, show its progress on the bar, and then open the updater and close the main program. The first time i created the form, i ran it and it would freeze and not download the file. After i stopped debugging, the design window of visual c# express 2010 would crash and I had to restart vc#. The window would show all of the items i had added except the webClient. So i tried debugging again and i got this message:
Error 1 Invalid Resx file. Type in the data at line 138, position 5, cannot be loaded because it threw the following exception during construction: No data is available for encoding 1252. C:\Users\DjLyriz\documents\visual studio 2010\Projects\TubeRip\TubeRip\updates.resx TubeRip
Error 2 TargetInvocationException: Type in the data at line 138, position 5, cannot be loaded because it threw the following exception during construction: No data is available for encoding 1252.
at System.Resources.ResXResourceReader.ParseXml(XmlTextReader reader)
at System.Resources.ResXResourceReader.EnsureResData()
at System.Resources.ResXResourceReader.GetEnumerator()
at Microsoft.Build.Tasks.ProcessResourceFiles.ReadResources(ReaderInfo readerInfo, IResourceReader reader, String fileName)
at Microsoft.Build.Tasks.ProcessResourceFiles.ReadResources(String filename, Boolean shouldUseSourcePath)
at Microsoft.Build.Tasks.ProcessResourceFiles.ProcessFile(String inFile, String outFileOrDir)
XmlException: Type in the data at line 138, position 5, cannot be loaded because it threw the following exception during construction: No data is available for encoding 1252. Line 138, position 5.
NotSupportedException: No data is available for encoding 1252.
at System.Text.BaseCodePageEncoding.LoadCodePageTables()
at System.Text.BaseCodePageEncoding..ctor(Int32 codepage, Int32 dataCodePage)
at System.Text.SBCSCodePageEncoding..ctor(SerializationInfo info, StreamingContext context)
C:\Users\DjLyriz\documents\visual studio 2010\Projects\TubeRip\TubeRip\updates.resx TubeRip
So i deleted the webClient from the form and retried. I got the same error a second time. So i googled the first error, and found one response on hackforums with no fixes. And now im entirely lost. All my webClients seem to be doing this now and i have no idea why.
Here's the code from my splash screen:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
namespace TubeRip
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
timer1.Start();
getupdates();
}
private void timer1_Tick(object sender, EventArgs e)
{
timer2.Start();
}
private void timer2_Tick_1(object sender, EventArgs e)
{
timer1.Stop();
label1.Text = "Loading Core Components..." + " " + progressBar1.Value.ToString() + "%";
if (progressBar1.Value < 20)
{
progressBar1.Value += 1;
}
else
{
timer3.Start();
}
}
private void end()
{
timer7.Stop();
mainpage home = new mainpage();
home.Show();
this.Dispose(false);
}
private void timer3_Tick(object sender, EventArgs e)
{
timer2.Stop();
label1.Text = "Loading Encryption Algorithyms..." + " " + progressBar1.Value.ToString() + "%";
if (progressBar1.Value < 40)
{
progressBar1.Value += 1;
}
else
{
timer4.Start();
}
}
private void timer4_Tick(object sender, EventArgs e)
{
timer3.Stop();
label1.Text = "Enabling Download Services..." + " " + progressBar1.Value.ToString() + "%";
if (progressBar1.Value < 60)
{
progressBar1.Value += 1;
}
else
{
timer5.Start();
}
}
private void timer5_Tick(object sender, EventArgs e)
{
timer4.Stop();
label1.Text = "Disabling Youtube's Download Protection..." + " " + progressBar1.Value.ToString() + "%";
if (progressBar1.Value < 80)
{
progressBar1.Value += 1;
}
else
{
timer6.Start();
}
}
private void timer6_Tick(object sender, EventArgs e)
{
timer5.Stop();
label1.Text = "Drawing GUI" + " " + progressBar1.Value.ToString() + "%";
if (progressBar1.Value < 90)
{
progressBar1.Value += 1;
}
else
{
timer7.Start();
}
}
private void timer7_Tick(object sender, EventArgs e)
{
timer6.Stop();
label1.Text = "Creating The first Humanlike Robot..." + " " + progressBar1.Value.ToString() + "%";
if (progressBar1.Value < 100)
{
progressBar1.Value += 1;
}
else
{
end();
}
}
private void getupdates()
{
try
{
string updateurl = "http://dl.dropbox.com/u/22054429/TubeRip_version.txt";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(updateurl);
WebResponse response = request.GetResponse();
System.IO.StreamReader sr = new System.IO.StreamReader(response.GetResponseStream(), System.Text.Encoding.GetEncoding("windows-1252"));
string update = sr.ReadToEnd();
int build = Convert.ToInt32(update);
int thisbuild = 2;
if (build > thisbuild)
{
label2.Visible = true;
TubeRip.Properties.Settings.Default.UpdateAvail = true;
}
else
{
label2.Visible = false;
TubeRip.Properties.Settings.Default.UpdateAvail = false;
}
}
catch
{
MessageBox.Show("Unable to connect to update server! Please try again later.");
}
}
}
}
And here's the code from my updater:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Net;
namespace TubeRip
{
public partial class updates : Form
{
public updates()
{
InitializeComponent();
}
private void updates_Load(object sender, EventArgs e)
{
Uri update = new Uri("http://dl.dropbox.com/u/22054429/TubeRip_Installer.exe");
webClient1.DownloadFileAsync(update, "update.exe");
webClient1.DownloadProgressChanged +=new DownloadProgressChangedEventHandler( webClient1_DownloadProgressChanged);
webClient1.DownloadFileCompleted += new AsyncCompletedEventHandler( webClient1_DownloadFileCompleted);
}
void webClient1_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
Process.Start("update.exe");
Environment.Exit(0);
}
void webClient1_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
label1.Text = "Downloading Updates Please Wait... " + e.ProgressPercentage.ToString() + "%";
}
}
}
I did not find any result to my specific error, but i did find a way to work around it. Instead of using the default encoding for a webClient, i set the encoding myself to UTF-8, which does not throw this error. The modified code is what ended up working.
WebClient client = new WebClient();
client.Encoding = System.Text.Encoding.UTF8;
Uri update = new Uri(uri);
client.DownloadFileAsync(update, "update.exe");
client.DownloadProgressChanged +=new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
Otherwise, i have found no fix to this issue, and no reason for this issue to appear.