also use refcounting for eTimers
[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/components/file_eraser.h>
12 #include <lib/base/init_num.h>
13 #include <lib/base/init.h>
14 #include <gst/gst.h>
15 #include <gst/pbutils/missing-plugins.h>
16 #include <sys/stat.h>
17 /* for subtitles */
18 #include <lib/gui/esubtitle.h>
19
20 // eServiceFactoryMP3
21
22 eServiceFactoryMP3::eServiceFactoryMP3()
23 {
24         ePtr<eServiceCenter> sc;
25         
26         eServiceCenter::getPrivInstance(sc);
27         if (sc)
28         {
29                 std::list<std::string> extensions;
30                 extensions.push_back("mp3");
31                 extensions.push_back("ogg");
32                 extensions.push_back("mpg");
33                 extensions.push_back("vob");
34                 extensions.push_back("wav");
35                 extensions.push_back("wave");
36                 extensions.push_back("mkv");
37                 extensions.push_back("avi");
38                 extensions.push_back("dat");
39                 extensions.push_back("flac");
40                 extensions.push_back("mp4");
41                 sc->addServiceFactory(eServiceFactoryMP3::id, this, extensions);
42         }
43
44         m_service_info = new eStaticServiceMP3Info();
45 }
46
47 eServiceFactoryMP3::~eServiceFactoryMP3()
48 {
49         ePtr<eServiceCenter> sc;
50         
51         eServiceCenter::getPrivInstance(sc);
52         if (sc)
53                 sc->removeServiceFactory(eServiceFactoryMP3::id);
54 }
55
56 DEFINE_REF(eServiceFactoryMP3)
57
58         // iServiceHandler
59 RESULT eServiceFactoryMP3::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
60 {
61                 // check resources...
62         ptr = new eServiceMP3(ref.path.c_str());
63         return 0;
64 }
65
66 RESULT eServiceFactoryMP3::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr)
67 {
68         ptr=0;
69         return -1;
70 }
71
72 RESULT eServiceFactoryMP3::list(const eServiceReference &, ePtr<iListableService> &ptr)
73 {
74         ptr=0;
75         return -1;
76 }
77
78 RESULT eServiceFactoryMP3::info(const eServiceReference &ref, ePtr<iStaticServiceInformation> &ptr)
79 {
80         ptr = m_service_info;
81         return 0;
82 }
83
84 class eMP3ServiceOfflineOperations: public iServiceOfflineOperations
85 {
86         DECLARE_REF(eMP3ServiceOfflineOperations);
87         eServiceReference m_ref;
88 public:
89         eMP3ServiceOfflineOperations(const eServiceReference &ref);
90         
91         RESULT deleteFromDisk(int simulate);
92         RESULT getListOfFilenames(std::list<std::string> &);
93 };
94
95 DEFINE_REF(eMP3ServiceOfflineOperations);
96
97 eMP3ServiceOfflineOperations::eMP3ServiceOfflineOperations(const eServiceReference &ref): m_ref((const eServiceReference&)ref)
98 {
99 }
100
101 RESULT eMP3ServiceOfflineOperations::deleteFromDisk(int simulate)
102 {
103         if (simulate)
104                 return 0;
105         else
106         {
107                 std::list<std::string> res;
108                 if (getListOfFilenames(res))
109                         return -1;
110                 
111                 eBackgroundFileEraser *eraser = eBackgroundFileEraser::getInstance();
112                 if (!eraser)
113                         eDebug("FATAL !! can't get background file eraser");
114                 
115                 for (std::list<std::string>::iterator i(res.begin()); i != res.end(); ++i)
116                 {
117                         eDebug("Removing %s...", i->c_str());
118                         if (eraser)
119                                 eraser->erase(i->c_str());
120                         else
121                                 ::unlink(i->c_str());
122                 }
123                 
124                 return 0;
125         }
126 }
127
128 RESULT eMP3ServiceOfflineOperations::getListOfFilenames(std::list<std::string> &res)
129 {
130         res.clear();
131         res.push_back(m_ref.path);
132         return 0;
133 }
134
135
136 RESULT eServiceFactoryMP3::offlineOperations(const eServiceReference &ref, ePtr<iServiceOfflineOperations> &ptr)
137 {
138         ptr = new eMP3ServiceOfflineOperations(ref);
139         return 0;
140 }
141
142 // eStaticServiceMP3Info
143
144
145 // eStaticServiceMP3Info is seperated from eServiceMP3 to give information
146 // about unopened files.
147
148 // probably eServiceMP3 should use this class as well, and eStaticServiceMP3Info
149 // should have a database backend where ID3-files etc. are cached.
150 // this would allow listing the mp3 database based on certain filters.
151
152 DEFINE_REF(eStaticServiceMP3Info)
153
154 eStaticServiceMP3Info::eStaticServiceMP3Info()
155 {
156 }
157
158 RESULT eStaticServiceMP3Info::getName(const eServiceReference &ref, std::string &name)
159 {
160         size_t last = ref.path.rfind('/');
161         if (last != std::string::npos)
162                 name = ref.path.substr(last+1);
163         else
164                 name = ref.path;
165         return 0;
166 }
167
168 int eStaticServiceMP3Info::getLength(const eServiceReference &ref)
169 {
170         return -1;
171 }
172
173 // eServiceMP3
174
175 eServiceMP3::eServiceMP3(const char *filename): m_filename(filename), m_pump(eApp, 1)
176 {
177         m_seekTimeout = eTimer::create(eApp);
178         m_stream_tags = 0;
179         m_currentAudioStream = 0;
180         m_currentSubtitleStream = 0;
181         m_subtitle_widget = 0;
182         m_currentTrickRatio = 0;
183         CONNECT(m_seekTimeout->timeout, eServiceMP3::seekTimeoutCB);
184         CONNECT(m_pump.recv_msg, eServiceMP3::gstPoll);
185         GstElement *source = 0;
186         
187         GstElement *decoder = 0, *conv = 0, *flt = 0, *sink = 0; /* for audio */
188         
189         GstElement *audio = 0, *switch_audio = 0, *queue_audio = 0, *video = 0, *queue_video = 0, *videodemux = 0;
190         
191         m_state = stIdle;
192         eDebug("SERVICEMP3 construct!");
193         
194                 /* FIXME: currently, decodebin isn't possible for 
195                    video streams. in that case, make a manual pipeline. */
196
197         const char *ext = strrchr(filename, '.');
198         if (!ext)
199                 ext = filename;
200
201         sourceStream sourceinfo;
202         if ( (strcasecmp(ext, ".mpeg") && strcasecmp(ext, ".mpg") && strcasecmp(ext, ".vob") && strcasecmp(ext, ".bin") && strcasecmp(ext, ".dat") ) == 0 )
203                 sourceinfo.containertype = ctMPEGPS;
204         else if ( strcasecmp(ext, ".ts") == 0 )
205                 sourceinfo.containertype = ctMPEGTS;
206         else if ( strcasecmp(ext, ".mkv") == 0 )
207                 sourceinfo.containertype = ctMKV;
208         else if ( strcasecmp(ext, ".avi") == 0 )
209                 sourceinfo.containertype = ctAVI;
210         else if ( strcasecmp(ext, ".mp4") == 0 )
211                 sourceinfo.containertype = ctMP4;
212         else if ( (strncmp(filename, "/autofs/", 8) || strncmp(filename+strlen(filename)-13, "/track-", 7) || strcasecmp(ext, ".wav")) == 0 )
213                 sourceinfo.containertype = ctCDA;
214         if ( strcasecmp(ext, ".dat") == 0 )
215                 sourceinfo.containertype = ctVCD;
216         if ( (strncmp(filename, "http://", 7)) == 0 )
217                 sourceinfo.is_streaming = TRUE;
218
219         sourceinfo.is_video = ( sourceinfo.containertype && sourceinfo.containertype != ctCDA );
220
221         eDebug("filename=%s, containertype=%d, is_video=%d, is_streaming=%d", filename, sourceinfo.containertype, sourceinfo.is_video, sourceinfo.is_streaming);
222
223         int all_ok = 0;
224
225         m_gst_pipeline = gst_pipeline_new ("mediaplayer");
226         if (!m_gst_pipeline)
227                 m_error_message = "failed to create GStreamer pipeline!\n";
228
229         if ( sourceinfo.containertype == ctCDA )
230         {
231                 source = gst_element_factory_make ("cdiocddasrc", "cda-source");
232                 if (source)
233                         g_object_set (G_OBJECT (source), "device", "/dev/cdroms/cdrom0", NULL);
234                 else
235                         sourceinfo.containertype = ctNone;
236         }
237         if ( !sourceinfo.is_streaming && sourceinfo.containertype != ctCDA )
238         {
239                 source = gst_element_factory_make ("filesrc", "file-source");
240                 if (source)
241                         g_object_set (G_OBJECT (source), "location", filename, NULL);
242                 else
243                         m_error_message = "GStreamer can't open filesrc " + (std::string)filename + "!\n";
244         }
245         else if ( sourceinfo.is_streaming ) 
246         {
247                 source = gst_element_factory_make ("neonhttpsrc", "http-source");
248                 if (source)
249                         g_object_set (G_OBJECT (source), "automatic-redirect", TRUE, NULL);
250                 else
251                         m_error_message = "GStreamer plugin neonhttpsrc not available!\n";
252         }
253         else
254         { 
255                 int track = atoi(filename+18);
256                 eDebug("play audio CD track #%i",track);
257                 if (track > 0)
258                         g_object_set (G_OBJECT (source), "track", track, NULL);
259         }
260         if ( sourceinfo.is_video )
261         {
262                         /* filesrc -> mpegdemux -> | queue_audio -> dvbaudiosink
263                                                    | queue_video -> dvbvideosink */
264
265                 audio = gst_element_factory_make("dvbaudiosink", "audiosink");
266                 if (!audio)
267                         m_error_message += "failed to create Gstreamer element dvbaudiosink\n";
268                 
269                 video = gst_element_factory_make("dvbvideosink", "videosink");
270                 if (!video)
271                         m_error_message += "failed to create Gstreamer element dvbvideosink\n";
272
273                 queue_audio = gst_element_factory_make("queue", "queue_audio");
274                 queue_video = gst_element_factory_make("queue", "queue_video");
275
276                 char demux_type[14];
277                 switch (sourceinfo.containertype)
278                 {
279                         case ctMPEGTS:
280                                 strcat(demux_type, "flutsdemux");
281                                 break;
282                         case ctMPEGPS:
283                         case ctVCD:
284                                 strcat(demux_type, "flupsdemux");
285                                 break;
286                         case ctMKV:
287                                 strcat(demux_type, "matroskademux");
288                                 break;
289                         case ctAVI:
290                                 strcat(demux_type, "avidemux");
291                                 break;
292                         case ctMP4:
293                                 strcat(demux_type, "qtdemux");
294                                 break;
295                         default:
296                                 break;
297                 }
298                 videodemux = gst_element_factory_make(demux_type, "videodemux");
299                 if (!videodemux)
300                         m_error_message = "GStreamer plugin " + (std::string)demux_type + " not available!\n";
301
302                 switch_audio = gst_element_factory_make ("input-selector", "switch_audio");
303                 if (!switch_audio)
304                         m_error_message = "GStreamer plugin input-selector not available!\n";
305
306                 if (audio && queue_audio && video && queue_video && videodemux && switch_audio)
307                 {
308                         g_object_set (G_OBJECT (queue_audio), "max-size-bytes", 256*1024, NULL);
309                         g_object_set (G_OBJECT (queue_audio), "max-size-buffers", 0, NULL);
310                         g_object_set (G_OBJECT (queue_audio), "max-size-time", (guint64)0, NULL);
311                         g_object_set (G_OBJECT (queue_video), "max-size-buffers", 0, NULL);
312                         g_object_set (G_OBJECT (queue_video), "max-size-bytes", 2*1024*1024, NULL);
313                         g_object_set (G_OBJECT (queue_video), "max-size-time", (guint64)0, NULL);
314                         g_object_set (G_OBJECT (switch_audio), "select-all", TRUE, NULL);
315                         all_ok = 1;
316                 }
317         } else /* is audio */
318         {
319
320                         /* filesrc -> decodebin -> audioconvert -> capsfilter -> alsasink */
321                 decoder = gst_element_factory_make ("decodebin", "decoder");
322                 if (!decoder)
323                         m_error_message += "failed to create Gstreamer element decodebin\n";
324
325                 conv = gst_element_factory_make ("audioconvert", "converter");
326                 if (!conv)
327                         m_error_message += "failed to create Gstreamer element audioconvert\n";
328
329                 flt = gst_element_factory_make ("capsfilter", "flt");
330                 if (!flt)
331                         m_error_message += "failed to create Gstreamer element capsfilter\n";
332
333                         /* for some reasons, we need to set the sample format to depth/width=16, because auto negotiation doesn't work. */
334                         /* endianness, however, is not required to be set anymore. */
335                 if (flt)
336                 {
337                         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, */NULL);
338                         g_object_set (G_OBJECT (flt), "caps", caps, NULL);
339                         gst_caps_unref(caps);
340                 }
341
342                 sink = gst_element_factory_make ("alsasink", "alsa-output");
343                 if (!sink)
344                         m_error_message += "failed to create Gstreamer element alsasink\n";
345
346                 if (source && decoder && conv && sink)
347                         all_ok = 1;
348         }
349         if (m_gst_pipeline && all_ok)
350         {
351                 gst_bus_set_sync_handler(gst_pipeline_get_bus (GST_PIPELINE (m_gst_pipeline)), gstBusSyncHandler, this);
352
353                 if ( sourceinfo.containertype == ctCDA )
354                 {
355                         queue_audio = gst_element_factory_make("queue", "queue_audio");
356                         g_object_set (G_OBJECT (sink), "preroll-queue-len", 80, NULL);
357                         gst_bin_add_many (GST_BIN (m_gst_pipeline), source, queue_audio, conv, sink, NULL);
358                         gst_element_link_many(source, queue_audio, conv, sink, NULL);
359                 }
360                 else if ( sourceinfo.is_video )
361                 {
362                         char srt_filename[strlen(filename)+1];
363                         strncpy(srt_filename,filename,strlen(filename)-3);
364                         srt_filename[strlen(filename)-3]='\0';
365                         strcat(srt_filename, "srt");
366                         struct stat buffer;
367                         if (stat(srt_filename, &buffer) == 0)
368                         {
369                                 eDebug("subtitle file found: %s",srt_filename);
370                                 GstElement *subsource = gst_element_factory_make ("filesrc", "srt_source");
371                                 g_object_set (G_OBJECT (subsource), "location", srt_filename, NULL);
372                                 gst_bin_add(GST_BIN (m_gst_pipeline), subsource);
373                                 GstPad *switchpad = gstCreateSubtitleSink(this, stSRT);
374                                 gst_pad_link(gst_element_get_pad (subsource, "src"), switchpad);
375                                 subtitleStream subs;
376                                 subs.pad = switchpad;
377                                 subs.type = stSRT;
378                                 subs.language_code = std::string("und");
379                                 m_subtitleStreams.push_back(subs);
380                         }
381                         gst_bin_add_many(GST_BIN(m_gst_pipeline), source, videodemux, audio, queue_audio, video, queue_video, switch_audio, NULL);
382
383                         if ( sourceinfo.containertype == ctVCD )
384                         {
385                                 GstElement *cdxaparse = gst_element_factory_make("cdxaparse", "cdxaparse");
386                                 gst_bin_add(GST_BIN(m_gst_pipeline), cdxaparse);
387                                 gst_element_link(source, cdxaparse);
388                                 gst_element_link(cdxaparse, videodemux);
389                         }
390                         else
391                                 gst_element_link(source, videodemux);
392
393                         gst_element_link(switch_audio, queue_audio);
394                         gst_element_link(queue_audio, audio);
395                         gst_element_link(queue_video, video);
396                         g_signal_connect(videodemux, "pad-added", G_CALLBACK (gstCBpadAdded), this);
397
398                 } else /* is audio*/
399                 {
400                         queue_audio = gst_element_factory_make("queue", "queue_audio");
401
402                         g_signal_connect (decoder, "new-decoded-pad", G_CALLBACK(gstCBnewPad), this);
403                         g_signal_connect (decoder, "unknown-type", G_CALLBACK(gstCBunknownType), this);
404
405                         g_object_set (G_OBJECT (sink), "preroll-queue-len", 80, NULL);
406
407                                 /* gst_bin will take the 'floating references' */
408                         gst_bin_add_many (GST_BIN (m_gst_pipeline),
409                                                 source, queue_audio, decoder, NULL);
410
411                                 /* in decodebin's case we can just connect the source with the decodebin, and decodebin will take care about id3demux (or whatever is required) */
412                         gst_element_link_many(source, queue_audio, decoder, NULL);
413
414                                 /* create audio bin with the audioconverter, the capsfilter and the audiosink */
415                         audio = gst_bin_new ("audiobin");
416
417                         GstPad *audiopad = gst_element_get_static_pad (conv, "sink");
418                         gst_bin_add_many(GST_BIN(audio), conv, flt, sink, NULL);
419                         gst_element_link_many(conv, flt, sink, NULL);
420                         gst_element_add_pad(audio, gst_ghost_pad_new ("sink", audiopad));
421                         gst_object_unref(audiopad);
422                         gst_bin_add (GST_BIN(m_gst_pipeline), audio);
423                 }
424         } else
425         {
426                 m_event((iPlayableService*)this, evUser+12);
427
428                 if (m_gst_pipeline)
429                         gst_object_unref(GST_OBJECT(m_gst_pipeline));
430                 if (source)
431                         gst_object_unref(GST_OBJECT(source));
432                 if (decoder)
433                         gst_object_unref(GST_OBJECT(decoder));
434                 if (conv)
435                         gst_object_unref(GST_OBJECT(conv));
436                 if (sink)
437                         gst_object_unref(GST_OBJECT(sink));
438
439                 if (audio)
440                         gst_object_unref(GST_OBJECT(audio));
441                 if (queue_audio)
442                         gst_object_unref(GST_OBJECT(queue_audio));
443                 if (video)
444                         gst_object_unref(GST_OBJECT(video));
445                 if (queue_video)
446                         gst_object_unref(GST_OBJECT(queue_video));
447                 if (videodemux)
448                         gst_object_unref(GST_OBJECT(videodemux));
449                 if (switch_audio)
450                         gst_object_unref(GST_OBJECT(switch_audio));
451
452                 eDebug("sorry, can't play: %s",m_error_message.c_str());
453                 m_gst_pipeline = 0;
454         }
455
456         gst_element_set_state (m_gst_pipeline, GST_STATE_PLAYING);
457 }
458
459 eServiceMP3::~eServiceMP3()
460 {
461         delete m_subtitle_widget;
462         if (m_state == stRunning)
463                 stop();
464         
465         if (m_stream_tags)
466                 gst_tag_list_free(m_stream_tags);
467         
468         if (m_gst_pipeline)
469         {
470                 gst_object_unref (GST_OBJECT (m_gst_pipeline));
471                 eDebug("SERVICEMP3 destruct!");
472         }
473 }
474
475 DEFINE_REF(eServiceMP3);        
476
477 RESULT eServiceMP3::connectEvent(const Slot2<void,iPlayableService*,int> &event, ePtr<eConnection> &connection)
478 {
479         connection = new eConnection((iPlayableService*)this, m_event.connect(event));
480         return 0;
481 }
482
483 RESULT eServiceMP3::start()
484 {
485         assert(m_state == stIdle);
486         
487         m_state = stRunning;
488         if (m_gst_pipeline)
489         {
490                 eDebug("starting pipeline");
491                 gst_element_set_state (m_gst_pipeline, GST_STATE_PLAYING);
492         }
493         m_event(this, evStart);
494         return 0;
495 }
496
497 RESULT eServiceMP3::stop()
498 {
499         assert(m_state != stIdle);
500         if (m_state == stStopped)
501                 return -1;
502         eDebug("MP3: %s stop\n", m_filename.c_str());
503         gst_element_set_state(m_gst_pipeline, GST_STATE_NULL);
504         m_state = stStopped;
505         return 0;
506 }
507
508 RESULT eServiceMP3::setTarget(int target)
509 {
510         return -1;
511 }
512
513 RESULT eServiceMP3::pause(ePtr<iPauseableService> &ptr)
514 {
515         ptr=this;
516         return 0;
517 }
518
519 RESULT eServiceMP3::setSlowMotion(int ratio)
520 {
521         /* we can't do slomo yet */
522         return -1;
523 }
524
525 RESULT eServiceMP3::setFastForward(int ratio)
526 {
527         m_currentTrickRatio = ratio;
528         if (ratio)
529                 m_seekTimeout->start(1000, 0);
530         else
531                 m_seekTimeout->stop();
532         return 0;
533 }
534
535 void eServiceMP3::seekTimeoutCB()
536 {
537         pts_t ppos, len;
538         getPlayPosition(ppos);
539         getLength(len);
540         ppos += 90000*m_currentTrickRatio;
541         
542         if (ppos < 0)
543         {
544                 ppos = 0;
545                 m_seekTimeout->stop();
546         }
547         if (ppos > len)
548         {
549                 ppos = 0;
550                 stop();
551                 m_seekTimeout->stop();
552                 return;
553         }
554         seekTo(ppos);
555 }
556
557                 // iPausableService
558 RESULT eServiceMP3::pause()
559 {
560         if (!m_gst_pipeline)
561                 return -1;
562         GstStateChangeReturn res = gst_element_set_state(m_gst_pipeline, GST_STATE_PAUSED);
563         if (res == GST_STATE_CHANGE_ASYNC)
564         {
565                 pts_t ppos;
566                 getPlayPosition(ppos);
567                 seekTo(ppos);
568         }
569         return 0;
570 }
571
572 RESULT eServiceMP3::unpause()
573 {
574         if (!m_gst_pipeline)
575                 return -1;
576
577         GstStateChangeReturn res;
578         res = gst_element_set_state(m_gst_pipeline, GST_STATE_PLAYING);
579         return 0;
580 }
581
582         /* iSeekableService */
583 RESULT eServiceMP3::seek(ePtr<iSeekableService> &ptr)
584 {
585         ptr = this;
586         return 0;
587 }
588
589 RESULT eServiceMP3::getLength(pts_t &pts)
590 {
591         if (!m_gst_pipeline)
592                 return -1;
593         if (m_state != stRunning)
594                 return -1;
595         
596         GstFormat fmt = GST_FORMAT_TIME;
597         gint64 len;
598         
599         if (!gst_element_query_duration(m_gst_pipeline, &fmt, &len))
600                 return -1;
601         
602                 /* len is in nanoseconds. we have 90 000 pts per second. */
603         
604         pts = len / 11111;
605         return 0;
606 }
607
608 RESULT eServiceMP3::seekTo(pts_t to)
609 {
610         if (!m_gst_pipeline)
611                 return -1;
612
613                 /* convert pts to nanoseconds */
614         gint64 time_nanoseconds = to * 11111LL;
615         if (!gst_element_seek (m_gst_pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
616                 GST_SEEK_TYPE_SET, time_nanoseconds,
617                 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
618         {
619                 eDebug("SEEK failed");
620                 return -1;
621         }
622         return 0;
623 }
624
625 RESULT eServiceMP3::seekRelative(int direction, pts_t to)
626 {
627         if (!m_gst_pipeline)
628                 return -1;
629
630         pts_t ppos;
631         getPlayPosition(ppos);
632         ppos += to * direction;
633         if (ppos < 0)
634                 ppos = 0;
635         seekTo(ppos);
636         
637         return 0;
638 }
639
640 RESULT eServiceMP3::getPlayPosition(pts_t &pts)
641 {
642         if (!m_gst_pipeline)
643                 return -1;
644         if (m_state != stRunning)
645                 return -1;
646         
647         GstFormat fmt = GST_FORMAT_TIME;
648         gint64 len;
649         
650         if (!gst_element_query_position(m_gst_pipeline, &fmt, &len))
651                 return -1;
652         
653                 /* len is in nanoseconds. we have 90 000 pts per second. */
654         pts = len / 11111;
655         return 0;
656 }
657
658 RESULT eServiceMP3::setTrickmode(int trick)
659 {
660                 /* trickmode is not yet supported by our dvbmediasinks. */
661         return -1;
662 }
663
664 RESULT eServiceMP3::isCurrentlySeekable()
665 {
666         return 1;
667 }
668
669 RESULT eServiceMP3::info(ePtr<iServiceInformation>&i)
670 {
671         i = this;
672         return 0;
673 }
674
675 RESULT eServiceMP3::getName(std::string &name)
676 {
677         name = m_filename;
678         size_t n = name.rfind('/');
679         if (n != std::string::npos)
680                 name = name.substr(n + 1);
681         return 0;
682 }
683
684 int eServiceMP3::getInfo(int w)
685 {
686         gchar *tag = 0;
687
688         switch (w)
689         {
690         case sTitle:
691         case sArtist:
692         case sAlbum:
693         case sComment:
694         case sTracknumber:
695         case sGenre:
696         case sVideoType:
697         case sTimeCreate:
698         case sUser+12:
699                 return resIsString;
700         case sCurrentTitle:
701                 tag = GST_TAG_TRACK_NUMBER;
702                 break;
703         case sTotalTitles:
704                 tag = GST_TAG_TRACK_COUNT;
705                 break;
706         default:
707                 return resNA;
708         }
709
710         if (!m_stream_tags || !tag)
711                 return 0;
712         
713         guint value;
714         if (gst_tag_list_get_uint(m_stream_tags, tag, &value))
715                 return (int) value;
716         
717         return 0;
718
719 }
720
721 std::string eServiceMP3::getInfoString(int w)
722 {
723         if ( !m_stream_tags )
724                 return "";
725         gchar *tag = 0;
726         switch (w)
727         {
728         case sTitle:
729                 tag = GST_TAG_TITLE;
730                 break;
731         case sArtist:
732                 tag = GST_TAG_ARTIST;
733                 break;
734         case sAlbum:
735                 tag = GST_TAG_ALBUM;
736                 break;
737         case sComment:
738                 tag = GST_TAG_COMMENT;
739                 break;
740         case sTracknumber:
741                 tag = GST_TAG_TRACK_NUMBER;
742                 break;
743         case sGenre:
744                 tag = GST_TAG_GENRE;
745                 break;
746         case sVideoType:
747                 tag = GST_TAG_VIDEO_CODEC;
748                 break;
749         case sTimeCreate:
750                 GDate *date;
751                 if (gst_tag_list_get_date(m_stream_tags, GST_TAG_DATE, &date))
752                 {
753                         gchar res[5];
754                         g_date_strftime (res, sizeof(res), "%Y", date); 
755                         return (std::string)res;
756                 }
757                 break;
758         case sUser+12:
759                 return m_error_message;
760         default:
761                 return "";
762         }
763         if ( !tag )
764                 return "";
765         gchar *value;
766         if (gst_tag_list_get_string(m_stream_tags, tag, &value))
767         {
768                 std::string res = value;
769                 g_free(value);
770                 return res;
771         }
772         return "";
773 }
774
775 RESULT eServiceMP3::audioChannel(ePtr<iAudioChannelSelection> &ptr)
776 {
777         ptr = this;
778         return 0;
779 }
780
781 RESULT eServiceMP3::audioTracks(ePtr<iAudioTrackSelection> &ptr)
782 {
783         ptr = this;
784         return 0;
785 }
786
787 RESULT eServiceMP3::subtitle(ePtr<iSubtitleOutput> &ptr)
788 {
789         ptr = this;
790         return 0;
791 }
792
793 int eServiceMP3::getNumberOfTracks()
794 {
795         return m_audioStreams.size();
796 }
797
798 int eServiceMP3::getCurrentTrack()
799 {
800         return m_currentAudioStream;
801 }
802
803 RESULT eServiceMP3::selectTrack(unsigned int i)
804 {
805         int ret = selectAudioStream(i);
806         /* flush */
807         pts_t ppos;
808         getPlayPosition(ppos);
809         seekTo(ppos);
810
811         return ret;
812 }
813
814 int eServiceMP3::selectAudioStream(int i)
815 {
816         gint nb_sources;
817         GstPad *active_pad;
818         GstElement *switch_audio = gst_bin_get_by_name(GST_BIN(m_gst_pipeline),"switch_audio");
819         if ( !switch_audio )
820         {
821                 eDebug("can't switch audio tracks! gst-plugin-selector needed");
822                 return -1;
823         }
824         g_object_get (G_OBJECT (switch_audio), "n-pads", &nb_sources, NULL);
825         if ( (unsigned int)i >= m_audioStreams.size() || i >= nb_sources || (unsigned int)m_currentAudioStream >= m_audioStreams.size() )
826                 return -2;
827         char sinkpad[8];
828         sprintf(sinkpad, "sink%d", i);
829         g_object_set (G_OBJECT (switch_audio), "active-pad", gst_element_get_pad (switch_audio, sinkpad), NULL);
830         g_object_get (G_OBJECT (switch_audio), "active-pad", &active_pad, NULL);
831         gchar *name;
832         name = gst_pad_get_name (active_pad);
833         eDebug ("switched audio to (%s)", name);
834         g_free(name);
835         m_currentAudioStream = i;
836         return 0;
837 }
838
839 int eServiceMP3::getCurrentChannel()
840 {
841         return STEREO;
842 }
843
844 RESULT eServiceMP3::selectChannel(int i)
845 {
846         eDebug("eServiceMP3::selectChannel(%i)",i);
847         return 0;
848 }
849
850 RESULT eServiceMP3::getTrackInfo(struct iAudioTrackInfo &info, unsigned int i)
851 {
852 //      eDebug("eServiceMP3::getTrackInfo(&info, %i)",i);
853         if (i >= m_audioStreams.size())
854                 return -2;
855         if (m_audioStreams[i].type == atMPEG)
856                 info.m_description = "MPEG";
857         else if (m_audioStreams[i].type == atMP3)
858                 info.m_description = "MP3";
859         else if (m_audioStreams[i].type == atAC3)
860                 info.m_description = "AC3";
861         else if (m_audioStreams[i].type == atAAC)
862                 info.m_description = "AAC";
863         else if (m_audioStreams[i].type == atDTS)
864                 info.m_description = "DTS";
865         else if (m_audioStreams[i].type == atPCM)
866                 info.m_description = "PCM";
867         else if (m_audioStreams[i].type == atOGG)
868                 info.m_description = "OGG";
869         else
870                 info.m_description = "???";
871         if (info.m_language.empty())
872                 info.m_language = m_audioStreams[i].language_code;
873         return 0;
874 }
875
876 void eServiceMP3::gstBusCall(GstBus *bus, GstMessage *msg)
877 {
878         if (!msg)
879                 return;
880         gchar *sourceName;
881         GstObject *source;
882
883         source = GST_MESSAGE_SRC(msg);
884         sourceName = gst_object_get_name(source);
885 #if 0
886         if (gst_message_get_structure(msg))
887         {
888                 gchar *string = gst_structure_to_string(gst_message_get_structure(msg));
889                 eDebug("gst_message from %s: %s", sourceName, string);
890                 g_free(string);
891         }
892         else
893                 eDebug("gst_message from %s: %s (without structure)", sourceName, GST_MESSAGE_TYPE_NAME(msg));
894 #endif
895         switch (GST_MESSAGE_TYPE (msg))
896         {
897         case GST_MESSAGE_EOS:
898                 m_event((iPlayableService*)this, evEOF);
899                 break;
900         case GST_MESSAGE_ERROR:
901         {
902                 gchar *debug;
903                 GError *err;
904
905                 gst_message_parse_error (msg, &err, &debug);
906                 g_free (debug);
907                 eWarning("Gstreamer error: %s (%i)", err->message, err->code );
908                 if ( err->domain == GST_STREAM_ERROR && err->code == GST_STREAM_ERROR_DECODE )
909                 {
910                         if ( g_strrstr(sourceName, "videosink") )
911                                 m_event((iPlayableService*)this, evUser+11);
912                 }
913                 g_error_free(err);
914                         /* TODO: signal error condition to user */
915                 break;
916         }
917         case GST_MESSAGE_TAG:
918         {
919                 GstTagList *tags, *result;
920                 gst_message_parse_tag(msg, &tags);
921
922                 result = gst_tag_list_merge(m_stream_tags, tags, GST_TAG_MERGE_PREPEND);
923                 if (result)
924                 {
925                         if (m_stream_tags)
926                                 gst_tag_list_free(m_stream_tags);
927                         m_stream_tags = result;
928                 }
929
930                 gchar *g_audiocodec;
931                 if ( gst_tag_list_get_string(tags, GST_TAG_AUDIO_CODEC, &g_audiocodec) && m_audioStreams.size() == 0 )
932                 {
933                         GstPad* pad = gst_element_get_pad (GST_ELEMENT(source), "src");
934                         GstCaps* caps = gst_pad_get_caps(pad);
935                         GstStructure* str = gst_caps_get_structure(caps, 0);
936                         if ( !str )
937                                 break;
938                         audioStream audio;
939                         audio.type = gstCheckAudioPad(str);
940                         m_audioStreams.push_back(audio);
941                 }
942
943                 gst_tag_list_free(tags);
944                 m_event((iPlayableService*)this, evUpdatedInfo);
945                 break;
946         }
947         case GST_MESSAGE_ASYNC_DONE:
948         {
949                 GstTagList *tags;
950                 for (std::vector<audioStream>::iterator IterAudioStream(m_audioStreams.begin()); IterAudioStream != m_audioStreams.end(); ++IterAudioStream)
951                 {
952                         if ( IterAudioStream->pad )
953                         {
954                                 g_object_get(IterAudioStream->pad, "tags", &tags, NULL);
955                                 gchar *g_language;
956                                 if ( gst_is_tag_list(tags) && gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &g_language) )
957                                 {
958                                         eDebug("found audio language %s",g_language);
959                                         IterAudioStream->language_code = std::string(g_language);
960                                         g_free (g_language);
961                                 }
962                         }
963                 }
964                 for (std::vector<subtitleStream>::iterator IterSubtitleStream(m_subtitleStreams.begin()); IterSubtitleStream != m_subtitleStreams.end(); ++IterSubtitleStream)
965                 {
966                         if ( IterSubtitleStream->pad )
967                         {
968                                 g_object_get(IterSubtitleStream->pad, "tags", &tags, NULL);
969                                 gchar *g_language;
970                                 if ( gst_is_tag_list(tags) && gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &g_language) )
971                                 {
972                                         eDebug("found subtitle language %s",g_language);
973                                         IterSubtitleStream->language_code = std::string(g_language);
974                                         g_free (g_language);
975                                 }
976                         }
977                 }
978         }
979         case GST_MESSAGE_ELEMENT:
980         {
981                 if ( gst_is_missing_plugin_message(msg) )
982                 {
983                         gchar *description = gst_missing_plugin_message_get_description(msg);                   
984                         if ( description )
985                         {
986                                 m_error_message = "GStreamer plugin " + (std::string)description + " not available!\n";
987                                 g_free(description);
988                                 m_event((iPlayableService*)this, evUser+12);
989                         }
990                 }
991         }
992         default:
993                 break;
994         }
995         g_free (sourceName);
996 }
997
998 GstBusSyncReply eServiceMP3::gstBusSyncHandler(GstBus *bus, GstMessage *message, gpointer user_data)
999 {
1000         eServiceMP3 *_this = (eServiceMP3*)user_data;
1001         _this->m_pump.send(1);
1002                 /* wake */
1003         return GST_BUS_PASS;
1004 }
1005
1006 audiotype_t eServiceMP3::gstCheckAudioPad(GstStructure* structure)
1007 {
1008         const gchar* type;
1009         type = gst_structure_get_name(structure);
1010
1011         if (!strcmp(type, "audio/mpeg")) {
1012                         gint mpegversion, layer = 0;
1013                         gst_structure_get_int (structure, "mpegversion", &mpegversion);
1014                         gst_structure_get_int (structure, "layer", &layer);
1015                         eDebug("mime audio/mpeg version %d layer %d", mpegversion, layer);
1016                         switch (mpegversion) {
1017                                 case 1:
1018                                 {
1019                                         if ( layer == 3 )
1020                                                 return atMP3;
1021                                         else
1022                                                 return atMPEG;
1023                                 }
1024                                 case 2:
1025                                         return atMPEG;
1026                                 case 4:
1027                                         return atAAC;
1028                                 default:
1029                                         return atUnknown;
1030                         }
1031                 }
1032         else
1033         {
1034                 eDebug("mime %s", type);
1035                 if (!strcmp(type, "audio/x-ac3") || !strcmp(type, "audio/ac3"))
1036                         return atAC3;
1037                 else if (!strcmp(type, "audio/x-dts") || !strcmp(type, "audio/dts"))
1038                         return atDTS;
1039                 else if (!strcmp(type, "audio/x-raw-int"))
1040                         return atPCM;
1041         }
1042         return atUnknown;
1043 }
1044
1045 void eServiceMP3::gstCBpadAdded(GstElement *decodebin, GstPad *pad, gpointer user_data)
1046 {
1047         const gchar* type;
1048         GstCaps* caps;
1049         GstStructure* str;
1050         caps = gst_pad_get_caps(pad);
1051         str = gst_caps_get_structure(caps, 0);
1052         type = gst_structure_get_name(str);
1053
1054         eDebug("A new pad %s:%s was created", GST_OBJECT_NAME (decodebin), GST_OBJECT_NAME (pad));
1055
1056         eServiceMP3 *_this = (eServiceMP3*)user_data;
1057         GstBin *pipeline = GST_BIN(_this->m_gst_pipeline);
1058         if (g_strrstr(type,"audio"))
1059         {
1060                 audioStream audio;
1061                 audio.type = _this->gstCheckAudioPad(str);
1062                 GstElement *switch_audio = gst_bin_get_by_name(pipeline , "switch_audio");
1063                 if ( switch_audio )
1064                 {
1065                         GstPad *sinkpad = gst_element_get_request_pad (switch_audio, "sink%d");
1066                         gst_pad_link(pad, sinkpad);
1067                         audio.pad = sinkpad;
1068                         _this->m_audioStreams.push_back(audio);
1069                 
1070                         if ( _this->m_audioStreams.size() == 1 )
1071                         {
1072                                 _this->selectAudioStream(0);
1073                                 gst_element_set_state (_this->m_gst_pipeline, GST_STATE_PLAYING);
1074                         }
1075                         else
1076                                 g_object_set (G_OBJECT (switch_audio), "select-all", FALSE, NULL);
1077                 }
1078                 else
1079                 {
1080                         gst_pad_link(pad, gst_element_get_static_pad(gst_bin_get_by_name(pipeline,"queue_audio"), "sink"));
1081                         _this->m_audioStreams.push_back(audio);
1082                 }
1083         }
1084         if (g_strrstr(type,"video"))
1085         {
1086                 gst_pad_link(pad, gst_element_get_static_pad(gst_bin_get_by_name(pipeline,"queue_video"), "sink"));
1087         }
1088         if (g_strrstr(type,"application/x-ssa") || g_strrstr(type,"application/x-ass"))
1089         {
1090                 GstPad *switchpad = _this->gstCreateSubtitleSink(_this, stSSA);
1091                 gst_pad_link(pad, switchpad);
1092                 subtitleStream subs;
1093                 subs.pad = switchpad;
1094                 subs.type = stSSA;
1095                 _this->m_subtitleStreams.push_back(subs);
1096         }
1097         if (g_strrstr(type,"text/plain"))
1098         {
1099                 GstPad *switchpad = _this->gstCreateSubtitleSink(_this, stPlainText);
1100                 gst_pad_link(pad, switchpad);
1101                 subtitleStream subs;
1102                 subs.pad = switchpad;
1103                 subs.type = stPlainText;
1104                 _this->m_subtitleStreams.push_back(subs);
1105         }
1106 }
1107
1108 GstPad* eServiceMP3::gstCreateSubtitleSink(eServiceMP3* _this, subtype_t type)
1109 {
1110         GstBin *pipeline = GST_BIN(_this->m_gst_pipeline);
1111         GstElement *switch_subparse = gst_bin_get_by_name(pipeline,"switch_subparse");
1112         if ( !switch_subparse )
1113         {
1114                 switch_subparse = gst_element_factory_make ("input-selector", "switch_subparse");
1115                 GstElement *sink = gst_element_factory_make("fakesink", "sink_subtitles");
1116                 gst_bin_add_many(pipeline, switch_subparse, sink, NULL);
1117                 gst_element_link(switch_subparse, sink);
1118                 g_object_set (G_OBJECT(sink), "signal-handoffs", TRUE, NULL);
1119                 g_object_set (G_OBJECT(sink), "sync", TRUE, NULL);
1120                 g_object_set (G_OBJECT(sink), "async", FALSE, NULL);
1121                 g_signal_connect(sink, "handoff", G_CALLBACK(_this->gstCBsubtitleAvail), _this);
1122         
1123                 // order is essential since requested sink pad names can't be explicitely chosen
1124                 GstElement *switch_substream_plain = gst_element_factory_make ("input-selector", "switch_substream_plain");
1125                 gst_bin_add(pipeline, switch_substream_plain);
1126                 GstPad *sinkpad_plain = gst_element_get_request_pad (switch_subparse, "sink%d");
1127                 gst_pad_link(gst_element_get_pad (switch_substream_plain, "src"), sinkpad_plain);
1128         
1129                 GstElement *switch_substream_ssa = gst_element_factory_make ("input-selector", "switch_substream_ssa");
1130                 GstElement *ssaparse = gst_element_factory_make("ssaparse", "ssaparse");
1131                 gst_bin_add_many(pipeline, switch_substream_ssa, ssaparse, NULL);
1132                 GstPad *sinkpad_ssa = gst_element_get_request_pad (switch_subparse, "sink%d");
1133                 gst_element_link(switch_substream_ssa, ssaparse);
1134                 gst_pad_link(gst_element_get_pad (ssaparse, "src"), sinkpad_ssa);
1135         
1136                 GstElement *switch_substream_srt = gst_element_factory_make ("input-selector", "switch_substream_srt");
1137                 GstElement *srtparse = gst_element_factory_make("subparse", "srtparse");
1138                 gst_bin_add_many(pipeline, switch_substream_srt, srtparse, NULL);
1139                 GstPad *sinkpad_srt = gst_element_get_request_pad (switch_subparse, "sink%d");
1140                 gst_element_link(switch_substream_srt, srtparse);
1141                 gst_pad_link(gst_element_get_pad (srtparse, "src"), sinkpad_srt);
1142                 g_object_set (G_OBJECT(srtparse), "subtitle-encoding", "ISO-8859-15", NULL);
1143         }
1144
1145         switch (type)
1146         {
1147                 case stSSA:
1148                         return gst_element_get_request_pad (gst_bin_get_by_name(pipeline,"switch_substream_ssa"), "sink%d");
1149                 case stSRT:
1150                         return gst_element_get_request_pad (gst_bin_get_by_name(pipeline,"switch_substream_srt"), "sink%d");
1151                 case stPlainText:
1152                 default:
1153                         break;
1154         }
1155         return gst_element_get_request_pad (gst_bin_get_by_name(pipeline,"switch_substream_plain"), "sink%d");
1156 }
1157
1158 void eServiceMP3::gstCBfilterPadAdded(GstElement *filter, GstPad *pad, gpointer user_data)
1159 {
1160         eServiceMP3 *_this = (eServiceMP3*)user_data;
1161         GstElement *decoder = gst_bin_get_by_name(GST_BIN(_this->m_gst_pipeline),"decoder");
1162         gst_pad_link(pad, gst_element_get_static_pad (decoder, "sink"));
1163 }
1164
1165 void eServiceMP3::gstCBnewPad(GstElement *decodebin, GstPad *pad, gboolean last, gpointer user_data)
1166 {
1167         eServiceMP3 *_this = (eServiceMP3*)user_data;
1168         GstCaps *caps;
1169         GstStructure *str;
1170         GstPad *audiopad;
1171
1172         /* only link once */
1173         GstElement *audiobin = gst_bin_get_by_name(GST_BIN(_this->m_gst_pipeline),"audiobin");
1174         audiopad = gst_element_get_static_pad (audiobin, "sink");
1175         if ( !audiopad || GST_PAD_IS_LINKED (audiopad)) {
1176                 eDebug("audio already linked!");
1177                 g_object_unref (audiopad);
1178                 return;
1179         }
1180
1181         /* check media type */
1182         caps = gst_pad_get_caps (pad);
1183         str = gst_caps_get_structure (caps, 0);
1184         eDebug("gst new pad! %s", gst_structure_get_name (str));
1185
1186         if (!g_strrstr (gst_structure_get_name (str), "audio")) {
1187                 gst_caps_unref (caps);
1188                 gst_object_unref (audiopad);
1189                 return;
1190         }
1191         
1192         gst_caps_unref (caps);
1193         gst_pad_link (pad, audiopad);
1194 }
1195
1196 void eServiceMP3::gstCBunknownType(GstElement *decodebin, GstPad *pad, GstCaps *caps, gpointer user_data)
1197 {
1198         GstStructure *str;
1199
1200         /* check media type */
1201         caps = gst_pad_get_caps (pad);
1202         str = gst_caps_get_structure (caps, 0);
1203         eDebug("unknown type: %s - this can't be decoded.", gst_structure_get_name (str));
1204         gst_caps_unref (caps);
1205 }
1206
1207 void eServiceMP3::gstPoll(const int&)
1208 {
1209                 /* ok, we have a serious problem here. gstBusSyncHandler sends 
1210                    us the wakup signal, but likely before it was posted.
1211                    the usleep, an EVIL HACK (DON'T DO THAT!!!) works around this.
1212                    
1213                    I need to understand the API a bit more to make this work 
1214                    proplerly. */
1215         usleep(1);
1216         
1217         GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (m_gst_pipeline));
1218         GstMessage *message;
1219         while ((message = gst_bus_pop (bus)))
1220         {
1221                 gstBusCall(bus, message);
1222                 gst_message_unref (message);
1223         }
1224 }
1225
1226 eAutoInitPtr<eServiceFactoryMP3> init_eServiceFactoryMP3(eAutoInitNumbers::service+1, "eServiceFactoryMP3");
1227
1228 void eServiceMP3::gstCBsubtitleAvail(GstElement *element, GstBuffer *buffer, GstPad *pad, gpointer user_data)
1229 {
1230         gint64 duration_ns = GST_BUFFER_DURATION(buffer);
1231         const unsigned char *text = (unsigned char *)GST_BUFFER_DATA(buffer);
1232         eDebug("gstCBsubtitleAvail: %s",text);
1233         eServiceMP3 *_this = (eServiceMP3*)user_data;
1234         if ( _this->m_subtitle_widget )
1235         {
1236                 ePangoSubtitlePage page;
1237                 gRGB rgbcol(0xD0,0xD0,0xD0);
1238                 page.m_elements.push_back(ePangoSubtitlePageElement(rgbcol, (const char*)text));
1239                 page.m_timeout = duration_ns / 1000000;
1240                 (_this->m_subtitle_widget)->setPage(page);
1241         }
1242 }
1243
1244 RESULT eServiceMP3::enableSubtitles(eWidget *parent, ePyObject tuple)
1245 {
1246         ePyObject entry;
1247         int tuplesize = PyTuple_Size(tuple);
1248         int pid;
1249         int type;
1250         gint nb_sources;
1251         GstPad *active_pad;
1252         GstElement *switch_substream = NULL;
1253         GstElement *switch_subparse = gst_bin_get_by_name (GST_BIN(m_gst_pipeline), "switch_subparse");
1254
1255         if (!PyTuple_Check(tuple))
1256                 goto error_out;
1257         if (tuplesize < 1)
1258                 goto error_out;
1259         entry = PyTuple_GET_ITEM(tuple, 1);
1260         if (!PyInt_Check(entry))
1261                 goto error_out;
1262         pid = PyInt_AsLong(entry);
1263         entry = PyTuple_GET_ITEM(tuple, 2);
1264         if (!PyInt_Check(entry))
1265                 goto error_out;
1266         type = PyInt_AsLong(entry);
1267
1268         switch ((subtype_t)type)
1269         {
1270                 case stPlainText:
1271                         switch_substream = gst_bin_get_by_name(GST_BIN(m_gst_pipeline),"switch_substream_plain");
1272                         break;
1273                 case stSSA:
1274                         switch_substream = gst_bin_get_by_name(GST_BIN(m_gst_pipeline),"switch_substream_ssa");
1275                         break;
1276                 case stSRT:
1277                         switch_substream = gst_bin_get_by_name(GST_BIN(m_gst_pipeline),"switch_substream_srt");
1278                         break;
1279                 default:
1280                         goto error_out;
1281         }
1282
1283         m_subtitle_widget = new eSubtitleWidget(parent);
1284         m_subtitle_widget->resize(parent->size()); /* full size */
1285
1286         if ( !switch_substream )
1287         {
1288                 eDebug("can't switch subtitle tracks! gst-plugin-selector needed");
1289                 return -2;
1290         }
1291         g_object_get (G_OBJECT (switch_substream), "n-pads", &nb_sources, NULL);
1292         if ( (unsigned int)pid >= m_subtitleStreams.size() || pid >= nb_sources || (unsigned int)m_currentSubtitleStream >= m_subtitleStreams.size() )
1293                 return -2;
1294         g_object_get (G_OBJECT (switch_subparse), "n-pads", &nb_sources, NULL);
1295         if ( type < 0 || type >= nb_sources )
1296                 return -2;
1297
1298         char sinkpad[6];
1299         sprintf(sinkpad, "sink%d", type);
1300         g_object_set (G_OBJECT (switch_subparse), "active-pad", gst_element_get_pad (switch_subparse, sinkpad), NULL);
1301         sprintf(sinkpad, "sink%d", pid);
1302         g_object_set (G_OBJECT (switch_substream), "active-pad", gst_element_get_pad (switch_substream, sinkpad), NULL);
1303         m_currentSubtitleStream = pid;
1304
1305         return 0;
1306 error_out:
1307         eDebug("enableSubtitles needs a tuple as 2nd argument!\n"
1308                 "for gst subtitles (2, subtitle_stream_count, subtitle_type)");
1309         return -1;
1310 }
1311
1312 RESULT eServiceMP3::disableSubtitles(eWidget *parent)
1313 {
1314         eDebug("eServiceMP3::disableSubtitles");
1315         delete m_subtitle_widget;
1316         m_subtitle_widget = 0;
1317         return 0;
1318 }
1319
1320 PyObject *eServiceMP3::getCachedSubtitle()
1321 {
1322         eDebug("eServiceMP3::getCachedSubtitle");
1323         Py_RETURN_NONE;
1324 }
1325
1326 PyObject *eServiceMP3::getSubtitleList()
1327 {
1328         eDebug("eServiceMP3::getSubtitleList");
1329
1330         ePyObject l = PyList_New(0);
1331         int stream_count[sizeof(subtype_t)];
1332         for ( unsigned int i = 0; i < sizeof(subtype_t); i++ )
1333                 stream_count[i] = 0;
1334
1335         for (std::vector<subtitleStream>::iterator IterSubtitleStream(m_subtitleStreams.begin()); IterSubtitleStream != m_subtitleStreams.end(); ++IterSubtitleStream)
1336         {
1337                 subtype_t type = IterSubtitleStream->type;
1338                 ePyObject tuple = PyTuple_New(5);
1339                 PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(2));
1340                 PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(stream_count[type]));
1341                 PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(int(type)));
1342                 PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(0));
1343                 PyTuple_SET_ITEM(tuple, 4, PyString_FromString((IterSubtitleStream->language_code).c_str()));
1344                 PyList_Append(l, tuple);
1345                 Py_DECREF(tuple);
1346                 stream_count[type]++;
1347         }
1348         return l;
1349 }
1350
1351 #else
1352 #warning gstreamer not available, not building media player
1353 #endif