banshee r4988 - in trunk/banshee: . src/Core/Banshee.Services/Banshee.MediaEngine src/Core/Banshee.Services/Banshee.Streaming src/Core/Banshee.ThickClient/Banshee.Gui src/Core/Banshee.ThickClient/Banshee.Gui.Widgets src/Core/Banshee.Widgets/Banshee.Widgets



Author: gburt
Date: Fri Jan 30 18:45:53 2009
New Revision: 4988
URL: http://svn.gnome.org/viewvc/banshee?rev=4988&view=rev

Log:
2009-01-30  Gabriel Burt  <gabriel burt gmail com>

	* src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs: Show the
	pause/stop button when Loading/Loaded.

	* src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/ConnectedSeekSlider.cs:
	Handle Loading player state, set Live property on the position label, and
	use the position label's enum state property instead of a handful of bools

	* src/Core/Banshee.Services/Banshee.Streaming/RadioTrackInfo.cs: Start
	playing internet radio playlists in a separate thread to avoid blocking
	the UI (BGO #548040, BNC #376532).  Handle the case where the user
	presses stop or switches to a different track while we're
	downloading/processing the playlist file (previously we would have started
	playing once ready, even if the user had already moved on).  Also, fix
	bug where errors reported by the MediaEngine (GStreamer) weren't shown to
	the user.

	* src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngine.cs: If the
	engine is in PlayerState.Contacting, don't Close/stop since this is a
	state we set in managed land for loading internet radio tracks.

	* src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs:
	Add Start/EndSynthesizeContacting internal methods for use by
	RadioTrackInfo - basically a hack until we move the playlist loading logic
	from RadioTrackInfo to the PlayerENgine to get UI bits updated
	appropriately.  Fix IsPlaying to include Contacting/Loading/Loaded, so
	that TogglePlaying will Pause if in any of those states (not just if in
	the Playing state), and Play only when in Ready, Idle, or Paused.

	* src/Core/Banshee.Widgets/Banshee.Widgets/StreamPositionLabel.cs: Use an
	enum to track the player state instead of a handful of bools.  Show the
	"Contacting..." label when inthe Loading state (for now - will change to
	Loading... in a separate patch).

	* src/Core/Banshee.Widgets/Banshee.Widgets/SeekSlider.cs: If the new Value
	is more than the Duration, set the Duration to MaxValue so that the slider
	stays at the left side of the Range, avoiding the glitchy-looking UI going
	from Idle (where the slider is all the way left) to playing radio (where
	it was all the way right).



Modified:
   trunk/banshee/ChangeLog
   trunk/banshee/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngine.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Streaming/RadioTrackInfo.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/ConnectedSeekSlider.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs
   trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/SeekSlider.cs
   trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/StreamPositionLabel.cs

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngine.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngine.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngine.cs	Fri Jan 30 18:45:53 2009
@@ -90,7 +90,7 @@
 
         private void HandleOpen (SafeUri uri)
         {
-            if (current_state != PlayerState.Idle && current_state != PlayerState.NotReady) {
+            if (current_state != PlayerState.Idle && current_state != PlayerState.NotReady && current_state != PlayerState.Contacting) {
                 Close (false);
             }
         
@@ -106,7 +106,7 @@
         public abstract void Play ();
 
         public abstract void Pause ();
-        
+
         public virtual void VideoExpose (IntPtr displayContext, bool direct)
         {
             throw new NotImplementedException ("Engine must implement VideoExpose since this method only gets called when SupportsVideo is true");

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs	Fri Jan 30 18:45:53 2009
@@ -55,6 +55,7 @@
         private PlayerEngine pending_engine;
         private object pending_playback_for_not_ready;
         private bool pending_playback_for_not_ready_play;
+        private TrackInfo synthesized_contacting_track;
 
         private string preferred_engine_id = null;
 
@@ -339,9 +340,9 @@
                 // If we're at least 50% done playing a song, mark it as played, otherwise as skipped
                 // If the Length <= 0, assume the song was finished and increment its play count
                 if (active_engine.Length <= 0 || active_engine.Position >= active_engine.Length / 2) {
-                    active_engine.CurrentTrack.IncrementPlayCount ();
+                    CurrentTrack.IncrementPlayCount ();
                 } else {
-                    active_engine.CurrentTrack.IncrementSkipCount ();
+                    CurrentTrack.IncrementSkipCount ();
                 }
                 incremented_last_played = true;
             }
@@ -406,12 +407,32 @@
                 active_engine.Pause ();
             }
         }
+
+        // For use by RadioTrackInfo
+        // TODO remove this method once RadioTrackInfo playlist downloading/parsing logic moved here?
+        internal void StartSynthesizeContacting (TrackInfo track)
+        {
+            //OnStateChanged (PlayerState.Contacting);
+            RaiseEvent (new PlayerEventStateChangeArgs (CurrentState, PlayerState.Contacting));
+            synthesized_contacting_track = track;
+        }
+
+        internal void EndSynthesizeContacting (TrackInfo track, bool idle)
+        {
+            if (track == synthesized_contacting_track) {
+                synthesized_contacting_track = null;
+
+                if (idle) {
+                    RaiseEvent (new PlayerEventStateChangeArgs (PlayerState.Contacting, PlayerState.Idle));
+                }
+            }
+        }
         
         public void TogglePlaying ()
         {
-            if (CurrentState == PlayerState.Playing) {
+            if (IsPlaying ()) {
                 Pause ();
-            } else {
+            } else if (CurrentState != PlayerState.NotReady) {
                 Play ();
             }
         }
@@ -440,7 +461,9 @@
         {
             return CurrentState == PlayerState.Playing || 
                 CurrentState == PlayerState.Paused || 
-                CurrentState == PlayerState.Loaded;
+                CurrentState == PlayerState.Loaded ||
+                CurrentState == PlayerState.Loading ||
+                CurrentState == PlayerState.Contacting;
         }
 
         private void CheckPending ()
@@ -456,7 +479,7 @@
         }
     
         public TrackInfo CurrentTrack {
-            get { return active_engine.CurrentTrack; }
+            get { return active_engine.CurrentTrack ?? synthesized_contacting_track; }
         }
         
         private Dictionary<string, object> dbus_sucks;
@@ -482,7 +505,7 @@
         }
         
         public PlayerState CurrentState {
-            get { return active_engine.CurrentState; }
+            get { return synthesized_contacting_track != null ? PlayerState.Contacting : active_engine.CurrentState; }
         }
         
         string IPlayerEngineService.CurrentState {
@@ -532,11 +555,11 @@
                 uint length = active_engine.Length;
                 if (length > 0) {
                     return length;
-                } else if (active_engine.CurrentTrack == null) {
+                } else if (CurrentTrack == null) {
                     return 0;
                 }
                 
-                return (uint) active_engine.CurrentTrack.Duration.TotalSeconds;
+                return (uint) CurrentTrack.Duration.TotalSeconds;
             }
         }
     

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Streaming/RadioTrackInfo.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Streaming/RadioTrackInfo.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Streaming/RadioTrackInfo.cs	Fri Jan 30 18:45:53 2009
@@ -37,6 +37,8 @@
 using Banshee.Base;
 using Banshee.Collection;
 using Banshee.ServiceStack;
