/// <summary>
        /// (Re)Start the transmission pipeline, getting to a state where we *can* send voice (but aren't yet)
        /// </summary>
        private void RestartTransmissionPipeline(string reason)
        {
            Log.Debug("Restarting transmission pipeline: '{0}'", reason);

            StopTransmissionPipeline();

            //Clear the flag for requesting an explicit reset.
            //We're about to apply a reset so the request will be satisfied.
            _pendingResetRequest = false;

            //No point starting a transmission pipeline if the network is not a client
            if (_network == null || !_network.Mode.IsClientEnabled())
            {
                return;
            }

            //Create new mic capture system
            var format = _microphone.StartCapture(_micName);

            //If we created a mic (can be null if e.g. there is no mic)
            if (format != null)
            {
                //Close and re-open all channels, propogating this restart to the receiving end
                _roomChannels.Refresh();
                _playerChannels.Refresh();

                //Create preprocessor and subscribe it to microphone (webrtc preprocessor always wants audio to drive VAD+AEC)
                _preprocessor = CreatePreprocessor(format);
                _preprocessor.UpstreamLatency = _microphone.Latency;
                _preprocessor.Start();
                _microphone.Subscribe(_preprocessor);

                //Sub VAD listeners to preprocessor
                for (var i = 0; i < _activationListeners.Count; i++)
                {
                    _preprocessor.Subscribe(_activationListeners[i]);
                }

                //Sub audio listeners to the preprocessor output
                for (var i = 0; i < _audioListeners.Count; i++)
                {
                    var al = _audioListeners[i];
                    al.Reset();
                    _preprocessor.Subscribe(al);
                }

                //Create encoder (not yet subscribed to receive audio data, we'll do that later)
                Log.AssertAndThrowPossibleBug(_network != null, "5F33336B-15B5-4A85-9B54-54352C74768E", "Network object is unexpectedly null");
                _encoder = new EncoderPipeline(_preprocessor.OutputFormat, _codecSettingsLoader.CreateEncoder(), _network);
            }
            else
            {
                Log.Warn("Failed to start microphone capture; local voice transmission will be disabled.");
            }
        }
        /// <summary>
        /// (Re)Start the transmission pipeline, getting to a state where we *can* send voice (but aren't yet)
        /// </summary>
        private void RestartTransmissionPipeline(string reason)
        {
            Log.Debug("Restarting transmission pipeline: '{0}'", reason);

            StopTransmissionPipeline();

            //If capture has been specifically disabled, exit out of starting it
            if (_encounteredFatalException)
            {
                return;
            }

#if !NCRUNCH
            Profiler.BeginSample("CapturePipelineManager: RestartTransmissionPipeline");
#endif

            try
            {
                //Clear the flag for requesting an explicit reset.
                //We're about to apply a reset so the request will be satisfied.
                _pendingResetRequest = false;

                //No point starting a transmission pipeline if the network is not a client
                if (_network == null || !_network.Mode.IsClientEnabled())
                {
                    return;
                }

                //Create new mic capture system
                var format = _microphone.StartCapture(_micName);

                //If we created a mic (can be null if e.g. there is no mic)
                if (format != null)
                {
                    //Close and re-open all channels, propogating this restart to the receiving end
                    _roomChannels.Refresh();
                    _playerChannels.Refresh();

                    //Create preprocessor and subscribe it to microphone (webrtc preprocessor always wants audio to drive VAD+AEC)
                    _preprocessor = CreatePreprocessor(format);
                    _preprocessor.UpstreamLatency = _microphone.Latency;
                    _preprocessor.Start();
                    _microphone.Subscribe(_preprocessor);

                    //Sub VAD listeners to preprocessor
                    for (var i = 0; i < _activationListeners.Count; i++)
                    {
                        _preprocessor.Subscribe(_activationListeners[i]);
                    }

                    //Sub audio listeners to the preprocessor output
                    for (var i = 0; i < _audioListeners.Count; i++)
                    {
                        var al = _audioListeners[i];
                        al.Reset();
                        _preprocessor.Subscribe(al);
                    }

                    //Create encoder (not yet subscribed to receive audio data, we'll do that later)
                    Log.AssertAndThrowPossibleBug(_network != null, "5F33336B-15B5-4A85-9B54-54352C74768E", "Network object is unexpectedly null");
                    _encoder = new EncoderPipeline(_preprocessor.OutputFormat, _codecSettingsLoader.CreateEncoder(), _network);
                }
                else
                {
                    Log.Warn("Failed to start microphone capture; local voice transmission will be disabled.");
                    _cannotStartMic = true;
                }
            }
            catch (Exception ex)
            {
                //We don't know what happened, but something went wrong. As a precaution kill the transmission pipeline (it will be restarted if necessary)
                StopTransmissionPipeline();

                Log.Error("Unexpected exception encountered starting microphone capture; local voice transmission will be disabled: {0}", ex);
                _encounteredFatalException = true;
            }
            finally
            {
#if !NCRUNCH
                Profiler.EndSample();
#endif
            }
        }
        public void Update(bool muted, float deltaTime)
        {
            //Delay the initial startup of the capture pipeline. This spreads out the cost of initialising Dissonance over more frames, preventing very large spikes caused by everything being set up at once
            _startupDelay--;
            if (_startupDelay > 0)
            {
                return;
            }

            _receivingPacketLossMonitor.Update();

            //Early exit if we don't need to record audio. Either because:
            // - Netmode doesn't require audio (i.e. dedicated server)
            // - A fatal exception occurred, permanently killing capture
            if (!_netModeRequiresPipeline || _encounteredFatalException || _cannotStartMic)
            {
                StopTransmissionPipeline();
                return;
            }

            //Update microphone and reset it either if there is a frame skip or the microphone requests a reset
            var skipped = _skipDetector.IsFrameSkip(deltaTime);
            var request = _microphone.IsRecording && _microphone.UpdateSubscribers();
            var netmode = _netModeRequiresPipeline && _encoder == null;

            if (skipped || request || _pendingResetRequest || netmode)
            {
                var reason = skipped ? "Detected a frame skip, forcing capture pipeline reset"
                             : netmode ? "Network mode changed"
                             : _pendingResetRequest ? "Applying external reset request"
                             : "Microphone requested a pipeline reset";

                if (skipped)
                {
                    //If warn level logging is turned on show some extra information in the reason (at the cost of a string allocation)
                    if (Log.IsWarn)
                    {
                        reason = string.Format("Detected a frame skip, forcing capture pipeline reset (Delta Time:{0})", deltaTime);
                    }
                    Log.Warn(reason);
                }

                RestartTransmissionPipeline(reason);
            }

            if (_encoder != null)
            {
                //If the encoder is finally stopped and still subscribed then unsubscribe and reset it. This puts it into a state ready to be used again.
                if (_encoder.Stopped && _encoderSubscribed)
                {
                    Log.Debug("Unsubscribing encoder from preprocessor");

                    _preprocessor.Unsubscribe(_encoder);
                    _encoder.Reset();
                    _encoderSubscribed = false;
                }

                //Determine if the encoder should be subscribed:
                // - If encoder is stopping (but has not yet stopped) then do not sub
                // - If mute is explicitly set then do not sub
                // - if there are open channels then sub
                var shouldSub = !(_encoder.Stopping && !_encoder.Stopped) &&
                                !muted &&
                                (_roomChannels.Count + _playerChannels.Count) > 0;

                //Change the encoder state to the desired state
                if (shouldSub != _encoderSubscribed)
                {
                    if (shouldSub)
                    {
                        Log.Debug("Subscribing encoder to preprocessor");

                        _encoder.Reset();
                        _preprocessor.Subscribe(_encoder);
                        _encoderSubscribed = true;
                    }
                    else
                    {
                        //If the encoder has not been told to stop, tell it now
                        if (!_encoder.Stopping)
                        {
                            //Set the encoder state to stopping - it will stop after it sends one final packet to end the stream
                            _encoder.Stop();
                            Log.Debug("Stopping encoder");
                        }
                        else
                        {
                            Log.Debug("Waiting for encoder to send last packet");
                        }
                    }
                }
                else
                {
                    //Log.Debug("Should Sub - Stopping:{0} Stopped:{1} Muted:{1}", _encoder.Stopping, _encoder.Stopped, muted);
                }

                //Propogate measured *incoming* packet loss to encoder as expected *outgoing* packet loss
                if (_encoder != null)
                {
                    _encoder.TransmissionPacketLoss = _receivingPacketLossMonitor.PacketLoss;
                }
            }
        }
        public void Update(bool muted, float deltaTime)
        {
            _receivingPacketLossMonitor.Update();

            //Update microphone and reset it either if there is a frame skip or the microphone requests a reset
            var skipped = _skipDetector.IsFrameSkip(deltaTime);
            var request = _microphone.IsRecording && _microphone.UpdateSubscribers();

            if (skipped || request)
            {
                if (skipped)
                {
                    Log.Warn("Detected a frame skip, forcing capture pipeline reset");
                }

                RestartTransmissionPipeline();
            }

            if (_encoder != null)
            {
                //If the encoder is finally stopped and still subscribed then unsubscribe and reset it. This puts it into a state ready to be used again.
                if (_encoder.Stopped && _encoderSubscribed)
                {
                    Log.Debug("Unsubscribing encoder from preprocessor");

                    _preprocessor.Unsubscribe(_encoder);
                    _encoder.Reset();
                    _encoderSubscribed = false;
                }

                //Check if the encoder state matches the desired state
                var shouldSub = !(_encoder.Stopping && !_encoder.Stopped) && !muted && (_roomChannels.Count + _playerChannels.Count) > 0;
                if (shouldSub != _encoderSubscribed)
                {
                    if (shouldSub)
                    {
                        Log.Debug("Subscribing encoder to preprocessor");

                        _encoder.Reset();
                        _preprocessor.Subscribe(_encoder);
                        _encoderSubscribed = true;
                    }
                    else
                    {
                        //Inform the encoder that it should stop encoding (after sending one final packet)
                        if (!_encoder.Stopping)
                        {
                            _encoder.Stop();
                            Log.Debug("Stopping encoder");
                        }
                        else
                        {
                            Log.Debug("Waiting for encoder to send last packet");
                        }
                    }
                }
                else
                {
                    //Log.Debug("Should Sub - Stopping:{0} Muted:{1}", _encoder.Stopping, muted);
                }

                //Propogate measured *incoming* packet loss to encoder as expected *outgoing* packet loss
                if (_encoder != null)
                {
                    _encoder.TransmissionPacketLoss = _receivingPacketLossMonitor.PacketLoss;
                }
            }
        }
Beispiel #5
0
 public void Subscribe(IMicrophoneHandler listener)
 {
     _preprocessing.Subscribe(listener);
 }