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