I have been trying to setup a MJPEG stream in ASP.NET. I want to retrieve a MJEPG stream from a URL, and send every frame that I get to every connected client. Examples that I have been able to find only read from a set file, instead of a continues stream from URL, and send the entire file through MultiStreamContent. Since I retrieve frame-by-frame, I cannot do this.
I would like to know if it is possible to do what I want with ASP.NET MVC. I'm currently using AForge video to retrieve the MJPEG stream from a link.
My code for the controller class:
using System.Net.Http;
using System.Web.Http;
using AForge.Video;
namespace VideoPrototypeMVC.Controllers
{
public class CameraController : ApiController
{
int framecounter = 0;
MJPEGStream stream = new MJPEGStream();
[HttpGet]
public void GetVideoContent()
{
stream.Source = #"http://127.0.0.1:5002/stream";
stream.NewFrame += new NewFrameEventHandler(showFrame);
stream.Start();
MultipartContent content = new MultipartContent();
while (stream.IsRunning)
{
//Continues streaming should be here?
}
}
//Can be used to display of a frame is available
private void showFrame(object sender, NewFrameEventArgs eventArgs)
{
framecounter++;
System.Diagnostics.Debug.WriteLine("New frame event: " + framecounter);
}
//Should be called at the end of the stream
private void stopStream(object sender, ReasonToFinishPlaying reason)
{
System.Diagnostics.Debug.WriteLine("Stop stream");
stream.Stop();
framecounter = 0;
}
}
}
This code is not final, but I just need to get the continues streaming down. I have found examples that use Socket servers, but I would like to stick to MVC since it allows me to set up the rest of the server easier.
To make sure other people will manage with this as well. I managed to combine what #Evk (Thank you once again) said, together with information I found here: creating my own MJPEG stream.
DO NOTE: The code below is just prototype/proof-of-concept! When I run this my processor shoots to 100% because of the endless while loop in StartStream. Will work on making this more event based but I thought the code below was more easely explained.
using System;
using System.IO;
using System.Net;
using System.Web;
using System.Net.Http;
using System.Web.Http;
using AForge.Video;
using System.Drawing;
using System.Text;
using System.Drawing.Imaging;
using System.Threading;
namespace VideoPrototypeMVC.Controllers
{
public class CameraController : ApiController
{
private MJPEGStream mjpegStream = new MJPEGStream();
private bool frameAvailable = false;
private Bitmap frame = null;
private string BOUNDARY = "frame";
/// <summary>
/// Initializer for the MJPEGstream
/// </summary>
CameraController()
{
mjpegStream.Source = #"{{INSERT STREAM URL}}";
mjpegStream.NewFrame += new NewFrameEventHandler(showFrameEvent);
}
[HttpGet]
public HttpResponseMessage GetVideoContent()
{
mjpegStream.Start();
var response = Request.CreateResponse();
response.Content = new PushStreamContent((Action<Stream, HttpContent, TransportContext>)StartStream);
response.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("multipart/x-mixed-replace; boundary=" + BOUNDARY);
return response;
}
/// <summary>
/// Craete an appropriate header.
/// </summary>
/// <param name="length"></param>
/// <returns></returns>
private byte[] CreateHeader(int length)
{
string header =
"--" + BOUNDARY + "\r\n" +
"Content-Type:image/jpeg\r\n" +
"Content-Length:" + length + "\r\n\r\n";
return Encoding.ASCII.GetBytes(header);
}
public byte[] CreateFooter()
{
return Encoding.ASCII.GetBytes("\r\n");
}
/// <summary>
/// Write the given frame to the stream
/// </summary>
/// <param name="stream">Stream</param>
/// <param name="frame">Bitmap format frame</param>
private void WriteFrame(Stream stream, Bitmap frame)
{
// prepare image data
byte[] imageData = null;
// this is to make sure memory stream is disposed after using
using (MemoryStream ms = new MemoryStream())
{
frame.Save(ms, ImageFormat.Jpeg);
imageData = ms.ToArray();
}
// prepare header
byte[] header = CreateHeader(imageData.Length);
// prepare footer
byte[] footer = CreateFooter();
// Start writing data
stream.Write(header, 0, header.Length);
stream.Write(imageData, 0, imageData.Length);
stream.Write(footer, 0, footer.Length);
}
/// <summary>
/// While the MJPEGStream is running and clients are connected,
/// continue sending frames.
/// </summary>
/// <param name="stream">Stream to write to.</param>
/// <param name="httpContent">The content information</param>
/// <param name="transportContext"></param>
private void StartStream(Stream stream, HttpContent httpContent, TransportContext transportContext)
{
while (mjpegStream.IsRunning && HttpContext.Current.Response.IsClientConnected)
{
if (frameAvailable)
{
try
{
WriteFrame(stream, frame);
frameAvailable = false;
} catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e);
}
}else
{
Thread.Sleep(30);
}
}
stopStream();
}
/// <summary>
/// This event is thrown when a new frame is detected by the MJPEGStream
/// </summary>
/// <param name="sender">Object that is sending the event</param>
/// <param name="eventArgs">Data from the event, including the frame</param>
private void showFrameEvent(object sender, NewFrameEventArgs eventArgs)
{
frame = new Bitmap(eventArgs.Frame);
frameAvailable = true;
}
/// <summary>
/// Stop the stream.
/// </summary>
private void stopStream()
{
System.Diagnostics.Debug.WriteLine("Stop stream");
mjpegStream.Stop();
}
}
}
Great answer from Arastelion however I noticed that if you leave the application then there is still a request going on in the background which can be a resource hog
popping in stream.FlushAsync();
stream.Close();
stream.Dispose(); after the stopStream seems to resolve that.
using System;
using System.IO;
using System.Net;
using System.Web;
using System.Net.Http;
using System.Web.Http;
using AForge.Video;
using System.Drawing;
using System.Text;
using System.Drawing.Imaging;
using System.Threading;
namespace VideoPrototypeMVC.Controllers
{
public class CameraController : ApiController
{
private MJPEGStream mjpegStream = new MJPEGStream();
private bool frameAvailable = false;
private Bitmap frame = null;
private string BOUNDARY = "frame";
/// <summary>
/// Initializer for the MJPEGstream
/// </summary>
CameraController()
{
mjpegStream.Source = #"{{INSERT STREAM URL}}";
mjpegStream.NewFrame += new NewFrameEventHandler(showFrameEvent);
}
[HttpGet]
public HttpResponseMessage GetVideoContent()
{
mjpegStream.Start();
var response = Request.CreateResponse();
response.Content = new PushStreamContent((Action<Stream, HttpContent, TransportContext>)StartStream);
response.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("multipart/x-mixed-replace; boundary=" + BOUNDARY);
return response;
}
/// <summary>
/// Craete an appropriate header.
/// </summary>
/// <param name="length"></param>
/// <returns></returns>
private byte[] CreateHeader(int length)
{
string header =
"--" + BOUNDARY + "\r\n" +
"Content-Type:image/jpeg\r\n" +
"Content-Length:" + length + "\r\n\r\n";
return Encoding.ASCII.GetBytes(header);
}
public byte[] CreateFooter()
{
return Encoding.ASCII.GetBytes("\r\n");
}
/// <summary>
/// Write the given frame to the stream
/// </summary>
/// <param name="stream">Stream</param>
/// <param name="frame">Bitmap format frame</param>
private void WriteFrame(Stream stream, Bitmap frame)
{
// prepare image data
byte[] imageData = null;
// this is to make sure memory stream is disposed after using
using (MemoryStream ms = new MemoryStream())
{
frame.Save(ms, ImageFormat.Jpeg);
imageData = ms.ToArray();
}
// prepare header
byte[] header = CreateHeader(imageData.Length);
// prepare footer
byte[] footer = CreateFooter();
// Start writing data
stream.Write(header, 0, header.Length);
stream.Write(imageData, 0, imageData.Length);
stream.Write(footer, 0, footer.Length);
}
/// <summary>
/// While the MJPEGStream is running and clients are connected,
/// continue sending frames.
/// </summary>
/// <param name="stream">Stream to write to.</param>
/// <param name="httpContent">The content information</param>
/// <param name="transportContext"></param>
private void StartStream(Stream stream, HttpContent httpContent, TransportContext transportContext)
{
while (mjpegStream.IsRunning && HttpContext.Current.Response.IsClientConnected)
{
if (frameAvailable)
{
try
{
WriteFrame(stream, frame);
frameAvailable = false;
} catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e);
}
}else
{
Thread.Sleep(30);
}
}
stopStream();
stream.FlushAsync();
stream.Close();
stream.Dispose();
}
/// <summary>
/// This event is thrown when a new frame is detected by the MJPEGStream
/// </summary>
/// <param name="sender">Object that is sending the event</param>
/// <param name="eventArgs">Data from the event, including the frame</param>
private void showFrameEvent(object sender, NewFrameEventArgs eventArgs)
{
frame = new Bitmap(eventArgs.Frame);
frameAvailable = true;
}
/// <summary>
/// Stop the stream.
/// </summary>
private void stopStream()
{
System.Diagnostics.Debug.WriteLine("Stop stream");
mjpegStream.Stop();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
stopStream();
}
base.Dispose(disposing);
}
}
}
Related
I found a project in Unity 5 where python can be run in Unity, and i want to add libraries to it. The trouble is, i can't seem to find where the python libraries from this script. To me, it looks like they're being generated out of thin air. Can i have a pointer as to where this might be coming from? I tried adding it to python path, and its not seeming to work.
using System;
using System.IO;
using System.Linq;
using UnityEngine;
using System.Text;
using System.Collections;
using IronPython.Hosting;
using IronPython.Modules;
using System.Collections.Generic;
using Microsoft.Scripting.Hosting;
/// <summary>
/// Interpreter for IronPython.
/// </summary>
public class Interpreter
{
/// <summary>
/// The scope.
/// </summary>
private ScriptScope Scope;
/// <summary>
/// The engine.
/// </summary>
private ScriptEngine Engine;
/// <summary>
/// The source.
/// </summary>
private ScriptSource Source;
/// <summary>
/// The compiled.
/// </summary>
private CompiledCode Compiled;
/// <summary>
/// The operation.
/// </summary>
private ObjectOperations Operation;
/// <summary>
/// The python class.
/// </summary>
private object PythonClass;
/// <summary>
/// Initializes a new instance of the <see cref="Interpreter"/> class.
/// </summary>
public Interpreter()
{
Engine = Python.CreateEngine();
Scope = Engine.CreateScope();
SetLibrary();
}
/// <summary>
/// Initializes a new instance of the <see cref="Interpreter"/> class.
/// </summary>
/// <param name="source">Source.</param>
public Interpreter(string src) : this()
{
Compile(src);
}
/// <summary>
/// Compile the specified src.
/// </summary>
/// <param name="src">Source.</param>
public string Compile(string src, Microsoft.Scripting.SourceCodeKind CodeKind =
Microsoft.Scripting.SourceCodeKind.SingleStatement)
{
if(src == string.Empty)
return string.Empty;
LoadRuntime();
Source = CodeKind == Microsoft.Scripting.SourceCodeKind.SingleStatement ?
Engine.CreateScriptSourceFromString(src, CodeKind) :
Engine.CreateScriptSourceFromFile(src);
ErrorHandle errors = new ErrorHandle();
MemoryStream stream = new MemoryStream();
//Set IO Ouput of execution
Engine.Runtime.IO.SetOutput(stream, new StreamWriter(stream));
Compiled = Source.Compile(errors);
Operation = Engine.CreateOperations();
try {
Compiled.Execute(Scope);
return FormatOutput(ReadFromStream(stream));
} catch(Exception ex) {
return Engine.GetService<ExceptionOperations>().FormatException(ex);
}
}
/// <summary>
/// Formats the output of execution
/// </summary>
/// <returns>The output.</returns>
/// <param name="output">Output.</param>
private string FormatOutput(string output)
{
return string.IsNullOrEmpty(output) ? string.Empty
: string.Join("\n", output.Remove(output.Length-1)
.Split('\n')
.Reverse().ToArray());
}
/// <summary>
/// Reads MemoryStream.
/// </summary>
/// <returns>The from stream.</returns>
/// <param name="ms">Ms.</param>
private string ReadFromStream(MemoryStream ms) {
int length = (int)ms.Length;
Byte[] bytes = new Byte[ms.Length];
ms.Seek(0, SeekOrigin.Begin);
ms.Read(bytes, 0, length);
return Encoding.GetEncoding("utf-8").GetString(bytes, 0, length);
}
/// <summary>
/// Set sys paths
/// </summary>
private void SetLibrary()
{
if(PythonBase.SysPath.Count > 0) {
ICollection<string> SysPath = Engine.GetSearchPaths();
foreach(string path in PythonBase.SysPath)
SysPath.Add(path);
Engine.SetSearchPaths(SysPath);
}
}
/// <summary>
/// Load runtime Assemblies of Unity3D
/// </summary>
private void LoadRuntime()
{
Engine.Runtime.LoadAssembly(typeof(GameObject).Assembly);
}
public void AddRuntime<T>()
{
Engine.Runtime.LoadAssembly(typeof(T).Assembly);
}
public void AddRuntime(Type type)
{
Engine.Runtime.LoadAssembly(type.Assembly);
}
/// <summary>
/// Gets the variable or class
/// </summary>
/// <returns>The variable.</returns>
/// <param name="name">Name.</param>
public object GetVariable(string name)
{
return Operation.Invoke(Scope.GetVariable(name));
}
/// <summary>
/// Calls the method.
/// </summary>
/// <param name="name">Name.</param>
public void InvokeMethod(object nameClass, string Method, params object[] parameters)
{
object output = new object();
if(Operation.TryGetMember(nameClass, Method, out output)) {
object Func = Operation.GetMember(nameClass, Method);
Operation.Invoke(Func, parameters);
}
}
}
So I have two laptops connected to the same wifi network with one running a very simple server and the other a very simple client connecting to it. When I run both the server and the client on one laptop they connect without a problem, but when running one on each laptop the client cannot connect to the server.
The code for the server is this:
using System;
using System.Net;
using System.Net.Sockets;
namespace Utils
{
/// <summary>
/// A base server which handles listening for client connections and has simple API to communicate back and forth
/// </summary>
public class BaseServer
{
#region Properties and Fields
/// <summary>
/// The listener we can use to detect incoming connections from clients to the server
/// </summary>
private TcpListener Listener { get; set; }
/// <summary>
/// Our interface to the single client we are supporting for now
/// </summary>
public Comms ClientComms { get; private set; }
/// <summary>
/// Determines whether we have clients connected
/// </summary>
public bool Connections { get; private set; }
#endregion
public BaseServer()
{
Listener = new TcpListener(IPAddress.Any, 1490);
Listener.Start();
ListenForNewClient();
}
/// <summary>
/// Starts an asynchronous check for new connections
/// </summary>
private void ListenForNewClient()
{
Listener.BeginAcceptTcpClient(AcceptClient, null);
}
/// <summary>
/// Callback for when a new client connects to the server
/// </summary>
/// <param name="asyncResult"></param>
protected virtual void AcceptClient(IAsyncResult asyncResult)
{
ClientComms = new Comms(Listener.EndAcceptTcpClient(asyncResult));
ClientComms.OnDataReceived += ProcessMessage;
ListenForNewClient();
}
#region Message Callbacks
/// <summary>
/// A function which is called when the Client sends a message to the server.
/// Override to perform custom message handling
/// </summary>
/// <param name="data"></param>
protected virtual void ProcessMessage(byte[] data) { }
#endregion
}
}
And the code for the client is this:
using System;
using System.Net.Sockets;
namespace Utils
{
/// <summary>
/// A base client class which connects and communicates with a remote server
/// </summary>
public class BaseClient
{
#region Properties and Fields
/// <summary>
/// The interface to the server
/// </summary>
public Comms ServerComms { get; private set; }
#endregion
public BaseClient(string ipAddress, int portNumber = 1490)
{
// Attempt to connect
try
{
ServerComms = new Comms(new TcpClient(ipAddress, portNumber));
ServerComms.OnDataReceived += OnMessageReceived;
ServerComms.OnDisconnect += OnServerDisconnect;
}
catch (Exception e)
{
Console.WriteLine("Connection failed");
}
}
#region Callbacks
/// <summary>
/// A function which is called when this client receives a message.
/// Override to perform behaviour when custom messages arrive.
/// </summary>
/// <param name="data"></param>
protected virtual void OnMessageReceived(byte[] data) { }
/// <summary>
/// A function called when this client can no longer communicate to the server it is connected to
/// </summary>
protected virtual void OnServerDisconnect() { }
#endregion
}
}
The server is started from the main loop like this:
using System;
namespace BuildServer
{
class Program
{
static void Main(string[] args)
{
BaseServer server = new BaseServer();
while (true)
{
}
}
}
}
and the Client is started like this:
using System;
using Utils;
namespace BuildServerClient
{
class Program
{
static void Main(string[] args)
{
BaseClient client = new BaseClient();
while (true)
{
Console.WriteLine("Ready");
string message = Console.ReadLine();
client.ServerComms.Send(message);
}
}
}
}
One final class is a Comms class which is really a wrapper around the TCPClient and not really used currently, but I am adding it for the same of completeness.
using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using static Utils.Delegates;
namespace Utils
{
/// <summary>
/// An interface to a client.
/// Hides the nuts and bolts and provides a public interface of just data input and output from a data sender/receiver.
/// </summary>
public class Comms
{
#region Properties and Fields
private TcpClient Client { get; set; }
private MemoryStream ReadStream { get; set; }
private MemoryStream WriteStream { get; set; }
private BinaryReader Reader { get; set; }
private BinaryWriter Writer { get; set; }
/// <summary>
/// Useful buffer for reading packeted messages from the server
/// </summary>
private byte[] ReadBuffer { get; set; }
/// <summary>
/// An event that is fired when this Comms receives a message
/// </summary>
public event OnDataReceived OnDataReceived;
/// <summary>
/// An event that is fired when this Comms can no longer communicate with the client sending it messages
/// </summary>
public event OnDisconnect OnDisconnect;
#endregion
public Comms(TcpClient client)
{
Client = client;
ReadStream = new MemoryStream();
WriteStream = new MemoryStream();
Reader = new BinaryReader(ReadStream);
Writer = new BinaryWriter(WriteStream);
ReadBuffer = new byte[2048];
Client.NoDelay = true;
StartListening();
}
#region Data Sending Functions
/// <summary>
/// Convert a string to a byte array and then send to our client
/// </summary>
/// <param name="client"></param>
/// <param name="str"></param>
public void Send(string str)
{
SendByteArray(Encoding.UTF8.GetBytes(str));
}
/// <summary>
/// Send a byte array to our client
/// </summary>
/// <param name="client"></param>
/// <param name="bytes"></param>
protected void SendByteArray(byte[] bytes)
{
Writer.Write(bytes);
int bytesWritten = (int)WriteStream.Position;
byte[] result = new byte[bytesWritten];
WriteStream.Position = 0;
WriteStream.Read(result, 0, bytesWritten);
WriteStream.Position = 0;
Client.GetStream().BeginWrite(result, 0, result.Length, null, null);
Writer.Flush();
}
#endregion
#region Data Receiving Functions
/// <summary>
/// Start listening for messages from the server
/// </summary>
private void StartListening()
{
try
{
Client.GetStream().BeginRead(ReadBuffer, 0, 2048, StreamReceived, null);
}
catch
{
OnDisconnect?.Invoke();
}
}
/// <summary>
/// Callback which processes a message sent from the server
/// </summary>
/// <param name="ar"></param>
private void StreamReceived(IAsyncResult ar)
{
int bytesRead = 0;
try
{
lock (Client.GetStream())
{
bytesRead = Client.GetStream().EndRead(ar);
}
}
catch { }
//Create the byte array with the number of bytes read
byte[] data = new byte[bytesRead];
//Populate the array
for (int i = 0; i < bytesRead; i++)
{
data[i] = ReadBuffer[i];
}
OnDataReceived?.Invoke(data);
//Listen for new data
StartListening();
}
#endregion
}
}
The IP Addresses and Port numbers are correct and since it works when running on the same machine, I was thinking it might be a firewall issue or something? Does anyone have any ideas as to what might be causing this problem?
Make sure the firewall (Windows Firewall, or whatever's there) is turned off on the server machine, or there's a firewall exception for your port number.
David's answer was correct. I previously tried it with just the firewall disabled for private networks. However, I disabled the firewall for guest and public networks and it worked a treat.
The method of testing proposed by codenoir was also very effective at ruling out my client. I suspected it was something to do with the firewall, but once you rule out the impossible...
Thanks to both
I am a electronic student, I am developing a GUI with Visual Studio; so I found examples and information in the next page.
http://www.voip-sip-sdk.com/
my principal class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Ozeki.Media;
using Ozeki.VoIP;
using Ozeki.Common;
namespace Consola2
{
/// <summary>
/// Basic softphone logic.
/// </summary>
/// <remarks>
/// This class is used to introduce how to declare, define and initialize a softphone,
/// how to handle some of it's events, and use some of it's functions.
/// The Program.cs uses this class to create a softphone,
/// uses the functions and events declared here as public.
/// </remarks>
class Softphone
{
ISoftPhone _softphone; // softphone object
IPhoneLine _phoneLine; // phone line object
IPhoneCall _call; // the call object
Microphone _microphone;
Speaker _speaker;
MediaConnector _connector; // connects the devices to each other (eg. microphone, speaker, mediaSender, mediaReceiver)
PhoneCallAudioSender _mediaSender; // after connected with the microphone, this will be attached to the call
PhoneCallAudioReceiver _mediaReceiver; // after connected with the speaker, this will be attached to the call
bool _incomingCall; // indicates wheter we have an incoming call (so, the phone is ringing)
#region Events
/// <summary>
/// Occurs when an incoming call received.
/// </summary>
public event EventHandler IncomingCall;
/// <summary>
/// Occurs when the registration state of the phone line has changed.
/// </summary>
public event EventHandler<RegistrationStateChangedArgs> PhoneLineStateChanged;
/// <summary>
/// Occurs when the state of the call has changed.
/// </summary>
public event EventHandler<CallStateChangedArgs> CallStateChanged;
#endregion
/// <summary>
/// Default constructor, initalizes the softphone with deafult parameters.
/// </summary>
public Softphone()
{
_softphone = SoftPhoneFactory.CreateSoftPhone(5000, 10000);
_microphone = Microphone.GetDefaultDevice();
_speaker = Speaker.GetDefaultDevice();
_connector = new MediaConnector();
_mediaSender = new PhoneCallAudioSender();
_mediaReceiver = new PhoneCallAudioReceiver();
_incomingCall = false;
}
/// <summary>
/// Registers the SIP account to the PBX.
/// Calls cannot be made while the SIP account is not registered.
/// If the SIP account requires no registration, the RegisterPhoneLine() must be called too to register the SIP account to the ISoftPhone.
/// </summary>
public void Register(bool registrationRequired, string displayName, string userName, string authenticationId, string registerPassword, string domainHost, int domainPort)
{
try
{
// We need to handle the event, when we have an incoming call.
_softphone.IncomingCall += softphone_IncomingCall;
// To register to a PBX, we need to create a SIP account
var account = new SIPAccount(registrationRequired, displayName, userName, authenticationId, registerPassword, domainHost, domainPort);
//Console.WriteLine("\nCreating SIP account {0}", account);
// With the SIP account and the NAT configuration, we can create a phoneline.
_phoneLine = _softphone.CreatePhoneLine(account);
//Console.WriteLine("Phoneline created.");
// The phoneline has states, we need to handle the event, when it is being changed.
_phoneLine.RegistrationStateChanged += phoneLine_PhoneLineStateChanged;
// If our phoneline is created, we can register that.
_softphone.RegisterPhoneLine(_phoneLine);
// For further information about the calling of the ConnectMedia(), please check the implementation of this method.
ConnectMedia();
}
catch (Exception ex)
{
Console.WriteLine("Error during SIP registration: " + ex);
}
}
/// <summary>
/// This will be called when the registration state of the phone line has changed.
/// </summary>
private void phoneLine_PhoneLineStateChanged(object sender, RegistrationStateChangedArgs e)
{
DispatchAsync(() =>
{
var handler = PhoneLineStateChanged;
if (handler != null)
handler(this, e);
});
}
/// <summary>
/// Starts the capturing and playing audio/video devices.
/// Other devices can be used (and started), for example: WebCamera or WaveStreamPlayback.
/// </summary>
private void StartDevices()
{
if (_microphone != null)
{
_microphone.Start();
}
if (_speaker != null)
{
_speaker.Start();
}
}
/// <summary>
/// Stops the capturing and playing audio/video devices.
/// Other devices can be stopped, for example: WebCamera.
/// </summary>
private void StopDevices()
{
if (_microphone != null)
{
_microphone.Stop();
}
if (_speaker != null)
{
_speaker.Stop();
}
}
#region Media handling guide
/*
To send our voice through the microphone to the other client's speaker, we need to connect them.
We send our voice through the mediaSender, and we get the other client's voice through the mediaSender to our speaker object.
To disconnect these handlers, we will use the DisconnectMedia() method.
It is possible to use other mediahandlers with the connector, for example we can connect a WaveStreamPlayback or an MP3StreamPlayback object to the MediaSender, so we can play music/voice
during the call. For exmaple: when can create an IVR (Interactive Voice Response), we can create voice recorder etc.
For example:
We can connect an .mp3 file player (which plays an mp3 file into the voice call) by the "connector.Connect(Mp3StreamPlayback, mediaSender); " line.
(We should also create an MP3StreamPlayback object: "MP3StreamPlayback Mp3StreamPlayback; "
and we need to tell to this object the details (what to play into the speaker, etc.))
*/
#endregion
/// <summary>
/// Connects the audio handling devices to each other.
/// The audio data will flow from the source to the destination.
/// </summary>
private void ConnectMedia()
{
if (_microphone != null)
{
_connector.Connect(_microphone, _mediaSender);
}
if (_speaker != null)
{
_connector.Connect(_mediaReceiver, _speaker);
}
}
/// <summary>
/// Disconnects the audio handling devices from each other.
/// </summary>
private void DisconnectMedia()
{
if (_microphone != null)
{
_connector.Disconnect(_microphone, _mediaSender);
}
if (_speaker != null)
{
_connector.Disconnect(_mediaReceiver, _speaker);
}
// You can close all of the connections by using: connector.Dispose();
}
/// <summary>
/// Subscribes to the events of a call to receive notifications such as the state of the call has changed.
/// In this sample subscribes only to the state changed and error occurred events.
/// </summary>
private void WireUpCallEvents()
{
_call.CallStateChanged += (call_CallStateChanged);
}
/// <summary>
/// Unsubscribes from the events of a call.
/// </summary>
private void WireDownCallEvents()
{
_call.CallStateChanged -= (call_CallStateChanged);
}
/// <summary>
/// This will be called when an incoming call received.
/// To receive notifications from the call (eg. ringing), the program need to subscribe to the events of the call.
/// </summary>
private void softphone_IncomingCall(object sender, VoIPEventArgs<IPhoneCall> e)
{
_call = e.Item;
WireUpCallEvents();
_incomingCall = true;
DispatchAsync(() =>
{
var handler = IncomingCall;
if (handler != null)
handler(this, EventArgs.Empty);
});
}
/// <summary>
/// This will be called when the state of the call call has changed.
/// </summary>
/// <remarks>
/// In this sample only three states will be handled: Answered, InCall, Ended
///
/// Answered: when the call has been answered, the audio devices will be started and attached to the call.
/// It is required to comminicate with the other party and hear them.
/// The devices are connected at softphone initialization time,
/// so no need to connect them every time when a call is being answered.
///
/// InCall: when the call is in an active state, the audio deveices will be started.
///
/// Ended: when the call ends, the audio devices will be stopped and detached from the call.
/// </remarks>
private void call_CallStateChanged(object sender, CallStateChangedArgs e)
{
// the call has been answered
if (e.State == CallState.Answered)
{
StartDevices();
_mediaReceiver.AttachToCall(_call);
_mediaSender.AttachToCall(_call);
}
// the call is in active communication state
// IMPORTANT: this state can occur multiple times. for example when answering the call or the call has been taken off hold.
if (e.State == CallState.InCall)
{
StartDevices();
}
// the call has ended
if (e.State.IsCallEnded())
{
if (_call != null)
{
CallFinished();
}
}
DispatchAsync(() =>
{
var handler = CallStateChanged;
if (handler != null)
handler(this, e);
});
}
/// <summary>
/// Starts calling the specified number.
/// In this sample an outgoing call can be made if there is no current call (outgoing or incoming) on the phone line.
/// </summary>
public void StartCall(string numberToDial)
{
if (_call == null)
{
_call = _softphone.CreateCallObject(_phoneLine, numberToDial);
WireUpCallEvents();
// To make a call simply call the Start() method of the call object.
_call.Start();
}
}
/// <summary>
/// Answers the current incoming call.
/// </summary>
public void AcceptCall()
{
// when the value of the incomingCall member is true, there is an incoming call
if (_incomingCall == true)
{
_incomingCall = false;
***_call.Answer();***
}
}
/// <summary>
/// Hangs up the current call.
/// </summary>
public void HangUp()
{
if (_call != null)
{
_call.HangUp();
_call = null;
}
}
/// <summary>
/// If the call ends, we won't need our speaker and microphone anymore to communicate,
/// until we enter into a call again, so we are calling the StopDevices() method.
/// The mediaHandlers are getting detached from the call object
/// (since we are not using our microphone and speaker, we have no media to send).
/// We won't need the call's events anymore, becouse our call is about to be ended,
/// and with setting the call to null, we are ending it.
/// </summary>
public void CallFinished()
{
StopDevices();
_mediaReceiver.Detach();
_mediaSender.Detach();
WireDownCallEvents();
_call = null;
}
/// <summary>
/// This method is used to solve the task blockings.
/// </summary>
private void DispatchAsync(Action action)
{
var task = new WaitCallback(o => action.Invoke());
ThreadPool.QueueUserWorkItem(task);
}
}
}
My GUI code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
using Ozeki.VoIP;
namespace Consola2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private static Softphone _mySoftphone; // softphone object
/// <summary>
/// The entry point of the program.
/// - initializes the softphone
/// - shows a greeting message
/// - registers the SIP account
/// </summary>
///
private static void InitSoftphone()
{
_mySoftphone = new Softphone();
_mySoftphone.PhoneLineStateChanged += mySoftphone_PhoneLineStateChanged;
_mySoftphone.CallStateChanged += mySoftphone_CallStateChanged;
_mySoftphone.IncomingCall += mySoftphone_IncomingCall;
}
/// <summary>
/// This will be called when the registration state of the phone line has changed.
/// </summary>
static void mySoftphone_PhoneLineStateChanged(object sender, RegistrationStateChangedArgs e)
{
Console.WriteLine("Phone line state changed: {0}", e.State);
if (e.State == RegState.Error || e.State == RegState.NotRegistered)
{
MessageBox.Show("Datos Invalidos", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
else if (e.State == RegState.RegistrationSucceeded)
{
MessageBox.Show("¡Registro Exitoso!");
//StartToDial();
}
}
/// <summary>
/// This will be called when an incoming call received.
/// In this sample when an incoming call receveived, it will be answered automatically.
/// </summary>
static void mySoftphone_IncomingCall(object sender, EventArgs e)
{
MessageBox.Show("Llamada entrante");
_mySoftphone.AcceptCall();
MessageBox.Show("Llamada aceptada:");
}
/// <summary>
/// This will be called when the state of the call has changed. (eg. ringing, answered, rejected)
/// </summary>
private static void mySoftphone_CallStateChanged(object sender, CallStateChangedArgs e)
{
Console.WriteLine("Call state changed: {0}", e.State);
if (e.State.IsCallEnded())
{
MessageBox.Show("Llamada terminada:");
}
if (e.State == CallState.Error)
{
Console.WriteLine("Call error occured. {0}", e.Reason);
}
}
public MainWindow()
{
InitializeComponent();
InitSoftphone();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
string id = textBox1.Text;
string user = textBox2.Text;
string displayed = textBox3.Text;
string pass = textBox4.Text;
string domain = textBox5.Text;
int port = Convert.ToInt32(textBox6.Text);
bool check = false;
if (checkBox1.IsChecked == true)
{
check = true;
}
_mySoftphone.Register(check, id, user, displayed, pass, domain, port);
}
private void Llamar_Click(object sender, RoutedEventArgs e)
{
string numero = textBox7.Text;
_mySoftphone.StartCall(numero);
}
private void button2_Click(object sender, RoutedEventArgs e)
{
_mySoftphone.AcceptCall();
}
}
}
With that, I can register into a PBX and make calls , the problem is that when you receive calls an error message appears "NullReferenceException was unhandled" In my class principal "AcceptCall()" method in the line "_call,Answer();".
Where is the problem? i'm not sure about de references with the "Ozeki" library.
I appreciate your help, or an example to make and receive calls.
Thanks.
Your call button (Llamar) instantiates _call inside StartCall. However, when AcceptCall is run, _call is still null, and cannot be used.
I am writing Windows Phone application and can't understand why the emulator crashes. It says that: "Unhandled exception at 0x773415de in XDE.exe: 0xC0000005: Access violation reading location 0x05190000." This problem appears only when I begin to scroll very fast my listbox. I load per 20 items to listbox (the number of all items is 200). The problem is in my class of downloading images. I create a Webclient request for each image to server or it's better to create a thread pool?
In XAML:
<Image Name="userPic" Margin="0,8,1,2"
DelayFix:LowProfileImageLoader.UriSource="{Binding photoUrl}" Stretch="Fill"
Width="60"
Height="60"/>
downloadImage class:
using System;
using System.Net;
using System.Windows.Resources;
using System.Windows.Media.Imaging;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Phone;
using System.Diagnostics;
namespace PhoneApp
{
public class downloadImage
{
public int ImageIndex;
public string ImageURL;
public Boolean Downloaded;
public BitmapImage Bitmap;
public Image destinationImage;
private WebClient client;
public delegate void FileCompleteHandler(object sender);
public event FileCompleteHandler FileCompleteEvent;
private static readonly object _syncBlock = new object();
public downloadImage(int index, string imageURL, Image destinationImage = null)
{
this.ImageIndex = index;
this.ImageURL = imageURL;
this.destinationImage = destinationImage;
}
public void Download()
{
client = new WebClient();
client.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
client.OpenReadAsync(new Uri(this.ImageURL, UriKind.Absolute));
}
private void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error == null && e.Result != null)
{
Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
{
StreamResourceInfo sri = new StreamResourceInfo(e.Result as Stream, null);
if (destinationImage == null)
{
this.Bitmap = new BitmapImage();
this.Bitmap.SetSource(sri.Stream);
}
else
{
this.destinationImage.Source = PictureDecoder.DecodeJpeg(e.Result);
}
this.Downloaded = true;
if (FileCompleteEvent != null)
FileCompleteEvent(this);
}));
}
}
}
}
The code of my class:
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Diagnostics;
using System.ComponentModel;
using Microsoft.Phone;
using Microsoft.Phone.Info;
namespace DelayFix
{
/// <summary>
/// Provides access to the Image.UriSource attached property which allows
/// Images to be loaded by Windows Phone with less impact to the UI thread.
/// </summary>
public static class LowProfileImageLoader
{
private static readonly object _syncBlock = new object();
private static bool _exiting;
/// <summary>
/// Gets the value of the Uri to use for providing the contents of the Image's Source property.
/// </summary>
/// <param name="obj">Image needing its Source property set.</param>
/// <returns>Uri to use for providing the contents of the Source property.</returns>
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "UriSource is applicable only to Image elements.")]
public static Uri GetUriSource(Image obj)
{
if (null == obj)
{
throw new ArgumentNullException("obj");
}
return (Uri)obj.GetValue(UriSourceProperty);
}
/// <summary>
/// Sets the value of the Uri to use for providing the contents of the Image's Source property.
/// </summary>
/// <param name="obj">Image needing its Source property set.</param>
/// <param name="value">Uri to use for providing the contents of the Source property.</param>
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "UriSource is applicable only to Image elements.")]
public static void SetUriSource(Image obj, Uri value)
{
if (null == obj)
{
throw new ArgumentNullException("obj");
}
obj.SetValue(UriSourceProperty, value);
}
/// <summary>
/// Identifies the UriSource attached DependencyProperty.
/// </summary>
public static readonly DependencyProperty UriSourceProperty = DependencyProperty.RegisterAttached(
"UriSource", typeof(Uri), typeof(LowProfileImageLoader), new PropertyMetadata(OnUriSourceChanged));
/// <summary>
/// Gets or sets a value indicating whether low-profile image loading is enabled.
/// </summary>
public static bool IsEnabled { get; set; }
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Static constructor performs additional tasks.")]
static LowProfileImageLoader()
{
Debug.WriteLine("Limit Memory:" + DeviceStatus.ApplicationMemoryUsageLimit / 1024 / 1024);
}
private static void HandleApplicationExit(object sender, EventArgs e)
{
Debug.WriteLine("Exit!");
}
private static void OnUriSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
lock (_syncBlock)
{
var image = (Image)o;
var uri = (Uri)e.NewValue;
Debug.WriteLine("Memory:" + DeviceStatus.ApplicationCurrentMemoryUsage / 1024 / 1024);
if (DesignerProperties.IsInDesignTool || !uri.IsAbsoluteUri)
{
image.Source = PhoneAppVKContest.ImageUtils.getJpegImage(uri.OriginalString);
}
else
{
PhoneAppVKContest.downloadImage di = new PhoneAppVKContest.downloadImage(0, uri.AbsoluteUri);
di.destinationImage = image;
di.FileCompleteEvent += new PhoneAppVKContest.downloadImage.FileCompleteHandler(di_FileCompleteEvent);
di.Download();
}
}
}
static void di_FileCompleteEvent(object sender)
{
Debug.WriteLine("Download success!");
}
}
}
When you are creating webClient request for each image, then a conflict may arise when the webClient is bombarded with all the requests. You need to order the requests sequentially via a queue. If the image in the queue's first element is already in the cache or IS, link it, else check if webclient is busy or not. If busy then send it to the end of the queue. And check for the next image. If its not busy, submit a downloading request. This is the method which I have followed. You might also consider binding the Imagesource for each image inorder to prevent auto reloading on page reload or due to problem in scroll cache. I hope this answer helps you.
I found this class on the internet but it didn't have any example usage. I tried reading it and the <events> are confusing me. When I try to initialize an event it stays null even after the irc.DataRead+=new EventHandler<DataReadEventArgs>(irc_DataRead);
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace asynchronous
{
/// <summary>
/// Represents an asynchronous Tcp Client.
/// </summary>
public class Client
{
/// <summary>
/// The default length for the read buffer.
/// </summary>
private const int DefaultClientReadBufferLength = 4096;
/// <summary>
/// The tcp client used for the outgoing connection.
/// </summary>
private readonly TcpClient client;
/// <summary>
/// The port to connect to on the remote server.
/// </summary>
private readonly int port;
/// <summary>
/// A reset event for use if a DNS lookup is required.
/// </summary>
private readonly ManualResetEvent dnsGetHostAddressesResetEvent = null;
/// <summary>
/// The length of the read buffer.
/// </summary>
private readonly int clientReadBufferLength;
/// <summary>
/// The addresses to try connection to.
/// </summary>
private IPAddress[] addresses;
/// <summary>
/// How many times to retry connection.
/// </summary>
private int retries;
/// <summary>
/// Occurs when the client connects to the server.
/// </summary>
public event EventHandler Connected;
/// <summary>
/// Occurs when the client disconnects from the server.
/// </summary>
public event EventHandler Disconnected;
/// <summary>
/// Occurs when data is read by the client.
/// </summary>
public event EventHandler<DataReadEventArgs> DataRead;
/// <summary>
/// Occurs when data is written by the client.
/// </summary>
public event EventHandler<DataWrittenEventArgs> DataWritten;
/// <summary>
/// Occurs when an exception is thrown during connection.
/// </summary>
public event EventHandler<ExceptionEventArgs> ClientConnectException;
/// <summary>
/// Occurs when an exception is thrown while reading data.
/// </summary>
public event EventHandler<ExceptionEventArgs> ClientReadException;
/// <summary>
/// Occurs when an exception is thrown while writing data.
/// </summary>
public event EventHandler<ExceptionEventArgs> ClientWriteException;
/// <summary>
/// Occurs when an exception is thrown while performing the DNS lookup.
/// </summary>
public event EventHandler<ExceptionEventArgs> DnsGetHostAddressesException;
/// <summary>
/// Constructor for a new client object based on a host name or server address string and a port.
/// </summary>
/// <param name="hostNameOrAddress">The host name or address of the server as a string.</param>
/// <param name="port">The port on the server to connect to.</param>
/// <param name="clientReadBufferLength">The clients read buffer length.</param>
public Client(string hostNameOrAddress, int port, int clientReadBufferLength = DefaultClientReadBufferLength)
: this(port, clientReadBufferLength)
{
this.dnsGetHostAddressesResetEvent = new ManualResetEvent(false);
Dns.BeginGetHostAddresses(hostNameOrAddress, this.DnsGetHostAddressesCallback, null);
}
/// <summary>
/// Constructor for a new client object based on a number of IP Addresses and a port.
/// </summary>
/// <param name="addresses">The IP Addresses to try connecting to.</param>
/// <param name="port">The port on the server to connect to.</param>
/// <param name="clientReadBufferLength">The clients read buffer length.</param>
public Client(IPAddress[] addresses, int port, int clientReadBufferLength = DefaultClientReadBufferLength)
: this(port, clientReadBufferLength)
{
this.addresses = addresses;
}
/// <summary>
/// Constructor for a new client object based on a single IP Address and a port.
/// </summary>
/// <param name="address">The IP Address to try connecting to.</param>
/// <param name="port">The port on the server to connect to.</param>
/// <param name="clientReadBufferLength">The clients read buffer length.</param>
public Client(IPAddress address, int port, int clientReadBufferLength = DefaultClientReadBufferLength)
: this(new[] { address }, port, clientReadBufferLength)
{
}
/// <summary>
/// Private constructor for a new client object.
/// </summary>
/// <param name="port">The port on the server to connect to.</param>
/// <param name="clientReadBufferLength">The clients read buffer length.</param>
private Client(int port, int clientReadBufferLength)
{
this.client = new TcpClient();
this.port = port;
this.clientReadBufferLength = clientReadBufferLength;
}
/// <summary>
/// Starts an asynchronous connection to the remote server.
/// </summary>
public void Connect()
{
if (this.dnsGetHostAddressesResetEvent != null)
this.dnsGetHostAddressesResetEvent.WaitOne();
this.retries = 0;
this.client.BeginConnect(this.addresses, this.port, this.ClientConnectCallback, null);
}
/// <summary>
/// Writes a string to the server using a given encoding.
/// </summary>
/// <param name="value">The string to write.</param>
/// <param name="encoding">The encoding to use.</param>
/// <returns>A Guid that can be used to match the data written to the confirmation event.</returns>
public Guid Write(string value, Encoding encoding)
{
byte[] buffer = encoding.GetBytes(value);
return this.Write(buffer);
}
/// <summary>
/// Writes a byte array to the server.
/// </summary>
/// <param name="buffer">The byte array to write.</param>
/// <returns>A Guid that can be used to match the data written to the confirmation event.</returns>
public Guid Write(byte[] buffer)
{
Guid guid = Guid.NewGuid();
NetworkStream networkStream = this.client.GetStream();
networkStream.BeginWrite(buffer, 0, buffer.Length, this.ClientWriteCallback, guid);
return guid;
}
/// <summary>
/// Callback from the asynchronous DNS lookup.
/// </summary>
/// <param name="asyncResult">The result of the async operation.</param>
private void DnsGetHostAddressesCallback(IAsyncResult asyncResult)
{
try
{
this.addresses = Dns.EndGetHostAddresses(asyncResult);
this.dnsGetHostAddressesResetEvent.Set();
}
catch (Exception ex)
{
if (this.DnsGetHostAddressesException != null)
this.DnsGetHostAddressesException(this, new ExceptionEventArgs(ex));
}
}
/// <summary>
/// Callback from the asynchronous Connect method.
/// </summary>
/// <param name="asyncResult">The result of the async operation.</param>
private void ClientConnectCallback(IAsyncResult asyncResult)
{
try
{
this.client.EndConnect(asyncResult);
if (this.Connected != null)
this.Connected(this, new EventArgs());
}
catch (Exception ex)
{
retries++;
if (retries < 3)
{
this.client.BeginConnect(this.addresses, this.port, this.ClientConnectCallback, null);
}
else
{
if (this.ClientConnectException != null)
this.ClientConnectException(this, new ExceptionEventArgs(ex));
}
return;
}
try
{
NetworkStream networkStream = this.client.GetStream();
byte[] buffer = new byte[this.clientReadBufferLength];
networkStream.BeginRead(buffer, 0, buffer.Length, this.ClientReadCallback, buffer);
}
catch (Exception ex)
{
if (this.ClientReadException != null)
this.ClientReadException(this, new ExceptionEventArgs(ex));
}
}
/// <summary>
/// Callback from the asynchronous Read method.
/// </summary>
/// <param name="asyncResult">The result of the async operation.</param>
private void ClientReadCallback(IAsyncResult asyncResult)
{
try
{
NetworkStream networkStream = this.client.GetStream();
int read = networkStream.EndRead(asyncResult);
if (read == 0)
{
if (this.Disconnected != null)
this.Disconnected(this, new EventArgs());
}
byte[] buffer = asyncResult.AsyncState as byte[];
if (buffer != null)
{
byte[] data = new byte[read];
Buffer.BlockCopy(buffer, 0, data, 0, read);
networkStream.BeginRead(buffer, 0, buffer.Length, this.ClientReadCallback, buffer);
if (this.DataRead != null)
this.DataRead(this, new DataReadEventArgs(data));
}
}
catch (Exception ex)
{
if (this.ClientReadException != null)
this.ClientReadException(this, new ExceptionEventArgs(ex));
}
}
/// <summary>
/// Callback from the asynchronous write callback.
/// </summary>
/// <param name="asyncResult">The result of the async operation.</param>
private void ClientWriteCallback(IAsyncResult asyncResult)
{
try
{
NetworkStream networkStream = this.client.GetStream();
networkStream.EndWrite(asyncResult);
Guid guid = (Guid)asyncResult.AsyncState;
if (this.DataWritten != null)
this.DataWritten(this, new DataWrittenEventArgs(guid));
}
catch (Exception ex)
{
if (this.ClientWriteException != null)
this.ClientWriteException(this, new ExceptionEventArgs(ex));
}
}
}
/// <summary>
/// Provides data for an exception occuring event.
/// </summary>
public class ExceptionEventArgs : EventArgs
{
/// <summary>
/// Constructor for a new Exception Event Args object.
/// </summary>
/// <param name="ex">The exception that was thrown.</param>
public ExceptionEventArgs(Exception ex)
{
this.Exception = ex;
}
public Exception Exception { get; private set; }
}
/// <summary>
/// Provides data for a data read event.
/// </summary>
public class DataReadEventArgs : EventArgs
{
/// <summary>
/// Constructor for a new Data Read Event Args object.
/// </summary>
/// <param name="data">The data that was read from the remote host.</param>
public DataReadEventArgs(byte[] data)
{
this.Data = data;
}
/// <summary>
/// Gets the data that has been read.
/// </summary>
public byte[] Data { get; private set; }
}
/// <summary>
/// Provides data for a data write event.
/// </summary>
public class DataWrittenEventArgs : EventArgs
{
/// <summary>
/// Constructor for a Data Written Event Args object.
/// </summary>
/// <param name="guid">The guid of the data written.</param>
public DataWrittenEventArgs(Guid guid)
{
this.Guid = guid;
}
/// <summary>
/// Gets the Guid used to match the data written to the confirmation event.
/// </summary>
public Guid Guid { get; private set; }
}
}
This is what I've tried but I don't get it. I'm new to c#:
using System;
using System.Text;
using System.Threading;
namespace asynchronous
{
class Program
{
private static EventHandler connection;
private static EventHandler<DataReadEventArgs> irc_DataRead;
static void Main(string[] args)
{
var irc = new Client("irc.rizon.net", 6667);
Console.WriteLine("Connecting...");
irc.Connect();
Thread.Sleep(2000);
irc.Write("Test", Encoding.UTF8);
irc.DataRead+=new EventHandler<DataReadEventArgs>(irc_DataRead);
Console.WriteLine(irc_DataRead);
Console.WriteLine("Connected.");
Console.ReadLine();
}
}
}
Could someone please help me setup that class to connect to IRC and read and write text?
Here you are hooking up a null instance of an event handler into an event. You need to hookup an actual method / lambda in order for this to work. Try the following
static void OnDataRead(object sender, DataReadEventArgs e) {
// Data reads call this method
}
static void Main(string[] args) {
...
irc.DataRead += OnDataRead;
...
}