private async Task <AudioEncodingProperties> GetEncodingPropertiesAsync(KeyValuePair <string, string>[] headers)
        {
            //if this happens to be an Icecast 2 server, it'll send the audio information for us.
            if (headers.Any(x => x.Key.ToLower() == "ice-audio-info"))
            {
                //looks like it is indeed an Icecast 2 server. lets strip out the data and be on our way.

                serverType = ShoutcastServerType.Icecast;

                /* example: ice-audio-info: ice-bitrate=32;ice-samplerate=32000;ice-channels=2
                 * "Note that unlike SHOUTcast, it is not necessary to parse ADTS audio frames to obtain the Audio Sample Rate."
                 * from: http://www.indexcom.com/streaming/player/Icecast2.html
                 */

                string headerValue = headers.First(x => x.Key.ToLower() == "ice-audio-info").Value;

                //split the properties and values and parsed them into a usable object.
                KeyValuePair <string, string>[] propertiesAndValues = headerValue.Split(';')
                                                                      .Select(x => new KeyValuePair <string, string>(
                                                                                  x.Substring(0, x.IndexOf("=")).ToLower().Trim(),
                                                                                  x.Substring(x.IndexOf("=") + 1))).ToArray();

                //grab each value that we need.

                if (AudioInfo.BitRate == 0) //usually this is sent in the regular headers. grab it if it isn't.
                {
                    AudioInfo.BitRate = uint.Parse(propertiesAndValues.First(x => x.Key == "ice-bitrate" || x.Key == "bitrate").Value);
                }


                if (propertiesAndValues.Any(x => x.Key == "ice-channels" || x.Key == "channels") && propertiesAndValues.Any(x => x.Key == "ice-samplerate" || x.Key == "samplerate"))
                {
                    AudioInfo.ChannelCount = uint.Parse(propertiesAndValues.First(x => x.Key == "ice-channels" || x.Key == "channels").Value);
                    AudioInfo.SampleRate   = uint.Parse(propertiesAndValues.First(x => x.Key == "ice-samplerate" || x.Key == "samplerate").Value);

                    //now just create the appropriate AudioEncodingProperties object.
                    switch (AudioInfo.AudioFormat)
                    {
                    case StreamAudioFormat.MP3:
                        return(AudioEncodingProperties.CreateMp3(AudioInfo.SampleRate, AudioInfo.ChannelCount, AudioInfo.BitRate));

                    case StreamAudioFormat.AAC:
                    case StreamAudioFormat.AAC_ADTS:
                        return(AudioEncodingProperties.CreateAacAdts(AudioInfo.SampleRate, AudioInfo.ChannelCount, AudioInfo.BitRate));
                    }
                }
                else
                {
                    //something is missing from audio-info so we need to fallback.

                    return(await ParseEncodingFromMediaAsync());
                }
            }
            else
            {
                return(await ParseEncodingFromMediaAsync());
            }

            return(null);
        }
        public ShoutcastMediaSourceStream(Uri url, ShoutcastServerType stationServerType = ShoutcastServerType.Shoutcast, string relativePath = ";", bool getMetadata = true)
        {
            StationInfo = new ShoutcastStationInfo();

            streamUrl = url;

            serverType = stationServerType;

            socket = new StreamSocket();

            AudioInfo = new ServerAudioInfo();

            ShouldGetMetadata = getMetadata;

            this.relativePath = relativePath;
        }
        private async Task <Tuple <bool, KeyValuePair <string, string>[]> > EstablishConnectionAsync()
        {
            //http://www.smackfu.com/stuff/programming/shoutcast.html
            try
            {
                await socket.ConnectAsync(new Windows.Networking.HostName(streamUrl.Host), streamUrl.Port.ToString());

                socketWriter = new DataWriter(socket.OutputStream);
                socketReader = new DataReader(socket.InputStream);
            }
            catch (Exception ex)
            {
                if (MediaStreamSource != null)
                {
                    MediaStreamSource.NotifyError(MediaStreamSourceErrorStatus.FailedToConnectToServer);
                }
                else
                {
                    throw new Exception("Connection Error", ex);
                }

                return(new Tuple <bool, KeyValuePair <string, string>[]>(false, null));
            }

            //todo figure out how to resolve http requests better to get rid of this hack.
            String httpPath = "";

            if (streamUrl.Host.Contains("radionomy.com") || serverType == ShoutcastServerType.Radionomy)
            {
                httpPath   = streamUrl.LocalPath;
                serverType = ShoutcastServerType.Radionomy;
            }
            else
            {
                httpPath = "/" + relativePath;
            }

            socketWriter.WriteString("GET " + httpPath + " HTTP/1.1" + Environment.NewLine);

            if (ShouldGetMetadata)
            {
                socketWriter.WriteString("Icy-MetaData: 1" + Environment.NewLine);
            }

            socketWriter.WriteString("Host: " + streamUrl.Host + (streamUrl.Port != 80 ? ":" + streamUrl.Port : "") + Environment.NewLine);
            socketWriter.WriteString("Connection: Keep-Alive" + Environment.NewLine);
            socketWriter.WriteString("User-Agent: " + (UserAgent ?? "Shoutcast Player (http://github.com/Amrykid/UWPShoutcastMSS)") + Environment.NewLine);
            socketWriter.WriteString(Environment.NewLine);
            await socketWriter.StoreAsync();

            await socketWriter.FlushAsync();

            string response = string.Empty;

            while (!response.EndsWith(Environment.NewLine + Environment.NewLine))
            {
                await socketReader.LoadAsync(1);

                response += socketReader.ReadString(1);
            }

            //todo support http 2.0. maybe usage of the http client would solve this.
            if (response.StartsWith("HTTP/1.0 200 OK") || response.StartsWith("HTTP/1.1 200 OK") || response.StartsWith("ICY 200"))
            {
                var headers = ParseResponse(response);

                return(new Tuple <bool, KeyValuePair <string, string>[]>(true, headers));
            }
            else
            {
                //wasn't successful. handle each case accordingly.

                if (response.StartsWith("HTTP /1.0 302") || response.StartsWith("HTTP/1.1 302"))
                {
                    socketReader.Dispose();
                    socketWriter.Dispose();
                    socket.Dispose();

                    var parsedResponse = ParseHttpResponseToKeyPairArray(response.Split(new string[] { "\r\n" }, StringSplitOptions.None).Skip(1).ToArray());

                    socket    = new StreamSocket();
                    streamUrl = new Uri(parsedResponse.First(x => x.Key.ToLower() == "location").Value);

                    return(await EstablishConnectionAsync());
                }
                else if (response.StartsWith("HTTP/1.0 404"))
                {
                    throw new Exception("Station is unavailable.");
                }
                else if (response.StartsWith("ICY 401")) //ICY 401 Service Unavailable
                {
                    if (MediaStreamSource != null)
                    {
                        MediaStreamSource.NotifyError(MediaStreamSourceErrorStatus.FailedToConnectToServer);
                    }
                    else
                    {
                        throw new Exception("Station is unavailable at this time. Maybe they're down for maintainence?");
                    }

                    return(new Tuple <bool, KeyValuePair <string, string>[]>(false, null));
                }
                else if (response.StartsWith("HTTP/1.1 503")) //HTTP/1.1 503 Server limit reached
                {
                    throw new Exception("Station is unavailable at this time. The maximum amount of listeners has been reached.");
                }
            }

            return(new Tuple <bool, KeyValuePair <string, string>[]>(false, null)); //not connected and no headers.
        }