HttpWebRequest from AudioPlayerAgent
Asked Answered
C

2

0

I'm creating an app that plays an endless audio stream. There is a separate web service that I can query to get the title and artist of the currently playing track. What I want to do is query that service every 20 seconds and then set the track title/artist accordingly. Currently I'm using a background AudioPlayerAgent so that the stream can be played outside of my application. Here is the code I have so far:

public AudioPlayer()
    {
        if (!_classInitialized)
        {
            _classInitialized = true;
            // Subscribe to the managed exception handler
            Deployment.Current.Dispatcher.BeginInvoke(delegate
            {
                Application.Current.UnhandledException += AudioPlayer_UnhandledException;

            });
            trackTimer = new Timer(TrackTimerTick, null, 1000, 5000);
        }
    }

    public void TrackTimerTick(object state) {             
            // Create a HttpWebrequest object to the desired URL.
            HttpWebRequest trackRequest = (HttpWebRequest)HttpWebRequest.Create("<stream url>");
            // Start the asynchronous request.
            IAsyncResult result = (IAsyncResult)trackRequest.BeginGetResponse(new AsyncCallback(TrackCallback), trackRequest);
    }

    public void TrackCallback(IAsyncResult result) {
        if (BackgroundAudioPlayer.Instance.PlayerState == PlayState.Playing && result != null) {
            try {
                // State of request is asynchronous.
                HttpWebRequest trackRequest = (HttpWebRequest)result.AsyncState;
                HttpWebResponse trackResponse = (HttpWebResponse)trackRequest.EndGetResponse(result);
                using (StreamReader httpwebStreamReader = new StreamReader(trackResponse.GetResponseStream())) {
                    string results = httpwebStreamReader.ReadToEnd();
                    StringReader str = new StringReader(results);
                    XDocument trackXml = XDocument.Load(str);

                    string title = (from t in trackXml.Descendants("channel") select t.Element("title").Value).First<string>();
                    string artist = (from t in trackXml.Descendants("channel") select t.Element("artist").Value).First<string>();
                    if (BackgroundAudioPlayer.Instance.Track != null) {
                        AudioTrack track = BackgroundAudioPlayer.Instance.Track;
                        track.BeginEdit();
                        track.Title = title;
                        track.Artist = artist;
                        track.EndEdit();
                    }

                }
                trackResponse.Close();
                NotifyComplete();
            } catch (WebException e) {
                Debug.WriteLine(e);
                Debug.WriteLine(e.Response);
            } catch (Exception e) {
                Debug.WriteLine(e);
            }
        }  
    }

A web exception is thrown anytime that I try to read the response from the HttpWebRequest. Is this the right way to do this? Does anyone have suggestions as to how I can fix this?

Chopfallen answered 6/5, 2012 at 21:59 Comment(2)
Don't catch Exception. #1743440 msdn.microsoft.com/en-us/library/ms182137%28v=vs.100%29.aspxFerminafermion
@Sedgwickz comment seems to partially fix it, I can now get the track data correctly. However, once I call NotifyComplete(), the timer no longer ticks - any tips on how to fix this?Chopfallen
H
0

You're not closing the HttpWebResponse, which is a necessity. Also, there's an overload of XDocument.Load() that takes a Stream so you don't need to use a StreamReader at all.

EDIT: Sorry, I overlooked the Close() call at the end. But the other comment still applies.

If it doesn't solve the problem, atleast it makes your code look cleaner:

public void TrackCallback(IAsyncResult result) {
    if (BackgroundAudioPlayer.Instance.PlayerState == PlayState.Playing && result != null) {
        try {
            // State of request is asynchronous.
            HttpWebRequest trackRequest = (HttpWebRequest)result.AsyncState;
            using (HttpWebResponse trackResponse = (HttpWebResponse)trackRequest.EndGetResponse(result)){
                XDocument trackXml = XDocument.Load(trackResponse.GetResponseStream());

                string title = (from t in trackXml.Descendants("channel") select t.Element("title").Value).First<string>();
                string artist = (from t in trackXml.Descendants("channel") select t.Element("artist").Value).First<string>();
                if (BackgroundAudioPlayer.Instance.Track != null) {
                    AudioTrack track = BackgroundAudioPlayer.Instance.Track;
                    track.BeginEdit();
                    track.Title = title;
                    track.Artist = artist;
                    track.EndEdit();
                }
            }
            }
            NotifyComplete();
        } catch (WebException e) {
            Debug.WriteLine(e);
            Debug.WriteLine(e.Response);
        } catch (Exception e) {
            Debug.WriteLine(e);
        }
    }  
}
Heulandite answered 6/5, 2012 at 22:12 Comment(1)
Thanks for the reply. Unfortunately it still doesn't work, but my code does look better :)Chopfallen
R
0

This has to do with the AudioPlayer going out of scope after it begins playing the music. The AudioPlayer only lives for a fraction of time and it terminated after the call to NotifyComplete

Take a look at my reply to this post: AudioPlayerAgent, timer and webservice

More info: The background audio thread will "suspend" after NotifyComplete is called. The way back in is when the user changes play (OnUserAction), or when the song ends (OnPlayStateChanged). If you will continue to play, get the new info within the OnPlayStateChanged method.

Repentant answered 15/5, 2012 at 23:21 Comment(6)
Thank you for the reply. Are you saying that if I create a timer inside the Songs class that you reference in your reply, that timer will continue to tick?Chopfallen
I tried this how I think it should work, but the timer and any async HttpWebRequest calls still get terminated when I call NotifyComplete. Any tips on putting these on the background thread that doesn't get terminated when NotifyComplete is called?Chopfallen
The problem is that the stream is infinitely long so OnPlayStateChanged will never get called. I feel like the only way to fix my problem is to use a timer somewhere. Any ideas?Chopfallen
Only other way would be on the main UI thread or through a scheduled agent. But the agent only fires about every 30 minutesRepentant
Can you tell me how to put it on the main UI thread? Is there a disadvantage to doing that?Chopfallen
The main UI thread is the thread of your app (eg MainPage.xaml.cs). The main disadvantage of this is that the name of songs will not be updated when the user exists the app.Repentant

© 2022 - 2024 — McMap. All rights reserved.