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

« back to all changes in this revision

Viewing changes to .pc/Initial-port-to-GStreamer-1.0.patch/libbanshee/banshee-player-pipeline.c

  • Committer: Package Import Robot
  • Author(s): Chow Loong Jin
  • Date: 2012-11-30 18:17:37 UTC
  • mfrom: (6.3.28 experimental)
  • Revision ID: package-import@ubuntu.com-20121130181737-67acktvo9zy57coc
Tags: 2.6.0-3ubuntu1
* [8bf15b5] Merge from Debian Experimental, 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~
    This is to pick up the arch-specific libgpod.dll binary (LP: #1070631)
  - [03c8cad] Set debian-branch to ubuntu/raring

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//
 
2
// banshee-player-pipeline.c
 
3
//
 
4
// Author:
 
5
//   Aaron Bockover <abockover@novell.com>
 
6
//   Julien Moutte <julien@fluendo.com>
 
7
//
 
8
// Copyright (C) 2005-2008 Novell, Inc.
 
9
// Copyright (C) 2010 Fluendo S.A.
 
10
//
 
11
// Permission is hereby granted, free of charge, to any person obtaining
 
12
// a copy of this software and associated documentation files (the
 
13
// "Software"), to deal in the Software without restriction, including
 
14
// without limitation the rights to use, copy, modify, merge, publish,
 
15
// distribute, sublicense, and/or sell copies of the Software, and to
 
16
// permit persons to whom the Software is furnished to do so, subject to
 
17
// the following conditions:
 
18
//
 
19
// The above copyright notice and this permission notice shall be
 
20
// included in all copies or substantial portions of the Software.
 
21
//
 
22
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
23
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
24
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 
25
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 
26
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 
27
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 
28
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
29
//
 
30
 
 
31
#include "banshee-player-pipeline.h"
 
32
#include "banshee-player-cdda.h"
 
33
#include "banshee-player-dvd.h"
 
34
#include "banshee-player-video.h"
 
35
#include "banshee-player-equalizer.h"
 
36
#include "banshee-player-missing-elements.h"
 
37
#include "banshee-player-replaygain.h"
 
38
#include "banshee-player-vis.h"
 
39
 
 
40
// ---------------------------------------------------------------------------
 
41
// Private Functions
 
42
// ---------------------------------------------------------------------------
 
43
 
 
44
static gboolean
 
45
bp_stream_has_video (GstElement *playbin)
 
46
{
 
47
    int n_video;
 
48
    g_object_get (G_OBJECT (playbin), "n-video", &n_video, NULL);
 
49
    return n_video > 0;
 
50
}
 
51
 
 
52
 
 
53
static void
 
54
bp_pipeline_process_tag (const GstTagList *tag_list, const gchar *tag_name, BansheePlayer *player)
 
55
{
 
56
    const GValue *value;
 
57
    gint value_count;
 
58
    
 
59
    g_return_if_fail (IS_BANSHEE_PLAYER (player));
 
60
 
 
61
    value_count = gst_tag_list_get_tag_size (tag_list, tag_name);
 
62
    if (value_count < 1) {
 
63
        return;
 
64
    }
 
65
    
 
66
    value = gst_tag_list_get_value_index (tag_list, tag_name, 0);
 
67
 
 
68
    if (value != NULL && player->tag_found_cb != NULL) {
 
69
        player->tag_found_cb (player, tag_name, value);
 
70
    }
 
71
}
 
72
 
 
73
static void
 
74
playbin_stream_changed_cb (GstElement * element, BansheePlayer *player)
 
75
{
 
76
    GstMessage *msg;
 
77
 
 
78
    // We're being called from the streaming thread, so don't do anything here
 
79
    msg = gst_message_new_application (GST_OBJECT (player->playbin), gst_structure_new ("stream-changed", NULL));
 
80
    gst_element_post_message (player->playbin, msg);
 
81
}
 
82
 
 
83
static gboolean
 
84
bp_next_track_starting (BansheePlayer *player)
 
85
{
 
86
    gboolean has_video;
 
87
 
 
88
    g_return_val_if_fail (IS_BANSHEE_PLAYER (player), FALSE);
 
89
    g_return_val_if_fail (GST_IS_ELEMENT (player->playbin), FALSE);
 
90
 
 
91
    // FIXME: Work around BGO #602437 - gapless transition between tracks with 
 
92
    // video streams results in broken behaviour - most obviously, huge A/V
 
93
    // sync issues.
 
94
    // Will be in GStreamer 0.10.31
 
95
    has_video = bp_stream_has_video (player->playbin);
 
96
    if (player->in_gapless_transition && has_video) {
 
97
        gchar *uri;
 
98
    
 
99
        bp_debug ("[Gapless]: Aborting gapless transition to stream with video.  Triggering normal track change");
 
100
        g_object_get (G_OBJECT (player->playbin), "uri", &uri, NULL);
 
101
        gst_element_set_state (player->playbin, GST_STATE_READY);
 
102
        
 
103
        g_object_set (G_OBJECT (player->playbin), "uri", uri, NULL);
 
104
        gst_element_set_state (player->playbin, GST_STATE_PLAYING);
 
105
        g_free (uri);
 
106
        player->in_gapless_transition = FALSE;
 
107
        // The transition to playing will happen asynchronously, and will trigger
 
108
        // a second track-starting message.  Stop processing this one.
 
109
        return FALSE;
 
110
    }
 
111
    player->in_gapless_transition = FALSE;
 
112
 
 
113
    if (player->next_track_starting_cb != NULL) {
 
114
        bp_debug ("[gapless] Triggering track-change signal");
 
115
        player->next_track_starting_cb (player);
 
116
    }
 
117
    return FALSE;
 
118
}
 
119
 
 
120
static gboolean
 
121
bp_pipeline_bus_callback (GstBus *bus, GstMessage *message, gpointer userdata)
 
122
{
 
123
    BansheePlayer *player = (BansheePlayer *)userdata;
 
124
 
 
125
    g_return_val_if_fail (IS_BANSHEE_PLAYER (player), FALSE);
 
126
    g_return_val_if_fail (message != NULL, FALSE);
 
127
    
 
128
    switch (GST_MESSAGE_TYPE (message)) {
 
129
        case GST_MESSAGE_EOS: {
 
130
            if (player->eos_cb != NULL) {
 
131
                player->eos_cb (player);
 
132
            }
 
133
            break;
 
134
        }
 
135
            
 
136
        case GST_MESSAGE_STATE_CHANGED: {
 
137
            GstState old, new, pending;
 
138
            gst_message_parse_state_changed (message, &old, &new, &pending);
 
139
            
 
140
            _bp_missing_elements_handle_state_changed (player, old, new);
 
141
            
 
142
            if (player->state_changed_cb != NULL && GST_MESSAGE_SRC (message) == GST_OBJECT (player->playbin)) {
 
143
                player->state_changed_cb (player, old, new, pending);
 
144
            }
 
145
            break;
 
146
        }
 
147
        
 
148
        case GST_MESSAGE_BUFFERING: {
 
149
            const GstStructure *buffering_struct;
 
150
            gint buffering_progress = 0;
 
151
            
 
152
            buffering_struct = gst_message_get_structure (message);
 
153
            if (!gst_structure_get_int (buffering_struct, "buffer-percent", &buffering_progress)) {
 
154
                g_warning ("Could not get completion percentage from BUFFERING message");
 
155
                break;
 
156
            }
 
157
            
 
158
            if (buffering_progress >= 100) {
 
159
                player->buffering = FALSE;
 
160
                if (player->target_state == GST_STATE_PLAYING) {
 
161
                    gst_element_set_state (player->playbin, GST_STATE_PLAYING);
 
162
                }
 
163
            } else if (!player->buffering && player->target_state == GST_STATE_PLAYING) {
 
164
                GstState current_state;
 
165
                gst_element_get_state (player->playbin, &current_state, NULL, 0);
 
166
                if (current_state == GST_STATE_PLAYING) {
 
167
                    gst_element_set_state (player->playbin, GST_STATE_PAUSED);
 
168
                }
 
169
                player->buffering = TRUE;
 
170
            } 
 
171
 
 
172
            if (player->buffering_cb != NULL) {
 
173
                player->buffering_cb (player, buffering_progress);
 
174
            }
 
175
            break;
 
176
        }
 
177
        
 
178
        case GST_MESSAGE_TAG: {
 
179
            GstTagList *tags;
 
180
            
 
181
            if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_TAG) {
 
182
                break;
 
183
            }
 
184
            
 
185
            gst_message_parse_tag (message, &tags);
 
186
            
 
187
            if (GST_IS_TAG_LIST (tags)) {
 
188
                gst_tag_list_foreach (tags, (GstTagForeachFunc)bp_pipeline_process_tag, player);
 
189
                gst_tag_list_free (tags);
 
190
            }
 
191
            break;
 
192
        }
 
193
    
 
194
        case GST_MESSAGE_ERROR: {
 
195
            GError *error;
 
196
            gchar *debug;
 
197
            
 
198
            _bp_pipeline_destroy (player);
 
199
            
 
200
            if (player->error_cb != NULL) {
 
201
                gst_message_parse_error (message, &error, &debug);
 
202
                player->error_cb (player, error->domain, error->code, error->message, debug);
 
203
                g_error_free (error);
 
204
                g_free (debug);
 
205
            }
 
206
            
 
207
            break;
 
208
        } 
 
209
        
 
210
        case GST_MESSAGE_ELEMENT: {
 
211
            const GstStructure *messageStruct;
 
212
            messageStruct = gst_message_get_structure (message);
 
213
            if (GST_MESSAGE_SRC (message) == GST_OBJECT (player->playbin) && gst_structure_has_name (messageStruct, "playbin2-stream-changed")) {
 
214
                bp_next_track_starting (player);
 
215
            }
 
216
            _bp_missing_elements_process_message (player, message);
 
217
            _bp_dvd_elements_process_message (player, message);
 
218
            break;
 
219
        }
 
220
 
 
221
        case GST_MESSAGE_APPLICATION: {
 
222
            const gchar * name;
 
223
            const GstStructure * s = gst_message_get_structure (message);
 
224
            name = gst_structure_get_name (s);
 
225
            if (name && !strcmp (name, "stream-changed")) {
 
226
                _bp_parse_stream_info (player);
 
227
            }
 
228
            break;
 
229
        }
 
230
        
 
231
        default: break;
 
232
    }
 
233
    
 
234
    return TRUE;
 
235
}
 
