/// <summary> /// Completely shuts down the AudioTrackStreamer. /// </summary> public static void ShutdownMediaStreamSource() { if (AudioTrackStreamer.mss != null) { lock (AudioTrackStreamer.syncRoot) { if (AudioTrackStreamer.mss != null) { // Because of the NotifyComplete(), we need to set this BEFORE the MSS ends. ShoutcastMediaStreamSource temp = AudioTrackStreamer.mss; AudioTrackStreamer.mss = null; temp.MetadataChanged -= new System.Windows.RoutedEventHandler(AudioTrackStreamer.MetadataChanged); temp.Dispose(); } } } }
/// <summary> /// Called when a new track requires audio decoding /// (typically because it is about to start playing) /// </summary> /// <param name="track"> /// The track that needs audio streaming /// </param> /// <param name="streamer"> /// The AudioStreamer object to which a MediaStreamSource should be /// attached to commence playback /// </param> /// <remarks> /// To invoke this method for a track set the Source parameter of the AudioTrack to null /// before setting into the Track property of the BackgroundAudioPlayer instance /// property set to true; /// otherwise it is assumed that the system will perform all streaming /// and decoding /// </remarks> protected override void OnBeginStreaming(AudioTrack track, AudioStreamer streamer) { lock (AudioTrackStreamer.syncRoot) { try { AudioTrackStreamer.mss = new ShoutcastMediaStreamSource(new Uri(track.Tag)); AudioTrackStreamer.mss.MetadataChanged += new RoutedEventHandler(AudioTrackStreamer.MetadataChanged); AudioTrackStreamer.mss.Closed += (s, e) => { this.NotifyComplete(); }; streamer.SetSource(AudioTrackStreamer.mss); } catch (Exception ex) { Debug.WriteLine("----------==============="); Debug.WriteLine("OnBeginStreaming:" + ex); } } }
/// <summary> /// Parses the passed in MediaStream to find the first frame and signals /// to its parent MediaElement that it is ready to begin playback by calling /// ReportOpenMediaCompleted. /// </summary> protected override void OpenMediaAsync() { System.Diagnostics.Debug.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString() + ": OpenMediaAsync()"); // So, here is why this is a little weird. // The Shoutcast server software has the ability to provide web pages. These pages just happen to be served from the SAME address as the media stream. // Putting a "/;" at the end of the Uri will tell the Shoutcast server that we aren't a web browser, so stream the data. The problem is that not ALL // Shoutcast servers are configured that way. So, we have to do a request to get the content type. If it is text/html, we append the "/;" and move on. // If it is an empty string, 99.9% of the time, this will be the media stream (If it's an ICY stream, the ICY "headers" don't parse properly). The ShoutcastStream // will handle this case, so we let it go through. HttpWebRequest contentTypeRequest = ShoutcastMediaStreamSource.CreateHttpWebRequest(this.StreamUri, this.IncludeMetadata); contentTypeRequest.BeginGetResponse( ia1 => { HttpWebRequest req1 = ia1.AsyncState as HttpWebRequest; try { HttpWebResponse res1 = (HttpWebResponse)req1.EndGetResponse(ia1); string contentType = res1.ContentType; if ((contentType == string.Empty) || (contentType == "audio/mpeg") || contentType == "audio/x-mpegurl") { try { this.audioStream = new ShoutcastStream(this, res1); this.audioStreamDescription = this.audioStream.AudioStreamDescription; this.ReportOpenMediaCompleted(this.audioStream.AudioSourceAttributes, new MediaStreamDescription[] { this.audioStream.AudioStreamDescription }); } catch (Exception ex) { this.CleanupAudioStream(); this.ErrorOccurred(ex.Message); } } else { // Close the original response. We need another one. res1.Close(); res1 = null; if (!this.StreamUri.OriginalString.EndsWith("/", StringComparison.Ordinal)) { this.StreamUri = new Uri(this.StreamUri.OriginalString + "/;", UriKind.Absolute); } else { this.StreamUri = new Uri(this.StreamUri.OriginalString + ";", UriKind.Absolute); } HttpWebRequest streamRequest = ShoutcastMediaStreamSource.CreateHttpWebRequest(this.StreamUri, this.IncludeMetadata); streamRequest.BeginGetResponse( ia => { HttpWebRequest req = ia.AsyncState as HttpWebRequest; try { HttpWebResponse res = (HttpWebResponse)req.EndGetResponse(ia); this.audioStream = new ShoutcastStream(this, res); this.audioStreamDescription = this.audioStream.AudioStreamDescription; this.ReportOpenMediaCompleted(this.audioStream.AudioSourceAttributes, new MediaStreamDescription[] { this.audioStream.AudioStreamDescription }); } catch (Exception ex) { if (res1 != null) { res1.Close(); } this.CleanupAudioStream(); this.ErrorOccurred(ex.Message); } }, streamRequest); } } catch (Exception ex) { this.CleanupAudioStream(); this.ErrorOccurred(ex.Message); } }, contentTypeRequest); }
/// <summary> /// Initializes a new instance of the ShoutcastStream class. /// </summary> /// <param name="mediaStreamSource">ShoutcastMediaStreamSource containing this ShoutcastStream.</param> /// <param name="httpWebResponse">HttpWebResponse for MP3 stream request.</param> /// <param name="numberOfSecondsToBuffer">Number of seconds of audio data to buffer.</param> /// <param name="metadataEncoding">Text encoding used to decode the Shoutcast metadata.</param> public ShoutcastStream(ShoutcastMediaStreamSource mediaStreamSource, HttpWebResponse httpWebResponse, int numberOfSecondsToBuffer, Encoding metadataEncoding) { System.Diagnostics.Debug.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString() + ": ShoutcastStream() ctor"); if (mediaStreamSource == null) { throw new ArgumentNullException("mediaStreamSource"); } if (httpWebResponse == null) { throw new ArgumentNullException("httpWebResponse"); } if (metadataEncoding == null) { throw new ArgumentNullException("encoding"); } this.mediaStreamSource = mediaStreamSource; this.includeMetadata = this.mediaStreamSource.IncludeMetadata; this.numberOfSecondsToBuffer = numberOfSecondsToBuffer; this.metadataEncoding = metadataEncoding; // If the request is bad, this will die first, so we can just not worry about it. this.innerStream = httpWebResponse.GetResponseStream(); try { // Read a chunk of data, but likely one smaller than the circular buffer size. byte[] initialBuffer = new byte[ShoutcastStream.DefaultInitialBufferSize]; // Make sure we have enough data to work with initially int bytesRead = this.ForceReadFromStream(initialBuffer, 0, initialBuffer.Length); if (bytesRead == 0) { // Should this be -1? // This means there was something wrong with the stream. throw new InvalidOperationException("Zero initial bytes read from stream."); } this.MediaInformation = this.FindStreamInformation(httpWebResponse, ref initialBuffer); if (this.MediaInformation == null) { throw new ArgumentException("Invalid MediaInformation"); } this.icyMetadata = this.MediaInformation.MetadataInterval; this.ParseInitialBuffer(initialBuffer); } catch (Exception) { // No matter what, we need to shut down! if (this.innerStream != null) { this.innerStream.Dispose(); this.innerStream = null; } // Rethrow throw; } this.backgroundWorker = new BackgroundWorker() { WorkerReportsProgress = false, WorkerSupportsCancellation = true }; this.backgroundWorker.DoWork += new DoWorkEventHandler(this.DoWork); this.backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.RunWorkerCompleted); this.backgroundWorker.RunWorkerAsync(this.innerStream); }
/// <summary> /// Initializes a new instance of the ShoutcastStream class. /// </summary> /// <param name="mediaStreamSource">ShoutcastMediaStreamSource containing this ShoutcastStream.</param> /// <param name="httpWebResponse">HttpWebResponse for MP3 stream request.</param> /// <param name="numberOfSecondsToBuffer">Number of seconds of audio data to buffer.</param> public ShoutcastStream(ShoutcastMediaStreamSource mediaStreamSource, HttpWebResponse httpWebResponse, int numberOfSecondsToBuffer) : this(mediaStreamSource, httpWebResponse, numberOfSecondsToBuffer, Encoding.UTF8) { }
/// <summary> /// Initializes a new instance of the ShoutcastStream class. /// </summary> /// <param name="mediaStreamSource">ShoutcastMediaStreamSource containing this ShoutcastStream.</param> /// <param name="httpWebResponse">HttpWebResponse for MP3 stream request.</param> public ShoutcastStream(ShoutcastMediaStreamSource mediaStreamSource, HttpWebResponse httpWebResponse) : this(mediaStreamSource, httpWebResponse, ShoutcastStream.DefaultSecondsToBuffer) { }