+using Banshee.MediaEngine;
+using Banshee.PlaybackController;
 using Banshee.Playlists.Formats;
  
 namespace Banshee.Streaming
@@ -75,11 +77,13 @@
             try {
                 RadioTrackInfo radio_track = new RadioTrackInfo (uri);
                 radio_track.ParsingPlaylistEvent += delegate {
-                    if (radio_track.PlaybackError != StreamPlaybackError.None) {
-                        Log.Error (Catalog.GetString ("Error opening stream"), 
-                            Catalog.GetString ("Could not open stream or playlist"), true);
-                        radio_track = null;
-                    }
+                    ThreadAssist.ProxyToMain (delegate {
+                        if (radio_track.PlaybackError != StreamPlaybackError.None) {
+                            Log.Error (Catalog.GetString ("Error opening stream"), 
+                                Catalog.GetString ("Could not open stream or playlist"), true);
+                            radio_track = null;
+                        }
+                    });
                 };
                 
                 return radio_track;
@@ -98,6 +102,7 @@
         private int stream_index = 0;
         private bool loaded = false;
         private bool parsing_playlist = false;
+        private bool trying_to_play;
         
         private TrackInfo parent_track;
         public TrackInfo ParentTrack {
@@ -135,14 +140,67 @@
 
         public void Play()
         {
-            if(!loaded) {
-                OnParsingPlaylistStarted();
-                ThreadPool.QueueUserWorkItem(delegate {
-                    LoadStreamUris();
-                });
+            if (trying_to_play) {
                 return;
             }
-            
+
+            trying_to_play = true;
+
+            if (loaded) {
+                PlayCore ();
+            } else {
+                // Stop playing until we load this radio station and play it
+                ServiceManager.PlayerEngine.Close ();
+
+                ServiceManager.PlayerEngine.TrackIntercept += OnTrackIntercept;
+
+                // Tell the seek slider that we're connecting
+                // TODO move all this playlist-downloading/parsing logic into PlayerEngine?
+                ServiceManager.PlayerEngine.StartSynthesizeContacting (this);
+
+                OnParsingPlaylistStarted ();
+                ThreadPool.QueueUserWorkItem (delegate {
+                    try {
+                        LoadStreamUris ();
+                    } catch (Exception e) {
+                        trying_to_play = false;
+                        Log.Exception (this.ToString (), e);
+                        SavePlaybackError (StreamPlaybackError.Unknown);
+                        OnParsingPlaylistFinished ();
+                        ServiceManager.PlayerEngine.Close ();
+                    }
+                });
+            }
+        }
+
+        public override StreamPlaybackError PlaybackError {
+            get { return ParentTrack == null ? base.PlaybackError : ParentTrack.PlaybackError; }
+            set {
+                if (value != StreamPlaybackError.None) {
+                    ServiceManager.PlayerEngine.EndSynthesizeContacting (this, true);
+                }
+
+                if (ParentTrack == null) {
+                    base.PlaybackError = value;
+                } else {
+                    ParentTrack.PlaybackError = value;
+                }
+            }
+        }
+
+        public new void SavePlaybackError (StreamPlaybackError value)
+        {
+            PlaybackError = value;
+            Save ();
+            if (ParentTrack != null) {
+                ParentTrack.Save ();
+            }
+        }
+
+        private void PlayCore ()
+        {
+            ServiceManager.PlayerEngine.EndSynthesizeContacting (this, false);
+
             if(track != null) {
                 TrackTitle = track.Title;
                 ArtistName = track.Creator;
@@ -158,6 +216,8 @@
                     ServiceManager.PlayerEngine.OpenPlay(this);
                 }
             }
+
+            trying_to_play = false;
         }
         
         public bool PlayNextStream()
@@ -167,6 +227,7 @@
                 Play();
                 return true;
             }
+            ServiceManager.PlaybackController.StopWhenFinished = true;
             return false;
         }
 
