不和.使用ffmpeg网络发送音频只能工作一次(第一次)



上下文:

所以我正在写一个小机器人,应该在命令下播放本地音频文件。我正在使用Discord。Net、ffmpeg、opus和libsodium。我有一个Speak((函数,理论上应该是

打开ffmpeg->encode.mp3->创建PCM流->将编码后的.mp3输出(PCM流(。

看起来是这样的:

public async Task Speak(IGuild guild, Sound.SoundName soundName)
{
IAudioClient client;
if (ConnectedChannels.TryGetValue(guild.Id, out client))
{
using (var ffmpeg = CreateStream(sound.Filename))
using (var stream = client.CreatePCMStream(AudioApplication.Voice, 98304))
using (var output = ffmpeg.StandardOutput.BaseStream)
{
try
{
await output.CopyToAsync(stream);                            
}
catch (Exception ex) { Console.WriteLine("Error " + ex.Message); Console.WriteLine($"- {ex.StackTrace}"); }
finally { await stream.FlushAsync(); }
Console.WriteLine("Spoken!");
}
}
private Process CreateStream(string _path) 
{
var process = Process.Start(new ProcessStartInfo
{
FileName = "ffmpeg",
Arguments = $"-hide_banner -loglevel panic -i "{_path}" -ac 2 -f s16le -ar 48000 pipe:1",
UseShellExecute = false,
RedirectStandardOutput = true,
});
return process;
}

它这样做,但只有一次。更具体地说,当我的机器人加入语音频道时,它会将VoiceChannel ID和IAudioClient保存在private static readonly ConcurrentDictionary<ulong, IAudioClient> ConnectedChannels = new ConcurrentDictionary<ulong, IAudioClient>();中。之后,它会自动调用Speak((函数来播放hello.mp3音频。这个过程是这样完成的:

[Command("join", RunMode = RunMode.Async)]
[RequireUserPermission(GuildPermission.MentionEveryone)]
public async Task JoinChannel(IVoiceChannel channel = null) 
{
// Get the Voice channel
channel = channel ?? (Context.User as IGuildUser)?.VoiceChannel;
if (channel == null) { await Context.Channel.SendMessageAsync("You are not in a VoiceChannel"); return; }
// Saving the AudioClient
var audioClient = await channel.ConnectAsync();
Console.WriteLine($"Connected to channel {channel.Name}");
ConnectedChannels.TryAdd(channel.Guild.Id, audioClient);
await Task.Delay(1000);
await Speak(Context.Guild, Sound.SoundName.hello);
}

这个效果很好,音频播放。

现在,机器人程序已连接到VoiceChannel,并且IAudioClient已保存到Dictionary中。只要机器人在VoiceChannel中,我应该可以随时调用Speak((函数,也可以从Whereever调用,对吧?

没有

然后带我去我的

问题:

在机器人现在安静地坐在语音通道中之后;说";代码中的命令如下:

[Command("speak")]
public async Task speakSingle() 
{
await Speak(Context.Guild, Sound.SoundName.Random);
}

但机器人仍然保持沉默,即使Discord中的语音指示器亮起!我错过了什么?我不明白。它是在发送一条空流吗
即使我断开机器人与VoiceChannel的连接并重新连接,它也不会发送音频。唯一有帮助的是从服务器重新连接Bot
我对C#、Streams和异步编程很陌生。有人能帮我找出问题并解决吗?

错误(CommandPromt Ouput(:

1#
当我运行";说";命令,im得到"的NullReferenceException;不一致WebSocket.pdb未加载";在VisualStudio中。尽管我找不到任何无效的东西。。。

17:34:17 Audio #1    System.Exception: WebSocket connection was closed
---> Discord.Net.WebSocketClosedException: The server sent close 4008: "Rate limited."
at Discord.Net.WebSockets.DefaultWebSocketClient.RunAsync(CancellationToken cancelToken)
--- End of inner exception stack trace ---
at Discord.ConnectionManager.<>c__DisplayClass29_0.<<StartAsync>b__0>d.MoveNext()

2#
当机器人重新加入语音频道并自动执行Speak((函数时,会发生这种情况。

Error A task was canceled.
-    at Discord.Audio.Streams.BufferedWriteStream.WriteAsync(Byte[] data, Int32 offset, Int32 count, CancellationToken cancelToken)
at Discord.Audio.Streams.OpusEncodeStream.WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancelToken)
at System.IO.Stream.CopyToAsyncInternal(Stream destination, Int32 bufferSize, CancellationToken cancellationToken)
at Bot.Modules.VoiceChannel.Speak(IGuild guild, SoundName soundName) in Y:DokumenteCodingC#Vs_StudioBotBotModulesVoiceChannel.cs:line 90



这是我在StackOverflow上的第一个问题,我希望我提供了足够的上下文,如果没有,请告诉我。

我知道这是一篇旧帖子,但希望这能帮助任何寻求解决方案的人。

问题似乎是,当CreatePCMStream被处理时,在将机器人重新连接到语音频道之前,您无法创建新的,我不确定这是discord.net的错误,还是discords api的工作原理。

我最终做的是在第一次调用speak方法后将AudioOutStream存储在ConcurrentDictionary中,然后在bot断开连接时处理音频流并将其从ConcurrentDiction中删除。

这解决了播放多个音频文件而不必断开机器人与语音频道的连接的问题。

相关内容

最新更新