2017-07-10 19:08:11 +00:00
|
|
|
|
using NLog;
|
2017-07-01 15:16:03 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
|
2017-07-17 02:37:51 +00:00
|
|
|
|
namespace NadekoBot.Modules.Music.Common
|
2017-07-01 15:16:03 +00:00
|
|
|
|
{
|
|
|
|
|
public class SongBuffer : IDisposable
|
|
|
|
|
{
|
2017-07-03 21:27:17 +00:00
|
|
|
|
const int readSize = 81920;
|
2017-07-01 15:16:03 +00:00
|
|
|
|
private Process p;
|
2017-07-07 10:16:01 +00:00
|
|
|
|
private Stream _outStream;
|
2017-07-01 15:16:03 +00:00
|
|
|
|
|
|
|
|
|
private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);
|
2017-07-02 11:53:09 +00:00
|
|
|
|
private readonly Logger _log;
|
2017-07-01 15:16:03 +00:00
|
|
|
|
|
|
|
|
|
public string SongUri { get; private set; }
|
|
|
|
|
|
2017-07-06 17:20:00 +00:00
|
|
|
|
public SongBuffer(string songUri, string skipTo, bool isLocal)
|
2017-07-01 15:16:03 +00:00
|
|
|
|
{
|
2017-07-02 11:53:09 +00:00
|
|
|
|
_log = LogManager.GetCurrentClassLogger();
|
2017-07-01 15:16:03 +00:00
|
|
|
|
this.SongUri = songUri;
|
2017-07-06 17:20:00 +00:00
|
|
|
|
this._isLocal = isLocal;
|
2017-07-10 19:21:45 +00:00
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
this.p = StartFFmpegProcess(SongUri, 0);
|
|
|
|
|
this._outStream = this.p.StandardOutput.BaseStream;
|
|
|
|
|
}
|
|
|
|
|
catch (System.ComponentModel.Win32Exception)
|
|
|
|
|
{
|
|
|
|
|
_log.Error(@"You have not properly installed or configured FFMPEG.
|
|
|
|
|
Please install and configure FFMPEG to play music.
|
|
|
|
|
Check the guides for your platform on how to setup ffmpeg correctly:
|
|
|
|
|
Windows Guide: https://goo.gl/OjKk8F
|
|
|
|
|
Linux Guide: https://goo.gl/ShjCUo");
|
|
|
|
|
}
|
|
|
|
|
catch (OperationCanceledException) { }
|
|
|
|
|
catch (InvalidOperationException) { } // when ffmpeg is disposed
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_log.Info(ex);
|
|
|
|
|
}
|
2017-07-04 09:23:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Process StartFFmpegProcess(string songUri, float skipTo = 0)
|
|
|
|
|
{
|
2017-07-06 17:20:00 +00:00
|
|
|
|
var args = $"-err_detect ignore_err -i {songUri} -f s16le -ar 48000 -vn -ac 2 pipe:1 -loglevel error";
|
|
|
|
|
if (!_isLocal)
|
|
|
|
|
args = "-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5 " + args;
|
|
|
|
|
|
2017-07-04 09:23:34 +00:00
|
|
|
|
return Process.Start(new ProcessStartInfo
|
2017-07-01 15:16:03 +00:00
|
|
|
|
{
|
|
|
|
|
FileName = "ffmpeg",
|
2017-07-06 17:20:00 +00:00
|
|
|
|
Arguments = args,
|
2017-07-01 15:16:03 +00:00
|
|
|
|
UseShellExecute = false,
|
|
|
|
|
RedirectStandardOutput = true,
|
2017-09-15 20:17:31 +00:00
|
|
|
|
RedirectStandardError = false,
|
2017-07-01 15:16:03 +00:00
|
|
|
|
CreateNoWindow = true,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-03 18:26:17 +00:00
|
|
|
|
private readonly object locker = new object();
|
2017-07-06 17:20:00 +00:00
|
|
|
|
private readonly bool _isLocal;
|
|
|
|
|
|
2017-07-03 18:26:17 +00:00
|
|
|
|
public int Read(byte[] b, int offset, int toRead)
|
2017-07-01 15:16:03 +00:00
|
|
|
|
{
|
2017-07-03 18:26:17 +00:00
|
|
|
|
lock (locker)
|
|
|
|
|
return _outStream.Read(b, offset, toRead);
|
2017-07-01 15:16:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
2017-07-03 19:05:35 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
this.p.StandardOutput.Dispose();
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_log.Error(ex);
|
|
|
|
|
}
|
2017-07-03 21:27:17 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if(!this.p.HasExited)
|
|
|
|
|
this.p.Kill();
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
}
|
2017-07-01 15:16:03 +00:00
|
|
|
|
_outStream.Dispose();
|
|
|
|
|
this.p.Dispose();
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-09-15 20:17:31 +00:00
|
|
|
|
}
|