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