@@ -177,9 +238,10 @@
                 Play();
                 return true;
             }
+            ServiceManager.PlaybackController.StopWhenFinished = true;
             return false;
         }
-        
+
         private void LoadStreamUris()
         {
             lock(stream_uris) {
@@ -193,19 +255,31 @@
                 
                 loaded = true;
             }
-            
-            ThreadAssist.ProxyToMain(delegate {
-                OnParsingPlaylistFinished();
-                Play();
-            });
+
+            ServiceManager.PlayerEngine.TrackIntercept -= OnTrackIntercept;
+            OnParsingPlaylistFinished();
+
+            if (ServiceManager.PlayerEngine.CurrentTrack == this) {
+                PlayCore();
+            } else {
+                trying_to_play = false;
+            }
+        }
+
+        private bool OnTrackIntercept (TrackInfo track)
+        {
+            if (track != this && track != ParentTrack) {
+                ServiceManager.PlayerEngine.EndSynthesizeContacting (this, false);
+                ServiceManager.PlayerEngine.TrackIntercept -= OnTrackIntercept;
+            }
+            return false;
         }
         
         private void LoadStreamUri(string uri)
         {
             try {
-                Log.Debug("Attempting to parse radio playlist", uri);
                 PlaylistParser parser = new PlaylistParser();
-                if(parser.Parse(new SafeUri(uri))) {
+                if (parser.Parse(new SafeUri(uri))) {
                     foreach(Dictionary<string, object> element in parser.Elements) {
                         if(element.ContainsKey("uri")) {
                             stream_uris.Add(new SafeUri(((Uri)element["uri"]).AbsoluteUri));
@@ -214,12 +288,14 @@
                 } else {
                     stream_uris.Add(new SafeUri(uri));
                 }
-            } catch(System.Net.WebException) {
-                SavePlaybackError (StreamPlaybackError.ResourceNotFound);
-            } catch(Exception e) {
-                Console.WriteLine(e);
+                Log.DebugFormat ("Parsed {0} URIs out of {1}", stream_uris.Count, this);
+            } catch (System.Net.WebException e) {
+                Hyena.Log.Exception (this.ToString (), e);
                 SavePlaybackError (StreamPlaybackError.ResourceNotFound);
-            }   
+            } catch (Exception e) {
+                Hyena.Log.Exception (this.ToString (), e);
+                SavePlaybackError (StreamPlaybackError.Unknown);
+            }
         }
         
         private void OnParsingPlaylistStarted()

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/ConnectedSeekSlider.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/ConnectedSeekSlider.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/ConnectedSeekSlider.cs	Fri Jan 30 18:45:53 2009
@@ -128,33 +128,41 @@
                     OnPlayerEngineTick ();
                     break;
                 case PlayerEvent.StartOfStream:
-                    stream_position_label.IsBuffering = false;
-                    seek_slider.CanSeek = true;
+                    stream_position_label.StreamState = StreamLabelState.Playing;
+                    seek_slider.CanSeek = ServiceManager.PlayerEngine.CanSeek;
                     break;
                 case PlayerEvent.Buffering:
                     PlayerEventBufferingArgs buffering = (PlayerEventBufferingArgs)args;
                     if (buffering.Progress >= 1.0) {
-                        stream_position_label.IsBuffering = false;
+                        stream_position_label.StreamState = StreamLabelState.Playing;
                         break;
                     }
                     
-                    stream_position_label.IsBuffering = true;
+                    stream_position_label.StreamState = StreamLabelState.Buffering;
                     stream_position_label.BufferingProgress = buffering.Progress;
                     seek_slider.SetIdle ();
                     break;
                 case PlayerEvent.StateChange:
                     switch (((PlayerEventStateChangeArgs)args).Current) {
                         case PlayerState.Contacting:
-                            stream_position_label.IsContacting = true;
+                            transitioning = false;
+                            stream_position_label.StreamState = StreamLabelState.Contacting;
                             seek_slider.SetIdle ();
                             break;
+                        case PlayerState.Loading:
+                            transitioning = false;
+                            if (((PlayerEventStateChangeArgs)args).Previous == PlayerState.Contacting) {
+                                stream_position_label.StreamState = StreamLabelState.Loading;
+                                seek_slider.SetIdle ();
+                            }
+                            break;
                         case PlayerState.Idle:
                             seek_slider.CanSeek = false;
                             if (!transitioning) {
+                                stream_position_label.StreamState = StreamLabelState.Idle;
                                 seek_slider.Duration = 0;
                                 seek_slider.SeekValue = 0;
                                 seek_slider.SetIdle ();
-                                stream_position_label.IsContacting = false;
                             }
                             break;
                         default:
@@ -171,12 +179,13 @@
                 return;
             }
             
-            uint stream_length = ServiceManager.PlayerEngine.Length;
-            uint stream_position = ServiceManager.PlayerEngine.Position;
-            stream_position_label.IsContacting = false;
+            stream_position_label.StreamState = StreamLabelState.Playing;
+            Banshee.Collection.TrackInfo track = ServiceManager.PlayerEngine.CurrentTrack;
+            stream_position_label.IsLive = track == null ? false : track.IsLive;
+            stream_position_label.StreamState = StreamLabelState.Playing;
+            seek_slider.Duration = ServiceManager.PlayerEngine.Length;
+            seek_slider.SeekValue = ServiceManager.PlayerEngine.Position;
             seek_slider.CanSeek = ServiceManager.PlayerEngine.CanSeek;
-            seek_slider.Duration = stream_length;
-            seek_slider.SeekValue = stream_position;
         }
         
         private void OnSeekRequested (object o, EventArgs args)

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs	Fri Jan 30 18:45:53 2009
@@ -143,8 +143,10 @@
 
             switch (args.Current) {
                 case PlayerState.Contacting:
+                case PlayerState.Loading:
+                case PlayerState.Loaded:
                 case PlayerState.Playing:
-                    ShowPlayAction ();
+                    ShowStopAction ();
                     break;
                 case PlayerState.Paused:
                     ShowPlay ();
@@ -169,10 +171,13 @@
                 this["RestartSongAction"].Sensitive = false;
                 this["SeekToAction"].Sensitive = false;
             }
+
+            // Disable all actions while NotReady
+            Sensitive = args.Current != PlayerState.NotReady;
         }
         
