#c# #.net #asynchronous #discord #discord.net
Вопрос:
Я писал бота discord на C#, чтобы воспроизводить музыку для моего личного канала discord. Все работает как волшебство при запуске в персональной версии Windows (Win10 и Win11). Когда я запускаю программу на любом из своих экземпляров Windows Server (WS2012, 2016 или 2022), она выполняет все, кроме потоковой передачи звука. Проблема, по-видимому, в том, что он зависает при записи в «PCMStream». Но я не могу понять, почему?
Может быть, на сервере отсутствует какой-то кодек? Или какое-то правило брандмауэра? Есть какие-нибудь идеи?
Соответствующий Код:
private async Task AudioPlaybackStreamAsync(IAudioClient client, SongRequests song)
{
_cancelToken = new CancellationTokenSource();
await Task.Delay(100);
// Start a new process and create an output stream. Decide between network or local.
m_Process = CreateNetworkStream(song.SongURL);
m_Stream = client.CreatePCMStream(AudioApplication.Music); // Consider setting custom bitrate, buffers, and packet loss props.
m_IsRunning = true;
m_IsPlaying = true;
int procID = m_Process.Id;
await Task.Delay(500); //allow ffmpeg to buffer some of the audio first.
bool exit = false;
Console.WriteLine($@"Playing '{song.SongTitle}' on thread '{procID}'");
int test = 0;
// We stream the audio in chunks.
while (!_cancelToken.IsCancellationRequested amp;amp; !exit)
{
test ;
// If the process is already over, we're finished. If something else kills this process, we stop.
if (m_Process == null || m_Process.HasExited)
{
break;
}
// If the stream is broken, we exit.
if (m_Stream == null) break;
// We pause within this function while it's 'not playing'.
if (!m_IsPlaying) continue;
// Read the stream in chunks.
int blockSize = m_BLOCK_SIZE; // Size of bytes to read per frame.
byte[] buffer = new byte[blockSize];
int byteCount;
try
{
byteCount = await m_Process.StandardOutput.BaseStream.ReadAsync(buffer, 0, blockSize, _cancelToken.Token);
// If the stream cannot be read or we reach the end of the file, we exit.
if (byteCount <= 0) break;
// Write out to the stream. Relies on m_Volume to adjust bytes accordingly.
Console.Write($@"Iteration {test}: Begin WriteAsync... ");//test
await m_Stream.WriteAsync(ScaleVolumeSafeAllocateBuffers(buffer, m_Volume), 0, byteCount, _cancelToken.Token);
Console.Write($@"Complete.{Environment.NewLine}");//test
}
catch (TaskCanceledException exception)
{
Console.WriteLine(exception);
LogHelper.Log(LogCategory.Error, LogSeverity.Normal, "TaskCanceledException", exception.ToString());
exit = true;
break;
}
catch (OperationCanceledException exception)
{
Console.WriteLine(exception);
LogHelper.Log(LogCategory.Error, LogSeverity.Normal, "OperationCanceledException", exception.ToString());
exit = true;
break;
}
catch (Exception exception)
{
Console.WriteLine(exception);
LogHelper.Log(LogCategory.Error, LogSeverity.Normal, "Playback Error", exception.ToString());
break;
}
}
// Kill the process, if it's lingering.
if (m_Process != null amp;amp; !m_Process.HasExited) m_Process.Kill();
// Flush the stream and wait until it's fully done before continuing.
if (m_Stream != null)
{
await m_Stream.FlushAsync(); //flush remaining
m_Stream.Close(); //close the connection
}
// Reset values. Basically clearing out values (Flush).
m_Process = null;
m_Stream = null;
m_IsPlaying = false;
// Set running to false.
m_IsRunning = false;
Console.WriteLine($@"Finished '{song.SongTitle}' on thread '{procID}'");
}
private Process CreateNetworkStream(string path)
{
try
{
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = "cmd.exe",
WorkingDirectory = Path.GetDirectoryName($@"{Directory.GetCurrentDirectory()}{ConfigurationManager.AppSettings["BinaryFolder"]}"),
Arguments = $@"/C youtube-dl -f 140 -o - {path} | ffmpeg -i pipe:0 -ac 2 -f s16le -ar 48000 pipe:1",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
};
return Process.Start(psi);
}
catch
{
Console.WriteLine($"Error while opening network stream : {path}");
return null;
}
}
Как вы можете видеть, я поместил некоторые выходные данные консоли отладки вокруг функции WriteAsync.
Console.Write($@"Iteration {test}: Begin WriteAsync... ");//test
await m_Stream.WriteAsync(ScaleVolumeSafeAllocateBuffers(buffer, m_Volume), 0, byteCount, _cancelToken.Token);
Console.Write($@"Complete.{Environment.NewLine}");//test
В личных окнах выходные данные будут записывать тысячи итераций и выполняться без проблем. Однако на серверах Windows он зависает очень быстро, обычно около 50-х годов. Он не ошибается и не возвращается, просто сидит там.
Это делается с помощью оболочки Discord Net (https://github.com/discord-net/Discord.Net), который, как я теперь понимаю, я должен просто опубликовать там, но я уже напечатал это, так что вот оно.
Комментарии:
1. На экземплярах Windows Server работает ли служба «Windows Audio» при проверке
services.msc
? Если он остановлен, можете ли вы запустить его, и проблема все еще возникает, если аудиосервис действительно может запуститься?