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