I'm subscribing to the following event and I wonder what the difference between the following two examples is and do I need to unsubscribe. The library I'm using is Binance.Net by JKorf.
public event Action<Exception> Exception
{
add => subscription.Exception += value;
remove => subscription.Exception -= value;
}
Example #1
// Subscribe
subscription.Data.Exception += PortfolioService_Exception;
// Unsubscribe
subscription.Data.Exception -= PortfolioService_Exception;
private void PortfolioService_Exception(Exception ex)
{
_logger.LogInformation($"Error: {ex.StackTrace}");
}
Example #2
If I do it this way, do I need to unsubscribe with -=?
subscription.Data.Exception += (ex) =>
{
_logger.LogInformation($"Error: {ex.StackTrace}");
};
Full code in addition
Snippet
private async Task<bool> InitAsync()
{
var restClient = new BinanceClient(new BinanceClientOptions
{
ApiCredentials = new ApiCredentials(ApiKey, SecretKey),
AutoTimestamp = true,
AutoTimestampRecalculationInterval = TimeSpan.FromMinutes(30),
TradeRulesBehaviour = TradeRulesBehaviour.AutoComply,
#if DEBUG
LogLevel = LogLevel.Debug,
LogWriters = new List<ILogger> { _logger } // TODO: FIX
#endif
});
var socketClient = new BinanceSocketClient(new BinanceSocketClientOptions
{
ApiCredentials = new ApiCredentials(ApiKey, SecretKey),
AutoReconnect = true,
ReconnectInterval = TimeSpan.FromSeconds(15),
#if DEBUG
LogLevel = LogLevel.Debug,
LogWriters = new List<ILogger> { _logger } // TODO: FIX
#endif
});
var listenKeyResult = await restClient.Spot.UserStream.StartUserStreamAsync();
if (!listenKeyResult.Success)
{
return false;
}
var subscription = await socketClient.Spot.SubscribeToUserDataUpdatesAsync(listenKeyResult.Data,
null,
null,
data =>
{
// TODO: Fix the warning
_logger.LogInformation($"ASD {data.Data.Balances}");
},
data =>
{
// TODO: Fix the warning
_logger.LogInformation($"BALANCE DELTA {data.Data.BalanceDelta}");
});
if (!subscription.Success)
{
return false;
}
// Auto unsubscribe?
subscription.Data.Exception += (ex) =>
{
_logger.LogInformation($"Error: {ex.StackTrace}");
};
// Subscribe
subscription.Data.Exception += PortfolioService_Exception;
// Unsubscribe
subscription.Data.Exception -= PortfolioService_Exception;
// TODO: Put a CancellationToken logic and `StopUserStreamAsync`. and subscription.Error -=
var keepAliveTask = Task.Run(async () =>
{
while (true)
{
// Listen key is kept alive for 60 minutes.
await restClient.Spot.UserStream.KeepAliveUserStreamAsync(listenKeyResult.Data);
await Task.Delay(TimeSpan.FromMinutes(50));
}
});
return true;
}
private void PortfolioService_Exception(Exception ex)
{
_logger.LogInformation($"Error: {ex.StackTrace}");
}
The library
using CryptoExchange.Net.Objects;
using System;
using System.Threading.Tasks;
namespace CryptoExchange.Net.Sockets
{
/// <summary>
/// Subscription to a data stream
/// </summary>
public class UpdateSubscription
{
private readonly SocketConnection connection;
private readonly SocketSubscription subscription;
/// <summary>
/// Event when the connection is lost. The socket will automatically reconnect when possible.
/// </summary>
public event Action ConnectionLost
{
add => connection.ConnectionLost += value;
remove => connection.ConnectionLost -= value;
}
/// <summary>
/// Event when the connection is closed. This event happens when reconnecting/resubscribing has failed too often based on the <see cref="SocketClientOptions.MaxReconnectTries"/> and <see cref="SocketClientOptions.MaxResubscribeTries"/> options,
/// or <see cref="SocketClientOptions.AutoReconnect"/> is false
/// </summary>
public event Action ConnectionClosed
{
add => connection.ConnectionClosed += value;
remove => connection.ConnectionClosed -= value;
}
/// <summary>
/// Event when the connection is restored. Timespan parameter indicates the time the socket has been offline for before reconnecting.
/// Note that when the executing code is suspended and resumed at a later period (for example laptop going to sleep) the disconnect time will be incorrect as the diconnect
/// will only be detected after resuming. This will lead to an incorrect disconnected timespan.
/// </summary>
public event Action<TimeSpan> ConnectionRestored
{
add => connection.ConnectionRestored += value;
remove => connection.ConnectionRestored -= value;
}
/// <summary>
/// Event when the connection to the server is paused based on a server indication. No operations can be performed while paused
/// </summary>
public event Action ActivityPaused
{
add => connection.ActivityPaused += value;
remove => connection.ActivityPaused -= value;
}
/// <summary>
/// Event when the connection to the server is unpaused after being paused
/// </summary>
public event Action ActivityUnpaused
{
add => connection.ActivityUnpaused += value;
remove => connection.ActivityUnpaused -= value;
}
/// <summary>
/// Event when an exception happens during the handling of the data
/// </summary>
public event Action<Exception> Exception
{
add => subscription.Exception += value;
remove => subscription.Exception -= value;
}
/// <summary>
/// The id of the socket
/// </summary>
public int SocketId => connection.Socket.Id;
/// <summary>
/// The id of the subscription
/// </summary>
public int Id => subscription.Id;
/// <summary>
/// ctor
/// </summary>
/// <param name="connection">The socket connection the subscription is on</param>
/// <param name="subscription">The subscription</param>
public UpdateSubscription(SocketConnection connection, SocketSubscription subscription)
{
this.connection = connection;
this.subscription = subscription;
}
/// <summary>
/// Close the subscription
/// </summary>
/// <returns></returns>
public Task CloseAsync()
{
return connection.CloseAsync(subscription);
}
/// <summary>
/// Close the socket to cause a reconnect
/// </summary>
/// <returns></returns>
internal Task ReconnectAsync()
{
return connection.Socket.CloseAsync();
}
/// <summary>
/// Unsubscribe a subscription
/// </summary>
/// <returns></returns>
internal async Task UnsubscribeAsync()
{
await connection.UnsubscribeAsync(subscription).ConfigureAwait(false);
}
/// <summary>
/// Resubscribe this subscription
/// </summary>
/// <returns></returns>
internal async Task<CallResult<bool>> ResubscribeAsync()
{
return await connection.ResubscribeAsync(subscription).ConfigureAwait(false);
}
}
}
Example #1 and Example #2 do exactly the same thing. The only difference is that in Example #1 your handler has a name while in Example #2 it does not have a name (i.e. is anonymous). Because it has no name, it is difficult to unregister the handler with -=, as you seem to have figured out.
If you want to be able to use a lambda but you also want to be able to unsubscribe, you can assign the lambda to a variable first.
Action<Exception> tmp = ex => _logger.LogInformation($"Error: {ex.StackTrace}");
subscription.Data.Exception += tmp;
subscription.Data.Exception -= tmp; //Unsubscribe
I try to get this work correct but wen i start to stream it contious repeat the image video.
I need it stream only one video.
The problem is wen i start to stream the video image it continous repeat the video, how can i stop the player to not repeat the image and only Get one.
This is what i get wen i start and i want to avoid the video to continuous repeating the video
I have edit and my script and add the lines of code
But It Continous Repeat The Video With Out Stop
This is my script
using UnityEngine;
using System;
using MP;
using MP.Net;
/// <summary>
/// Movie streamer
/// </summary>
public class MovieStreamer : MoviePlayerBase
{
#region ----- public ------
/// <summary>
/// Movie source url
/// </summary>
public string sourceUrl;
/// <summary>
/// Movie load options. The Load() methods on this component will use
/// this unless you're provinding your own.
/// </summary>
public LoadOptions loadOptions = LoadOptions.Default;
/// <summary>
/// Background thread status
/// </summary>
public string status;
public long bytesReceived;
public bool IsConnected
{
get {
return movie==null || movie.demux==null ? false : ((Streamer)movie.demux).IsConnected;
}
}
/// <summary>
/// Connects to an URL for streaming.
///
/// In case it fails, exception text is logged and FALSE is returned
/// </summary>
public bool Load (string srcUrl)
{
return Load (srcUrl, null);
}
public bool Load (string srcUrl, LoadOptions loadOptions)
{
this.sourceUrl = srcUrl;
if(loadOptions == null) {
loadOptions = this.loadOptions;
} else {
this.loadOptions = loadOptions;
}
try {
base.Load(new MovieSource() { url = srcUrl }, loadOptions);
return true;
}
catch (Exception e) {
if(loadOptions.enableExceptionThrow) {
throw e;
} else {
Debug.LogError (e);
return false;
}
}
}
[ContextMenu("Reconnect")]
public bool ReConnect ()
{
bool success = true;
if (!string.IsNullOrEmpty(sourceUrl)) {
success = Load (sourceUrl, loadOptions);
}
return success;
}
#endregion ------ / public ------
#region ----- private -----
private int lastVideoFrame = -1;
void Start ()
{
ReConnect ();
}
void OnGUI ()
{
if (!IsConnected || !movie.demux.hasVideo)
return;
// if we're playing the movie directly to screen, but don't
// show it before we've received at least one frame
if (drawToScreen && framebuffer != null && ((Streamer)movie.demux).VideoPosition > 0) {
DrawFramebufferToScreen ();
}
}
void Update ()
{
// get the thread status and write it here
if(movie != null && movie.demux != null)
{
if(movie.demux is HttpMjpegStreamer) {
status = ((HttpMjpegStreamer)movie.demux).Status;
bytesReceived = ((HttpMjpegStreamer)movie.demux).BytesReceived;
}
}
// if this.play changed, Play or Stop the movie
HandlePlayStop ();
// decode a frame when necessary
if(play) {
HandleFrameDecode ();
DrawFramebufferToScreen();
HandlePlayStop();
}
}
protected void HandleFrameDecode ()
{
if (!IsConnected || !movie.demux.hasVideo || movie.videoDecoder == null)
return;
// decode a frame if there's a new one available
if (movie.videoDecoder.Position != lastVideoFrame)
{
if(movie.videoDecoder.Position >= 0)
{
movie.videoDecoder.DecodeNext ();
// update the aspect ration of the video
movie.demux.videoStreamInfo.width = framebuffer.width;
movie.demux.videoStreamInfo.height = framebuffer.height;
}
lastVideoFrame = movie.videoDecoder.Position;
}
}
#endregion
}
i'm developing an app, and i need to play music from url.
i'm using this code, and it works fine.
But, while a song is playing, and the screen switches off, then the music stops. I need that the music continues playing until its end, even if the screen switches off.
i suppose i have to change this file, but i don't know how:
using System;
using AudioToolbox;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
namespace SecondoSenso
{
/// <summary>
/// A Class to hold the AudioBuffer with all setting together
/// </summary>
internal class AudioBuffer
{
public IntPtr Buffer { get; set; }
public List<AudioStreamPacketDescription> PacketDescriptions { get; set; }
public int CurrentOffset { get; set; }
public bool IsInUse { get; set; }
}
/// <summary>
/// Wrapper around OutputQueue and AudioFileStream to allow streaming of various filetypes
/// </summary>
public class StreamingPlayback : IDisposable
{
public bool boolDispose = false;
public event EventHandler Finished;
public event Action<OutputAudioQueue> OutputReady;
// the AudioToolbox decoder
AudioFileStream fileStream;
int bufferSize = 128 * 256;
List<AudioBuffer> outputBuffers;
AudioBuffer currentBuffer;
// Maximum buffers
int maxBufferCount = 4;
// Keep track of all queued up buffers, so that we know that the playback finished
int queuedBufferCount = 0;
// Current Filestream Position - if we don't keep track we don't know when to push the last uncompleted buffer
long currentByteCount = 0;
//Used to trigger a dump of the last buffer.
bool lastPacket;
public OutputAudioQueue OutputQueue;
public bool Started { get; private set; }
public float Volume {
get {
return OutputQueue.Volume;
}
set {
OutputQueue.Volume = value;
}
}
/// <summary>
/// Defines the size forearch buffer, when using a slow source use more buffers with lower buffersizes
/// </summary>
public int BufferSize {
get {
return bufferSize;
}
set {
bufferSize = value;
}
}
/// <summary>
/// Defines the maximum Number of Buffers to use, the count can only change after Reset is called or the
/// StreamingPlayback is freshly instantiated
/// </summary>
public int MaxBufferCount {
get {
return maxBufferCount;
}
set {
maxBufferCount = value;
}
}
public StreamingPlayback () : this (AudioFileType.MP3)
{
}
public StreamingPlayback (AudioFileType type)
{
fileStream = new AudioFileStream (type);
fileStream.PacketDecoded += AudioPacketDecoded;
fileStream.PropertyFound += AudioPropertyFound;
}
public void Reset ()
{
if (fileStream != null) {
fileStream.Close ();
fileStream = new AudioFileStream (AudioFileType.MP3);
currentByteCount = 0;
fileStream.PacketDecoded += AudioPacketDecoded;
fileStream.PropertyFound += AudioPropertyFound;
}
}
public void ResetOutputQueue ()
{
if (OutputQueue != null) {
OutputQueue.Stop (true);
OutputQueue.Reset ();
foreach (AudioBuffer buf in outputBuffers) {
buf.PacketDescriptions.Clear ();
OutputQueue.FreeBuffer (buf.Buffer);
}
outputBuffers = null;
OutputQueue.Dispose ();
}
}
/// <summary>
/// Stops the OutputQueue
/// </summary>
public void Pause ()
{
OutputQueue.Pause ();
Started = false;
}
/// <summary>
/// Starts the OutputQueue
/// </summary>
public void Play ()
{
OutputQueue.Start ();
Started = true;
}
/// <summary>
/// Main methode to kick off the streaming, just send the bytes to this method
/// </summary>
public void ParseBytes (byte[] buffer, int count, bool discontinuity, bool lastPacket)
{
this.lastPacket = lastPacket;
fileStream.ParseBytes (buffer, 0, count, discontinuity);
}
public void Dispose ()
{
Dispose (true);
GC.SuppressFinalize (this);
}
public void Dispose(int tt) {
}
/// <summary>
/// Cleaning up all the native Resource
/// </summary>
protected virtual void Dispose (bool disposing)
{
if (disposing) {
if (OutputQueue != null)
OutputQueue.Stop(true);
if (outputBuffers != null) {
foreach (var b in outputBuffers)
OutputQueue.FreeBuffer (b.Buffer);
outputBuffers.Clear ();
outputBuffers = null;
}
if (fileStream != null) {
fileStream.Close ();
fileStream = null;
}
if (OutputQueue != null) {
OutputQueue.Dispose ();
OutputQueue = null;
}
}
}
/// <summary>
/// Saving the decoded Packets to our active Buffer, if the Buffer is full queue it into the OutputQueue
/// and wait until another buffer gets freed up
/// </summary>
void AudioPacketDecoded (object sender, PacketReceivedEventArgs args)
{
foreach (var p in args.PacketDescriptions) {
currentByteCount += p.DataByteSize;
AudioStreamPacketDescription pd = p;
int left = bufferSize - currentBuffer.CurrentOffset;
if (left < pd.DataByteSize) {
EnqueueBuffer ();
WaitForBuffer ();
}
AudioQueue.FillAudioData (currentBuffer.Buffer, currentBuffer.CurrentOffset, args.InputData, (int)pd.StartOffset, pd.DataByteSize);
// Set new offset for this packet
pd.StartOffset = currentBuffer.CurrentOffset;
// Add the packet to our Buffer
currentBuffer.PacketDescriptions.Add (pd);
// Add the Size so that we know how much is in the buffer
currentBuffer.CurrentOffset += pd.DataByteSize;
}
if ((fileStream != null && currentByteCount == fileStream.DataByteCount) || lastPacket)
EnqueueBuffer ();
}
/// <summary>
/// Flush the current buffer and close the whole thing up
/// </summary>
public void FlushAndClose ()
{
if (OutputQueue != null) {
EnqueueBuffer ();
OutputQueue.Flush ();
}
Dispose ();
}
/// <summary>
/// Enqueue the active buffer to the OutputQueue
/// </summary>
void EnqueueBuffer ()
{
currentBuffer.IsInUse = true;
OutputQueue.EnqueueBuffer (currentBuffer.Buffer, currentBuffer.CurrentOffset, currentBuffer.PacketDescriptions.ToArray ());
queuedBufferCount++;
StartQueueIfNeeded ();
}
/// <summary>
/// Wait until a buffer is freed up
/// </summary>
void WaitForBuffer ()
{
int curIndex = outputBuffers.IndexOf (currentBuffer);
currentBuffer = outputBuffers [curIndex < outputBuffers.Count - 1 ? curIndex + 1 : 0];
lock (currentBuffer) {
while (currentBuffer.IsInUse)
Monitor.Wait (currentBuffer);
}
}
void StartQueueIfNeeded ()
{
if (Started)
return;
Play ();
}
/// <summary>
/// When a AudioProperty in the fed packets is found this callback is called
/// </summary>
void AudioPropertyFound (object sender, PropertyFoundEventArgs args)
{
if (args.Property == AudioFileStreamProperty.ReadyToProducePackets) {
Started = false;
if (OutputQueue != null)
OutputQueue.Dispose ();
OutputQueue = new OutputAudioQueue (fileStream.StreamBasicDescription);
if (OutputReady != null)
OutputReady (OutputQueue);
currentByteCount = 0;
OutputQueue.BufferCompleted += HandleBufferCompleted;
outputBuffers = new List<AudioBuffer> ();
for (int i = 0; i < MaxBufferCount; i++) {
IntPtr outBuffer;
OutputQueue.AllocateBuffer (BufferSize, out outBuffer);
outputBuffers.Add (new AudioBuffer () {
Buffer = outBuffer,
PacketDescriptions = new List<AudioStreamPacketDescription> ()
});
}
currentBuffer = outputBuffers.First ();
OutputQueue.MagicCookie = fileStream.MagicCookie;
}
}
/// <summary>
/// Is called when a buffer is completly read and can be freed up
/// </summary>
void HandleBufferCompleted (object sender, BufferCompletedEventArgs e)
{
queuedBufferCount--;
IntPtr buf = e.IntPtrBuffer;
foreach (var buffer in outputBuffers) {
if (buffer.Buffer != buf)
continue;
// free Buffer
buffer.PacketDescriptions.Clear ();
buffer.CurrentOffset = 0;
lock (buffer) {
buffer.IsInUse = false;
Monitor.Pulse (buffer);
}
}
if (queuedBufferCount == 0 && Finished != null)
Finished (this, new EventArgs ());
}
}
}
how can i change the code to allow that?
thanks in advance for helps.
Put this following code in your AppDelegate.csin the FinishedLaunchingmethod.
NSError sessionError = null;
AVAudioSession.SharedInstance().SetCategory(AVAudioSession.CategoryAmbient, out sessionError);
AVAudioSession.SharedInstance().SetActive(true, out sessionError);
Apps get suspended when the phone is put to sleep. If you wan't your app to continue executing, you have to register for background execution.
Here is a nice guide Jonathan Sagorin (if you don't mind putting it over from Swift/obj-c).
Basically the class you are looking for is AVAudioSession and it's SetActive Method to activate a background audio session.
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.
The System.Threading.EventWaitHandle type does not contain a definition for 'dispose'.
How am I supposed to dispose of the EventWaitHandle when I want to kill the object in which it is contained? I've been having a serious memory leak problem and I've tried setting the EventWaitHandle to null and the memory leak persists.
Here's my code (I'm using Bob Craven's library, I added only the dispose() method):
using System;
using System.Threading;
namespace Rlc.Cron
{
public class CronObject
{
public delegate void CronEvent(CronObject cronObject);
public event CronEvent OnCronTrigger;
public event CronEvent OnStarted;
public event CronEvent OnStopped;
public event CronEvent OnThreadAbort;
private CronObjectDataContext _cronObjectDataContext;
private Guid _id = Guid.NewGuid();
private object _startStopLock = new object();
private EventWaitHandle _wh = new AutoResetEvent(false);
private Thread _thread;
public bool _isStarted;
private bool _isStopRequested;
private DateTime _nextCronTrigger;
public Guid Id { get { return _id; } }
public object Object { get { return _cronObjectDataContext.Object; } }
public DateTime LastTigger { get { return _cronObjectDataContext.LastTrigger; } }
/// <summary>
/// Initializes a new instance of the <see cref="CronObject"/> class.
/// </summary>
/// <param name="cronObjectDataContext">The cron object data context.</param>
public CronObject(CronObjectDataContext cronObjectDataContext)
{
if (cronObjectDataContext == null)
{
throw new ArgumentNullException("cronObjectDataContext");
}
if (cronObjectDataContext.Object == null)
{
throw new ArgumentException("cronObjectDataContext.Object");
}
if (cronObjectDataContext.CronSchedules == null || cronObjectDataContext.CronSchedules.Count == 0)
{
throw new ArgumentException("cronObjectDataContext.CronSchedules");
}
_cronObjectDataContext = cronObjectDataContext;
}
/// <summary>
/// Starts this instance.
/// </summary>
/// <returns></returns>
public bool Start()
{
lock (_startStopLock)
{
// Can't start if already started.
//
if (_isStarted)
{
return false;
}
_isStarted = true;
_isStopRequested = false;
// This is a long running process. Need to run on a thread
// outside the thread pool.
//
_thread = new Thread(ThreadRoutine);
_thread.Start();
}
// Raise the started event.
//
if(OnStarted != null)
{
OnStarted(this);
}
return true;
}
/// <summary>
/// Stops this instance.
/// </summary>
/// <returns></returns>
public bool Stop()
{
lock (_startStopLock)
{
// Can't stop if not started.
//
if (!_isStarted)
{
return false;
}
_isStarted = false;
_isStopRequested = true;
// Signal the thread to wake up early
//
_wh.Set();
// Wait for the thread to join.
//
if(!_thread.Join(5000))
{
_thread.Abort();
// Raise the thread abort event.
//
if(OnThreadAbort != null)
{
OnThreadAbort(this);
}
}
}
// Raise the stopped event.
//
if(OnStopped != null)
{
OnStopped(this);
}
return true;
}
public void dispose(){
this.Stop ();
this.OnCronTrigger = null;
this.OnStarted=null;
this.OnStopped=null;
this.OnThreadAbort=null;
this._cronObjectDataContext=null;
this._startStopLock = null;
this._wh = null;
this._thread=null;
}
/// <summary>
/// Cron object thread routine.
/// </summary>
private void ThreadRoutine()
{
// Continue until stop is requested.
//
while(!_isStopRequested)
{
// Determine the next cron trigger
//
DetermineNextCronTrigger(out _nextCronTrigger);
TimeSpan sleepSpan = _nextCronTrigger - DateTime.Now;
if(sleepSpan.TotalMilliseconds < 0)
{
// Next trigger is in the past. Trigger the right away.
//
sleepSpan = new TimeSpan(0, 0, 0, 0, 50);
}
// Wait here for the timespan or until I am triggered
// to wake up.
//
if(!_wh.WaitOne(sleepSpan))
{
// Timespan is up...raise the trigger event
//
if(OnCronTrigger != null)
{
OnCronTrigger(this);
}
// Update the last trigger time.
//
_cronObjectDataContext.LastTrigger = DateTime.Now;
}
}
}
/// <summary>
/// Determines the next cron trigger.
/// </summary>
/// <param name="nextTrigger">The next trigger.</param>
private void DetermineNextCronTrigger(out DateTime nextTrigger)
{
nextTrigger = DateTime.MaxValue;
foreach (CronSchedule cronSchedule in _cronObjectDataContext.CronSchedules)
{
DateTime thisTrigger;
if(cronSchedule.GetNext(LastTigger, out thisTrigger))
{
if (thisTrigger < nextTrigger)
{
nextTrigger = thisTrigger;
}
}
}
}
~CronObject(){
Console.WriteLine ("===================CRONOBJECT DESTROYED!!===============");
}
}
}