-        private void ShowPlayAction ()
-        { 
+        private void ShowStopAction ()
+        {
             if (ServiceManager.PlayerEngine.CanPause) {
                 ShowPause ();
             } else {

Modified: trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/SeekSlider.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/SeekSlider.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/SeekSlider.cs	Fri Jan 30 18:45:53 2009
@@ -107,10 +107,12 @@
                 raise_seek_requested = false;
                 
                 if(value > Duration) {
-                    Duration = value;
+                    Duration = Int64.MaxValue;
+                    Value = value;
+                } else {
+                    Value = value;
                 }
-                
-                Value = value;
+
                 raise_seek_requested = true;
             }
         }

Modified: trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/StreamPositionLabel.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/StreamPositionLabel.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/StreamPositionLabel.cs	Fri Jan 30 18:45:53 2009
@@ -32,14 +32,22 @@
 
 namespace Banshee.Widgets
 {
+    public enum StreamLabelState {
+        Idle,
+        Contacting,
+        Loading,
+        Buffering,
+        Playing
+    }
+
     public class StreamPositionLabel : Alignment
     {
         private double buffering_progress;
-        private bool is_buffering;
-        private bool is_contacting;
+        private bool is_live;
         private SeekSlider seekRange;
         private string format_string = "<small>{0}</small>";
         private Pango.Layout layout;
+        private StreamLabelState state;
         
         public StreamPositionLabel (SeekSlider seekRange) : base (0.0f, 0.0f, 1.0f, 1.0f)
         {
@@ -101,7 +109,7 @@
             int bar_width = (int)((double)Allocation.Width * buffering_progress);
             bool render_bar = false;
             
-            if (bar_width > 0 && is_buffering) {
+            if (bar_width > 0 && IsBuffering) {
                 bar_width -= 2 * Style.XThickness;
                 render_bar = true;
                 
@@ -146,15 +154,20 @@
                 return;
             }
             
-            if (is_buffering) {
+            if (IsBuffering) {
                 double progress = buffering_progress * 100.0;
                 UpdateLabel (String.Format ("{0}: {1}%", Catalog.GetString("Buffering"), progress.ToString ("0.0")));
-            } else if (is_contacting) {
+            } else if (IsContacting) {
                 UpdateLabel (contacting);
-            } else if (seekRange.Value == 0 && seekRange.Duration == 0) {
+            } else if (IsLoading) {
+                // TODO replace w/ "Loading..." after string freeze
+                UpdateLabel (contacting);
+            } else if (IsIdle) {
                 UpdateLabel (idle);
-            } else if (seekRange.Value == seekRange.Duration) {
+            } else if (seekRange.Duration == Int64.MaxValue) {
                 UpdateLabel (FormatDuration ((long)seekRange.Value));
+            } else if (seekRange.Value == 0 && seekRange.Duration == 0) {
+                // nop
             } else {
                 UpdateLabel (String.Format (Catalog.GetString ("{0} of {1}"),
                     FormatDuration ((long)seekRange.Value), FormatDuration ((long)seekRange.Adjustment.Upper)));
@@ -193,22 +206,38 @@
             }
         }
         
+        public bool IsIdle {
+            get { return StreamState == StreamLabelState.Idle; }
+        }
+
         public bool IsBuffering {
-            get { return is_buffering; }
+            get { return StreamState == StreamLabelState.Buffering; }
+        }
+        
+        public bool IsContacting {
+            get { return StreamState == StreamLabelState.Contacting; }
+        }
+
+        public bool IsLoading {
+            get { return StreamState == StreamLabelState.Loading; }
+        }
+
+        public StreamLabelState StreamState {
+            get { return state; }
             set { 
-                if (is_buffering != value) {
-                    is_buffering = value;
+                if (state != value) {
+                    state = value;
                     UpdateLabel ();
                     QueueDraw ();
                 }
             }
         }
-        
-        public bool IsContacting {
-            get { return is_contacting; }
+
+        public bool IsLive {
+            get { return is_live; }
             set { 
-                if (is_contacting != value) {
-                    is_contacting = value;
+                if (is_live != value) {
+                    is_live = value;
                     UpdateLabel ();
                     QueueDraw ();
                 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]