~ubuntu-branches/ubuntu/trusty/banshee/trusty-proposed

« back to all changes in this revision

Viewing changes to .pc/Remove-IDBusExportable-inheritance-from-exported-int.patch/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs

  • Committer: Package Import Robot
  • Author(s): Chow Loong Jin
  • Date: 2014-03-02 18:34:46 UTC
  • Revision ID: package-import@ubuntu.com-20140302183446-r0tdfaajen6cnxbf
Tags: 2.9.0+really2.6.2-2ubuntu1
* [4cc1f7c] Merge from Debian Unstable, remaining changes:
  - Enable and recommend SoundMenu and Disable NotificationArea by default
  - Disable boo and karma extensions
  - Move desktop file for Meego UI to /usr/share/une/applications
  - Change the url for the Amazon store redirector
  - [9b356d6] Add workaround for set_Height exception.
  - [ccbcbbd] Make Banshee translatable in Launchpad
  - [2094ee5] Bump libgpod build-dep version to 0.8.2-7~
  - [a7156c0] Filter out libgpod-cil-dev versions built against gtk#3
  - [e7c634d] Update dversionmangle for extracting +really version out

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//
 
2
// PlayerEngineService.cs
 
3
//
 
4
// Author:
 
5
//   Aaron Bockover <abockover@novell.com>
 
6
//
 
7
// Copyright (C) 2006-2008 Novell, Inc.
 
8
//
 
9
// Permission is hereby granted, free of charge, to any person obtaining
 
10
// a copy of this software and associated documentation files (the
 
11
// "Software"), to deal in the Software without restriction, including
 
12
// without limitation the rights to use, copy, modify, merge, publish,
 
13
// distribute, sublicense, and/or sell copies of the Software, and to
 
14
// permit persons to whom the Software is furnished to do so, subject to
 
15
// the following conditions:
 
16
//
 
17
// The above copyright notice and this permission notice shall be
 
18
// included in all copies or substantial portions of the Software.
 
19
//
 
20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
21
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
22
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 
23
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 
24
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 
25
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 
26
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
27
//
 
28
 
 
29
using System;
 
30
using System.IO;
 
31
using System.Collections.Generic;
 
32
using System.Reflection;
 
33
 
 
34
using Mono.Unix;
 
35
using Mono.Addins;
 
36
 
 
37
using Hyena;
 
38
using Banshee.Base;
 
39
using Banshee.Streaming;
 
40
using Banshee.ServiceStack;
 
41
using Banshee.Sources;
 
42
using Banshee.Query;
 
43
using Banshee.Metadata;
 
44
using Banshee.Configuration;
 
45
using Banshee.Collection;
 
46
using Banshee.Equalizer;
 
47
using Banshee.Collection.Database;
 
48
 
 
49
namespace Banshee.MediaEngine
 
