2

I am new to UCMA and I am learning as I go through examples. I am trying to build 2 Lync clients A and B with the scenario as follows,

  1. A calls B
  2. B answers
  3. A plays audio
  4. B records it using Recorder.

I am stuck at trying to record the call at B. For B its an incoming call. I need to attach the audiovideoflow to the recorder, but I am not sure on how to do it. I will appreciate any help. Apologies on the unformatted code, I am not sure how to format it properly, I tried.

Thanks. Kris

Client B Code:

  1. Accepts an incoming call
  2. Records the media received in the incoming call. ***This is the part I have trouble

    using System; using System.Threading; using Microsoft.Rtc.Collaboration; using Microsoft.Rtc.Collaboration.AudioVideo; using Microsoft.Rtc.Signaling; using Microsoft.Rtc.Collaboration.Lync;

    namespace Microsoft.Rtc.Collaboration.LyncUAS { public class LyncUAS { #region Locals private LyncUASConfigurationHelper _helper; private UserEndpoint _userEndpoint; private AudioVideoCall _audioVideoCall; private AudioVideoFlow _audioVideoFlow; private Conversation _incomingConversation;

        //Wait handles are only present to keep things synchronous and easy to read.
        private AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
        private EventHandler<AudioVideoFlowConfigurationRequestedEventArgs> _audioVideoFlowConfigurationRequestedEventHandler;
        private EventHandler<MediaFlowStateChangedEventArgs> _audioVideoFlowStateChangedEventHandler;
        private AutoResetEvent _waitForAudioVideoCallEstablishCompleted = new AutoResetEvent(false);
        private AutoResetEvent _waitForAudioVideoFlowStateChangedToActiveCompleted = new AutoResetEvent(false);
        private AutoResetEvent _waitForPrepareSourceCompleted = new AutoResetEvent(false);
    
        #endregion
    
        #region Methods
        /// <summary>
        /// Instantiate and run the DeclineIncomingCall quickstart.
        /// </summary>
        /// <param name="args">unused</param>
        public static void Main(string[] args)
        {
            LyncUAS lyncUAS = new LyncUAS();
            lyncUAS.Run();
        }
    
        private void Run()
        {
           string filename = "received.wma";
    
            _helper = new LyncUASConfigurationHelper();
    
            // Create a user endpoint, using the network credential object 
            // defined above.
            _userEndpoint = _helper.CreateEstablishedUserEndpoint("Lync UAS" /*endpointFriendlyName*/);
            _userEndpoint.RegisterForIncomingCall<AudioVideoCall>(On_AudioVideoCall_Received);
            Console.WriteLine("Waiting for incoming call...");
            _autoResetEvent.WaitOne();
            Console.WriteLine("came after call is connected");
            //start recording for audio.
    
            Recorder recorder = new Recorder();            
    
            recorder.StateChanged += new EventHandler<RecorderStateChangedEventArgs>(recorder_StateChanged);
            recorder.VoiceActivityChanged += new EventHandler<VoiceActivityChangedEventArgs>(recorder_VoiceActivityChanged);         
    

    //**********This is the issue, currently _audioVideoFlow is null, it is not attached to the flow //So this will fail, how to attach _audioVideoFlow to an incoming call ?? HELP !!!

           // recorder.AttachFlow(_audioVideoFlow);  ------------> HELP!
    
    
            WmaFileSink sink = new WmaFileSink(filename);
            recorder.SetSink(sink);
            recorder.Start();
            Console.WriteLine("Started Recording ...");
            _autoResetEvent.WaitOne();
            recorder.Stop();
            Console.WriteLine("Stopped Recording ...");
            recorder.DetachFlow();
            Console.WriteLine("Exiting");
            Thread.Sleep(2000); 
        }
    
    
        private void audioVideoFlow_StateChanged(object sender, MediaFlowStateChangedEventArgs e)
        {
            Console.WriteLine("Flow state changed from " + e.PreviousState + " to " + e.State);
    
            //When flow is active, media operations can begin
            if (e.State == MediaFlowState.Active)
            {
                // Flow-related media operations normally begin here.
                _waitForAudioVideoFlowStateChangedToActiveCompleted.Set();
            }
    
            // call sample event handler
            if (_audioVideoFlowStateChangedEventHandler != null)
            {
                _audioVideoFlowStateChangedEventHandler(sender, e);
            }
        }
    
    
        void recorder_VoiceActivityChanged(object sender, VoiceActivityChangedEventArgs e)
        {
            Console.WriteLine("Recorder detected " + (e.IsVoice ? "voice" : "silence") + " at " + e.TimeStamp);
        }
    
        void recorder_StateChanged(object sender, RecorderStateChangedEventArgs e)
        {
            Console.WriteLine("Recorder state changed from " + e.PreviousState + " to " + e.State);
        }
    
        void On_AudioVideoCall_Received(object sender, CallReceivedEventArgs<AudioVideoCall> e)
        {
            //Type checking was done by the platform; no risk of this being any 
            // type other than the type expected.
            _audioVideoCall = e.Call;
    
            // Call: StateChanged: Only hooked up for logging, to show the call 
            // state transitions.
            _audioVideoCall.StateChanged += new
                EventHandler<CallStateChangedEventArgs>(_audioVideoCall_StateChanged);
            _incomingConversation = new Conversation(_userEndpoint);
    
            Console.WriteLine("Call Received! From: " + e.RemoteParticipant.Uri + " Toast is: " +e.ToastMessage.Message);
            _audioVideoCall.BeginAccept(
                 ar =>
                 {
                     try { 
                         _audioVideoCall.EndAccept(ar);
                         Console.WriteLine("Call must be connected at this point. "+_audioVideoCall.State);
    
                         _autoResetEvent.Set();
                          } catch (RealTimeException ex) { Console.WriteLine(ex); }
                 }, null);
    
        }
    
        //Just to record the state transitions in the console.
        void _audioVideoCall_StateChanged(object sender, CallStateChangedEventArgs e)
        {
            Console.WriteLine("Call has changed state. The previous call state was: " + e.PreviousState +
                " and the current state is: " + e.State);
            if (e.State == CallState.Terminated)
            {               
                Console.WriteLine("Shutting down");
                _autoResetEvent.Set();
                _helper.ShutdownPlatform();
            }
        }
        #endregion
    }
    

    }