236
 
 
237
#ifdef ENABLE_GAPLESS
 
238
static void bp_about_to_finish_callback (GstElement *playbin, BansheePlayer *player)
 
239
{
 
240
    g_return_if_fail (IS_BANSHEE_PLAYER (player));
 
241
    g_return_if_fail (GST_IS_ELEMENT (playbin));
 
242
 
 
243
    if (bp_stream_has_video (playbin)) {
 
244
        bp_debug ("[Gapless]: Not attempting gapless transition from stream with video");
 
245
        return;
 
246
    }
 
247
 
 
248
    if (player->about_to_finish_cb != NULL) {
 
249
        player->in_gapless_transition = TRUE;
 
250
 
 
251
        bp_debug ("[Gapless] Requesting next track");
 
252
        player->about_to_finish_cb (player);
 
253
    }
 
254
}
 
255
#endif //ENABLE_GAPLESS
 
256
 
 
257
static void bp_volume_changed_callback (GstElement *playbin, GParamSpec *spec, BansheePlayer *player)
 
258
{
 
259
    gdouble volume;
 
260
 
 
261
    g_return_if_fail (IS_BANSHEE_PLAYER (player));
 
262
    g_return_if_fail (GST_IS_ELEMENT (playbin));
 
263
 
 
264
    g_object_get (G_OBJECT (playbin), "volume", &volume, NULL);
 
265
 
 
266
    player->current_volume = volume;
 
267
 
 
268
    if (player->volume_changed_cb != NULL) {
 
269
        player->volume_changed_cb (player, volume);
 
270
    }
 
271
}
 
