private async Task CheckPrebufferingAsync(MusicBuffer inStream, CancellationToken cancelToken, long size) { while (!inStream.BufferingCompleted && inStream.Length < size) { await Task.Delay(100, cancelToken); } Log.Message(LogSeverity.Debug, "Buffering successfull."); }
private async Task StreamAsync(Track currentTrack) { try { var response = await new HttpClient().GetStringAsync(string.Format("https://www.googleapis.com/youtube/v3/videos?part=contentDetails&id={0}&key={1}", currentTrack.Id, LumpiBot.Configuration.Bot.GoogleAPIKey)); var json = JsonConvert.DeserializeObject <YTRootObject>(response); if (json != null) { foreach (var o in json.items) { TotalTime = XmlConvert.ToTimeSpan(o.contentDetails.duration); } CurrentTrack = currentTrack.SourceVideo.Title; await currentTrack.sourceTextChannel.SendMessageAsync(string.Format("🎶 Now Playing **{0}** *({1})*", currentTrack.SourceVideo.Title, TotalTime)); } } catch (Exception ex) { Log.Message(LogSeverity.Error, ex.Message); } Music.isPlaying = true; bytesSent = (ulong)currentTrack.SkipTo * 3840 * 50; if (!Directory.Exists(LumpiBot.CacheFolder + Path.DirectorySeparatorChar + MusicCacheFolder + Path.DirectorySeparatorChar)) { Directory.CreateDirectory(LumpiBot.CacheFolder + Path.DirectorySeparatorChar + MusicCacheFolder + Path.DirectorySeparatorChar); } var filename = LumpiBot.CacheFolder + Path.DirectorySeparatorChar + MusicCacheFolder + Path.DirectorySeparatorChar + DateTime.Now.Ticks.ToString(); var inStream = new MusicBuffer(currentTrack, filename, _frameBytes * 100); var bufferTask = inStream.BufferSong(currentTrack.CancelTokenSource.Token).ConfigureAwait(false); try { var attempt = 0; var prebufferingTask = CheckPrebufferingAsync(inStream, currentTrack.CancelTokenSource.Token, 1.MiB()); //Fast connection can do this easy var finished = false; var count = 0; var sw = new Stopwatch(); var slowconnection = false; sw.Start(); while (!finished) { var t = await Task.WhenAny(prebufferingTask, Task.Delay(2000, currentTrack.CancelTokenSource.Token)); if (t != prebufferingTask) { count++; if (count == 10) { slowconnection = true; prebufferingTask = CheckPrebufferingAsync(inStream, currentTrack.CancelTokenSource.Token, 20.MiB()); Log.Message(LogSeverity.Warning, "Slow connection buffering more to ensure no disruption, consider hosting in cloud"); continue; } if (inStream.BufferingCompleted && count == 1) { Log.Message(LogSeverity.Warning, "Prebuffering canceled. Cannot get any data from the stream."); Music.isPlaying = false; return; } else { continue; } } else if (prebufferingTask.IsCanceled) { Log.Message(LogSeverity.Warning, "Prebuffering canceled. Cannot get any data from the stream."); Music.isPlaying = false; return; } finished = true; } sw.Stop(); Log.Message(LogSeverity.Debug, "Prebuffering successfully completed in " + sw.Elapsed); var outStream = audioClient.CreatePCMStream(AudioApplication.Music); int nextTime = Environment.TickCount + _milliseconds; byte[] buffer = new byte[_frameBytes]; while (!currentTrack.CancelTokenSource.Token.IsCancellationRequested) { var read = await inStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); if (read < _frameBytes) { Log.Message(LogSeverity.Debug, $"read {read}"); } unchecked { bytesSent += (ulong)read; } CurrentTime = TimeSpan.FromSeconds(bytesSent / (float)_frameBytes / (1000 / (float)_milliseconds)); if (read < _frameBytes) { if (read == 0) { if (inStream.BufferingCompleted) { break; } if (attempt++ == 20) { currentTrack.CancelTokenSource.Cancel(); break; } if (slowconnection) { Log.Message(LogSeverity.Warning, "Slow connection has disrupted music, waiting a bit for buffer..."); await Task.Delay(1000, currentTrack.CancelTokenSource.Token).ConfigureAwait(false); nextTime = Environment.TickCount + _milliseconds; } else { await Task.Delay(100, currentTrack.CancelTokenSource.Token).ConfigureAwait(false); nextTime = Environment.TickCount + _milliseconds; } } else { attempt = 0; } } else { attempt = 0; } while (currentTrack.isPaused) { await Task.Delay(200, currentTrack.CancelTokenSource.Token).ConfigureAwait(false); nextTime = Environment.TickCount + _milliseconds; } float calcvol = Volume / 100; buffer = ScaleVolumeSafeAllocateBuffers(buffer, calcvol); if (read != _frameBytes) { continue; } nextTime = unchecked (nextTime + _milliseconds); int delayMillis = unchecked (nextTime - Environment.TickCount); if (delayMillis > 0) { await Task.Delay(delayMillis, currentTrack.CancelTokenSource.Token).ConfigureAwait(false); } await outStream.WriteAsync(buffer, 0, read).ConfigureAwait(false); } } finally { await bufferTask; inStream.Dispose(); Music.isPlaying = false; await LumpiBot.Client.SetGameAsync(string.Empty); } }