1062 lines
25 KiB
C#
1062 lines
25 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using Crosstales.Radio.Model;
|
|
using Crosstales.Radio.Model.Enum;
|
|
using Crosstales.Radio.Util;
|
|
using NAudio.Wave;
|
|
using NLayer;
|
|
using NVorbis;
|
|
using UnityEngine;
|
|
|
|
namespace Crosstales.Radio
|
|
{
|
|
[ExecuteInEditMode]
|
|
[RequireComponent(typeof(AudioSource))]
|
|
[HelpURLAttribute("https://www.crosstales.com/media/data/assets/radio/api/class_crosstales_1_1_radio_1_1_radio_player.html")]
|
|
public class RadioPlayer : BasePlayer
|
|
{
|
|
[Header("Radio Station")]
|
|
[Tooltip("Radio station for this RadioPlayer.")]
|
|
public RadioStation Station;
|
|
|
|
[Tooltip("Play the radio on start on/off (default: false).")]
|
|
[Header("Behaviour Settings")]
|
|
public bool PlayOnStart;
|
|
|
|
[Tooltip("Starts and stops the RadioPlayer depending on the focus and running state (default: false).")]
|
|
public bool HandleFocus;
|
|
|
|
[Tooltip("Size of cache stream in KB (default: 512).")]
|
|
[Header("General Settings")]
|
|
public int CacheStreamSize = 512;
|
|
|
|
[Tooltip("Enable or disable legacy mode. This disables all record informations, but is more stable (default: false).")]
|
|
public bool LegacyMode;
|
|
|
|
[Tooltip("Capture the encoded PCM-stream from this RadioPlayer (default: false).")]
|
|
public bool CaptureDataStream;
|
|
|
|
private bool error;
|
|
|
|
private string errorMessage;
|
|
|
|
private MpegFile nLayerReader;
|
|
|
|
private Mp3FileReader nAudioReader;
|
|
|
|
private VorbisReader nVorbisReader;
|
|
|
|
private int oggCacheCleanFrameCount;
|
|
|
|
private bool stopped = true;
|
|
|
|
private bool bufferAvailable;
|
|
|
|
private bool playback;
|
|
|
|
private bool restarted;
|
|
|
|
private float maxPlayTime;
|
|
|
|
private Thread worker;
|
|
|
|
private RecordInfo recordInfo = new RecordInfo();
|
|
|
|
private RecordInfo nextRecordInfo = new RecordInfo();
|
|
|
|
private RecordInfo lastNextRecordInfo;
|
|
|
|
private float nextRecordDelay;
|
|
|
|
private Stream ms;
|
|
|
|
private static bool loggedUnsupportedPlatform;
|
|
|
|
private static int playCounter;
|
|
|
|
private static bool ignoreExcludeCodec;
|
|
|
|
private bool wasRunning;
|
|
|
|
public override RadioStation RadioStation
|
|
{
|
|
get
|
|
{
|
|
return Station;
|
|
}
|
|
set
|
|
{
|
|
Station = value;
|
|
}
|
|
}
|
|
|
|
public override bool isLegacyMode
|
|
{
|
|
get
|
|
{
|
|
return LegacyMode;
|
|
}
|
|
set
|
|
{
|
|
LegacyMode = value;
|
|
}
|
|
}
|
|
|
|
public override bool isCaptureDataStream
|
|
{
|
|
get
|
|
{
|
|
return CaptureDataStream;
|
|
}
|
|
set
|
|
{
|
|
CaptureDataStream = value;
|
|
}
|
|
}
|
|
|
|
public override AudioSource Source { get; protected set; }
|
|
|
|
public override AudioCodec Codec { get; protected set; }
|
|
|
|
public override float PlayTime { get; protected set; }
|
|
|
|
public override float BufferProgress { get; protected set; }
|
|
|
|
public override bool isPlayback
|
|
{
|
|
get
|
|
{
|
|
return playback;
|
|
}
|
|
}
|
|
|
|
public override bool isAudioPlaying
|
|
{
|
|
get
|
|
{
|
|
return playback && !isBuffering;
|
|
}
|
|
}
|
|
|
|
public override bool isBuffering
|
|
{
|
|
get
|
|
{
|
|
return !bufferAvailable;
|
|
}
|
|
}
|
|
|
|
public override float RecordPlayTime { get; protected set; }
|
|
|
|
public override RecordInfo RecordInfo
|
|
{
|
|
get
|
|
{
|
|
return recordInfo;
|
|
}
|
|
}
|
|
|
|
public override RecordInfo NextRecordInfo
|
|
{
|
|
get
|
|
{
|
|
return nextRecordInfo;
|
|
}
|
|
}
|
|
|
|
public override float NextRecordDelay
|
|
{
|
|
get
|
|
{
|
|
return nextRecordDelay;
|
|
}
|
|
}
|
|
|
|
public override long CurrentBufferSize
|
|
{
|
|
get
|
|
{
|
|
if (ms != null)
|
|
{
|
|
return ms.Length - ms.Position;
|
|
}
|
|
return 0L;
|
|
}
|
|
}
|
|
|
|
public override long CurrentDownloadSpeed
|
|
{
|
|
get
|
|
{
|
|
if (ms != null && PlayTime > 0f)
|
|
{
|
|
return (long)((float)ms.Length / PlayTime);
|
|
}
|
|
return 0L;
|
|
}
|
|
}
|
|
|
|
public override MemoryCacheStream DataStream { get; protected set; }
|
|
|
|
public override int Channels { get; protected set; }
|
|
|
|
public override int SampleRate { get; protected set; }
|
|
|
|
public static bool isPlaying
|
|
{
|
|
get
|
|
{
|
|
return playCounter > 0;
|
|
}
|
|
}
|
|
|
|
public void Awake()
|
|
{
|
|
Channels = 2;
|
|
SampleRate = 44100;
|
|
oggCacheCleanFrameCount = UnityEngine.Random.Range(Constants.OGG_CLEAN_INTERVAL_MIN, Constants.OGG_CLEAN_INTERVAL_MAX);
|
|
Source = GetComponent<AudioSource>();
|
|
Source.playOnAwake = false;
|
|
Source.Stop();
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
if (PlayOnStart && !Helper.isEditorMode)
|
|
{
|
|
Play();
|
|
}
|
|
}
|
|
|
|
public void Update()
|
|
{
|
|
if (isAudioPlaying && !restarted)
|
|
{
|
|
if (lastNextRecordInfo == null || !lastNextRecordInfo.Equals(NextRecordInfo))
|
|
{
|
|
lastNextRecordInfo = NextRecordInfo;
|
|
onNextRecordChange(Station, NextRecordInfo, nextRecordDelay);
|
|
}
|
|
float num = Time.unscaledDeltaTime * Source.pitch;
|
|
Context.TotalPlayTime += num;
|
|
Station.TotalPlayTime += num;
|
|
PlayTime += num;
|
|
RecordPlayTime += num;
|
|
nextRecordDelay -= num;
|
|
onAudioPlayTimeUpdate(Station, PlayTime);
|
|
onRecordPlayTimeUpdate(Station, RecordInfo, RecordPlayTime);
|
|
onNextRecordDelayUpdate(Station, NextRecordInfo, nextRecordDelay);
|
|
if (PlayTime > maxPlayTime)
|
|
{
|
|
restarted = true;
|
|
Restart();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnDisable()
|
|
{
|
|
Stop();
|
|
}
|
|
|
|
public void OnValidate()
|
|
{
|
|
if (Station != null)
|
|
{
|
|
if (Station.Bitrate <= 0)
|
|
{
|
|
Station.Bitrate = Config.DEFAULT_BITRATE;
|
|
}
|
|
else
|
|
{
|
|
Station.Bitrate = Helper.NearestBitrate(Station.Bitrate, Station.Format);
|
|
}
|
|
if (Station.ChunkSize <= 0)
|
|
{
|
|
Station.ChunkSize = Config.DEFAULT_CHUNKSIZE;
|
|
}
|
|
else if (Station.ChunkSize > Config.MAX_CACHESTREAMSIZE)
|
|
{
|
|
Station.ChunkSize = Config.MAX_CACHESTREAMSIZE;
|
|
}
|
|
if (Station.BufferSize <= 0)
|
|
{
|
|
Station.BufferSize = Config.DEFAULT_BUFFERSIZE;
|
|
}
|
|
else
|
|
{
|
|
if (Station.Format == AudioFormat.MP3)
|
|
{
|
|
if (Station.BufferSize < Config.DEFAULT_BUFFERSIZE / 4)
|
|
{
|
|
Station.BufferSize = Config.DEFAULT_BUFFERSIZE / 4;
|
|
}
|
|
}
|
|
else if (Station.Format == AudioFormat.OGG && Station.BufferSize < 64)
|
|
{
|
|
Station.BufferSize = 64;
|
|
}
|
|
if (Station.BufferSize < Station.ChunkSize)
|
|
{
|
|
Station.BufferSize = Station.ChunkSize;
|
|
}
|
|
else if (Station.BufferSize > Config.MAX_CACHESTREAMSIZE)
|
|
{
|
|
Station.BufferSize = Config.MAX_CACHESTREAMSIZE;
|
|
}
|
|
}
|
|
}
|
|
if (CacheStreamSize <= 0)
|
|
{
|
|
CacheStreamSize = Config.DEFAULT_CACHESTREAMSIZE;
|
|
}
|
|
else if (Station != null && CacheStreamSize <= Station.BufferSize)
|
|
{
|
|
CacheStreamSize = Station.BufferSize;
|
|
}
|
|
else if (CacheStreamSize > Config.MAX_CACHESTREAMSIZE)
|
|
{
|
|
CacheStreamSize = Config.MAX_CACHESTREAMSIZE;
|
|
}
|
|
}
|
|
|
|
public void OnApplicationFocus(bool hasFocus)
|
|
{
|
|
if (!Application.runInBackground && HandleFocus)
|
|
{
|
|
if (!hasFocus)
|
|
{
|
|
wasRunning = playback;
|
|
Stop();
|
|
}
|
|
else if (wasRunning)
|
|
{
|
|
Play();
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void Play()
|
|
{
|
|
if (Station == null || Station.Name == null)
|
|
{
|
|
Debug.LogError("Station == null || Station.Name == null; check Radios_user.txt");
|
|
}
|
|
else if (Helper.isSupportedPlatform)
|
|
{
|
|
if (stopped)
|
|
{
|
|
if (Helper.isInternetAvailable)
|
|
{
|
|
if (Helper.isSane(ref Station))
|
|
{
|
|
Codec = Helper.AudioCodecForAudioFormat(Station.Format);
|
|
if (Codec == AudioCodec.None)
|
|
{
|
|
errorMessage = string.Concat(Station, Environment.NewLine, "Audio format not supported - can't play station: ", Station.Format);
|
|
Debug.LogError(errorMessage);
|
|
onErrorInfo(Station, errorMessage);
|
|
}
|
|
else if (!ignoreExcludeCodec && Station.ExcludedCodec == Codec)
|
|
{
|
|
errorMessage = string.Concat(Station, Environment.NewLine, "Excluded codec matched - can't play station: ", Codec);
|
|
Debug.LogError(errorMessage);
|
|
onErrorInfo(Station, errorMessage);
|
|
}
|
|
else
|
|
{
|
|
StartCoroutine(playAudioFromUrl());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorMessage = string.Concat(Station, Environment.NewLine, "Could not start playback. Please verify the station settings.");
|
|
Debug.LogError(errorMessage);
|
|
onErrorInfo(Station, errorMessage);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorMessage = "No internet connection available! Can't play (stream) any stations!";
|
|
Debug.LogError(errorMessage);
|
|
onErrorInfo(Station, errorMessage);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorMessage = string.Concat(Station, Environment.NewLine, "Station is already playing!");
|
|
Debug.LogWarning(errorMessage);
|
|
onErrorInfo(Station, errorMessage);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logUnsupportedPlatform();
|
|
}
|
|
}
|
|
|
|
public override void Stop()
|
|
{
|
|
playback = false;
|
|
if (Source != null)
|
|
{
|
|
Source.Stop();
|
|
Source.clip = null;
|
|
}
|
|
stopped = true;
|
|
}
|
|
|
|
public override void Silence()
|
|
{
|
|
Source.volume = 0f;
|
|
}
|
|
|
|
public override void Restart(float invokeDelay = 0.4f)
|
|
{
|
|
Stop();
|
|
Invoke("Play", invokeDelay);
|
|
}
|
|
|
|
public string ToShortString()
|
|
{
|
|
StringBuilder stringBuilder = new StringBuilder();
|
|
stringBuilder.Append("Station='");
|
|
stringBuilder.Append(Station);
|
|
stringBuilder.Append(Constants.TEXT_TOSTRING_DELIMITER);
|
|
stringBuilder.Append("PlayOnStart='");
|
|
stringBuilder.Append(PlayOnStart);
|
|
stringBuilder.Append(Constants.TEXT_TOSTRING_DELIMITER);
|
|
stringBuilder.Append("CacheStreamSize='");
|
|
stringBuilder.Append(CacheStreamSize);
|
|
stringBuilder.Append(Constants.TEXT_TOSTRING_DELIMITER_END);
|
|
return stringBuilder.ToString();
|
|
}
|
|
|
|
public void Load()
|
|
{
|
|
Debug.LogWarning("Not implemented!");
|
|
}
|
|
|
|
public void Save()
|
|
{
|
|
Debug.LogWarning("Not implemented!");
|
|
}
|
|
|
|
private IEnumerator playAudioFromUrl()
|
|
{
|
|
onPlaybackStart(Station);
|
|
playback = false;
|
|
restarted = false;
|
|
error = false;
|
|
errorMessage = string.Empty;
|
|
PlayTime = 0f;
|
|
RecordPlayTime = 0f;
|
|
BufferProgress = 0f;
|
|
bufferAvailable = false;
|
|
float _bufferCurrentProgress = 0f;
|
|
recordInfo = new RecordInfo();
|
|
nextRecordInfo = new RecordInfo();
|
|
nextRecordDelay = 0f;
|
|
onBufferingStart(Station);
|
|
onBufferingProgressUpdate(Station, BufferProgress);
|
|
using (Stream stream = (ms = new MemoryCacheStream(CacheStreamSize * 1024, Config.MAX_CACHESTREAMSIZE * 1024)))
|
|
{
|
|
if (LegacyMode)
|
|
{
|
|
worker = new Thread((ThreadStart)delegate
|
|
{
|
|
readStreamLegacy(ref Station, ref playback, ref ms, ref error, ref errorMessage);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
worker = new Thread((ThreadStart)delegate
|
|
{
|
|
readStream(ref Station, ref playback, ref ms, ref error, ref errorMessage, ref nextRecordInfo, ref nextRecordDelay);
|
|
});
|
|
}
|
|
worker.Start();
|
|
do
|
|
{
|
|
yield return null;
|
|
}
|
|
while (!playback && !stopped && !error);
|
|
int bufferSize = Station.BufferSize * 1024 + Station.ChunkSize * 1024;
|
|
do
|
|
{
|
|
BufferProgress = (float)ms.Length / (float)bufferSize;
|
|
if (BufferProgress != _bufferCurrentProgress)
|
|
{
|
|
onBufferingProgressUpdate(Station, BufferProgress);
|
|
_bufferCurrentProgress = BufferProgress;
|
|
}
|
|
yield return null;
|
|
}
|
|
while (playback && !stopped && ms.Length < bufferSize);
|
|
BufferProgress = 1f;
|
|
onBufferingProgressUpdate(Station, BufferProgress);
|
|
bufferAvailable = true;
|
|
onBufferingEnd(Station);
|
|
if (playback && !stopped)
|
|
{
|
|
bool _success = false;
|
|
try
|
|
{
|
|
if (Codec == AudioCodec.MP3_NLayer)
|
|
{
|
|
nLayerReader = new MpegFile(ms);
|
|
SampleRate = nLayerReader.SampleRate;
|
|
Channels = nLayerReader.Channels;
|
|
}
|
|
else if (Codec == AudioCodec.MP3_NAudio)
|
|
{
|
|
nAudioReader = new Mp3FileReader(ms);
|
|
SampleRate = nAudioReader.WaveFormat.SampleRate;
|
|
Channels = nAudioReader.WaveFormat.Channels;
|
|
}
|
|
else if (Codec == AudioCodec.OGG_NVorbis)
|
|
{
|
|
nVorbisReader = new VorbisReader(ms, false);
|
|
SampleRate = nVorbisReader.SampleRate;
|
|
Channels = nVorbisReader.Channels;
|
|
}
|
|
_success = true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogError(string.Concat(Station, Environment.NewLine, "Could not read data from url!", Environment.NewLine, ex));
|
|
}
|
|
if (!_success)
|
|
{
|
|
error = true;
|
|
errorMessage = string.Concat(Station, Environment.NewLine, "Could not play the stream -> Please try another station!");
|
|
Debug.LogError(errorMessage);
|
|
playback = false;
|
|
}
|
|
else
|
|
{
|
|
if (Codec == AudioCodec.OGG_NVorbis)
|
|
{
|
|
ms.Position = 0L;
|
|
}
|
|
maxPlayTime = int.MaxValue / SampleRate - 240;
|
|
DataStream = new MemoryCacheStream(131072, 524288);
|
|
AudioClip clip = AudioClip.Create(Station.Name, int.MaxValue, Channels, SampleRate, true, readPCMData);
|
|
Source.clip = clip;
|
|
Source.Play();
|
|
onAudioStart(Station);
|
|
}
|
|
do
|
|
{
|
|
yield return null;
|
|
if (Codec == AudioCodec.OGG_NVorbis && Time.frameCount % oggCacheCleanFrameCount == 0)
|
|
{
|
|
if (Constants.DEV_DEBUG)
|
|
{
|
|
Debug.Log("Clean cache: " + oggCacheCleanFrameCount + " - " + PlayTime);
|
|
}
|
|
Mdct.ClearSetupCache();
|
|
}
|
|
}
|
|
while (playback && !stopped);
|
|
Source.Stop();
|
|
Source.clip = null;
|
|
if (_success)
|
|
{
|
|
onAudioEnd(Station);
|
|
}
|
|
if (Codec == AudioCodec.MP3_NLayer)
|
|
{
|
|
if (nLayerReader != null)
|
|
{
|
|
nLayerReader.Dispose();
|
|
nLayerReader = null;
|
|
}
|
|
}
|
|
else if (Codec == AudioCodec.MP3_NAudio)
|
|
{
|
|
if (nAudioReader != null)
|
|
{
|
|
nAudioReader.Dispose();
|
|
nAudioReader = null;
|
|
}
|
|
}
|
|
else if (Codec == AudioCodec.OGG_NVorbis && nVorbisReader != null)
|
|
{
|
|
nVorbisReader.Dispose();
|
|
nVorbisReader = null;
|
|
Mdct.ClearSetupCache();
|
|
}
|
|
}
|
|
}
|
|
if (DataStream != null)
|
|
{
|
|
DataStream.Dispose();
|
|
}
|
|
if (error)
|
|
{
|
|
onErrorInfo(Station, errorMessage);
|
|
}
|
|
onPlaybackEnd(Station);
|
|
}
|
|
|
|
private void readPCMData(float[] data)
|
|
{
|
|
if (playback && !stopped && bufferAvailable)
|
|
{
|
|
if (Codec == AudioCodec.MP3_NLayer)
|
|
{
|
|
if (nLayerReader != null)
|
|
{
|
|
try
|
|
{
|
|
if (nLayerReader.ReadSamples(data, 0, data.Length) <= 0)
|
|
{
|
|
logNoMoreData();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logDataError(ex);
|
|
}
|
|
}
|
|
}
|
|
else if (Codec == AudioCodec.MP3_NAudio)
|
|
{
|
|
if (nAudioReader != null)
|
|
{
|
|
byte[] array = new byte[data.Length * 2];
|
|
try
|
|
{
|
|
int count;
|
|
if ((count = nAudioReader.Read(array, 0, array.Length)) > 0)
|
|
{
|
|
Buffer.BlockCopy(Helper.ConvertByteArrayToFloatArray(array, count), 0, data, 0, data.Length * 4);
|
|
}
|
|
else
|
|
{
|
|
logNoMoreData();
|
|
}
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
logDataError(ex2);
|
|
}
|
|
}
|
|
}
|
|
else if (Codec == AudioCodec.OGG_NVorbis && nVorbisReader != null)
|
|
{
|
|
try
|
|
{
|
|
if (nVorbisReader.ReadSamples(data, 0, data.Length) <= 0)
|
|
{
|
|
logNoMoreData();
|
|
}
|
|
}
|
|
catch (Exception ex3)
|
|
{
|
|
logDataError(ex3);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Buffer.BlockCopy(new float[data.Length], 0, data, 0, data.Length * 4);
|
|
}
|
|
if (CaptureDataStream && DataStream != null)
|
|
{
|
|
byte[] array2 = Helper.ConvertFloatArrayToByteArray(data, data.Length);
|
|
DataStream.Write(array2, 0, array2.Length);
|
|
}
|
|
}
|
|
|
|
private void readStream(ref RadioStation _station, ref bool _playback, ref Stream _ms, ref bool _error, ref string _errorMessage, ref RecordInfo _nextRecordInfo, ref float _nextRecordDelay)
|
|
{
|
|
if (_station.Url.StartsWith(Constants.PREFIX_HTTP) || _station.Url.StartsWith(Constants.PREFIX_HTTPS))
|
|
{
|
|
try
|
|
{
|
|
ServicePointManager.ServerCertificateValidationCallback = Helper.RemoteCertificateValidationCallback;
|
|
using (CTWebClient cTWebClient = new CTWebClient(int.MaxValue))
|
|
{
|
|
HttpWebRequest httpWebRequest = (HttpWebRequest)cTWebClient.CTGetWebRequest(_station.Url);
|
|
httpWebRequest.Headers.Clear();
|
|
httpWebRequest.Headers.Add("GET", "/ HTTP/1.1");
|
|
httpWebRequest.Headers.Add("Icy-MetaData", "1");
|
|
httpWebRequest.UserAgent = "WinampMPEG/5.09";
|
|
using (HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse())
|
|
{
|
|
int num = ((!string.IsNullOrEmpty(httpWebResponse.GetResponseHeader("icy-metaint"))) ? Convert.ToInt32(httpWebResponse.GetResponseHeader("icy-metaint")) : int.MaxValue);
|
|
if (Constants.DEV_DEBUG)
|
|
{
|
|
Debug.LogWarning("metaint: " + num);
|
|
}
|
|
using (Stream stream = httpWebResponse.GetResponseStream())
|
|
{
|
|
if (stream != null)
|
|
{
|
|
int num2 = 0;
|
|
byte[] array = new byte[_station.ChunkSize * 1024];
|
|
_playback = true;
|
|
Context.TotalDataRequests++;
|
|
_station.TotalDataRequests++;
|
|
int num3 = 0;
|
|
int num4 = 0;
|
|
bool flag = true;
|
|
_nextRecordDelay = 0f;
|
|
do
|
|
{
|
|
int num5;
|
|
if ((num5 = stream.Read(array, 0, array.Length)) > 0)
|
|
{
|
|
Context.TotalDataSize += num5;
|
|
_station.TotalDataSize += num5;
|
|
num3 = 0;
|
|
if (num > 0 && num5 + num4 > num)
|
|
{
|
|
int num6 = 0;
|
|
while (num6 < num5 && _playback)
|
|
{
|
|
if (num4 == num)
|
|
{
|
|
num4 = 0;
|
|
_ms.Write(array, num3, num6 - num3);
|
|
num3 = num6;
|
|
num2 = Convert.ToInt32(array[num6]) * 16;
|
|
num6++;
|
|
num3++;
|
|
if (num2 > 0)
|
|
{
|
|
if (num2 + num3 <= num5)
|
|
{
|
|
byte[] array2 = new byte[num2];
|
|
Array.Copy(array, num6, array2, 0, num2);
|
|
_nextRecordInfo = new RecordInfo(Encoding.UTF8.GetString(array2));
|
|
if (!flag)
|
|
{
|
|
_nextRecordDelay = (float)(_ms.Length - _ms.Position) / (float)(Station.Bitrate * 125);
|
|
}
|
|
else
|
|
{
|
|
flag = false;
|
|
}
|
|
num6 += num2;
|
|
num3 += num2;
|
|
if (Constants.DEV_DEBUG)
|
|
{
|
|
Debug.LogWarning("RecordInfo read: " + _nextRecordInfo);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Constants.DEV_DEBUG)
|
|
{
|
|
Debug.LogError("Info-frame outside of the buffer!");
|
|
}
|
|
num6 = num5;
|
|
num4 = num5 - (num2 + num3);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
num4++;
|
|
num6++;
|
|
}
|
|
}
|
|
if (num3 < num5)
|
|
{
|
|
_ms.Write(array, num3, num5 - num3);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
num4 += num5;
|
|
_ms.Write(array, 0, num5);
|
|
}
|
|
}
|
|
}
|
|
while (_playback);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_error = true;
|
|
_errorMessage = _station.Name + Environment.NewLine + "Could not read url after " + Helper.FormatSecondsToHourMinSec(PlayTime) + "!" + Environment.NewLine + ex;
|
|
Debug.LogError(_errorMessage);
|
|
_playback = false;
|
|
return;
|
|
}
|
|
}
|
|
readStreamLegacy(ref _station, ref _playback, ref _ms, ref _error, ref _errorMessage);
|
|
}
|
|
|
|
private void readStreamLegacy(ref RadioStation _station, ref bool _playback, ref Stream _ms, ref bool _error, ref string _errorMessage)
|
|
{
|
|
try
|
|
{
|
|
ServicePointManager.ServerCertificateValidationCallback = Helper.RemoteCertificateValidationCallback;
|
|
using (CTWebClient cTWebClient = new CTWebClient(int.MaxValue))
|
|
{
|
|
using (WebResponse webResponse = cTWebClient.CTGetWebRequest(_station.Url).GetResponse())
|
|
{
|
|
using (Stream stream = webResponse.GetResponseStream())
|
|
{
|
|
if (stream == null)
|
|
{
|
|
return;
|
|
}
|
|
byte[] array = new byte[_station.ChunkSize * 1024];
|
|
_playback = true;
|
|
Context.TotalDataRequests++;
|
|
_station.TotalDataRequests++;
|
|
do
|
|
{
|
|
int num;
|
|
if ((num = stream.Read(array, 0, array.Length)) > 0)
|
|
{
|
|
Context.TotalDataSize += num;
|
|
_station.TotalDataSize += num;
|
|
if (_playback)
|
|
{
|
|
_ms.Write(array, 0, num);
|
|
}
|
|
}
|
|
}
|
|
while (_playback);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_error = true;
|
|
_errorMessage = _station.Name + Environment.NewLine + "Could not read url after " + Helper.FormatSecondsToHourMinSec(PlayTime) + "!" + Environment.NewLine + ex;
|
|
Debug.LogError(_errorMessage);
|
|
_playback = false;
|
|
}
|
|
}
|
|
|
|
private void logNoMoreData()
|
|
{
|
|
error = true;
|
|
errorMessage = Station.Name + Environment.NewLine + "No more data to read after " + Helper.FormatSecondsToHourMinSec(PlayTime) + "! Please restart this station or choose another one.";
|
|
Debug.LogError(errorMessage);
|
|
playback = false;
|
|
}
|
|
|
|
private void logDataError(Exception ex)
|
|
{
|
|
error = true;
|
|
errorMessage = Station.Name + Environment.NewLine + "Could not read audio after " + Helper.FormatSecondsToHourMinSec(PlayTime) + "! This is typically a sign of a buffer underun -> Please try to increment the 'ChunkSize' and 'BufferSize':" + Environment.NewLine + ex;
|
|
Debug.LogError(errorMessage);
|
|
playback = false;
|
|
}
|
|
|
|
private void logUnsupportedPlatform()
|
|
{
|
|
if (!loggedUnsupportedPlatform)
|
|
{
|
|
errorMessage = "'Radio' is not supported on your platform!";
|
|
Debug.LogWarning(errorMessage);
|
|
onErrorInfo(Station, errorMessage);
|
|
}
|
|
}
|
|
|
|
private void onPlaybackStart(RadioStation station)
|
|
{
|
|
stopped = false;
|
|
playCounter++;
|
|
if (Config.DEBUG)
|
|
{
|
|
Debug.Log("onPlaybackStart: " + station);
|
|
}
|
|
if (_playbackStart != null)
|
|
{
|
|
_playbackStart(station);
|
|
}
|
|
}
|
|
|
|
private void onPlaybackEnd(RadioStation station)
|
|
{
|
|
stopped = true;
|
|
playCounter--;
|
|
if (Config.DEBUG)
|
|
{
|
|
Debug.Log("onPlaybackEnd: " + station);
|
|
}
|
|
if (recordInfo != null)
|
|
{
|
|
recordInfo.Duration = RecordPlayTime;
|
|
recordInfo = new RecordInfo();
|
|
}
|
|
if (_playbackEnd != null)
|
|
{
|
|
_playbackEnd(station);
|
|
}
|
|
}
|
|
|
|
private void onBufferingStart(RadioStation station)
|
|
{
|
|
if (Config.DEBUG)
|
|
{
|
|
Debug.Log("onBufferingStart: " + station);
|
|
}
|
|
if (_bufferingStart != null)
|
|
{
|
|
_bufferingStart(station);
|
|
}
|
|
}
|
|
|
|
private void onBufferingEnd(RadioStation station)
|
|
{
|
|
if (Config.DEBUG)
|
|
{
|
|
Debug.Log("onBufferingEnd: " + station);
|
|
}
|
|
if (_bufferingEnd != null)
|
|
{
|
|
_bufferingEnd(station);
|
|
}
|
|
}
|
|
|
|
private void onBufferingProgressUpdate(RadioStation station, float progress)
|
|
{
|
|
if (_bufferingProgressUpdate != null)
|
|
{
|
|
_bufferingProgressUpdate(station, progress);
|
|
}
|
|
}
|
|
|
|
private void onAudioStart(RadioStation station)
|
|
{
|
|
if (Config.DEBUG)
|
|
{
|
|
Debug.Log("onAudioStart: " + station);
|
|
}
|
|
if (_audioStart != null)
|
|
{
|
|
_audioStart(station);
|
|
}
|
|
}
|
|
|
|
private void onAudioEnd(RadioStation station)
|
|
{
|
|
if (Config.DEBUG)
|
|
{
|
|
Debug.Log("onAudioEnd: " + station);
|
|
}
|
|
if (_audioEnd != null)
|
|
{
|
|
_audioEnd(station);
|
|
}
|
|
}
|
|
|
|
private void onAudioPlayTimeUpdate(RadioStation station, float playtime)
|
|
{
|
|
if (_audioPlayTimeUpdate != null)
|
|
{
|
|
_audioPlayTimeUpdate(station, playtime);
|
|
}
|
|
}
|
|
|
|
private void onErrorInfo(RadioStation station, string info)
|
|
{
|
|
if (Config.DEBUG)
|
|
{
|
|
Debug.Log(string.Concat("onErrorInfo: ", station, " - ", info));
|
|
}
|
|
if (_errorInfo != null)
|
|
{
|
|
_errorInfo(station, info);
|
|
}
|
|
}
|
|
|
|
private void onRecordChange(RadioStation station, RecordInfo newRecord)
|
|
{
|
|
if (!newRecord.Equals(recordInfo))
|
|
{
|
|
if (Config.DEBUG)
|
|
{
|
|
Debug.Log(string.Concat("onRecordChange: ", station, " - ", newRecord));
|
|
}
|
|
if (recordInfo != null)
|
|
{
|
|
recordInfo.Duration = RecordPlayTime;
|
|
}
|
|
recordInfo = newRecord;
|
|
RecordPlayTime = 0f;
|
|
if (!string.IsNullOrEmpty(recordInfo.Info))
|
|
{
|
|
Station.PlayedRecords.Add(recordInfo);
|
|
}
|
|
if (_recordChange != null)
|
|
{
|
|
_recordChange(station, recordInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void onRecordPlayTimeUpdate(RadioStation station, RecordInfo record, float playtime)
|
|
{
|
|
if (_recordPlayTimeUpdate != null)
|
|
{
|
|
_recordPlayTimeUpdate(station, record, playtime);
|
|
}
|
|
}
|
|
|
|
private void onNextRecordChange(RadioStation station, RecordInfo nextRecord, float delay)
|
|
{
|
|
if (Config.DEBUG)
|
|
{
|
|
Debug.Log(string.Concat("onNextRecordChange: ", station, " - ", nextRecord));
|
|
}
|
|
if (_nextRecordChange != null)
|
|
{
|
|
_nextRecordChange(station, nextRecord, delay);
|
|
}
|
|
}
|
|
|
|
private void onNextRecordDelayUpdate(RadioStation station, RecordInfo nextRecord, float delay)
|
|
{
|
|
if (delay > 0f)
|
|
{
|
|
if (_nextRecordDelayUpdate != null)
|
|
{
|
|
_nextRecordDelayUpdate(station, nextRecord, delay);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
onRecordChange(station, nextRecord);
|
|
}
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
StringBuilder stringBuilder = new StringBuilder();
|
|
stringBuilder.Append(GetType().Name);
|
|
stringBuilder.Append(Constants.TEXT_TOSTRING_START);
|
|
stringBuilder.Append("Station='");
|
|
stringBuilder.Append(Station);
|
|
stringBuilder.Append(Constants.TEXT_TOSTRING_DELIMITER);
|
|
stringBuilder.Append("PlayOnStart='");
|
|
stringBuilder.Append(PlayOnStart);
|
|
stringBuilder.Append(Constants.TEXT_TOSTRING_DELIMITER);
|
|
stringBuilder.Append("CacheStreamSize='");
|
|
stringBuilder.Append(CacheStreamSize);
|
|
stringBuilder.Append(Constants.TEXT_TOSTRING_DELIMITER_END);
|
|
stringBuilder.Append(Constants.TEXT_TOSTRING_END);
|
|
return stringBuilder.ToString();
|
|
}
|
|
}
|
|
}
|