just export gFBDC.getInstance and setResolution to python
[enigma2.git] / lib / service / servicemp3.cpp
1 #ifdef HAVE_GSTREAMER
2
3         /* note: this requires gstreamer 0.10.x and a big list of plugins. */
4         /* it's currently hardcoded to use a big-endian alsasink as sink. */
5 #include <lib/base/eerror.h>
6 #include <lib/base/object.h>
7 #include <lib/base/ebase.h>
8 #include <string>
9 #include <lib/service/servicemp3.h>
10 #include <lib/service/service.h>
11 #include <lib/base/init_num.h>
12 #include <lib/base/init.h>
13 #include <gst/gst.h>
14
15 // eServiceFactoryMP3
16
17 eServiceFactoryMP3::eServiceFactoryMP3()
18 {
19         ePtr<eServiceCenter> sc;
20         
21         eServiceCenter::getPrivInstance(sc);
22         if (sc)
23                 sc->addServiceFactory(eServiceFactoryMP3::id, this);
24
25         m_service_info = new eStaticServiceMP3Info();
26 }
27
28 eServiceFactoryMP3::~eServiceFactoryMP3()
29 {
30         ePtr<eServiceCenter> sc;
31         
32         eServiceCenter::getPrivInstance(sc);
33         if (sc)
34                 sc->removeServiceFactory(eServiceFactoryMP3::id);
35 }
36
37 DEFINE_REF(eServiceFactoryMP3)
38
39         // iServiceHandler
40 RESULT eServiceFactoryMP3::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
41 {
42                 // check resources...
43         ptr = new eServiceMP3(ref.path.c_str());
44         return 0;
45 }
46
47 RESULT eServiceFactoryMP3::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr)
48 {
49         ptr=0;
50         return -1;
51 }
52
53 RESULT eServiceFactoryMP3::list(const eServiceReference &, ePtr<iListableService> &ptr)
54 {
55         ptr=0;
56         return -1;
57 }
58
59 RESULT eServiceFactoryMP3::info(const eServiceReference &ref, ePtr<iStaticServiceInformation> &ptr)
60 {
61         ptr = m_service_info;
62         return 0;
63 }
64
65 RESULT eServiceFactoryMP3::offlineOperations(const eServiceReference &, ePtr<iServiceOfflineOperations> &ptr)
66 {
67         ptr = 0;
68         return -1;
69 }
70
71
72 // eStaticServiceMP3Info
73
74
75 // eStaticServiceMP3Info is seperated from eServiceMP3 to give information
76 // about unopened files.
77
78 // probably eServiceMP3 should use this class as well, and eStaticServiceMP3Info
79 // should have a database backend where ID3-files etc. are cached.
80 // this would allow listing the mp3 database based on certain filters.
81
82 DEFINE_REF(eStaticServiceMP3Info)
83
84 eStaticServiceMP3Info::eStaticServiceMP3Info()
85 {
86 }
87
88 RESULT eStaticServiceMP3Info::getName(const eServiceReference &ref, std::string &name)
89 {
90         size_t last = ref.path.rfind('/');
91         if (last != std::string::npos)
92                 name = ref.path.substr(last+1);
93         else
94                 name = ref.path;
95         return 0;
96 }
97
98 int eStaticServiceMP3Info::getLength(const eServiceReference &ref)
99 {
100         return -1;
101 }
102
103 // eServiceMP3
104
105 eServiceMP3::eServiceMP3(const char *filename): m_filename(filename), m_pump(eApp, 1)
106 {
107         m_stream_tags = 0;
108         CONNECT(m_pump.recv_msg, eServiceMP3::gstPoll);
109         GstElement *source = 0;
110         
111         GstElement *filter = 0, *decoder = 0, *conv = 0, *flt = 0, *sink = 0; /* for audio */
112         
113         GstElement *audio = 0, *queue_audio = 0, *video = 0, *queue_video = 0, *mpegdemux = 0;
114         
115         m_state = stIdle;
116         eDebug("SERVICEMP3 construct!");
117         
118                 /* FIXME: currently, decodebin isn't possible for 
119                    video streams. in that case, make a manual pipeline. */
120
121         const char *ext = strrchr(filename, '.');
122         if (!ext)
123                 ext = filename;
124
125         int is_mpeg_ps = !(strcasecmp(ext, ".mpeg") && strcasecmp(ext, ".mpg") && strcasecmp(ext, ".vob") && strcasecmp(ext, ".bin"));
126         int is_mpeg_ts = !strcasecmp(ext, ".ts");
127         int is_mp3 = !strcasecmp(ext, ".mp3"); /* force mp3 instead of decodebin */
128         int is_video = is_mpeg_ps || is_mpeg_ts;
129         int is_streaming = !strncmp(filename, "http://", 7);
130         
131         eDebug("filename: %s, is_mpeg_ps: %d, is_mpeg_ts: %d, is_video: %d, is_streaming: %d, is_mp3: %d", filename, is_mpeg_ps, is_mpeg_ts, is_video, is_streaming, is_mp3);
132         
133         int is_audio = !is_video;
134         
135         int all_ok = 0;
136
137         m_gst_pipeline = gst_pipeline_new ("audio-player");
138         if (!m_gst_pipeline)
139                 eWarning("failed to create pipeline");
140
141         if (!is_streaming)
142                 source = gst_element_factory_make ("filesrc", "file-source");
143         else
144         {
145                 source = gst_element_factory_make ("neonhttpsrc", "http-source");
146                 if (source)
147                         g_object_set (G_OBJECT (source), "automatic-redirect", TRUE, NULL);
148         }
149
150         if (!source)
151                 eWarning("failed to create %s", is_streaming ? "neonhttpsrc" : "filesrc");
152         else
153                                 /* configure source */
154                 g_object_set (G_OBJECT (source), "location", filename, NULL);
155
156         if (is_audio)
157         {
158                         /* filesrc -> decodebin -> audioconvert -> capsfilter -> alsasink */
159                 const char *decodertype = is_mp3 ? "mad" : "decodebin";
160
161                 decoder = gst_element_factory_make (decodertype, "decoder");
162                 if (!decoder)
163                         eWarning("failed to create %s decoder", decodertype);
164
165                         /* mp3 decoding needs id3demux to extract ID3 data. 'decodebin' would do that internally. */
166                 if (is_mp3)
167                 {
168                         filter = gst_element_factory_make ("id3demux", "filter");
169                         if (!filter)
170                                 eWarning("failed to create id3demux");
171                 }
172
173                 conv = gst_element_factory_make ("audioconvert", "converter");
174                 if (!conv)
175                         eWarning("failed to create audioconvert");
176
177                 flt = gst_element_factory_make ("capsfilter", "flt");
178                 if (!flt)
179                         eWarning("failed to create capsfilter");
180
181                         /* for some reasons, we need to set the sample format to depth/width=16, because auto negotiation doesn't work. */
182                         /* endianness, however, is not required to be set anymore. */
183                 if (flt)
184                 {
185                         GstCaps *caps = gst_caps_new_simple("audio/x-raw-int", /* "endianness", G_TYPE_INT, 4321, */ "depth", G_TYPE_INT, 16, "width", G_TYPE_INT, 16, "channels", G_TYPE_INT, 2, (char*)0);
186                         g_object_set (G_OBJECT (flt), "caps", caps, (char*)0);
187                         gst_caps_unref(caps);
188                 }
189
190                 sink = gst_element_factory_make ("alsasink", "alsa-output");
191                 if (!sink)
192                         eWarning("failed to create osssink");
193
194                 if (source && decoder && conv && sink)
195                         all_ok = 1;
196         } else /* is_video */
197         {
198                         /* filesrc -> mpegdemux -> | queue_audio -> dvbaudiosink
199                                                    | queue_video -> dvbvideosink */
200
201                 audio = gst_element_factory_make("dvbaudiosink", "audio");
202                 queue_audio = gst_element_factory_make("queue", "queue_audio");
203                 
204                 video = gst_element_factory_make("dvbvideosink", "video");
205                 queue_video = gst_element_factory_make("queue", "queue_video");
206                 
207                 if (is_mpeg_ps)
208                         mpegdemux = gst_element_factory_make("flupsdemux", "mpegdemux");
209                 else
210                         mpegdemux = gst_element_factory_make("flutsdemux", "mpegdemux");
211                         
212                 if (!mpegdemux)
213                 {
214                         eDebug("fluendo mpegdemux not available, falling back to mpegdemux\n");
215                         mpegdemux = gst_element_factory_make("mpegdemux", "mpegdemux");
216                 }
217                 
218                 eDebug("audio: %p, queue_audio %p, video %p, queue_video %p, mpegdemux %p", audio, queue_audio, video, queue_video, mpegdemux);
219                 if (audio && queue_audio && video && queue_video && mpegdemux)
220                 {
221                         g_object_set (G_OBJECT (queue_audio), "max-size-buffers", 0, NULL);
222                         g_object_set (G_OBJECT (queue_audio), "max-size-time", (guint64)0, NULL);
223                         g_object_set (G_OBJECT (queue_video), "max-size-buffers", 0, NULL);
224                         g_object_set (G_OBJECT (queue_video), "max-size-time", (guint64)0, NULL);
225                         all_ok = 1;
226                 }
227         }
228         
229         if (m_gst_pipeline && all_ok)
230         {
231                 gst_bus_set_sync_handler(gst_pipeline_get_bus (GST_PIPELINE (m_gst_pipeline)), gstBusSyncHandler, this);
232
233                 if (is_audio)
234                 {
235                         if (!is_mp3)
236                         {
237                                         /* decodebin has dynamic pads. When they get created, we connect them to the audio bin */
238                                 g_signal_connect (decoder, "new-decoded-pad", G_CALLBACK(gstCBnewPad), this);
239                                 g_signal_connect (decoder, "unknown-type", G_CALLBACK(gstCBunknownType), this);
240                         }
241
242                                 /* gst_bin will take the 'floating references' */
243                         gst_bin_add_many (GST_BIN (m_gst_pipeline),
244                                                 source, decoder, NULL);
245
246                         if (filter)
247                         {
248                                         /* id3demux also has dynamic pads, which need to be connected to the decoder (this is done in the 'gstCBfilterPadAdded' CB) */
249                                 gst_bin_add(GST_BIN(m_gst_pipeline), filter);
250                                 gst_element_link(source, filter);
251                                 m_decoder = decoder;
252                                 g_signal_connect (filter, "pad-added", G_CALLBACK(gstCBfilterPadAdded), this);
253                         } else
254                                         /* in decodebin's case we can just connect the source with the decodebin, and decodebin will take care about id3demux (or whatever is required) */
255                                 gst_element_link(source, decoder);
256
257                                 /* create audio bin with the audioconverter, the capsfilter and the audiosink */
258                         m_gst_audio = gst_bin_new ("audiobin");
259
260                         GstPad *audiopad = gst_element_get_pad (conv, "sink");
261                         gst_bin_add_many(GST_BIN(m_gst_audio), conv, flt, sink, (char*)0);
262                         gst_element_link_many(conv, flt, sink, (char*)0);
263                         gst_element_add_pad(m_gst_audio, gst_ghost_pad_new ("sink", audiopad));
264                         gst_object_unref(audiopad);
265                         gst_bin_add (GST_BIN(m_gst_pipeline), m_gst_audio);
266
267                                 /* in mad's case, we can directly connect the decoder to the audiobin. otherwise, we do this in gstCBnewPad */
268                         if (is_mp3)
269                                 gst_element_link(decoder, m_gst_audio);
270                 } else
271                 {
272                         gst_bin_add_many(GST_BIN(m_gst_pipeline), source, mpegdemux, audio, queue_audio, video, queue_video, NULL);
273                         gst_element_link(source, mpegdemux);
274                         gst_element_link(queue_audio, audio);
275                         gst_element_link(queue_video, video);
276                         
277                         m_gst_audioqueue = queue_audio;
278                         m_gst_videoqueue = queue_video;
279                         
280                         g_signal_connect(mpegdemux, "pad-added", G_CALLBACK (gstCBpadAdded), this);
281                 }
282         } else
283         {
284                 if (m_gst_pipeline)
285                         gst_object_unref(GST_OBJECT(m_gst_pipeline));
286                 if (source)
287                         gst_object_unref(GST_OBJECT(source));
288                 if (decoder)
289                         gst_object_unref(GST_OBJECT(decoder));
290                 if (conv)
291                         gst_object_unref(GST_OBJECT(conv));
292                 if (sink)
293                         gst_object_unref(GST_OBJECT(sink));
294
295                 if (audio)
296                         gst_object_unref(GST_OBJECT(audio));
297                 if (queue_audio)
298                         gst_object_unref(GST_OBJECT(queue_audio));
299                 if (video)
300                         gst_object_unref(GST_OBJECT(video));
301                 if (queue_video)
302                         gst_object_unref(GST_OBJECT(queue_video));
303                 if (mpegdemux)
304                         gst_object_unref(GST_OBJECT(mpegdemux));
305
306                 eDebug("sorry, can't play.");
307                 m_gst_pipeline = 0;
308         }
309         
310         gst_element_set_state (m_gst_pipeline, GST_STATE_PLAYING);
311 }
312
313 eServiceMP3::~eServiceMP3()
314 {
315         if (m_state == stRunning)
316                 stop();
317         
318         if (m_stream_tags)
319                 gst_tag_list_free(m_stream_tags);
320         
321         if (m_gst_pipeline)
322         {
323                 gst_object_unref (GST_OBJECT (m_gst_pipeline));
324                 eDebug("SERVICEMP3 destruct!");
325         }
326 }
327
328 DEFINE_REF(eServiceMP3);        
329
330 RESULT eServiceMP3::connectEvent(const Slot2<void,iPlayableService*,int> &event, ePtr<eConnection> &connection)
331 {
332         connection = new eConnection((iPlayableService*)this, m_event.connect(event));
333         return 0;
334 }
335
336 RESULT eServiceMP3::start()
337 {
338         assert(m_state == stIdle);
339         
340         m_state = stRunning;
341         if (m_gst_pipeline)
342         {
343                 eDebug("starting pipeline");
344                 gst_element_set_state (m_gst_pipeline, GST_STATE_PLAYING);
345         }
346         m_event(this, evStart);
347         return 0;
348 }
349
350 RESULT eServiceMP3::stop()
351 {
352         assert(m_state != stIdle);
353         if (m_state == stStopped)
354                 return -1;
355         printf("MP3: %s stop\n", m_filename.c_str());
356         gst_element_set_state(m_gst_pipeline, GST_STATE_NULL);
357         m_state = stStopped;
358         return 0;
359 }
360
361 RESULT eServiceMP3::setTarget(int target)
362 {
363         return -1;
364 }
365
366 RESULT eServiceMP3::pause(ePtr<iPauseableService> &ptr)
367 {
368         ptr=this;
369         return 0;
370 }
371
372 RESULT eServiceMP3::setSlowMotion(int ratio)
373 {
374         return -1;
375 }
376
377 RESULT eServiceMP3::setFastForward(int ratio)
378 {
379         return -1;
380 }
381   
382                 // iPausableService
383 RESULT eServiceMP3::pause()
384 {
385         if (!m_gst_pipeline)
386                 return -1;
387         gst_element_set_state(m_gst_pipeline, GST_STATE_PAUSED);
388         return 0;
389 }
390
391 RESULT eServiceMP3::unpause()
392 {
393         if (!m_gst_pipeline)
394                 return -1;
395         gst_element_set_state(m_gst_pipeline, GST_STATE_PLAYING);
396         return 0;
397 }
398
399         /* iSeekableService */
400 RESULT eServiceMP3::seek(ePtr<iSeekableService> &ptr)
401 {
402         ptr = this;
403         return 0;
404 }
405
406 RESULT eServiceMP3::getLength(pts_t &pts)
407 {
408         if (!m_gst_pipeline)
409                 return -1;
410         if (m_state != stRunning)
411                 return -1;
412         
413         GstFormat fmt = GST_FORMAT_TIME;
414         gint64 len;
415         
416         if (!gst_element_query_duration(m_gst_pipeline, &fmt, &len))
417                 return -1;
418         
419                 /* len is in nanoseconds. we have 90 000 pts per second. */
420         
421         pts = len / 11111;
422         return 0;
423 }
424
425 RESULT eServiceMP3::seekTo(pts_t to)
426 {
427         if (!m_gst_pipeline)
428                 return -1;
429
430                 /* convert pts to nanoseconds */
431         gint64 time_nanoseconds = to * 11111LL;
432         if (!gst_element_seek (m_gst_pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
433                 GST_SEEK_TYPE_SET, time_nanoseconds,
434                 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
435         {
436                 eDebug("SEEK failed");
437                 return -1;
438         }
439         return 0;
440 }
441
442 RESULT eServiceMP3::seekRelative(int direction, pts_t to)
443 {
444         if (!m_gst_pipeline)
445                 return -1;
446
447         pause();
448
449         pts_t ppos;
450         getPlayPosition(ppos);
451         ppos += to * direction;
452         if (ppos < 0)
453                 ppos = 0;
454         seekTo(ppos);
455         
456         unpause();
457
458         return 0;
459 }
460
461 RESULT eServiceMP3::getPlayPosition(pts_t &pts)
462 {
463         if (!m_gst_pipeline)
464                 return -1;
465         if (m_state != stRunning)
466                 return -1;
467         
468         GstFormat fmt = GST_FORMAT_TIME;
469         gint64 len;
470         
471         if (!gst_element_query_position(m_gst_pipeline, &fmt, &len))
472                 return -1;
473         
474                 /* len is in nanoseconds. we have 90 000 pts per second. */
475         pts = len / 11111;
476         return 0;
477 }
478
479 RESULT eServiceMP3::setTrickmode(int trick)
480 {
481                 /* trickmode currently doesn't make any sense for us. */
482         return -1;
483 }
484
485 RESULT eServiceMP3::isCurrentlySeekable()
486 {
487         return 1;
488 }
489
490 RESULT eServiceMP3::info(ePtr<iServiceInformation>&i)
491 {
492         i = this;
493         return 0;
494 }
495
496 RESULT eServiceMP3::getName(std::string &name)
497 {
498         name = m_filename;
499         size_t n = name.rfind('/');
500         if (n != std::string::npos)
501                 name = name.substr(n + 1);
502         return 0;
503 }
504
505 int eServiceMP3::getInfo(int w)
506 {
507         switch (w)
508         {
509         case sTitle:
510         case sArtist:
511         case sAlbum:
512         case sComment:
513         case sTracknumber:
514         case sGenre:
515                 return resIsString;
516
517         default:
518                 return resNA;
519         }
520 }
521
522 std::string eServiceMP3::getInfoString(int w)
523 {
524         gchar *tag = 0;
525         switch (w)
526         {
527         case sTitle:
528                 tag = GST_TAG_TITLE;
529                 break;
530         case sArtist:
531                 tag = GST_TAG_ARTIST;
532                 break;
533         case sAlbum:
534                 tag = GST_TAG_ALBUM;
535                 break;
536         case sComment:
537                 tag = GST_TAG_COMMENT;
538                 break;
539         case sTracknumber:
540                 tag = GST_TAG_TRACK_NUMBER;
541                 break;
542         case sGenre:
543                 tag = GST_TAG_GENRE;
544                 break;
545         default:
546                 return "";
547         }
548         
549         if (!m_stream_tags || !tag)
550                 return "";
551         
552         gchar *value;
553         
554         if (gst_tag_list_get_string(m_stream_tags, tag, &value))
555         {
556                 std::string res = value;
557                 g_free(value);
558                 return res;
559         }
560         
561         return "";
562 }
563
564
565                 void foreach(const GstTagList *list, const gchar *tag, gpointer user_data)
566                 {
567                         if (tag)
568                                 eDebug("Tag: %c%c%c%c", tag[0], tag[1], tag[2], tag[3]);
569                         
570                 }
571
572 void eServiceMP3::gstBusCall(GstBus *bus, GstMessage *msg)
573 {
574         if (msg)
575         {
576                 gchar *string = gst_structure_to_string(gst_message_get_structure(msg));
577                 eDebug("gst_message: %s", string);
578                 g_free(string);
579         }
580         
581         switch (GST_MESSAGE_TYPE (msg))
582         {
583         case GST_MESSAGE_EOS:
584                 m_event((iPlayableService*)this, evEOF);
585                 break;
586         case GST_MESSAGE_ERROR:
587         {
588                 gchar *debug;
589                 GError *err;
590                 gst_message_parse_error (msg, &err, &debug);
591                 g_free (debug);
592                 eWarning("Gstreamer error: %s", err->message);
593                 g_error_free(err);
594                         /* TODO: signal error condition to user */
595                 break;
596         }
597         case GST_MESSAGE_TAG:
598         {
599                 GstTagList *tags, *result;
600                 gst_message_parse_tag(msg, &tags);
601
602                 result = gst_tag_list_merge(m_stream_tags, tags, GST_TAG_MERGE_PREPEND);
603                 if (result)
604                 {
605                         if (m_stream_tags)
606                                 gst_tag_list_free(m_stream_tags);
607                         m_stream_tags = result;
608                 }
609                 gst_tag_list_free(tags);
610                 
611                 m_event((iPlayableService*)this, evUpdatedInfo);
612                 break;
613         }
614         default:
615                 break;
616         }
617 }
618
619 GstBusSyncReply eServiceMP3::gstBusSyncHandler(GstBus *bus, GstMessage *message, gpointer user_data)
620 {
621         eServiceMP3 *_this = (eServiceMP3*)user_data;
622         _this->m_pump.send(1);
623                 /* wake */
624         return GST_BUS_PASS;
625 }
626
627 void eServiceMP3::gstCBpadAdded(GstElement *decodebin, GstPad *pad, gpointer user_data)
628 {
629         eServiceMP3 *_this = (eServiceMP3*)user_data;
630         
631         gchar *name;
632         name = gst_pad_get_name (pad);
633         g_print ("A new pad %s was created\n", name);
634         if (!strncmp(name, "audio_", 6)) // mpegdemux uses video_nn with n=0,1,.., flupsdemux uses stream id
635                 gst_pad_link(pad, gst_element_get_pad (_this->m_gst_audioqueue, "sink"));
636         if (!strncmp(name, "video_", 6))
637                 gst_pad_link(pad, gst_element_get_pad (_this->m_gst_videoqueue, "sink"));
638         g_free (name);
639         
640 }
641
642 void eServiceMP3::gstCBfilterPadAdded(GstElement *filter, GstPad *pad, gpointer user_data)
643 {
644         eServiceMP3 *_this = (eServiceMP3*)user_data;
645         gst_pad_link(pad, gst_element_get_pad (_this->m_decoder, "sink"));
646 }
647
648 void eServiceMP3::gstCBnewPad(GstElement *decodebin, GstPad *pad, gboolean last, gpointer user_data)
649 {
650         eServiceMP3 *_this = (eServiceMP3*)user_data;
651         GstCaps *caps;
652         GstStructure *str;
653         GstPad *audiopad;
654
655         /* only link once */
656         audiopad = gst_element_get_pad (_this->m_gst_audio, "sink");
657         if (GST_PAD_IS_LINKED (audiopad)) {
658                 eDebug("audio already linked!");
659                 g_object_unref (audiopad);
660                 return;
661         }
662
663         /* check media type */
664         caps = gst_pad_get_caps (pad);
665         str = gst_caps_get_structure (caps, 0);
666         eDebug("gst new pad! %s", gst_structure_get_name (str));
667         
668         if (!g_strrstr (gst_structure_get_name (str), "audio")) {
669                 gst_caps_unref (caps);
670                 gst_object_unref (audiopad);
671                 return;
672         }
673         
674         gst_caps_unref (caps);
675         gst_pad_link (pad, audiopad);
676 }
677
678 void eServiceMP3::gstCBunknownType(GstElement *decodebin, GstPad *pad, GstCaps *caps, gpointer user_data)
679 {
680         eServiceMP3 *_this = (eServiceMP3*)user_data;
681         GstStructure *str;
682
683         /* check media type */
684         caps = gst_pad_get_caps (pad);
685         str = gst_caps_get_structure (caps, 0);
686         eDebug("unknown type: %s - this can't be decoded.", gst_structure_get_name (str));
687         gst_caps_unref (caps);
688 }
689
690 void eServiceMP3::gstPoll(const int&)
691 {
692                 /* ok, we have a serious problem here. gstBusSyncHandler sends 
693                    us the wakup signal, but likely before it was posted.
694                    the usleep, an EVIL HACK (DON'T DO THAT!!!) works around this.
695                    
696                    I need to understand the API a bit more to make this work 
697                    proplerly. */
698         usleep(1);
699         
700         GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (m_gst_pipeline));
701         GstMessage *message;
702         while ((message = gst_bus_pop (bus)))
703         {
704                 gstBusCall(bus, message);
705                 gst_message_unref (message);
706         }
707 }
708
709 eAutoInitPtr<eServiceFactoryMP3> init_eServiceFactoryMP3(eAutoInitNumbers::service+1, "eServiceFactoryMP3");
710 #else
711 #warning gstreamer not available, not building media player
712 #endif