public void Write(Span <byte> data, Meta meta)
        {
            UpdatedSubscriptionCache();

            var codec = meta?.Codec ?? Codec.OpusMusic;             // XXX a bit hacky

            switch (SendMode)
            {
            case TargetSendMode.None:
                break;

            case TargetSendMode.Voice:
                client.SendAudio(data, codec);
                break;

            case TargetSendMode.Whisper:
                client.SendAudioWhisper(data, codec, channelSubscriptionsCache, clientSubscriptionsCache);
                break;

            case TargetSendMode.WhisperGroup:
                client.SendAudioGroupWhisper(data, codec, GroupWhisperType, GroupWhisperTarget, GroupWhisperTargetId);
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(SendMode), "Unknown send target");
            }
        }
Example #2
0
        private void AudioSend()
        {
            lock (ffmpegLock)
            {
                if (ffmpegProcess == null)
                {
                    return;
                }

                if (audioBuffer == null || audioBuffer.Length < encoder.OptimalPacketSize)
                {
                    audioBuffer = new byte[encoder.OptimalPacketSize];
                }

                UpdatedSubscriptionCache();

                while (audioTimer.RemainingBufferDuration < audioBufferLength)
                {
                    int read = ffmpegProcess.StandardOutput.BaseStream.Read(audioBuffer, 0, encoder.OptimalPacketSize);
                    if (read == 0)
                    {
                        // check for premature connection drop
                        if (ffmpegProcess.HasExited && !hasTriedToReconnectAudio)
                        {
                            var expectedStopLength = GetCurrentSongLength();
                            if (expectedStopLength != TimeSpan.Zero)
                            {
                                var actualStopPosition = audioTimer.SongPosition;
                                if (actualStopPosition + retryOnDropBeforeEnd < expectedStopLength)
                                {
                                    Log.Write(Log.Level.Debug, "Connection to song lost, retrying at {0}", actualStopPosition);
                                    hasTriedToReconnectAudio = true;
                                    Position = actualStopPosition;
                                    return;
                                }
                            }
                        }

                        if (ffmpegProcess.HasExited &&
                            audioTimer.RemainingBufferDuration < TimeSpan.Zero &&
                            !encoder.HasPacket)
                        {
                            AudioStop();
                            OnSongEnd?.Invoke(this, new EventArgs());
                        }
                        return;
                    }

                    hasTriedToReconnectAudio = false;
                    audioTimer.PushBytes(read);

                    bool doSend = true;

                    switch (SendMode)
                    {
                    case TargetSendMode.None:
                        doSend = false;
                        break;

                    case TargetSendMode.Voice:
                        break;

                    case TargetSendMode.Whisper:
                    case TargetSendMode.WhisperGroup:
                        if (isStall)
                        {
                            if (++stallCount % StallCountInterval == 0)
                            {
                                stallNoErrorCount++;
                                if (stallNoErrorCount > StallNoErrorCountMax)
                                {
                                    stallCount = 0;
                                    isStall    = false;
                                }
                            }
                            else
                            {
                                doSend = false;
                            }
                        }
                        if (SendMode == TargetSendMode.Whisper)
                        {
                            doSend &= channelSubscriptionsCache.Length > 0 || clientSubscriptionsCache.Length > 0;
                        }
                        break;

                    default:
                        throw new InvalidOperationException();
                    }

                    // Save cpu when we know there is noone to send to
                    if (!doSend)
                    {
                        break;
                    }

                    AudioModifier.AdjustVolume(audioBuffer, read, volume);
                    encoder.PushPcmAudio(audioBuffer, read);

                    while (encoder.HasPacket)
                    {
                        var packet = encoder.GetPacket();
                        switch (SendMode)
                        {
                        case TargetSendMode.Voice:
                            tsFullClient.SendAudio(packet.Array, packet.Length, encoder.Codec);
                            break;

                        case TargetSendMode.Whisper:
                            tsFullClient.SendAudioWhisper(packet.Array, packet.Length, encoder.Codec, channelSubscriptionsCache, clientSubscriptionsCache);
                            break;

                        case TargetSendMode.WhisperGroup:
                            tsFullClient.SendAudioGroupWhisper(packet.Array, packet.Length, encoder.Codec, GroupWhisperType, GroupWhisperTarget);
                            break;
                        }
                        encoder.ReturnPacket(packet.Array);
                    }
                }
            }
        }