Kris A
  • 21
  • 2
  • +1 On this. I have kinda same issue. Having a incoming AVCall, where my Flow object is null, which means I cannot retrieve it back from hold etc. – grmihel Nov 12 '14 at 15:32

1 Answers1

0

I think I have figured out what's not quite right here.

Your Code

    // Create a user endpoint, using the network credential object 
    // defined above.
    _userEndpoint = _helper.CreateEstablishedUserEndpoint("Lync UAS" /*endpointFriendlyName*/);
    _userEndpoint.RegisterForIncomingCall<AudioVideoCall>(On_AudioVideoCall_Received);
    Console.WriteLine("Waiting for incoming call...");
    _autoResetEvent.WaitOne();
    Console.WriteLine("came after call is connected");
    //start recording for audio.

    Recorder recorder = new Recorder();            

    recorder.StateChanged += new EventHandler<RecorderStateChangedEventArgs>(recorder_StateChanged);
    recorder.VoiceActivityChanged += new EventHandler<VoiceActivityChangedEventArgs>(recorder_VoiceActivityChanged);         

     //**********This is the issue, currently _audioVideoFlow is null, it is not attached to the flow //So this will fail, how to attach _audioVideoFlow to an incoming call ?? HELP !!!
     // recorder.AttachFlow(_audioVideoFlow);  ------------> HELP!

Looking good so far. I'm assuming you're establishing and such in your CreateEstablishedUserEndpoint method, but I'm not seeing where you're getting the value for _audioVideoFlow.

I'm guessing you might be doing it elsewhere, but on the off chance that's actually where you're running into problems, here's that bit:

Simplest pattern to get AVFlow

    public static void RegisterForIncomingCall(LocalEndpoint localEndpoint)
    {
        localEndpoint.RegisterForIncomingCall
                          <AudioVideoCall>(IncomingCallDelegate);
    }

    private static void IncomingCallDelegate(object sender, CallReceivedEventArgs<AudioVideoCall> e)
    {
        e.Call.AudioVideoFlowConfigurationRequested += IncomingCallOnAudioVideoFlowConfigurationRequested;
    }

    private static void IncomingCallOnAudioVideoFlowConfigurationRequested(object sender, AudioVideoFlowConfigurationRequestedEventArgs e)
    {
        AudioVideoFlow audioVideoFlow = e.Flow; // <--- There's your flow, gentleman.
    }

Now, instead of registering for your incoming call, just call RegisterForIncomingCall(_userEndpoint);.

Your AVFlow will be hanging off e.Flow above, you could then pass that into your recorder: recorder.AttachFlow(e.Flow) or simply assign the flow to a field in your class and autoResetEvent.WaitOne(); and set that up where you're setting that up now.

Obviously this is a pretty naive implementation. A lot can go wrong in those few lines of code (exception handling/static event handler memory leak comes immediately to mind); don't forget to wire up events related to status changes on the conversation/call and endpoints, as well as any of the recovery related items.

mdip
  • 600
  • 4
  • 10