272
 
 
273
// ---------------------------------------------------------------------------
 
274
// Internal Functions
 
275
// ---------------------------------------------------------------------------
 
276
 
 
277
gboolean 
 
278
_bp_pipeline_construct (BansheePlayer *player)
 
279
{
 
280
    GstBus *bus;
 
281
    GstPad *teepad;
 
282
    GstPad *pad;
 
283
    GstElement *audiosink;
 
284
    GstElement *audiosinkqueue;
 
285
    GstElement *eq_audioconvert = NULL;
 
286
    GstElement *eq_audioconvert2 = NULL;
 
287
    
 
288
    g_return_val_if_fail (IS_BANSHEE_PLAYER (player), FALSE);
 
289
    
 
290
    // Playbin is the core element that handles autoplugging (finding the right
 
291
    // source and decoder elements) based on source URI and stream content
 
292
    player->playbin = gst_element_factory_make ("playbin2", "playbin");
 
293
 
 
294
#ifdef ENABLE_GAPLESS
 
295
    // FIXME: Connect a proxy about-to-finish callback that will generate a next-track-starting callback.
 
296
    // This can be removed once playbin2 generates its own next-track signal.
 
297
    // bgo#584987 - this is included in >= 0.10.26
 
298
    g_signal_connect (player->playbin, "about-to-finish", G_CALLBACK (bp_about_to_finish_callback), player);
 
299
#endif //ENABLE_GAPLESS
 
300
 
 
301
    g_return_val_if_fail (player->playbin != NULL, FALSE);
 
302
 
 
303
    g_signal_connect (player->playbin, "notify::volume", G_CALLBACK (bp_volume_changed_callback), player);
 
304
    g_signal_connect (player->playbin, "video-changed", G_CALLBACK (playbin_stream_changed_cb), player);
 
305
    g_signal_connect (player->playbin, "audio-changed", G_CALLBACK (playbin_stream_changed_cb), player);
 
306
    g_signal_connect (player->playbin, "text-changed", G_CALLBACK (playbin_stream_changed_cb), player);
 
307
 
 
308
    // Try to find an audio sink, prefer gconf, which typically is set to auto these days,
 
309
    // fall back on auto, which should work on windows, and as a last ditch, try alsa
 
310
    audiosink = gst_element_factory_make ("gconfaudiosink", "audiosink");
 
311
    if (audiosink == NULL) {
 
312
        audiosink = gst_element_factory_make ("directsoundsink", "audiosink");
 
313
        if (audiosink != NULL) {
 
314
            g_object_set (G_OBJECT (audiosink), "volume", 1.0, NULL);
 
315
        } else {
 
316
            audiosink = gst_element_factory_make ("autoaudiosink", "audiosink");
 
317
            if (audiosink == NULL) {
 
318
                audiosink = gst_element_factory_make ("alsasink", "audiosink");
 
319
            }
 
320
        }
 
321
    }
 
322
    
 
323
    g_return_val_if_fail (audiosink != NULL, FALSE);
 
324
 
 
325
    // Set the profile to "music and movies" (gst-plugins-good 0.10.3)
 
326
    if (g_object_class_find_property (G_OBJECT_GET_CLASS (audiosink), "profile")) {
 
327
        g_object_set (G_OBJECT (audiosink), "profile", 1, NULL);
 
328
    }
 
329
 
 
330
    /* Set the audio sink to READY so it can autodetect the right sink element
 
331
     * if needed, as this allows us to correctly determine whether it has a
 
332
     * volume */
 
333
    gst_element_set_state (audiosink, GST_STATE_READY);
 
334
 
 
335
    // See if the audiosink has a 'volume' property.  If it does, we assume it saves and restores
 
336
    // its volume information - and that we shouldn't
 
337
    player->audiosink_has_volume = FALSE;
 
338
    if (!GST_IS_BIN (audiosink)) {
 
339
        player->audiosink_has_volume = g_object_class_find_property (G_OBJECT_GET_CLASS (audiosink), "volume") != NULL;
 
340
    } else {
 
341
        GstIterator *elem_iter = gst_bin_iterate_recurse (GST_BIN (audiosink));
 
342
        BANSHEE_GST_ITERATOR_ITERATE (elem_iter, GstElement *, element, TRUE, {
 
343
            player->audiosink_has_volume |= g_object_class_find_property (G_OBJECT_GET_CLASS (element), "volume") != NULL;
 
344
            gst_object_unref (element);
 
345
        });
 
346
    }
 
347
    bp_debug ("Audiosink has volume: %s",
 
348
        player->audiosink_has_volume ? "YES" : "NO");
 
349
        
 
350
    
 
351
    // Create a custom audio sink bin that will hold the real primary sink
 
352
    player->audiobin = gst_bin_new ("audiobin");
 
353
    g_return_val_if_fail (player->audiobin != NULL, FALSE);
 
354
    
 
355
    // Our audio sink is a tee, so plugins can attach their own pipelines
 
356
    player->audiotee = gst_element_factory_make ("tee", "audiotee");
 
357
    g_return_val_if_fail (player->audiotee != NULL, FALSE);
 
358
 
 
359
    // Create a volume control with low latency
 
360
    player->volume = gst_element_factory_make ("volume", NULL);
 
361
    g_return_val_if_fail (player->volume != NULL, FALSE);
 
362
 
 
363
    // call the volume changed callback once so the volume from the pipeline is
 
364
    // set in the player object
 
365
    bp_volume_changed_callback (player->playbin, NULL, player);
 
366
 
 
367
    audiosinkqueue = gst_element_factory_make ("queue", "audiosinkqueue");
 
368
    g_return_val_if_fail (audiosinkqueue != NULL, FALSE);
 
369
 
 
370
    player->equalizer = _bp_equalizer_new (player);
 
371
    player->preamp = NULL;
 
372
    if (player->equalizer != NULL) {
 
373
        eq_audioconvert = gst_element_factory_make ("audioconvert", "audioconvert");
 
374
        eq_audioconvert2 = gst_element_factory_make ("audioconvert", "audioconvert2");
 
375
        player->preamp = gst_element_factory_make ("volume", "preamp");
 
376
    }
 
377
    
 
378
    // Add elements to custom audio sink
 
379
    gst_bin_add_many (GST_BIN (player->audiobin), player->audiotee, player->volume, audiosinkqueue, audiosink, NULL);
 
380
    
 
381
    if (player->equalizer != NULL) {
 
382
        gst_bin_add_many (GST_BIN (player->audiobin), eq_audioconvert, eq_audioconvert2, player->equalizer, player->preamp, NULL);
 
383
    }
 
384
   
 
385
    // Ghost pad the audio bin so audio is passed from the bin into the tee
 
386
    teepad = gst_element_get_pad (player->audiotee, "sink");
 
387
    gst_element_add_pad (player->audiobin, gst_ghost_pad_new ("sink", teepad));
 
388
    gst_object_unref (teepad);
 
389
 
 
390
    // Link the queue and the actual audio sink
 
391
    if (player->equalizer != NULL) {
 
392
        // link in equalizer, preamp and audioconvert.
 
393
        gst_element_link_many (audiosinkqueue, eq_audioconvert, player->preamp, 
 
394
            player->equalizer, eq_audioconvert2, player->volume, audiosink, NULL);
 
395
    } else {
 
396
        // link the queue with the real audio sink
 
397
        gst_element_link_many (audiosinkqueue, player->volume, audiosink, NULL);
 
398
    }
 
399
    player->before_rgvolume = player->volume;
 
400
    player->after_rgvolume = player->audiosink = audiosink;
 
401
    player->rgvolume_in_pipeline = FALSE;
 
402
    _bp_replaygain_pipeline_rebuild (player);
 
403
 
 
404
    _bp_vis_pipeline_setup (player);
 
405
    
 
406
    // Now that our internal audio sink is constructed, tell playbin to use it
 
407
    g_object_set (G_OBJECT (player->playbin), "audio-sink", player->audiobin, NULL);
 
408
    
 
409
    // Connect to the bus to get messages
 
410
    bus = gst_pipeline_get_bus (GST_PIPELINE (player->playbin));    
 
411
    gst_bus_add_watch (bus, bp_pipeline_bus_callback, player);
 
412
 
 
413
    // Link the first tee pad to the primary audio sink queue
 
414
    GstPad *sinkpad = gst_element_get_pad (audiosinkqueue, "sink");
 
415
    pad = gst_element_get_request_pad (player->audiotee, "src%d");
 
416
    g_object_set(player->audiotee, "alloc-pad", pad, NULL);
 
417
    gst_pad_link (pad, sinkpad);
 
418
    gst_object_unref (GST_OBJECT (pad));
 
419
 
 
420
    // Now allow specialized pipeline setups
 
421
    _bp_cdda_pipeline_setup (player);
 
422
    _bp_dvd_pipeline_setup (player);
 
423
    _bp_video_pipeline_setup (player, bus);
 
424
    _bp_dvd_find_navigation (player);
 
425
 
 
426
    return TRUE;
 
427
}
 
428
 
 
429
void
 
430
_bp_pipeline_destroy (BansheePlayer *player)
 
431
{
 
432
    g_return_if_fail (IS_BANSHEE_PLAYER (player));
 
433
    
 
434
    if (player->playbin == NULL) {
 
435
        return;
 
436
    }
 
437
    
 
438
    if (GST_IS_ELEMENT (player->playbin)) {
 
439
        player->target_state = GST_STATE_NULL;
 
440
        gst_element_set_state (player->playbin, GST_STATE_NULL);
 
441
 
 
442
        // The audiosink was set READY early to detect sink volume control in
 
443
        // case it is out of sync with the playbin state ensure it's in NULL now
 
444
        if (player->audiosink != NULL && GST_STATE (player->audiosink) != GST_STATE_NULL)
 
445
          gst_element_set_state (player->audiosink, GST_STATE_NULL);
 
446
 
 
447
        gst_object_unref (GST_OBJECT (player->playbin));
 
448
    }
 
449
    
 
450
    _bp_vis_pipeline_destroy (player);
 
451
    
 
452
    player->playbin = NULL;
 
453
}