~ubuntu-branches/ubuntu/quantal/banshee/quantal-updates

« back to all changes in this revision

Viewing changes to src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp/PlayerEngine.cs

  • Committer: Bazaar Package Importer
  • Author(s): Chow Loong Jin
  • Date: 2011-08-25 20:48:45 UTC
  • mfrom: (1.15.2 upstream) (6.3.16 experimental)
  • Revision ID: james.westby@ubuntu.com-20110825204845-1rdbhdoitkv3n5u8
Tags: 2.1.3-1ubuntu1
* [e356872] Merge from Debian Experimental, remaining changes:
  + Enable SoundMenu and Disable NotificationArea by default
  + Disable boo and karma extensions
  + Enable and recommnd u1ms and soundmenu extensions
  + Move desktop file for Meego UI to /usr/share/une/applications
  + Change the url for the Amazon store redirector
  + Create the U1MS widget earlier and bump libu1 requirement
  + [9d7c600] Drop upstreamed u1ms-initialize-earlier patch

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
 
29
29
using System;
30
30
using System.Collections;
 
31
using System.Collections.Generic;
31
32
using System.Linq;
32
33
using System.Runtime.InteropServices;
33
34
using System.Threading;
35
36
using Mono.Unix;
36
37
 
37
38
using Gst;
 
39
using Gst.PbUtils;
38
40
using Gst.BasePlugins;
 
41
using Gst.CorePlugins;
39
42
 
40
43
using Hyena;
41
44
using Hyena.Data;
49
52
 