50
{
 
51
    public delegate bool TrackInterceptHandler (TrackInfo track);
 
52
 
 
53
    public class PlayerEngineService : IInitializeService, IDelayedInitializeService,
 
54
        IRequiredService, IPlayerEngineService, IDisposable
 
55
    {
 
56
        private List<PlayerEngine> engines = new List<PlayerEngine> ();
 
57
        private PlayerEngine active_engine;
 
58
        private PlayerEngine default_engine;
 
59
        private PlayerEngine pending_engine;
 
60
        private object pending_playback_for_not_ready;
 
61
        private bool pending_playback_for_not_ready_play;
 
62
        private TrackInfo synthesized_contacting_track;
 
63
 
 
64
        private string preferred_engine_id = null;
 
65
 
 
66
        public event EventHandler PlayWhenIdleRequest;
 
67
        public event TrackInterceptHandler TrackIntercept;
 
68
        public event Action<PlayerEngine> EngineBeforeInitialize;
 
69
        public event Action<PlayerEngine> EngineAfterInitialize;
 
70
 
 
71
        private event DBusPlayerEventHandler dbus_event_changed;
 
72
        event DBusPlayerEventHandler IPlayerEngineService.EventChanged {
 
73
            add { dbus_event_changed += value; }
 
74
            remove { dbus_event_changed -= value; }
 
75
        }
 
76
 
 
77
        private event DBusPlayerStateHandler dbus_state_changed;
 
78
        event DBusPlayerStateHandler IPlayerEngineService.StateChanged {
 
79
            add { dbus_state_changed += value; }
 
80
            remove { dbus_state_changed -= value; }
 
81
        }
 
82
 
 
83
        public PlayerEngineService ()
 
84
        {
 
85
        }
 
86
 
 
87
        void IInitializeService.Initialize ()
 
88
        {
 
89
            preferred_engine_id = EngineSchema.Get();
 
90
 
 
91
            if (default_engine == null && engines.Count > 0) {
 
92
                default_engine = engines[0];
 
93
            }
 
94
 
 
95
            foreach (TypeExtensionNode node in AddinManager.GetExtensionNodes ("/Banshee/MediaEngine/PlayerEngine")) {
 
96
                LoadEngine (node);
 
97
            }
 
98
 
 
99
            if (default_engine != null) {
 
100
                active_engine = default_engine;
 
101
                Log.Debug (Catalog.GetString ("Default player engine"), active_engine.Name);
 
102
            } else {
 
103
                default_engine = active_engine;
 
104
            }
 
105
 
 
106
            if (default_engine == null || active_engine == null || engines == null || engines.Count == 0) {
 
107
                Log.Warning (Catalog.GetString (
 
108
                    "No player engines were found. Please ensure Banshee has been cleanly installed."),
 
109
                    "Using the featureless NullPlayerEngine.");
 
110
                PlayerEngine null_engine = new NullPlayerEngine ();
 
111
                LoadEngine (null_engine);
 
112
                active_engine = null_engine;
 
113
                default_engine = null_engine;
 
114
            }
 
115
 
 
116
            MetadataService.Instance.HaveResult += OnMetadataServiceHaveResult;
 
117
 
 
118
            TrackInfo.IsPlayingMethod = track => IsPlaying (track) &&
 
119
                track.CacheModelId == CurrentTrack.CacheModelId &&
 
120
                (track.CacheEntryId == null || track.CacheEntryId.Equals (CurrentTrack.CacheEntryId));
 
121
        }
 
122
 
 
123
        private void InitializeEngine (PlayerEngine engine)
 
124
        {
 
125
            var handler = EngineBeforeInitialize;
 
126
            if (handler != null) {
 
127
                handler (engine);
 
128
            }
 
129
 
 
130
            engine.Initialize ();
 
131
            engine.IsInitialized = true;
 
132
 
 
133
            handler = EngineAfterInitialize;
 
134
            if (handler != null) {
 
135
                handler (engine);
 
136
            }
 
137
        }
 
138
 
 
139
        void IDelayedInitializeService.DelayedInitialize ()
 
140
        {
 
141
            foreach (var engine in Engines) {
 
142
                if (engine.DelayedInitialize) {
 
143
                    InitializeEngine (engine);
 
144
                }
 
145
            }
 
146
        }
 
147
 
 
148
        private void LoadEngine (TypeExtensionNode node)
 
149
        {
 
150
            LoadEngine ((PlayerEngine) node.CreateInstance (typeof (PlayerEngine)));
 
151
        }
 
152
 
 
153
        private void LoadEngine (PlayerEngine engine)
 
154
        {
 
155
            if (!engine.DelayedInitialize) {
 
156
                InitializeEngine (engine);
 
157
            }
 
158
 
 
159
            engine.EventChanged += OnEngineEventChanged;
 
160
 
 
161
            if (engine.Id == preferred_engine_id) {
 
162
                DefaultEngine = engine;
 
163
            } else {
 
164
                if (active_engine == null) {
 
165
                    active_engine = engine;
 
166
                }
 
167
                engines.Add (engine);
 
168
            }
 
169
        }
 
170
 
 
171
        public void Dispose ()
 
172
        {
 
173
            MetadataService.Instance.HaveResult -= OnMetadataServiceHaveResult;
 
174
 
 
175
            foreach (PlayerEngine engine in engines) {
 
176
                engine.Dispose ();
 
177
            }
 
178
 
 
179
            active_engine = null;
 
180
            default_engine = null;
 
181
            pending_engine = null;
 
182
 
 
183
            preferred_engine_id = null;
 
184
 
 
185
            engines.Clear ();
 
186
        }
 
187
 
 
188
        private void OnMetadataServiceHaveResult (object o, MetadataLookupResultArgs args)
 
189
        {
 
190
            if (CurrentTrack != null && CurrentTrack.TrackEqual (args.Track as TrackInfo)) {
 
191
                foreach (StreamTag tag in args.ResultTags) {
 
192
                    StreamTagger.TrackInfoMerge (CurrentTrack, tag);
 
193
                }
 
194
 
 
195
                OnEngineEventChanged (new PlayerEventArgs (PlayerEvent.TrackInfoUpdated));
 
196
            }
 
197
        }
 
198
 
 
199
        private void HandleStateChange (PlayerEventStateChangeArgs args)
 
200
        {
 
201
            if (args.Current == PlayerState.Loaded && CurrentTrack != null) {
 
202
                MetadataService.Instance.Lookup (CurrentTrack);
 
203
            } else if (args.Current == PlayerState.Ready) {
 
204
                // Enable our preferred equalizer if it exists and was enabled last time.
 
205
                if (SupportsEqualizer) {
 
206
                    EqualizerManager.Instance.Select ();
 
207
                }
 
208
 
 
209
                if (pending_playback_for_not_ready != null) {
 
210
                    OpenCheck (pending_playback_for_not_ready, pending_playback_for_not_ready_play);
 
211
                    pending_playback_for_not_ready = null;
 
212
                    pending_playback_for_not_ready_play = false;
 
213
                }
 
214
            }
 
215
 
 
216
            DBusPlayerStateHandler dbus_handler = dbus_state_changed;
 
217
            if (dbus_handler != null) {
 
218
                dbus_handler (args.Current.ToString ().ToLower ());
 
219
            }
 
220
        }
 
221
 
 
222
        private void OnEngineEventChanged (PlayerEventArgs args)
 
223
        {
 
224
            if (CurrentTrack != null) {
 
225
                if (args.Event == PlayerEvent.Error
 
226
                    && CurrentTrack.PlaybackError == StreamPlaybackError.None) {
 
227
                    CurrentTrack.SavePlaybackError (StreamPlaybackError.Unknown);
 
228
                } else if (args.Event == PlayerEvent.Iterate
 
229
                    && CurrentTrack.PlaybackError != StreamPlaybackError.None) {
 
230
                    CurrentTrack.SavePlaybackError (StreamPlaybackError.None);
 
231
                }
 
232
            }
 
233
 
 
234
            if (args.Event == PlayerEvent.StartOfStream) {
 
235
                incremented_last_played = false;
 
236
            } else if (args.Event == PlayerEvent.EndOfStream) {
 
237
                IncrementLastPlayed ();
 
238
            }
 
239
 
 
240
            RaiseEvent (args);
 
241
 
 
242
            // Do not raise iterate across DBus to avoid so many calls;
 
243
            // DBus clients should do their own iterating and
 
244
            // event/state checking locally
 
245
            if (args.Event == PlayerEvent.Iterate) {
 
246
                return;
 
247
            }
 
248
 
 
249
            DBusPlayerEventHandler dbus_handler = dbus_event_changed;
 
250
            if (dbus_handler != null) {
 
251
                dbus_handler (args.Event.ToString ().ToLower (),
 
252
                    args is PlayerEventErrorArgs ? ((PlayerEventErrorArgs)args).Message : String.Empty,
 
253
                    args is PlayerEventBufferingArgs ? ((PlayerEventBufferingArgs)args).Progress : 0
 
254
                );
 
255
            }
 
256
        }
 
257
 
 
258
        private void OnPlayWhenIdleRequest ()
 
259
        {
 
260
            EventHandler handler = PlayWhenIdleRequest;
 
261
            if (handler != null) {
 
262
                handler (this, EventArgs.Empty);
 
263
            }
 
264
        }
 
265
 
 
266
        private bool OnTrackIntercept (TrackInfo track)
 
267
        {
 
268
            TrackInterceptHandler handler = TrackIntercept;
 
269
            if (handler == null) {
 
270
                return false;
 
271
            }
 
272
 
 
273
            bool handled = false;
 
274
 
 
275
            foreach (TrackInterceptHandler single_handler in handler.GetInvocationList ()) {
 
276
                handled |= single_handler (track);
 
277
            }
 
278
 
 
279
            return handled;
 
280
        }
 
281
 
 
282
        public void Open (TrackInfo track)
 
283
        {
 
284
            OpenPlay (track, false);
 
285
        }
 
286
 
 
287
        public void Open (SafeUri uri)
 
288
        {
 
289
             // Check if the uri exists
 
290
            if (uri == null || !File.Exists (uri.AbsolutePath)) {
 
291
                return;
 
292
            }
 
293
 
 
294
            bool found = false;
 
295
            if (ServiceManager.DbConnection != null) {
 
296
                // Try to find uri in the library
 
297
                int track_id = DatabaseTrackInfo.GetTrackIdForUri (uri);
 
298
                if (track_id > 0) {
 
299
                    DatabaseTrackInfo track_db = DatabaseTrackInfo.Provider.FetchSingle (track_id);
 
300
                    found = true;
 
301
                    Open (track_db);
 
302
                }
 
303
            }
 
304
            
 
305
            if (!found) {
 
306
                // Not in the library, get info from the file
 
307
                TrackInfo track = new TrackInfo ();
 
308
                using (var file = StreamTagger.ProcessUri (uri)) {
 
309
                    StreamTagger.TrackInfoMerge (track, file, false);
 
310
                }
 
311
                Open (track);
 
312
            }
 
313
        }
 
314
 
 
315
        void IPlayerEngineService.Open (string uri)
 
316
        {
 
317
            Open (new SafeUri (uri));
 
318
        }
 
319
 
 
320
        public void SetNextTrack (TrackInfo track)
 
321
        {
 
322
            if (track != null && EnsureActiveEngineCanPlay (track.Uri)) {
 
323
                active_engine.SetNextTrack (track);
 
324
            } else {
 
325
                active_engine.SetNextTrack ((TrackInfo) null);
 
326
            }
 
327
        }
 
328
 
 
329
        public void SetNextTrack (SafeUri uri)
 
330
        {
 
331
            if (EnsureActiveEngineCanPlay (uri)) {
 
332
                active_engine.SetNextTrack (uri);
 
333
            } else {
 
334
                active_engine.SetNextTrack ((SafeUri) null);
 
335
            }
 
336
        }
 
337
 
 
338
        private bool EnsureActiveEngineCanPlay (SafeUri uri)
 
339
        {
 
340
            if (uri == null) {
 
341
                // No engine can play the null URI.
 
342
                return false;
 
343
            }
 
344
            if (active_engine != FindSupportingEngine (uri)) {
 
345
                if (active_engine.CurrentState == PlayerState.Playing) {
 
346
                    // If we're currently playing then we can't switch engines now.
 
347
                    // We can't ensure the active engine can play this URI.
 
348
                    return false;
 
349
                } else {
 
350
                    // If we're not playing, we can switch the active engine to
 
351
                    // something that will play this URI.
 
352
                    SwitchToEngine (FindSupportingEngine (uri));
 
353
                    CheckPending ();
 
354
                    return true;
 
355
                }
 
356
            }
 
357
            return true;
 
358
        }
 
359
 
 
360
        public void OpenPlay (TrackInfo track)
 
361
        {
 
362
            OpenPlay (track, true);
 
363
        }
 
364
 
 
365
        private void OpenPlay (TrackInfo track, bool play)
 
366
        {
 
367
            if (track == null || !track.CanPlay || OnTrackIntercept (track)) {
 
368
                return;
 
369
            }
 
370
 
 
371
            try {
 
372
                OpenCheck (track, play);
 
373
            } catch (Exception e) {
 
374
                Log.Exception (e);
 
375
                Log.Error (Catalog.GetString ("Problem with Player Engine"), e.Message, true);
 
376
                Close ();
 
377
                ActiveEngine = default_engine;
 
378
            }
 
379
        }
 
380
 
 
381
        private void OpenCheck (object o, bool play)
 
382
        {
 
383
            if (CurrentState == PlayerState.NotReady) {
 
384
                pending_playback_for_not_ready = o;
 
385
                pending_playback_for_not_ready_play = play;
 
386
                return;
 
387
            }
 
388
 
 
389
            SafeUri uri = null;
 
390
            TrackInfo track = null;
 
391
 
 
392
            if (o is SafeUri) {
 
393
                uri = (SafeUri)o;
 
394
            } else if (o is TrackInfo) {
 
395
                track = (TrackInfo)o;
 
396
                uri = track.Uri;
 
397
            } else {
 
398
                return;
 
399
            }
 
400
 
 
401
            if (track != null && (track.MediaAttributes & TrackMediaAttributes.ExternalResource) != 0) {
 
402
                RaiseEvent (new PlayerEventArgs (PlayerEvent.EndOfStream));
 
403
                return;
 
404
            }
 
405
 
 
406
            PlayerEngine supportingEngine = FindSupportingEngine (uri);
 
407
            SwitchToEngine (supportingEngine);
 
408
            CheckPending ();
 
409
 
 
410
            if (track != null) {
 
411
                active_engine.Open (track);
 
412
            } else if (uri != null) {
 
413
                active_engine.Open (uri);
 
414
            }
 
415
 
 
416
            if (play) {
 
417
                active_engine.Play ();
 
418
            }
 
419
        }
 
420
 
 
421
        public void IncrementLastPlayed ()
 
422
        {
 
423
            // If Length <= 0 assume 100% completion.
 
424
            IncrementLastPlayed (active_engine.Length <= 0
 
425
                ? 1.0
 
426
                : (double)active_engine.Position / active_engine.Length);
 
427
        }
 
428
 
 
429
        private bool incremented_last_played = true;
 
430
        public void IncrementLastPlayed (double completed)
 
431
        {
 
432
            if (!incremented_last_played && CurrentTrack != null && CurrentTrack.PlaybackError == StreamPlaybackError.None) {
 
433
                CurrentTrack.OnPlaybackFinished (completed);
 
434
                incremented_last_played = true;
 
435
            }
 
436
        }
 
437
 
 
438
        private PlayerEngine FindSupportingEngine (SafeUri uri)
 
439
        {
 
440
            foreach (PlayerEngine engine in engines) {
 
441
                foreach (string extension in engine.ExplicitDecoderCapabilities) {
 
442
                    if (!uri.AbsoluteUri.EndsWith (extension)) {
 
443
                        continue;
 
444
                    }
 
445
                    return engine;
 
446
                }
 
447
            }
 
448
 
 
449
            foreach (PlayerEngine engine in engines) {
 
450
                foreach (string scheme in engine.SourceCapabilities) {
 
451
                    bool supported = scheme == uri.Scheme;
 
452
                    if (supported) {
 
453
                        return engine;
 
454
                    }
 
455
                }
 
456
            }
 
457
            // If none of our engines support this URI, return the currently active one.
 
458
            // There doesn't seem to be anything better to do.
 
459
            return active_engine;
 
460
        }
 
461
 
 
462
        private bool SwitchToEngine (PlayerEngine switchTo)
 
463
        {
 
464
            if (active_engine != switchTo) {
 
465
                Close ();
 
466
                pending_engine = switchTo;
 
467
                Log.DebugFormat ("Switching engine to: {0}", switchTo.GetType ());
 
468
                return true;
 
469
            }
 
470
            return false;
 
471
        }
 
472
 
 
473
        public void Close ()
 
474
        {
 
475
            Close (false);
 
476
        }
 
477
 
 
478
        public void Close (bool fullShutdown)
 
479
        {
 
480
            IncrementLastPlayed ();
 
481
            active_engine.Close (fullShutdown);
 
482
        }
 
483
 
 
484
        public void Play ()
 
485
        {
 
486
            if (CurrentState == PlayerState.Idle) {
 
487
                OnPlayWhenIdleRequest ();
 
488
            } else {
 
489
                active_engine.Play ();
 
490
            }
 
491
        }
 
492
 
 
493
        public void Pause ()
 
494
        {
 
495
            if (!CanPause) {
 
496
                Close ();
 
497
            } else {
 
498
                active_engine.Pause ();
 
499
            }
 
500
        }
 
501
 
 
502
        public void RestartCurrentTrack ()
 
503
        {
 
504
            var track = CurrentTrack;
 
505
            if (track != null) {
 
506
                // Don't process the track as played through IncrementLastPlayed, just play it again
 
507
                active_engine.Close (false);
 
508
                OpenPlay (track);
 
509
            }
 
510
        }
 
511
 
 
512
        // For use by RadioTrackInfo
 
513
        // TODO remove this method once RadioTrackInfo playlist downloading/parsing logic moved here?
 
514
        internal void StartSynthesizeContacting (TrackInfo track)
 
515
        {
 
516
            //OnStateChanged (PlayerState.Contacting);
 
517
            RaiseEvent (new PlayerEventStateChangeArgs (CurrentState, PlayerState.Contacting));
 
518
            synthesized_contacting_track = track;
 
519
        }
 
520
 
 
521
        internal void EndSynthesizeContacting (TrackInfo track, bool idle)
 
522
        {
 
523
            if (track == synthesized_contacting_track) {
 
524
                synthesized_contacting_track = null;
 
525
 
 
526
                if (idle) {
 
527
                    RaiseEvent (new PlayerEventStateChangeArgs (PlayerState.Contacting, PlayerState.Idle));
 
528
                }
 
529
            }
 
530
        }
 
531
 
 
532
        public void TogglePlaying ()
 
533
        {
 
534
            if (IsPlaying () && CurrentState != PlayerState.Paused) {
 
535
                Pause ();
 
536
            } else if (CurrentState != PlayerState.NotReady) {
 
537
                Play ();
 
538
            }
 
539
        }
 
540
 
 
541
        public void VideoExpose (IntPtr displayContext, bool direct)
 
542
        {
 
543
            active_engine.VideoExpose (displayContext, direct);
 
544
        }
 
545
 
 
546
        public void VideoWindowRealize (IntPtr displayContext)
 
547
        {
 
548
            active_engine.VideoWindowRealize (displayContext);
 
549
        }
 
550
 
 
551
        public IntPtr VideoDisplayContext {
 
552
            set { active_engine.VideoDisplayContext = value; }
 
553
            get { return active_engine.VideoDisplayContext; }
 
554
        }
 
555
 
 
556
        public void TrackInfoUpdated ()
 
557
        {
 
558
            active_engine.TrackInfoUpdated ();
 
559
        }
 
560
 
 
561
        public bool IsPlaying (TrackInfo track)
 
562
        {
 
563
            return IsPlaying () && track != null && track.TrackEqual (CurrentTrack);
 
564
        }
 
565
 
 
566
        public bool IsPlaying ()
 
567
        {
 
568
            return CurrentState == PlayerState.Playing ||
 
569
                CurrentState == PlayerState.Paused ||
 
570
                CurrentState == PlayerState.Loaded ||
 
571
                CurrentState == PlayerState.Loading ||
 
572
                CurrentState == PlayerState.Contacting;
 
573
        }
 
574
 
 
575
        public string GetSubtitleDescription (int index)
 
576
        {
 
577
            return active_engine.GetSubtitleDescription (index);
 
578
        }
 
579
 
 
580
        public void NotifyMouseMove (double x, double y)
 
581
        {
 
582
            active_engine.NotifyMouseMove (x, y);
 
583
        }
 
584
 
 
585
        public void NotifyMouseButtonPressed (int button, double x, double y)
 
586
        {
 
587
            active_engine.NotifyMouseButtonPressed (button, x, y);
 
588
        }
 
589
 
 
590
        public void NotifyMouseButtonReleased (int button, double x, double y)
 
591
        {
 
592
            active_engine.NotifyMouseButtonReleased (button, x, y);
 
593
        }
 
594
 
 
595
        public void NavigateToLeftMenu ()
 
596
        {
 
597
            active_engine.NavigateToLeftMenu ();
 
598
        }
 
599
 
 
600
        public void NavigateToRightMenu ()
 
601
        {
 
602
            active_engine.NavigateToRightMenu ();
 
603
        }
 
604
 
 
605
        public void NavigateToUpMenu ()
 
606
        {
 
607
            active_engine.NavigateToUpMenu ();
 
608
        }
 
609
 
 
610
        public void NavigateToDownMenu ()
 
611
        {
 
612
            active_engine.NavigateToDownMenu ();
 
613
        }
 
614
 
 
615
        public void NavigateToMenu ()
 
616
        {
 
617
            active_engine.NavigateToMenu ();
 
618
        }
 
619
 
 
620
        public void ActivateCurrentMenu ()
 
621
        {
 
622
            active_engine.ActivateCurrentMenu ();
 
623
        }
 
624
 
 
625
        public void GoToNextChapter ()
 
626
        {
 
627
            active_engine.GoToNextChapter ();
 
628
        }
 
629
 
 
630
        public void GoToPreviousChapter ()
 
631
        {
 
632
            active_engine.GoToPreviousChapter ();
 
633
        }
 
634
 
 
635
        private void CheckPending ()
 
636
        {
 
637
            if (pending_engine != null && pending_engine != active_engine) {
 
638
                if (active_engine.CurrentState == PlayerState.Idle) {
 
639
                    Close ();
 
640
                }
 
641
 
 
642
                active_engine = pending_engine;
 
643
                pending_engine = null;
 
644
            }
 
645
        }
 
646
 
 
647
        public TrackInfo CurrentTrack {
 
648
            get {
 
649
                return active_engine == null ? null : active_engine.CurrentTrack ?? synthesized_contacting_track;
 
650
            }
 
651
        }
 
652
 
 
653
        private Dictionary<string, object> dbus_sucks;
 
654
        IDictionary<string, object> IPlayerEngineService.CurrentTrack {
 
655
            get {
 
656
                // FIXME: Managed DBus sucks - it explodes if you transport null
 
657
                // or even an empty dictionary (a{sv} in our case). Piece of shit.
 
658
                if (dbus_sucks == null) {
 
659
                    dbus_sucks = new Dictionary<string, object> ();
 
660
                    dbus_sucks.Add (String.Empty, String.Empty);
 
661
                }
 
662
 
 
663
                return CurrentTrack == null ? dbus_sucks : CurrentTrack.GenerateExportable ();
 
664
            }
 
665
        }
 
666
 
 
667
        public SafeUri CurrentSafeUri {
 
668
            get { return active_engine.CurrentUri; }
 
669
        }
 
670
 
 
671
        string IPlayerEngineService.CurrentUri {
 
672
            get { return CurrentSafeUri == null ? String.Empty : CurrentSafeUri.AbsoluteUri; }
 
673
        }
 
674
 
 
675
        public PlayerState CurrentState {
 
676
            get { return synthesized_contacting_track != null ? PlayerState.Contacting : active_engine.CurrentState; }
 
677
        }
 
678
 
 
679
        string IPlayerEngineService.CurrentState {
 
680
            get { return CurrentState.ToString ().ToLower (); }
 
681
        }
 
682
 
 
683
        public PlayerState LastState {
 
684
            get { return active_engine.LastState; }
 
685
        }
 
686
 
 
687
        string IPlayerEngineService.LastState {
 
688
            get { return LastState.ToString ().ToLower (); }
 
689
        }
 
690
 
 
691
        public ushort Volume {
 
692
            get { return active_engine.Volume; }
 
693
            set {
 
694
                foreach (PlayerEngine engine in engines) {
 
695
                    engine.Volume = value;
 
696
                }
 
697
            }
 
698
        }
 
699
 
 
700
        public uint Position {
 
701
            get { return active_engine.Position; }
 
702
            set { active_engine.Position = value; }
 
703
        }
 
704
 
 
705
        public byte Rating {
 
706
            get { return (byte)(CurrentTrack == null ? 0 : CurrentTrack.Rating); }
 
707
            set {
 
708
                if (CurrentTrack != null) {
 
709
                    CurrentTrack.Rating = (int)Math.Min (5u, value);
 
710
                    CurrentTrack.Save ();
 
711
 
 
712
                    foreach (var source in ServiceManager.SourceManager.Sources) {
 
713
                        var psource = source as PrimarySource;
 
714
                        if (psource != null) {
 
715
                            psource.NotifyTracksChanged (BansheeQuery.RatingField);
 
716
                        }
 
717
                    }
 
718
                }
 
719
            }
 
720
        }
 
721
 
 
722
        public bool CanSeek {
 
723
            get { return active_engine.CanSeek; }
 
724
        }
 
725
 
 
726
        public bool CanPause {
 
727
            get { return CurrentTrack != null && !CurrentTrack.IsLive; }
 
728
        }
 
729
 
 
730
        public bool SupportsEqualizer {
 
731
            get { return ((active_engine is IEqualizer) && active_engine.SupportsEqualizer); }
 
732
        }
 
733
 
 
734
        public int SubtitleCount {
 
735
            get { return active_engine.SubtitleCount; }
 
736
        }
 
737
 
 
738
        public int SubtitleIndex {
 
739
            set { active_engine.SubtitleIndex = value; }
 
740
        }
 
741
 
 
742
        public SafeUri SubtitleUri {
 
743
            set { active_engine.SubtitleUri = value; }
 
744
            get { return active_engine.SubtitleUri; }
 
745
        }
 
746
 
 
747
        public bool InDvdMenu {
 
748
            get { return active_engine.InDvdMenu; }
 
749
        }
 
750
 
 
751
        public VideoDisplayContextType VideoDisplayContextType {
 
752
            get { return active_engine.VideoDisplayContextType; }
 
753
        }
 
754
 
 
755
        public uint Length {
 
756
            get {
 
757
                uint length = active_engine.Length;
 
758
                if (length > 0) {
 
759
                    return length;
 
760
                } else if (CurrentTrack == null) {
 
761
                    return 0;
 
762
                }
 
763
 
 
764
                return (uint) CurrentTrack.Duration.TotalSeconds;
 
765
            }
 
766
        }
 
767
 
 
768
        public PlayerEngine ActiveEngine {
 
769
            get { return active_engine; }
 
770
            set { pending_engine = value; }
 
771
        }
 
772
 
 
773
        public PlayerEngine DefaultEngine {
 
774
            get { return default_engine; }
 
775
            set {
 
776
                if (engines.Contains (value)) {
 
777
                    engines.Remove (value);
 
778
                }
 
779
 
 
780
                engines.Insert (0, value);
 
781
 
 
782
                default_engine = value;
 
783
                EngineSchema.Set (value.Id);
 
784
            }
 
785
        }
 
786
 
 
787
        public IEnumerable<PlayerEngine> Engines {
 
788
            get { return engines; }
 
789
        }
 
790
 
 
791
#region Player Event System
 
792
 
 
793
        private LinkedList<PlayerEventHandlerSlot> event_handlers = new LinkedList<PlayerEventHandlerSlot> ();
 
794
 
 
795
        private struct PlayerEventHandlerSlot
 
796
        {
 
797
            public PlayerEvent EventMask;
 
798
            public PlayerEventHandler Handler;
 
799
 
 
800
            public PlayerEventHandlerSlot (PlayerEvent mask, PlayerEventHandler handler)
 
801
            {
 
802
                EventMask = mask;
 
803
                Handler = handler;
 
804
            }
 
805
        }
 
806
 
 
807
        private const PlayerEvent event_all_mask = PlayerEvent.Iterate
 
808
            | PlayerEvent.StateChange
 
809
            | PlayerEvent.StartOfStream
 
810
            | PlayerEvent.EndOfStream
 
811
            | PlayerEvent.Buffering
 
812
            | PlayerEvent.Seek
 
813
            | PlayerEvent.Error
 
814
            | PlayerEvent.Volume
 
815
            | PlayerEvent.Metadata
 
816
            | PlayerEvent.TrackInfoUpdated
 
817
            | PlayerEvent.RequestNextTrack
 
818
            | PlayerEvent.PrepareVideoWindow;
 
819
 
 
820
        private const PlayerEvent event_default_mask = event_all_mask & ~PlayerEvent.Iterate;
 
821
 
 
822
        private static void VerifyEventMask (PlayerEvent eventMask)
 
823
        {
 
824
            if (eventMask <= PlayerEvent.None || eventMask > event_all_mask) {
 
825
                throw new ArgumentOutOfRangeException ("eventMask", "A valid event mask must be provided");
 
826
            }
 
827
        }
 
828
 
 
829
        public void ConnectEvent (PlayerEventHandler handler)
 
830
        {
 
831
            ConnectEvent (handler, event_default_mask, false);
 
832
        }
 
833
 
 
834
        public void ConnectEvent (PlayerEventHandler handler, PlayerEvent eventMask)
 
835
        {
 
836
            ConnectEvent (handler, eventMask, false);
 
837
        }
 
838
 
 
839
        public void ConnectEvent (PlayerEventHandler handler, bool connectAfter)
 
840
        {
 
841
            ConnectEvent (handler, event_default_mask, connectAfter);
 
842
        }
 
843
 
 
844
        public void ConnectEvent (PlayerEventHandler handler, PlayerEvent eventMask, bool connectAfter)
 
845
        {
 
846
            lock (event_handlers) {
 
847
                VerifyEventMask (eventMask);
 
848
 
 
849
                PlayerEventHandlerSlot slot = new PlayerEventHandlerSlot (eventMask, handler);
 
850
 
 
851
                if (connectAfter) {
 
852
                    event_handlers.AddLast (slot);
 
853
                } else {
 
854
                    event_handlers.AddFirst (slot);
 
855
                }
 
856
            }
 
857
        }
 
858
 
 
859
        private LinkedListNode<PlayerEventHandlerSlot> FindEventNode (PlayerEventHandler handler)
 
860
        {
 
861
            LinkedListNode<PlayerEventHandlerSlot> node = event_handlers.First;
 
862
            while (node != null) {
 
863
                if (node.Value.Handler == handler) {
 
864
                    return node;
 
865
                }
 
866
                node = node.Next;
 
867
            }
 
868
 
 
869
            return null;
 
870
        }
 
871
 
 
872
        public void DisconnectEvent (PlayerEventHandler handler)
 
873
        {
 
874
            lock (event_handlers) {
 
875
                LinkedListNode<PlayerEventHandlerSlot> node = FindEventNode (handler);
 
876
                if (node != null) {
 
877
                    event_handlers.Remove (node);
 
878
                }
 
879
            }
 
880
        }
 
881
 
 
882
        public void ModifyEvent (PlayerEvent eventMask, PlayerEventHandler handler)
 
883
        {
 
884
            lock (event_handlers) {
 
885
                VerifyEventMask (eventMask);
 
886
 
 
887
                LinkedListNode<PlayerEventHandlerSlot> node = FindEventNode (handler);
 
888
                if (node != null) {
 
889
                    PlayerEventHandlerSlot slot = node.Value;
 
890
                    slot.EventMask = eventMask;
 
891
                    node.Value = slot;
 
892
                }
 
893
            }
 
894
        }
 
895
 
 
896
        private void RaiseEvent (PlayerEventArgs args)
 
897
        {
 
898
            lock (event_handlers) {
 
899
                if (args.Event == PlayerEvent.StateChange && args is PlayerEventStateChangeArgs) {
 
900
                    HandleStateChange ((PlayerEventStateChangeArgs)args);
 
901
                }
 
902
 
 
903
                LinkedListNode<PlayerEventHandlerSlot> node = event_handlers.First;
 
904
                while (node != null) {
 
905
                    if ((node.Value.EventMask & args.Event) == args.Event) {
 
906
                        try {
 
907
                            node.Value.Handler (args);
 
908
                        } catch (Exception e) {
 
909
                            Log.Exception (String.Format ("Error running PlayerEngine handler for {0}", args.Event), e);
 
910
                        }
 
911
                    }
 
912
                    node = node.Next;
 
913
                }
 
914
            }
 
915
        }
 
916
 
 
917
#endregion
 
918
 
 
919
        string IService.ServiceName {
 
920
            get { return "PlayerEngine"; }
 
921
        }
 
922
 
 
923
        IDBusExportable IDBusExportable.Parent {
 
924
            get { return null; }
 
925
        }
 
926
 
 
927
        public static readonly SchemaEntry<int> VolumeSchema = new SchemaEntry<int> (
 
928
            "player_engine", "volume",
 
929
            80,
 
930
            "Volume",
 
931
            "Volume of playback relative to mixer output"
 
932
        );
 
933
 
 
934
        public static readonly SchemaEntry<string> EngineSchema = new SchemaEntry<string> (
 
935
            "player_engine", "backend",
 
936
            "helix-remote",
 
937
            "Backend",
 
938
            "Name of media playback engine backend"
 
939
        );
 
940
    }
 
941
}