/// <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; } } }
public void Subscribe(IMicrophoneHandler listener) { _preprocessing.Subscribe(listener); }