50
53
namespace Banshee.GStreamerSharp
51
54
{
52
 
    public class PlayerEngine : Banshee.MediaEngine.PlayerEngine
 
55
    public class PlayerEngine : Banshee.MediaEngine.PlayerEngine, IEqualizer, IVisualizationDataSource
53
56
    {
54
 
        Pipeline pipeline;
 
57
        private class AudioSinkBin : Bin
 
58
        {
 
59
            Element hw_audio_sink;
 
60
            Element volume;
 
61
            Element rgvolume;
 
62
            Element equalizer;
 
63
            Element preamp;
 
64
            Element first;
 
65
            GhostPad visible_sink;
 
66
            Tee audiotee;
 
67
            object pipeline_lock = new object ();
 
68
 
 
69
            public AudioSinkBin (IntPtr o) : base(o)
 
70
            {
 
71
                Name = "audiobin";
 
72
            }
 
73
 
 
74
            public AudioSinkBin (string elementName) : base(elementName)
 
75
            {
 
76
                hw_audio_sink = SelectAudioSink ();
 
77
                Add (hw_audio_sink);
 
78
                first = hw_audio_sink;
 
79
 
 
80
                // Our audio sink is a tee, so plugins can attach their own pipelines
 
81
                audiotee = ElementFactory.Make ("tee", "audiotee") as Tee;
 
82
                if (audiotee == null) {
 
83
                    Log.Error ("Can not create audio tee!");
 
84
                } else {
 
85
                    Add (audiotee);
 
86
                }
 
87
 
 
88
                volume = FindVolumeProvider (hw_audio_sink);
 
89
                if (volume != null) {
 
90
                    // If the sink provides its own volume property we assume that it will
 
91
                    // also save that value across program runs.  Pulsesink has this behaviour.
 
92
                    VolumeNeedsSaving = false;
 
93
                } else {
 
94
                    volume = ElementFactory.Make ("volume", "volume");
 
95
                    VolumeNeedsSaving = true;
 
96
                    Add (volume);
 
97
                    volume.Link (hw_audio_sink);
 
98
                    first = volume;
 
99
                }
 
100
 
 
101
                equalizer = ElementFactory.Make ("equalizer-10bands", "equalizer-10bands");
 
102
                if (equalizer != null) {
 
103
                    Element eq_audioconvert = ElementFactory.Make ("audioconvert", "audioconvert");
 
104
                    Element eq_audioconvert2 = ElementFactory.Make ("audioconvert", "audioconvert2");
 
105
                    preamp = ElementFactory.Make ("volume", "preamp");
 
106
 
 
107
                    Add (eq_audioconvert, preamp, equalizer, eq_audioconvert2);
 
108
                    Element.Link (eq_audioconvert, preamp, equalizer, eq_audioconvert2, first);
 
109
 
 
110
                    first = eq_audioconvert;
 
111
                    Log.Debug ("Built and linked Equalizer");
 
112
                }
 
113
 
 
114
                // Link the first tee pad to the primary audio sink queue
 
115
                Pad sinkpad = first.GetStaticPad ("sink");
 
116
                Pad pad = audiotee.GetRequestPad ("src%d");
 
117
                audiotee.AllocPad = pad;
 
118
                pad.Link (sinkpad);
 
119
                first = audiotee;
 
120
 
 
121
                visible_sink = new GhostPad ("sink", first.GetStaticPad ("sink"));
 
122
                AddPad (visible_sink);
 
123
            }
 
124
 
 
125
            static Element FindVolumeProvider (Element sink)
 
126
            {
 
127
                Element volumeProvider = null;
 
128
                // Sinks which automatically select between a number of possibilities
 
129
                // (such as autoaudiosink and gconfaudiosink) need to be at least in
 
130
                // the Ready state before they'll contain an actual sink.
 
131
                sink.SetState (State.Ready);
 
132
 
 
133
                if (sink.HasProperty ("volume")) {
 
134
                    volumeProvider = sink;
 
135
                    Log.DebugFormat ("Sink {0} has native volume.", volumeProvider.Name);
 
136
                } else {
 
137
                    var sinkBin = sink as Bin;
 
138
                    if (sinkBin != null) {
 
139
                        foreach (Element e in sinkBin.ElementsRecurse) {
 
140
                            if (e.HasProperty ("volume")) {
 
141
                                volumeProvider = e;
 
142
                                Log.DebugFormat ("Found volume provider {0} in {1}.",
 
143
                                    volumeProvider.Name, sink.Name);
 
144
                            }
 
145
                        }
 
146
                    }
 
147
                }
 
148
                return volumeProvider;
 
149
            }
 
150
 
 
151
            static Element SelectAudioSink ()
 
152
            {
 
153
                Element audiosink = null;
 
154
 
 
155
                // Default to GConfAudioSink, which should Do The Right Thing.
 
156
                audiosink = ElementFactory.Make ("gconfaudiosink", "audiosink");
 
157
                if (audiosink == null) {
 
158
                    // Try DirectSoundSink, which should work on Windows
 
159
                    audiosink = ElementFactory.Make ("directsoundsink", "audiosink");
 
160
                    if (audiosink != null) {
 
161
                        // The unmanaged code sets the volume on the directsoundsink here.
 
162
                        // Presumably this fixes a problem, but there's no reference as to what it is.
 
163
                        audiosink["volume"] = 1.0;
 
164
                    } else {
 
165
                        audiosink = ElementFactory.Make ("autoaudiosink", "audiosink");
 
166
                        if (audiosink == null) {
 
167
                            // As a last-ditch effort try ALSA.
 
168
                            audiosink = ElementFactory.Make ("alsasink", "audiosink");
 
169
                        }
 
170
                    }
 
171
                }
 
172
                return audiosink;
 
173
            }
 
174
 
 
175
            public bool ReplayGainEnabled {
 
176
                get { return rgvolume != null; }
 
177
                set {
 
178
                    if (value && rgvolume == null) {
 
179
                        visible_sink.SetBlocked (true, InsertReplayGain);
 
180
                        Log.Debug ("Enabled ReplayGain volume scaling.");
 
181
                    } else if (!value && rgvolume != null) {
 
182
                        visible_sink.SetBlocked (false, RemoveReplayGain);
 
183
                        Log.Debug ("Disabled ReplayGain volume scaling.");
 
184
                    }
 
185
                }
 
186
            }
 
187
 
 
188
            void InsertReplayGain (Pad pad, bool blocked)
 
189
            {
 
190
                lock (pipeline_lock) {
 
191
                    if (rgvolume == null) {
 
192
                        rgvolume = ElementFactory.Make ("rgvolume", "rgvolume");
 
193
                        Add (rgvolume);
 
194
                        rgvolume.SyncStateWithParent ();
 
195
                        visible_sink.SetTarget (rgvolume.GetStaticPad ("sink"));
 
196
                        rgvolume.Link (first);
 
197
                        first = rgvolume;
 
198
                    }
 
199
                }
 
200
                visible_sink.SetBlocked (false, (_, __) => { });
 
201
            }
 
202
 
 
203
            void RemoveReplayGain (Pad pad, bool blocked)
 
204
            {
 
205
                lock (pipeline_lock) {
 
206
                    if (rgvolume != null) {
 
207
                        first = rgvolume.GetStaticPad ("src").Peer.Parent as Element;
 
208
                        rgvolume.Unlink (first);
 
209
                        rgvolume.SetState (State.Null);
 
210
                        Remove (rgvolume);
 
211
                        rgvolume = null;
 
212
                        visible_sink.SetTarget (first.GetStaticPad ("sink"));
 
213
                    }
 
214
                }
 
215
                visible_sink.SetBlocked (false, (_, __) => { });
 
216
            }
 
217
 
 
218
 
 
219
            public bool VolumeNeedsSaving { get; private set; }
 
220
            public double Volume {
 
221
                get {
 
222
                    return (double)volume["volume"];
 
223
                }
 
224
                set {
 
225
                    if (value < 0 || value > 10.0) {
 
226
                        throw new ArgumentOutOfRangeException ("value", "Volume must be between 0 and 10.0");
 
227
                    }
 
228
                    Log.DebugFormat ("Setting volume to {0:0.00}", value);
 
229
                    volume["volume"] = value;
 
230
                }
 
231
            }
 
232
 
 
233
            public bool SupportsEqualizer { get {return preamp != null && equalizer != null;} }
 
234
 
 
235
            public double AmplifierLevel {
 
236
                set { preamp ["volume"] = Math.Pow (10.0, value / 20.0); }
 
237
            }
 
238
 
 
239
            public int [] BandRange {
 
240
                get {
 
241
                    int min = -1;
 
242
                    int max = -1;
 
243
 
 
244
                    PropertyInfo pspec = new PropertyInfo();
 
245
 
 
246
                    if (equalizer.HasProperty ("band0::gain")) {
 
247
                        pspec = equalizer.GetPropertyInfo ("band0::gain");
 
248
                    } else if (equalizer.HasProperty ("band0")) {
 
249
                        pspec = equalizer.GetPropertyInfo ("band0");
 
250
                    }
 
251
 
 
252
                    if (pspec.Name != null) {
 
253
                        min = (int)((double)pspec.Min);
 
254
                        max = (int)((double)pspec.Max);
 
255
                    }
 
256
 
 
257
                    return new int [] { min, max };
 
258
                }
 
259
            }
 
260
 
 
261
            private uint GetNBands ()
 
262
            {
 
263
                if (equalizer == null) {
 
264
                    return 0;
 
265
                }
 
266
 
 
267
                return ChildProxyAdapter.GetObject (equalizer).ChildrenCount;
 
268
            }
 
269
 
 
270
            public uint [] EqualizerFrequencies {
 
271
                get {
 
272
                    uint count = GetNBands ();
 
273
                    uint[] ret = new uint[count];
 
274
 
 
275
                    if (equalizer != null) {
 
276
                        ChildProxy equalizer_child_proxy = ChildProxyAdapter.GetObject (equalizer);
 
277
                        for (uint i = 0; i < count; i++) {
 
278
                            Gst.Object band = equalizer_child_proxy.GetChildByIndex (i);
 
279
                            ret [i] = (uint)(double)band ["freq"];
 
280
                        }
 
281
                    }
 
282
 
 
283
                    return ret;
 
284
                }
 
285
            }
 
286
 
 
287
            public void SetEqualizerGain (uint band, double value)
 
288
            {
 
289
                if (equalizer != null) {
 
290
                    if (band >= GetNBands ()) {
 
291
                        throw new ArgumentOutOfRangeException ("band", "Attempt to set out-of-range equalizer band");
 
292
                    }
 
293
                    Gst.Object the_band = ChildProxyAdapter.GetObject (equalizer).GetChildByIndex (band);
 
294
                    the_band ["gain"] = value;
 
295
                }
 
296
            }
 
297
 
 
298
            public Pad RequestTeePad ()
 
299
            {
 
300
                return audiotee.GetRequestPad ("src%d");
 
301
            }
 
302
        }
 
303
 
 
304
 
55
305
        PlayBin2 playbin;
 
306
        AudioSinkBin audio_sink;
56
307
        uint iterate_timeout_id = 0;
 
308
        List<string> missing_details = new List<string> ();
 
309
        ManualResetEvent next_track_set;
 
310
        CddaManager cdda_manager;
 
311
        VideoManager video_manager = null;
 
312
        Visualization visualization;
57
313
 
58
314
        public PlayerEngine ()
59
315
        {
79
335
            }
80
336
 
81
337
            Gst.Application.Init ();
82
 
            pipeline = new Pipeline ();
83
338
            playbin = new PlayBin2 ();
84
 
            pipeline.Add (playbin);
85
 
 
86
 
            // Remember the volume from last time
87
 
            Volume = (ushort)PlayerEngineService.VolumeSchema.Get ();
 
339
 
 
340
            next_track_set = new ManualResetEvent (false);
 
341
 
 
342
            audio_sink = new AudioSinkBin ("audiobin");
 
343
 
 
344
            playbin["audio-sink"] = audio_sink;
 
345
 
 
346
            if (audio_sink.VolumeNeedsSaving) {
 
347
                // Remember the volume from last time
 
348
                Volume = (ushort)PlayerEngineService.VolumeSchema.Get ();
 
349
            }
 
350
 
 
351
            Pad teepad = audio_sink.RequestTeePad ();
 
352
            visualization = new Visualization (audio_sink, teepad);
88
353
 
89
354
            playbin.AddNotification ("volume", OnVolumeChanged);
90
 
            pipeline.Bus.AddWatch (OnBusMessage);
 
355
            playbin.Bus.AddWatch (OnBusMessage);
 
356
            playbin.AboutToFinish += OnAboutToFinish;
 
357
 
 
358
            cdda_manager = new CddaManager (playbin);
 
359
            // FIXME: Disable video stuff until GLib# 3 is used instead of the sopy bundled in GStreamerSharp
 
360
            //video_manager = new VideoManager (playbin);
 
361
            //video_manager.PrepareWindow += OnVideoPrepareWindow;
 
362
            //video_manager.Initialize ();
91
363
 
92
364
            OnStateChanged (PlayerState.Ready);
93
365
        }
94
366
 
 
367
        protected override bool DelayedInitialize {
 
368
            get {
 
369
                return true;
 
370
            }
 
371
        }
 
372
 
 
373
        protected override void Initialize ()
 
374
        {
 
375
            base.Initialize ();
 
376
            InstallPreferences ();
 
377
            audio_sink.ReplayGainEnabled = ReplayGainEnabledSchema.Get ();
 
378
        }
 
379
 
 
380
        public override void Dispose ()
 
381
        {
 
382
            UninstallPreferences ();
 
383
            base.Dispose ();
 
384
        }
 
385
 
 
386
        public event VisualizationDataHandler DataAvailable {
 
387
            add {
 
388
                visualization.DataAvailable += value;
 
389
            }
 
390
 
 
391
            remove {
 
392
                visualization.DataAvailable -= value;
 
393
            }
 
394
        }
 
395
 
 
396
        public override void VideoExpose (IntPtr window, bool direct)
 
397
        {
 
398
            video_manager.WindowExpose (window, direct);
 
399
        }
 
400
 
 
401
        public override void VideoWindowRealize (IntPtr window)
 
402
        {
 
403
            video_manager.WindowRealize (window);
 
404
        }
 
405
 
 
406
        private void OnVideoPrepareWindow ()
 
407
        {
 
408
            OnEventChanged (PlayerEvent.PrepareVideoWindow);
 
409
        }
 
410
 
 
411
        void OnAboutToFinish (object o, Gst.GLib.SignalArgs args)
 
412
        {
 
413
            // This is needed to make Shuffle-by-* work.
 
414
            // Shuffle-by-* uses the LastPlayed field to determine what track in the grouping to play next.
 
415
            // Therefore, we need to update this before requesting the next track.
 
416
            //
 
417
            // This will be overridden by IncrementLastPlayed () called by
 
418
            // PlaybackControllerService's EndOfStream handler.
 
419
            CurrentTrack.UpdateLastPlayed ();
 
420
 
 
421
            next_track_set.Reset ();
 
422
            OnEventChanged (PlayerEvent.RequestNextTrack);
 
423
 
 
424
            if (!next_track_set.WaitOne (1000, false)) {
 
425
                Log.Warning ("[Gapless]: Timed out while waiting for next track to be set.");
 
426
                next_track_set.Set ();
 
427
            }
 
428
        }
 
429
 
 
430
        public override void SetNextTrackUri (SafeUri uri, bool maybeVideo)
 
431
        {
 
432
            if (next_track_set.WaitOne (0, false)) {
 
433
                // We've been asked to set the next track, but have taken too
 
434
                // long to get here.  Bail for now, and the EoS handling will
 
435
                // pick up the pieces.
 
436
                return;
 
437
            }
 
438
            playbin.Uri = uri.AbsoluteUri;
 
439
 
 
440
            if (maybeVideo) {
 
441
                LookupForSubtitle (uri);
 
442
            }
 
443
 
 
444
            next_track_set.Set ();
 
445
        }
 
446
 
95
447
        private bool OnBusMessage (Bus bus, Message msg)
96
448
        {
97
449
            switch (msg.Type) {
103
455
                    break;
104
456
 
105
457
                case MessageType.StateChanged:
106
 
                    State old_state, new_state, pending_state;
107
 
                    msg.ParseStateChanged (out old_state, out new_state, out pending_state);
108
 
                    HandleStateChange (old_state, new_state, pending_state);
 
458
                    if (msg.Src == playbin) {
 
459
                        State old_state, new_state, pending_state;
 
460
                        msg.ParseStateChanged (out old_state, out new_state, out pending_state);
 
461
                        HandleStateChange (old_state, new_state, pending_state);
 
462
                    }
109
463
                    break;
110
464
 
111
465
                case MessageType.Buffering:
128
482
                    msg.ParseError (out error_type, out err_msg, out debug);
129
483
 
130
484
                    HandleError (error_type, err_msg, debug);
131
 
 
 
485
                    break;
 
486
 
 
487
                case MessageType.Element:
 
488
                    if (MissingPluginMessage.IsMissingPluginMessage (msg)) {
 
489
                        string detail = MissingPluginMessage.GetInstallerDetail (msg);
 
490
 
 
491
                        if (detail == null)
 
492
                            return false;
 
493
 
 
494
                        if (missing_details.Contains (detail)) {
 
495
                            Log.DebugFormat ("Ignoring missing element details, already prompted ('{0}')", detail);
 
496
                            return false;
 
497
                        }
 
498
 
 
499
                        Log.DebugFormat ("Saving missing element details ('{0}')", detail);
 
500
                        missing_details.Add (detail);
 
501
 
 
502
                        Log.Error ("Missing GStreamer Plugin", MissingPluginMessage.GetDescription (msg), true);
 
503
 
 
504
                        InstallPluginsContext install_context = new InstallPluginsContext ();
 
505
                        Install.InstallPlugins (missing_details.ToArray (), install_context, OnInstallPluginsReturn);
 
506
                    } else if (msg.Src == playbin && msg.Structure.Name == "playbin2-stream-changed") {
 
507
                        HandleStreamChanged ();
 
508
                    }
 
509
                    break;
 
510
                case MessageType.Application:
 
511
                    string name;
 
512
                    Structure s = msg.Structure;
 
513
                    name = s.Name;
 
514
                    if (String.IsNullOrEmpty (name) && name == "stream-changed") {
 
515
                        video_manager.ParseStreamInfo ();
 
516
                    }
132
517
                    break;
133
518
            }
134
519
 
135
520
            return true;
136
521
        }
137
522
 
 
523
        private void OnInstallPluginsReturn (InstallPluginsReturn status)
 
524
        {
 
525
            Log.InformationFormat ("GStreamer plugin installer returned: {0}", status);
 
526
            if (status == InstallPluginsReturn.Success || status == InstallPluginsReturn.InstallInProgress) {
 
527
            }
 
528
        }
 
529
 
138
530
        private void OnVolumeChanged (object o, Gst.GLib.NotifyArgs args)
139
531
        {
140
532
            OnEventChanged (PlayerEvent.Volume);
141
533
        }
142
534
 
 
535
        private void HandleStreamChanged ()
 
536
        {
 
537
            // Set the current track as fully played before signaling EndOfStream.
 
538
            ServiceManager.PlayerEngine.IncrementLastPlayed (1.0);
 
539
            OnEventChanged (PlayerEvent.EndOfStream);
 
540
            OnEventChanged (PlayerEvent.StartOfStream);
 
541
        }
 
542
 
143
543
        private void HandleError (Enum domain, string error_message, string debug)
144
544
        {
145
545
            Close (true);
261
661
 
262
662
        protected override void OpenUri (SafeUri uri, bool maybeVideo)
263
663
        {
264
 
            if (pipeline.CurrentState == State.Playing || pipeline.CurrentState == State.Paused) {
265
 
                pipeline.SetState (Gst.State.Ready);
 
664
            if (cdda_manager.HandleURI (playbin, uri.AbsoluteUri)) {
 
665
                return;
 
666
            } else if (playbin == null) {
 
667
                throw new ApplicationException ("Could not open resource");
 
668
            }
 
669
 
 
670
            if (playbin.CurrentState == State.Playing || playbin.CurrentState == State.Paused) {
 
671
                playbin.SetState (Gst.State.Ready);
266
672
            }
267
673
 
268
674
            playbin.Uri = uri.AbsoluteUri;
 
675
            if (maybeVideo) {
 
676
                // Lookup for subtitle files with same name/folder
 
677
                LookupForSubtitle (uri);
 
678
            }
 
679
        }
 
680
 
 
681
        private void LookupForSubtitle (SafeUri uri)
 
682
        {
 
683
            string scheme, filename, subfile;
 
684
            SafeUri suburi;
 
685
            int dot;
 
686
            // Always enable rendering of subtitles
 
687
            int flags;
 
688
            flags = (int)playbin.Flags;
 
689
            flags |= (1 << 2);//GST_PLAY_FLAG_TEXT
 
690
            playbin.Flags = (ObjectFlags)flags;
 
691
 
 
692
            Log.Debug ("[subtitle]: looking for subtitles for video file");
 
693
            scheme = uri.Scheme;
 
694
            string[] subtitle_extensions = { ".srt", ".sub", ".smi", ".txt", ".mpl", ".dks", ".qtx" };
 
695
            if (scheme == null || scheme == "file") {
 
696
                return;
 
697
            }
 
698
 
 
699
            dot = uri.AbsoluteUri.LastIndexOf ('.');
 
700
            if (dot == -1) {
 
701
                return;
 
702
            }
 
703
            filename = uri.AbsoluteUri.Substring (0, dot);
 
704
        
 
705
            foreach (string extension in subtitle_extensions) {
 
706
                subfile = filename + extension;
 
707
                suburi = new SafeUri (subfile);
 
708
                if (Banshee.IO.File.Exists (suburi)) {
 
709
                    Log.DebugFormat ("[subtitle]: Found subtitle file: {0}", subfile);
 
710
                    playbin.Suburi = subfile;
 
711
                    return;
 
712
                }
 
713
            }
269
714
        }
270
715
 
271
716
        public override void Play ()
272
717
        {
273
 
            // HACK, I think that directsoundsink has a bug that resets its volume to 1.0 every time
274
 
            // This seems to fix bgo#641427
275
 
            Volume = Volume;
276
 
            pipeline.SetState (Gst.State.Playing);
 
718
            playbin.SetState (Gst.State.Playing);
277
719
            OnStateChanged (PlayerState.Playing);
278
720
        }
279
721
 
280
722
        public override void Pause ()
281
723
        {
282
 
            pipeline.SetState (Gst.State.Paused);
 
724
            playbin.SetState (Gst.State.Paused);
283
725
            OnStateChanged (PlayerState.Paused);
284
726
        }
285
727
 
286
728
        public override void Close (bool fullShutdown)
287
729
        {
288
 
            pipeline.SetState (State.Null);
 
730
            playbin.SetState (State.Null);
289
731
            base.Close (fullShutdown);
290
732
        }
291
733
 
298
740
        }
299
741
 
300
742
        public override ushort Volume {
301
 
            get { return (ushort) Math.Round (playbin.Volume * 100.0); }
 
743
            get { return (ushort) Math.Round (audio_sink.Volume * 100.0); }
302
744
            set {
303
 
                double volume = Math.Min (1.0, Math.Max (0, value / 100.0));
304
 
                playbin.Volume = volume;
305
 
                PlayerEngineService.VolumeSchema.Set (value);
 
745
                double volume = ((double)value) / 100.0;
 
746
                audio_sink.Volume = volume;
 
747
                if (audio_sink.VolumeNeedsSaving) {
 
748
                    PlayerEngineService.VolumeSchema.Set (value);
 
749
                }
306
750
            }
307
751
        }
308
752
 
349
793
        }
350
794
 
351
795
        public override bool SupportsEqualizer {
352
 
            get { return false; }
 
796
            get { return audio_sink != null && audio_sink.SupportsEqualizer; }
 
797
        }
 
798
 
 
799
        public double AmplifierLevel {
 
800
            set {
 
801
                if (SupportsEqualizer) {
 
802
                    audio_sink.AmplifierLevel = value;
 
803
                }
 
804
            }
 
805
        }
 
806
        public int [] BandRange {
 
807
            get {
 
808
                if (SupportsEqualizer) {
 
809
                    return audio_sink.BandRange;
 
810
                }
 
811
                return new int [] {};
 
812
            }
 
813
        }
 
814
 
 
815
        public uint [] EqualizerFrequencies {
 
816
            get {
 
817
                if (SupportsEqualizer) {
 
818
                    return audio_sink.EqualizerFrequencies;
 
819
                }
 
820
                return new uint [] {};
 
821
            }
 
822
        }
 
823
 
 
824
        public void SetEqualizerGain (uint band, double gain)
 
825
        {
 
826
            if (SupportsEqualizer) {
 
827
                audio_sink.SetEqualizerGain (band, gain);
 
828
            }
353
829
        }
354
830
 
355
831
        public override VideoDisplayContextType VideoDisplayContextType {
356
 
            get { return VideoDisplayContextType.Unsupported; }
 
832
            get { return video_manager != null ? video_manager.VideoDisplayContextType : VideoDisplayContextType.Unsupported; }
 
833
        }
 
834
 
 
835
        public override IntPtr VideoDisplayContext {
 
836
            set {
 
837
                if (video_manager != null)
 
838
                    video_manager.VideoDisplayContext = value;
 
839
            }
 
840
            get { return video_manager != null ? video_manager.VideoDisplayContext : IntPtr.Zero; }
357
841
        }
358
842
 
359
843
        public override int SubtitleCount {
362
846
 
363
847
        public override int SubtitleIndex {
364
848
            set {
365
 
                if (value >= 0 && value < SubtitleCount) {
 
849
                if (SubtitleCount == 0 || value < -1 || value >= SubtitleCount)
 
850
                    return;
 
851
                int flags = (int)playbin.Flags;
 
852
 
 
853
                if (value == -1) {
 
854
                    flags &= ~(1 << 2);//GST_PLAY_FLAG_TEXT
 
855
                    playbin.Flags = (ObjectFlags)flags;
 
856
                } else {
 
857
                    flags |= (1 << 2);//GST_PLAY_FLAG_TEXT
 
858
                    playbin.Flags = (ObjectFlags)flags;
366
859
                    playbin.CurrentText = value;
367
860
                }
368
861
            }
369
862
        }
370
863
 
371
864
        public override SafeUri SubtitleUri {
372
 
            set { playbin.Suburi = value.AbsoluteUri; }
 
865
            set {
 
866
                long pos = -1;
 
867
                State state;
 
868
                Format format = Format.Bytes;
 
869
                bool paused = false;
 
870
 
 
871
                // GStreamer playbin does not support setting the suburi during playback
 
872
                // so we have to stop/play and seek
 
873
                playbin.GetState (out state, 0);
 
874
                paused = (state == State.Paused);
 
875
                if (state >= State.Paused) {
 
876
                    playbin.QueryPosition (ref format, out pos);
 
877
                    playbin.SetState (State.Ready);
 
878
                    // Wait for the state change to complete
 
879
                    playbin.GetState (out state, 0);
 
880
                }
 
881
 
 
882
                playbin.Suburi = value.AbsoluteUri;
 
883
                playbin.SetState (paused ? State.Paused : State.Playing);
 
884
 
 
885
                // Wait for the state change to complete
 
886
                playbin.GetState (out state, 0);
 
887
 
 
888
                if (pos != -1) {
 
889
                    playbin.Seek (format, SeekFlags.Flush | SeekFlags.KeyUnit, pos);
 
890
                }
 
891
            }
373
892
            get { return new SafeUri (playbin.Suburi); }
374
893
        }
 
894
 
 
895
        private PreferenceBase replaygain_preference;
 
896
 
 
897
        private void InstallPreferences ()
 
898
        {
 
899
            PreferenceService service = ServiceManager.Get<PreferenceService> ();
 
900
            if (service == null) {
 
901
                return;
 
902
            }
 
903
 
 
904
            replaygain_preference = service["general"]["misc"].Add (new SchemaPreference<bool> (ReplayGainEnabledSchema,
 
905
                Catalog.GetString ("_Enable ReplayGain correction"),
 
906
                Catalog.GetString ("For tracks that have ReplayGain data, automatically scale (normalize) playback volume"),
 
907
                delegate { audio_sink.ReplayGainEnabled = ReplayGainEnabledSchema.Get (); }
 
908
            ));
 
909
        }
 
910
 
 
911
        private void UninstallPreferences ()
 
912
        {
 
913
            PreferenceService service = ServiceManager.Get<PreferenceService> ();
 
914
            if (service == null) {
 
915
                return;
 
916
            }
 
917
 
 
918
            service["general"]["misc"].Remove (replaygain_preference);
 
919
            replaygain_preference = null;
 
920
        }
 
921
 
 
922
        public static readonly SchemaEntry<bool> ReplayGainEnabledSchema = new SchemaEntry<bool> (
 
923
            "player_engine", "replay_gain_enabled",
 
924
            false,
 
925
            "Enable ReplayGain",
 
926
            "If ReplayGain data is present on tracks when playing, allow volume scaling"
 
927
        );
 
928
 
375
929
    }
376
930
}