[ScanSetup] Send 5v enable option to DVB-T2 blindscan binary.
[vuplus_dvbapp] / lib / service / servicemp3.cpp
1         /* note: this requires gstreamer 0.10.x and a big list of plugins. */
2         /* it's currently hardcoded to use a big-endian alsasink as sink. */
3 #include <lib/base/ebase.h>
4 #include <lib/base/eerror.h>
5 #include <lib/base/init_num.h>
6 #include <lib/base/init.h>
7 #include <lib/base/nconfig.h>
8 #include <lib/base/object.h>
9 #include <lib/dvb/decoder.h>
10 #include <lib/components/file_eraser.h>
11 #include <lib/gui/esubtitle.h>
12 #include <lib/service/servicemp3.h>
13 #include <lib/service/service.h>
14 #include <lib/gdi/gpixmap.h>
15
16 #include <string>
17
18 #include <gst/gst.h>
19 #include <gst/pbutils/missing-plugins.h>
20 #include <sys/stat.h>
21
22 #define GSTREAMER_SUBTITLE_SYNC_MODE_BUG
23
24 #define SUBTITLE_DEBUG
25
26 #define HTTP_TIMEOUT 10
27
28 typedef enum
29 {
30         GST_PLAY_FLAG_VIDEO         = 0x00000001,
31         GST_PLAY_FLAG_AUDIO         = 0x00000002,
32         GST_PLAY_FLAG_TEXT          = 0x00000004,
33         GST_PLAY_FLAG_VIS           = 0x00000008,
34         GST_PLAY_FLAG_SOFT_VOLUME   = 0x00000010,
35         GST_PLAY_FLAG_NATIVE_AUDIO  = 0x00000020,
36         GST_PLAY_FLAG_NATIVE_VIDEO  = 0x00000040,
37         GST_PLAY_FLAG_DOWNLOAD      = 0x00000080,
38         GST_PLAY_FLAG_BUFFERING     = 0x00000100
39 } GstPlayFlags;
40
41 // eServiceFactoryMP3
42
43 eServiceFactoryMP3::eServiceFactoryMP3()
44 {
45         ePtr<eServiceCenter> sc;
46         
47         eServiceCenter::getPrivInstance(sc);
48         if (sc)
49         {
50                 std::list<std::string> extensions;
51                 extensions.push_back("mp2");
52                 extensions.push_back("mp3");
53                 extensions.push_back("ogg");
54                 extensions.push_back("mpg");
55                 extensions.push_back("vob");
56                 extensions.push_back("wav");
57                 extensions.push_back("wave");
58                 extensions.push_back("m4v");
59                 extensions.push_back("mkv");
60                 extensions.push_back("avi");
61                 extensions.push_back("divx");
62                 extensions.push_back("dat");
63                 extensions.push_back("flac");
64                 extensions.push_back("mp4");
65                 extensions.push_back("mov");
66                 extensions.push_back("m4a");
67                 extensions.push_back("flv");
68                 extensions.push_back("3gp");
69                 extensions.push_back("3g2");
70                 extensions.push_back("dts");
71                 extensions.push_back("wmv");
72                 extensions.push_back("asf");
73                 extensions.push_back("wma");
74                 sc->addServiceFactory(eServiceFactoryMP3::id, this, extensions);
75         }
76
77         m_service_info = new eStaticServiceMP3Info();
78 }
79
80 eServiceFactoryMP3::~eServiceFactoryMP3()
81 {
82         ePtr<eServiceCenter> sc;
83         
84         eServiceCenter::getPrivInstance(sc);
85         if (sc)
86                 sc->removeServiceFactory(eServiceFactoryMP3::id);
87 }
88
89 DEFINE_REF(eServiceFactoryMP3)
90
91         // iServiceHandler
92 RESULT eServiceFactoryMP3::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
93 {
94                 // check resources...
95         ptr = new eServiceMP3(ref);
96         return 0;
97 }
98
99 RESULT eServiceFactoryMP3::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr)
100 {
101         ptr=0;
102         return -1;
103 }
104
105 RESULT eServiceFactoryMP3::list(const eServiceReference &, ePtr<iListableService> &ptr)
106 {
107         ptr=0;
108         return -1;
109 }
110
111 RESULT eServiceFactoryMP3::info(const eServiceReference &ref, ePtr<iStaticServiceInformation> &ptr)
112 {
113         ptr = m_service_info;
114         return 0;
115 }
116
117 class eMP3ServiceOfflineOperations: public iServiceOfflineOperations
118 {
119         DECLARE_REF(eMP3ServiceOfflineOperations);
120         eServiceReference m_ref;
121 public:
122         eMP3ServiceOfflineOperations(const eServiceReference &ref);
123         
124         RESULT deleteFromDisk(int simulate);
125         RESULT getListOfFilenames(std::list<std::string> &);
126         RESULT reindex();
127 };
128
129 DEFINE_REF(eMP3ServiceOfflineOperations);
130
131 eMP3ServiceOfflineOperations::eMP3ServiceOfflineOperations(const eServiceReference &ref): m_ref((const eServiceReference&)ref)
132 {
133 }
134
135 RESULT eMP3ServiceOfflineOperations::deleteFromDisk(int simulate)
136 {
137         if (!simulate)
138         {
139                 std::list<std::string> res;
140                 if (getListOfFilenames(res))
141                         return -1;
142                 
143                 eBackgroundFileEraser *eraser = eBackgroundFileEraser::getInstance();
144                 if (!eraser)
145                         eDebug("FATAL !! can't get background file eraser");
146                 
147                 for (std::list<std::string>::iterator i(res.begin()); i != res.end(); ++i)
148                 {
149                         eDebug("Removing %s...", i->c_str());
150                         if (eraser)
151                                 eraser->erase(i->c_str());
152                         else
153                                 ::unlink(i->c_str());
154                 }
155         }
156         return 0;
157 }
158
159 RESULT eMP3ServiceOfflineOperations::getListOfFilenames(std::list<std::string> &res)
160 {
161         res.clear();
162         res.push_back(m_ref.path);
163         return 0;
164 }
165
166 RESULT eMP3ServiceOfflineOperations::reindex()
167 {
168         return -1;
169 }
170
171
172 RESULT eServiceFactoryMP3::offlineOperations(const eServiceReference &ref, ePtr<iServiceOfflineOperations> &ptr)
173 {
174         ptr = new eMP3ServiceOfflineOperations(ref);
175         return 0;
176 }
177
178 // eStaticServiceMP3Info
179
180
181 // eStaticServiceMP3Info is seperated from eServiceMP3 to give information
182 // about unopened files.
183
184 // probably eServiceMP3 should use this class as well, and eStaticServiceMP3Info
185 // should have a database backend where ID3-files etc. are cached.
186 // this would allow listing the mp3 database based on certain filters.
187
188 DEFINE_REF(eStaticServiceMP3Info)
189
190 eStaticServiceMP3Info::eStaticServiceMP3Info()
191 {
192 }
193
194 RESULT eStaticServiceMP3Info::getName(const eServiceReference &ref, std::string &name)
195 {
196         if ( ref.name.length() )
197                 name = ref.name;
198         else
199         {
200                 size_t last = ref.path.rfind('/');
201                 if (last != std::string::npos)
202                         name = ref.path.substr(last+1);
203                 else
204                         name = ref.path;
205         }
206         return 0;
207 }
208
209 int eStaticServiceMP3Info::getLength(const eServiceReference &ref)
210 {
211         return -1;
212 }
213
214 int eStaticServiceMP3Info::getInfo(const eServiceReference &ref, int w)
215 {
216         switch (w)
217         {
218         case iServiceInformation::sTimeCreate:
219         {
220                 struct stat s;
221                 if(stat(ref.path.c_str(), &s) == 0)
222                 {
223                   return s.st_mtime;
224                 }
225                 return iServiceInformation::resNA;
226         }
227         default: break;
228         }
229         return iServiceInformation::resNA;
230 }
231  
232
233 // eServiceMP3
234 int eServiceMP3::ac3_delay,
235     eServiceMP3::pcm_delay;
236
237 eServiceMP3::eServiceMP3(eServiceReference ref)
238         :m_ref(ref), m_pump(eApp, 1)
239 {
240         m_subtitle_sync_timer = eTimer::create(eApp);
241         m_streamingsrc_timeout = 0;
242         m_stream_tags = 0;
243         m_currentAudioStream = -1;
244         m_currentSubtitleStream = -1;
245         m_subtitle_widget = 0;
246         m_currentTrickRatio = 1.0;
247         m_buffer_size = 1*1024*1024;
248         m_prev_decoder_time = -1;
249         m_decoder_time_valid_state = 0;
250         m_errorInfo.missing_codec = "";
251
252         audioSink = videoSink = NULL;
253         CONNECT(m_subtitle_sync_timer->timeout, eServiceMP3::pushSubtitles);
254         CONNECT(m_pump.recv_msg, eServiceMP3::gstPoll);
255         m_aspect = m_width = m_height = m_framerate = m_progressive = -1;
256
257         m_state = stIdle;
258         eDebug("eServiceMP3::construct!");
259
260         const char *filename = m_ref.path.c_str();
261         const char *ext = strrchr(filename, '.');
262         if (!ext)
263                 ext = filename + strlen(filename);
264
265         m_sourceinfo.is_video = FALSE;
266         m_sourceinfo.audiotype = atUnknown;
267         if ( (strcasecmp(ext, ".mpeg") && strcasecmp(ext, ".mpg") && strcasecmp(ext, ".vob") && strcasecmp(ext, ".bin") && strcasecmp(ext, ".dat") ) == 0 )
268         {
269                 m_sourceinfo.containertype = ctMPEGPS;
270                 m_sourceinfo.is_video = TRUE;
271         }
272         else if ( strcasecmp(ext, ".ts") == 0 )
273         {
274                 m_sourceinfo.containertype = ctMPEGTS;
275                 m_sourceinfo.is_video = TRUE;
276         }
277         else if ( strcasecmp(ext, ".mkv") == 0 )
278         {
279                 m_sourceinfo.containertype = ctMKV;
280                 m_sourceinfo.is_video = TRUE;
281         }
282         else if ( strcasecmp(ext, ".avi") == 0 || strcasecmp(ext, ".divx") == 0)
283         {
284                 m_sourceinfo.containertype = ctAVI;
285                 m_sourceinfo.is_video = TRUE;
286         }
287         else if ( strcasecmp(ext, ".mp4") == 0 || strcasecmp(ext, ".mov") == 0 || strcasecmp(ext, ".m4v") == 0)
288         {
289                 m_sourceinfo.containertype = ctMP4;
290                 m_sourceinfo.is_video = TRUE;
291         }
292         else if ( strcasecmp(ext, ".m4a") == 0 )
293         {
294                 m_sourceinfo.containertype = ctMP4;
295                 m_sourceinfo.audiotype = atAAC;
296         }
297         else if ( strcasecmp(ext, ".mp3") == 0 )
298                 m_sourceinfo.audiotype = atMP3;
299         else if ( (strncmp(filename, "/autofs/", 8) || strncmp(filename+strlen(filename)-13, "/track-", 7) || strcasecmp(ext, ".wav")) == 0 )
300                 m_sourceinfo.containertype = ctCDA;
301         if ( strcasecmp(ext, ".dat") == 0 )
302         {
303                 m_sourceinfo.containertype = ctVCD;
304                 m_sourceinfo.is_video = TRUE;
305         }
306         if ( strstr(filename, "://") )
307                 m_sourceinfo.is_streaming = TRUE;
308
309         gchar *uri;
310
311         if ( m_sourceinfo.is_streaming )
312         {
313                 uri = g_strdup_printf ("%s", filename);
314                 if (strstr(filename, "rtmp://") || strstr(filename, "rtsp://")) {
315                         m_streamingsrc_timeout = eTimer::create(eApp);
316                 }
317                 if (m_streamingsrc_timeout) {
318                         CONNECT(m_streamingsrc_timeout->timeout, eServiceMP3::sourceTimeout);
319                 }
320                 std::string config_str;
321                 if( ePythonConfigQuery::getConfigValue("config.mediaplayer.useAlternateUserAgent", config_str) == 0 )
322                 {
323                         if ( config_str == "True" )
324                                 ePythonConfigQuery::getConfigValue("config.mediaplayer.alternateUserAgent", m_useragent);
325                 }
326                 if ( m_useragent.length() == 0 )
327                         m_useragent = "Enigma2 Mediaplayer";
328         }
329         else if ( m_sourceinfo.containertype == ctCDA )
330         {
331                 int i_track = atoi(filename+18);
332                 uri = g_strdup_printf ("cdda://%i", i_track);
333         }
334         else if ( m_sourceinfo.containertype == ctVCD )
335         {
336                 int fd = open(filename,O_RDONLY);
337                 char tmp[128*1024];
338                 int ret = read(fd, tmp, 128*1024);
339                 close(fd);
340                 if ( ret == -1 ) // this is a "REAL" VCD
341                         uri = g_strdup_printf ("vcd://");
342                 else
343                         uri = g_filename_to_uri(filename, NULL, NULL);
344         }
345         else
346
347                 uri = g_filename_to_uri(filename, NULL, NULL);
348
349         eDebug("eServiceMP3::playbin uri=%s", uri);
350
351 #if GST_VERSION_MAJOR < 1
352         m_gst_playbin = gst_element_factory_make("playbin2", "playbin");
353 #else
354         m_gst_playbin = gst_element_factory_make("playbin", "playbin");
355 #endif  
356
357         if ( m_gst_playbin )
358         {
359                 int flags;
360                 g_object_get(G_OBJECT (m_gst_playbin), "flags", &flags, NULL);
361                 flags |= GST_PLAY_FLAG_NATIVE_VIDEO;
362                 flags &= ~GST_PLAY_FLAG_SOFT_VOLUME;
363                 g_object_set (G_OBJECT (m_gst_playbin), "flags", flags, NULL);
364                 g_object_set (G_OBJECT (m_gst_playbin), "uri", uri, NULL);
365                 g_free(uri);
366
367                 GstElement *subsink = gst_element_factory_make("subsink", "subtitle_sink");
368                 if (!subsink)
369                         eDebug("eServiceMP3::sorry, can't play: missing gst-plugin-subsink");
370                 else
371                 {
372                         m_subs_to_pull_handler_id = g_signal_connect (subsink, "new-buffer", G_CALLBACK (gstCBsubtitleAvail), this);
373                         g_object_set (G_OBJECT (subsink), "caps", gst_caps_from_string("text/plain; text/x-plain; text/x-raw; text/x-pango-markup; video/x-dvd-subpicture; subpicture/x-pgs"), NULL);
374                         g_object_set (G_OBJECT (m_gst_playbin), "text-sink", subsink, NULL);
375                         g_object_set (G_OBJECT (m_gst_playbin), "current-text", m_currentSubtitleStream, NULL);
376                 }
377                 GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (m_gst_playbin));
378 #if GST_VERSION_MAJOR < 1
379                 gst_bus_set_sync_handler(bus, gstBusSyncHandler, this);
380 #else
381                 gst_bus_set_sync_handler(bus, gstBusSyncHandler, this, NULL);
382 #endif
383                 gst_object_unref(bus);
384                 char srt_filename[ext - filename + 5];
385                 strncpy(srt_filename, filename, ext - filename);
386                 srt_filename[ext - filename]='\0';
387                 strcat(srt_filename, ".srt");
388                 struct stat buffer;
389                 if (stat(srt_filename, &buffer) == 0)
390                 {
391                         eDebug("eServiceMP3::subtitle uri: %s", g_filename_to_uri(srt_filename, NULL, NULL));
392                         g_object_set (G_OBJECT (m_gst_playbin), "suburi", g_filename_to_uri(srt_filename, NULL, NULL), NULL);
393                 }
394                 if ( m_sourceinfo.is_streaming )
395                 {
396                         g_signal_connect (G_OBJECT (m_gst_playbin), "notify::source", G_CALLBACK (gstHTTPSourceSetAgent), this);
397
398                         flags |= GST_PLAY_FLAG_BUFFERING;
399                         g_object_set(G_OBJECT(m_gst_playbin), "buffer-duration", 5LL * GST_SECOND, NULL);
400                         g_object_set(G_OBJECT(m_gst_playbin), "buffer-size", m_buffer_size, NULL);
401                 }
402         } else
403         {
404                 m_event((iPlayableService*)this, evUser+12);
405
406                 if (m_gst_playbin)
407                         gst_object_unref(GST_OBJECT(m_gst_playbin));
408
409                 m_errorInfo.error_message = "failed to create GStreamer pipeline!\n";
410                 eDebug("eServiceMP3::sorry, can't play: %s",m_errorInfo.error_message.c_str());
411                 m_gst_playbin = 0;
412         }
413 }
414
415 eServiceMP3::~eServiceMP3()
416 {
417         // disconnect subtitle callback
418         GstElement *subsink = gst_bin_get_by_name(GST_BIN(m_gst_playbin), "subtitle_sink");
419
420         if (subsink)
421         {
422                 g_signal_handler_disconnect (subsink, m_subs_to_pull_handler_id);
423                 gst_object_unref(subsink);
424         }
425
426         if (m_subtitle_widget)
427                 delete m_subtitle_widget;
428
429         if (m_gst_playbin)
430         {
431                 // disconnect sync handler callback
432                 GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (m_gst_playbin));
433 #if GST_VERSION_MAJOR < 1
434                 gst_bus_set_sync_handler(bus, NULL, NULL);
435 #else
436                 gst_bus_set_sync_handler(bus, NULL, NULL, NULL);
437 #endif
438                 gst_object_unref(bus);
439         }
440
441         if (m_state == stRunning)
442                 stop();
443
444         if (m_stream_tags)
445                 gst_tag_list_free(m_stream_tags);
446
447         if (audioSink)
448         {
449                 gst_object_unref(GST_OBJECT(audioSink));
450                 audioSink = NULL;
451         }
452         if (videoSink)
453         {
454                 gst_object_unref(GST_OBJECT(videoSink));
455                 videoSink = NULL;
456         }
457         
458         if (m_gst_playbin)
459         {
460                 gst_object_unref (GST_OBJECT (m_gst_playbin));
461                 eDebug("eServiceMP3::destruct!");
462         }
463 }
464
465 DEFINE_REF(eServiceMP3);
466
467 RESULT eServiceMP3::connectEvent(const Slot2<void,iPlayableService*,int> &event, ePtr<eConnection> &connection)
468 {
469         connection = new eConnection((iPlayableService*)this, m_event.connect(event));
470         return 0;
471 }
472
473 RESULT eServiceMP3::start()
474 {
475         ASSERT(m_state == stIdle);
476
477         m_state = stRunning;
478         if (m_gst_playbin)
479         {
480                 eDebug("eServiceMP3::starting pipeline");
481                 gst_element_set_state (m_gst_playbin, GST_STATE_PLAYING);
482         }
483
484         m_event(this, evStart);
485
486         return 0;
487 }
488
489 void eServiceMP3::sourceTimeout()
490 {
491         eDebug("eServiceMP3::http source timeout! issuing eof...");
492         m_event((iPlayableService*)this, evEOF);
493 }
494
495 RESULT eServiceMP3::stop()
496 {
497         ASSERT(m_state != stIdle);
498
499         if (!m_gst_playbin || m_state == stStopped)
500                 return -1;
501         
502         //GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(m_gst_playbin),GST_DEBUG_GRAPH_SHOW_ALL,"e2-playbin");
503
504         //eDebug("eServiceMP3::stop %s", m_ref.path.c_str());
505         gst_element_set_state(m_gst_playbin, GST_STATE_NULL);
506         m_state = stStopped;
507
508         return 0;
509 }
510
511 RESULT eServiceMP3::setTarget(int target)
512 {
513         return -1;
514 }
515
516 RESULT eServiceMP3::pause(ePtr<iPauseableService> &ptr)
517 {
518         ptr=this;
519         return 0;
520 }
521
522 RESULT eServiceMP3::setSlowMotion(int ratio)
523 {
524         if (!ratio)
525                 return 0;
526         eDebug("eServiceMP3::setSlowMotion ratio=%f",1/(float)ratio);
527         return trickSeek(1.0/(gdouble)ratio);
528 }
529
530 RESULT eServiceMP3::setFastForward(int ratio)
531 {
532         eDebug("eServiceMP3::setFastForward ratio=%i",ratio);
533         return trickSeek(ratio);
534 }
535
536                 // iPausableService
537 RESULT eServiceMP3::pause()
538 {
539         if (!m_gst_playbin || m_state != stRunning)
540                 return -1;
541
542         gst_element_set_state(m_gst_playbin, GST_STATE_PAUSED);
543
544         return 0;
545 }
546
547 RESULT eServiceMP3::unpause()
548 {
549         if (!m_gst_playbin || m_state != stRunning)
550                 return -1;
551
552         if (m_currentTrickRatio != 1.0)
553                 trickSeek(1.0);
554
555         gst_element_set_state(m_gst_playbin, GST_STATE_PLAYING);
556
557         return 0;
558 }
559
560         /* iSeekableService */
561 RESULT eServiceMP3::seek(ePtr<iSeekableService> &ptr)
562 {
563         ptr = this;
564         return 0;
565 }
566
567 RESULT eServiceMP3::getLength(pts_t &pts)
568 {
569         if (!m_gst_playbin || m_state != stRunning)
570                 return -1;
571
572         GstFormat fmt = GST_FORMAT_TIME;
573         gint64 len;
574 #if GST_VERSION_MAJOR < 1       
575         if (!gst_element_query_duration(m_gst_playbin, &fmt, &len))
576 #else
577         if (!gst_element_query_duration(m_gst_playbin, fmt, &len))
578 #endif
579                 return -1;
580                 /* len is in nanoseconds. we have 90 000 pts per second. */
581         
582         pts = len / 11111LL;
583         return 0;
584 }
585
586 RESULT eServiceMP3::seekToImpl(pts_t to)
587 {
588                 /* convert pts to nanoseconds */
589         gint64 time_nanoseconds = to * 11111LL;
590 #if GST_VERSION_MAJOR < 1
591         if (!gst_element_seek (m_gst_playbin, m_currentTrickRatio, GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH),
592                 GST_SEEK_TYPE_SET, time_nanoseconds,
593                 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
594 #else
595         if (!gst_element_seek (m_gst_playbin, m_currentTrickRatio, GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT),
596                 GST_SEEK_TYPE_SET, time_nanoseconds,
597                 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
598 #endif
599         {
600                 eDebug("eServiceMP3::seekTo failed");
601                 return -1;
602         }
603
604         return 0;
605 }
606
607 RESULT eServiceMP3::seekTo(pts_t to)
608 {
609         RESULT ret = -1;
610
611         if (m_gst_playbin) {
612                 m_subtitle_sync_timer->stop();
613                 m_subtitle_pages.clear();
614                 m_prev_decoder_time = -1;
615                 m_decoder_time_valid_state = 0;
616                 ret = seekToImpl(to);
617         }
618
619         return ret;
620 }
621
622
623 RESULT eServiceMP3::trickSeek(gdouble ratio)
624 {
625         if (!m_gst_playbin)
626                 return -1;
627         if (!ratio)
628                 return seekRelative(0, 0);
629
630         GstEvent *s_event;
631         int flags;
632         flags = GST_SEEK_FLAG_NONE;
633         flags |= GST_SEEK_FLAG_FLUSH;
634 #if GST_VERSION_MAJOR >= 1
635         flags |= GST_SEEK_FLAG_KEY_UNIT;
636 #endif
637
638         GstFormat fmt = GST_FORMAT_TIME;
639         bool validposition = false;
640         pts_t pos;
641         gint64 len;
642         
643 #if GST_VERSION_MAJOR < 1
644         gst_element_query_duration(m_gst_playbin, &fmt, &len);
645 #else
646         gst_element_query_duration(m_gst_playbin, fmt, &len);
647 #endif
648
649         if (getPlayPosition(pos) >= 0)
650         {
651                 validposition = true;
652                 pos = pos * 11111LL;
653         }
654
655         if ( validposition )
656         {
657                 if ( ratio >= 0.0 )
658                 {
659                         s_event = gst_event_new_seek (ratio, GST_FORMAT_TIME, (GstSeekFlags)flags, GST_SEEK_TYPE_SET, pos, GST_SEEK_TYPE_SET, len);
660
661                         eDebug("eServiceMP3::trickSeek with rate %lf to %" GST_TIME_FORMAT " ", ratio, GST_TIME_ARGS (pos));
662                 }
663                 else
664                 {
665                         s_event = gst_event_new_seek (ratio, GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_SKIP|GST_SEEK_FLAG_FLUSH), GST_SEEK_TYPE_NONE, -1, GST_SEEK_TYPE_NONE, -1);
666                 }
667
668                 if (!gst_element_send_event ( GST_ELEMENT (m_gst_playbin), s_event))
669                 {
670                         eDebug("eServiceMP3::trickSeek failed");
671                         return -1;
672                 }
673         }
674         m_subtitle_pages.clear();
675         m_prev_decoder_time = -1;
676         m_decoder_time_valid_state = 0;
677         m_currentTrickRatio = ratio;
678         return 0;
679 }
680
681
682 RESULT eServiceMP3::seekRelative(int direction, pts_t to)
683 {
684         if (!m_gst_playbin)
685                 return -1;
686
687         pts_t ppos;
688
689         if (getPlayPosition(ppos) < 0 )
690                 return -1;
691
692         ppos += to * direction;
693         if (ppos < 0)
694                 ppos = 0;
695         
696         return seekTo(ppos);
697 }
698
699 RESULT eServiceMP3::getPlayPosition(pts_t &pts)
700 {
701         GstFormat fmt = GST_FORMAT_TIME;
702         gint64 pos;
703         pts = 0;
704
705         if (!m_gst_playbin || m_state != stRunning)
706                 return -1;
707
708         if (audioSink || videoSink)
709         {
710                 g_signal_emit_by_name(audioSink ? audioSink : videoSink, "get-decoder-time", &pos);
711                 if (!GST_CLOCK_TIME_IS_VALID(pos))
712                         return -1;
713         }
714         else
715         {
716 #if GST_VERSION_MAJOR < 1
717                 if(!gst_element_query_position(m_gst_playbin, &fmt, &pos))
718 #else
719                 if(!gst_element_query_position(m_gst_playbin, fmt, &pos))
720 #endif
721                 {
722                         eDebug("gst_element_query_position failed in getPlayPosition");
723                         return -1;
724                 }
725         }
726
727         /* pos is in nanoseconds. we have 90 000 pts per second. */
728         pts = pos / 11111LL;
729 //      eDebug("gst_element_query_position %lld pts (%lld ms)", pts, pos/1000000LL);
730         return 0;
731 }
732
733 RESULT eServiceMP3::setTrickmode(int trick)
734 {
735                 /* trickmode is not yet supported by our dvbmediasinks. */
736         return -1;
737 }
738
739 RESULT eServiceMP3::isCurrentlySeekable()
740 {
741         int ret = 3; // seeking and fast/slow winding possible
742         GstElement *sink;
743
744         if (!m_gst_playbin)
745                 return 0;
746         if (m_state != stRunning)
747                 return 0;
748
749         g_object_get (G_OBJECT (m_gst_playbin), "video-sink", &sink, NULL);
750
751         // disable fast winding yet when a dvbvideosink or dvbaudiosink is used
752         // for this we must do some changes on different places.. (gstreamer.. our sinks.. enigma2)
753         if (sink) {
754                 ret &= ~2; // only seeking possible
755                 gst_object_unref(sink);
756         }
757         else {
758                 g_object_get (G_OBJECT (m_gst_playbin), "audio-sink", &sink, NULL);
759                 if (sink) {
760                         ret &= ~2; // only seeking possible
761                         gst_object_unref(sink);
762                 }
763         }
764
765         return ret;
766 }
767
768 RESULT eServiceMP3::info(ePtr<iServiceInformation>&i)
769 {
770         i = this;
771         return 0;
772 }
773
774 RESULT eServiceMP3::getName(std::string &name)
775 {
776         std::string title = m_ref.getName();
777         if (title.empty())
778         {
779                 name = m_ref.path;
780                 size_t n = name.rfind('/');
781                 if (n != std::string::npos)
782                         name = name.substr(n + 1);
783         }
784         else
785                 name = title;
786         return 0;
787 }
788
789 int eServiceMP3::getInfo(int w)
790 {
791         const gchar *tag = 0;
792
793         switch (w)
794         {
795         case sServiceref: return m_ref;
796         case sVideoHeight: return m_height;
797         case sVideoWidth: return m_width;
798         case sFrameRate: return m_framerate;
799         case sProgressive: return m_progressive;
800         case sAspect: return m_aspect;
801         case sTagTitle:
802         case sTagArtist:
803         case sTagAlbum:
804         case sTagTitleSortname:
805         case sTagArtistSortname:
806         case sTagAlbumSortname:
807         case sTagDate:
808         case sTagComposer:
809         case sTagGenre:
810         case sTagComment:
811         case sTagExtendedComment:
812         case sTagLocation:
813         case sTagHomepage:
814         case sTagDescription:
815         case sTagVersion:
816         case sTagISRC:
817         case sTagOrganization:
818         case sTagCopyright:
819         case sTagCopyrightURI:
820         case sTagContact:
821         case sTagLicense:
822         case sTagLicenseURI:
823         case sTagCodec:
824         case sTagAudioCodec:
825         case sTagVideoCodec:
826         case sTagEncoder:
827         case sTagLanguageCode:
828         case sTagKeywords:
829         case sTagChannelMode:
830         case sUser+12:
831                 return resIsString;
832         case sTagTrackGain:
833         case sTagTrackPeak:
834         case sTagAlbumGain:
835         case sTagAlbumPeak:
836         case sTagReferenceLevel:
837         case sTagBeatsPerMinute:
838         case sTagImage:
839         case sTagPreviewImage:
840         case sTagAttachment:
841                 return resIsPyObject;
842         case sTagTrackNumber:
843                 tag = GST_TAG_TRACK_NUMBER;
844                 break;
845         case sTagTrackCount:
846                 tag = GST_TAG_TRACK_COUNT;
847                 break;
848         case sTagAlbumVolumeNumber:
849                 tag = GST_TAG_ALBUM_VOLUME_NUMBER;
850                 break;
851         case sTagAlbumVolumeCount:
852                 tag = GST_TAG_ALBUM_VOLUME_COUNT;
853                 break;
854         case sTagBitrate:
855                 tag = GST_TAG_BITRATE;
856                 break;
857         case sTagNominalBitrate:
858                 tag = GST_TAG_NOMINAL_BITRATE;
859                 break;
860         case sTagMinimumBitrate:
861                 tag = GST_TAG_MINIMUM_BITRATE;
862                 break;
863         case sTagMaximumBitrate:
864                 tag = GST_TAG_MAXIMUM_BITRATE;
865                 break;
866         case sTagSerial:
867                 tag = GST_TAG_SERIAL;
868                 break;
869         case sTagEncoderVersion:
870                 tag = GST_TAG_ENCODER_VERSION;
871                 break;
872         case sTagCRC:
873                 tag = "has-crc";
874                 break;
875         default:
876                 return resNA;
877         }
878
879         if (!m_stream_tags || !tag)
880                 return 0;
881         
882         guint value;
883         if (gst_tag_list_get_uint(m_stream_tags, tag, &value))
884                 return (int) value;
885
886         return 0;
887 }
888
889 std::string eServiceMP3::getInfoString(int w)
890 {
891         if ( !m_stream_tags && w < sUser && w > 26 )
892                 return "";
893         const gchar *tag = 0;
894         switch (w)
895         {
896         case sTagTitle:
897                 tag = GST_TAG_TITLE;
898                 break;
899         case sTagArtist:
900                 tag = GST_TAG_ARTIST;
901                 break;
902         case sTagAlbum:
903                 tag = GST_TAG_ALBUM;
904                 break;
905         case sTagTitleSortname:
906                 tag = GST_TAG_TITLE_SORTNAME;
907                 break;
908         case sTagArtistSortname:
909                 tag = GST_TAG_ARTIST_SORTNAME;
910                 break;
911         case sTagAlbumSortname:
912                 tag = GST_TAG_ALBUM_SORTNAME;
913                 break;
914         case sTagDate:
915                 GDate *date;
916 #if GST_VERSION_MAJOR >= 1
917                 GstDateTime *date_time;
918 #endif
919                 if (gst_tag_list_get_date(m_stream_tags, GST_TAG_DATE, &date))
920                 {
921                         gchar res[5];
922                         g_date_strftime (res, sizeof(res), "%Y-%M-%D", date); 
923                         return (std::string)res;
924                 }
925 #if GST_VERSION_MAJOR >= 1
926                 else if (gst_tag_list_get_date_time(m_stream_tags, GST_TAG_DATE_TIME, &date_time))
927                 {
928                         if (gst_date_time_has_year(date_time))
929                         {
930                                 gchar res[5];
931                                 snprintf(res, sizeof(res), "%04d", gst_date_time_get_year(date_time));
932                                 gst_date_time_unref(date_time);
933                                 return (std::string)res;
934                         }
935                         gst_date_time_unref(date_time);
936                 }
937 #endif
938                 break;
939         case sTagComposer:
940                 tag = GST_TAG_COMPOSER;
941                 break;
942         case sTagGenre:
943                 tag = GST_TAG_GENRE;
944                 break;
945         case sTagComment:
946                 tag = GST_TAG_COMMENT;
947                 break;
948         case sTagExtendedComment:
949                 tag = GST_TAG_EXTENDED_COMMENT;
950                 break;
951         case sTagLocation:
952                 tag = GST_TAG_LOCATION;
953                 break;
954         case sTagHomepage:
955                 tag = GST_TAG_HOMEPAGE;
956                 break;
957         case sTagDescription:
958                 tag = GST_TAG_DESCRIPTION;
959                 break;
960         case sTagVersion:
961                 tag = GST_TAG_VERSION;
962                 break;
963         case sTagISRC:
964                 tag = GST_TAG_ISRC;
965                 break;
966         case sTagOrganization:
967                 tag = GST_TAG_ORGANIZATION;
968                 break;
969         case sTagCopyright:
970                 tag = GST_TAG_COPYRIGHT;
971                 break;
972         case sTagCopyrightURI:
973                 tag = GST_TAG_COPYRIGHT_URI;
974                 break;
975         case sTagContact:
976                 tag = GST_TAG_CONTACT;
977                 break;
978         case sTagLicense:
979                 tag = GST_TAG_LICENSE;
980                 break;
981         case sTagLicenseURI:
982                 tag = GST_TAG_LICENSE_URI;
983                 break;
984         case sTagCodec:
985                 tag = GST_TAG_CODEC;
986                 break;
987         case sTagAudioCodec:
988                 tag = GST_TAG_AUDIO_CODEC;
989                 break;
990         case sTagVideoCodec:
991                 tag = GST_TAG_VIDEO_CODEC;
992                 break;
993         case sTagEncoder:
994                 tag = GST_TAG_ENCODER;
995                 break;
996         case sTagLanguageCode:
997                 tag = GST_TAG_LANGUAGE_CODE;
998                 break;
999         case sTagKeywords:
1000                 tag = GST_TAG_KEYWORDS;
1001                 break;
1002         case sTagChannelMode:
1003                 tag = "channel-mode";
1004                 break;
1005         case sUser+12:
1006                 return m_errorInfo.error_message;
1007         default:
1008                 return "";
1009         }
1010         if ( !tag )
1011                 return "";
1012         gchar *value;
1013         if (gst_tag_list_get_string(m_stream_tags, tag, &value))
1014         {
1015                 std::string res = value;
1016                 g_free(value);
1017                 return res;
1018         }
1019         return "";
1020 }
1021
1022 PyObject *eServiceMP3::getInfoObject(int w)
1023 {
1024         const gchar *tag = 0;
1025         bool isBuffer = false;
1026         switch (w)
1027         {
1028                 case sTagTrackGain:
1029                         tag = GST_TAG_TRACK_GAIN;
1030                         break;
1031                 case sTagTrackPeak:
1032                         tag = GST_TAG_TRACK_PEAK;
1033                         break;
1034                 case sTagAlbumGain:
1035                         tag = GST_TAG_ALBUM_GAIN;
1036                         break;
1037                 case sTagAlbumPeak:
1038                         tag = GST_TAG_ALBUM_PEAK;
1039                         break;
1040                 case sTagReferenceLevel:
1041                         tag = GST_TAG_REFERENCE_LEVEL;
1042                         break;
1043                 case sTagBeatsPerMinute:
1044                         tag = GST_TAG_BEATS_PER_MINUTE;
1045                         break;
1046                 case sTagImage:
1047                         tag = GST_TAG_IMAGE;
1048                         isBuffer = true;
1049                         break;
1050                 case sTagPreviewImage:
1051                         tag = GST_TAG_PREVIEW_IMAGE;
1052                         isBuffer = true;
1053                         break;
1054                 case sTagAttachment:
1055                         tag = GST_TAG_ATTACHMENT;
1056                         isBuffer = true;
1057                         break;
1058                 default:
1059                         break;
1060         }
1061
1062         if (m_stream_tags && tag)
1063         {
1064                 if ( isBuffer )
1065                 {
1066                         const GValue *gv_buffer = gst_tag_list_get_value_index(m_stream_tags, tag, 0);
1067                         if ( gv_buffer )
1068                         {
1069                                 GstBuffer *buffer;
1070                                 unsigned char *bufferData;
1071                                 unsigned int bufferSize;
1072                                 buffer = gst_value_get_buffer (gv_buffer);
1073 #if GST_VERSION_MAJOR < 1
1074                                 bufferData = GST_BUFFER_DATA(buffer);
1075                                 bufferSize = GST_BUFFER_SIZE(buffer);
1076 #else
1077                                 GstMapInfo map;
1078                                 gst_buffer_map(buffer, &map, GST_MAP_READ);
1079                                 bufferData = map.data;
1080                                 bufferSize = map.size;
1081 #endif
1082                                 PyObject *ret = PyBuffer_FromMemory(bufferData, bufferSize);
1083 #if GST_VERSION_MAJOR >= 1
1084                                 gst_buffer_unmap(buffer, &map);
1085 #endif
1086                                 return ret;
1087                         }
1088                 }
1089                 else
1090                 {
1091                         gdouble value = 0.0;
1092                         gst_tag_list_get_double(m_stream_tags, tag, &value);
1093                         return PyFloat_FromDouble(value);
1094                 }
1095         }
1096
1097         Py_RETURN_NONE;
1098 }
1099
1100 RESULT eServiceMP3::audioChannel(ePtr<iAudioChannelSelection> &ptr)
1101 {
1102         ptr = this;
1103         return 0;
1104 }
1105
1106 RESULT eServiceMP3::audioTracks(ePtr<iAudioTrackSelection> &ptr)
1107 {
1108         ptr = this;
1109         return 0;
1110 }
1111
1112 RESULT eServiceMP3::subtitle(ePtr<iSubtitleOutput> &ptr)
1113 {
1114         ptr = this;
1115         return 0;
1116 }
1117
1118 RESULT eServiceMP3::audioDelay(ePtr<iAudioDelay> &ptr)
1119 {
1120         ptr = this;
1121         return 0;
1122 }
1123
1124 int eServiceMP3::getNumberOfTracks()
1125 {
1126         return m_audioStreams.size();
1127 }
1128
1129 int eServiceMP3::getCurrentTrack()
1130 {
1131         if (m_currentAudioStream == -1)
1132                 g_object_get (G_OBJECT (m_gst_playbin), "current-audio", &m_currentAudioStream, NULL);
1133         return m_currentAudioStream;
1134 }
1135
1136 RESULT eServiceMP3::selectTrack(unsigned int i)
1137 {
1138         bool validposition = false;
1139         pts_t ppos;
1140         if (getPlayPosition(ppos) >= 0)
1141         {
1142                 validposition = true;
1143                 ppos -= 90000;
1144                 if (ppos < 0)
1145                         ppos = 0;
1146         }
1147
1148         int ret = selectAudioStream(i);
1149         if (!ret) {
1150                 if (validposition)
1151                 {
1152                         /* flush */
1153                         seekTo(ppos);
1154                 }
1155         }
1156
1157         return ret;
1158 }
1159
1160 int eServiceMP3::selectAudioStream(int i)
1161 {
1162         int current_audio;
1163         g_object_set (G_OBJECT (m_gst_playbin), "current-audio", i, NULL);
1164         g_object_get (G_OBJECT (m_gst_playbin), "current-audio", &current_audio, NULL);
1165         if ( current_audio == i )
1166         {
1167                 eDebug ("eServiceMP3::switched to audio stream %i", current_audio);
1168                 m_currentAudioStream = i;
1169                 return 0;
1170         }
1171         return -1;
1172 }
1173
1174 int eServiceMP3::getCurrentChannel()
1175 {
1176         return STEREO;
1177 }
1178
1179 RESULT eServiceMP3::selectChannel(int i)
1180 {
1181         eDebug("eServiceMP3::selectChannel(%i)",i);
1182         return 0;
1183 }
1184
1185 RESULT eServiceMP3::getTrackInfo(struct iAudioTrackInfo &info, unsigned int i)
1186 {
1187         if (i >= m_audioStreams.size())
1188                 return -2;
1189                 info.m_description = m_audioStreams[i].codec;
1190 /*      if (m_audioStreams[i].type == atMPEG)
1191                 info.m_description = "MPEG";
1192         else if (m_audioStreams[i].type == atMP3)
1193                 info.m_description = "MP3";
1194         else if (m_audioStreams[i].type == atAC3)
1195                 info.m_description = "AC3";
1196         else if (m_audioStreams[i].type == atAAC)
1197                 info.m_description = "AAC";
1198         else if (m_audioStreams[i].type == atDTS)
1199                 info.m_description = "DTS";
1200         else if (m_audioStreams[i].type == atPCM)
1201                 info.m_description = "PCM";
1202         else if (m_audioStreams[i].type == atOGG)
1203                 info.m_description = "OGG";
1204         else if (m_audioStreams[i].type == atFLAC)
1205                 info.m_description = "FLAC";
1206         else
1207                 info.m_description = "???";*/
1208         if (info.m_language.empty())
1209                 info.m_language = m_audioStreams[i].language_code;
1210         return 0;
1211 }
1212
1213 subtype_t getSubtitleType(GstPad* pad, gchar *g_codec=NULL)
1214 {
1215         subtype_t type = stUnknown;
1216 #if GST_VERSION_MAJOR < 1
1217         GstCaps* caps = gst_pad_get_negotiated_caps(pad);
1218 #else
1219         GstCaps* caps = gst_pad_get_current_caps(pad);
1220 #endif
1221         if (!caps && !g_codec)
1222         {
1223                 caps = gst_pad_get_allowed_caps(pad);
1224         }
1225
1226         if ( caps && !gst_caps_is_empty(caps))
1227         {
1228                 GstStructure* str = gst_caps_get_structure(caps, 0);
1229                 const gchar *g_type = gst_structure_get_name(str);
1230                 eDebug("getSubtitleType::subtitle probe caps type=%s", g_type);
1231
1232                 if ( !strcmp(g_type, "video/x-dvd-subpicture") )
1233                         type = stVOB;
1234                 else if ( !strcmp(g_type, "text/x-pango-markup") )
1235                         type = stSSA;
1236                 else if ( !strcmp(g_type, "text/plain") || !strcmp(g_type, "text/x-plain") || !strcmp(g_type, "text/x-raw"))
1237                         type = stPlainText;
1238                 else if ( !strcmp(g_type, "subpicture/x-pgs") )
1239                         type = stPGS;
1240                 else
1241                         eDebug("getSubtitleType::unsupported subtitle caps %s (%s)", g_type, g_codec);
1242         }
1243         else if ( g_codec )
1244         {
1245                 eDebug("getSubtitleType::subtitle probe codec tag=%s", g_codec);
1246                 if ( !strcmp(g_codec, "VOB") )
1247                         type = stVOB;
1248                 else if ( !strcmp(g_codec, "SubStation Alpha") || !strcmp(g_codec, "SSA") )
1249                         type = stSSA;
1250                 else if ( !strcmp(g_codec, "ASS") )
1251                         type = stASS;
1252                 else if ( !strcmp(g_codec, "UTF-8 plain text") )
1253                         type = stPlainText;
1254                 else
1255                         eDebug("getSubtitleType::unsupported subtitle codec %s", g_codec);
1256         }
1257         else
1258                 eDebug("getSubtitleType::unidentifiable subtitle stream!");
1259
1260         return type;
1261 }
1262
1263 #if GST_VERSION_MAJOR < 1
1264 gint eServiceMP3::match_sinktype(GstElement *element, gpointer type)
1265 {
1266         return strcmp(g_type_name(G_OBJECT_TYPE(element)), (const char*)type);
1267 }
1268 #else
1269 gint eServiceMP3::match_sinktype(const GValue *velement, const gchar *type)
1270 {
1271         GstElement *element = GST_ELEMENT_CAST(g_value_get_object(velement));
1272         return strcmp(g_type_name(G_OBJECT_TYPE(element)), type);
1273 }
1274 #endif
1275
1276 void eServiceMP3::gstBusCall(GstBus *bus, GstMessage *msg)
1277 {
1278         if (!msg)
1279                 return;
1280         gchar *sourceName = NULL;
1281         GstObject *source = NULL;
1282         GstElement *subsink = NULL;
1283         source = GST_MESSAGE_SRC(msg);
1284         if (!GST_IS_OBJECT(source))
1285                 return;
1286         sourceName = gst_object_get_name(source);
1287 #if 0
1288         gchar *string;
1289         if (gst_message_get_structure(msg))
1290                 string = gst_structure_to_string(gst_message_get_structure(msg));
1291         else
1292                 string = g_strdup(GST_MESSAGE_TYPE_NAME(msg));
1293         eDebug("eTsRemoteSource::gst_message from %s: %s", sourceName, string);
1294         g_free(string);
1295 #endif
1296         switch (GST_MESSAGE_TYPE (msg))
1297         {
1298                 case GST_MESSAGE_EOS:
1299                         m_event((iPlayableService*)this, evEOF);
1300                         break;
1301                 case GST_MESSAGE_STATE_CHANGED:
1302                 {
1303                         if(GST_MESSAGE_SRC(msg) != GST_OBJECT(m_gst_playbin))
1304                         {
1305                                 break;
1306                         }
1307
1308                         GstState old_state, new_state;
1309                         gst_message_parse_state_changed(msg, &old_state, &new_state, NULL);
1310                 
1311                         if(old_state == new_state)
1312                                 break;
1313         
1314                         eDebug("eServiceMP3::state transition %s -> %s", gst_element_state_get_name(old_state), gst_element_state_get_name(new_state));
1315         
1316                         GstStateChange transition = (GstStateChange)GST_STATE_TRANSITION(old_state, new_state);
1317         
1318                         switch(transition)
1319                         {
1320                                 case GST_STATE_CHANGE_NULL_TO_READY:
1321                                 {
1322                                 }       break;
1323                                 case GST_STATE_CHANGE_READY_TO_PAUSED:
1324                                 {
1325 #if GST_VERSION_MAJOR >= 1
1326                                         GValue result = { 0, };
1327 #endif
1328                                         GstIterator *children;
1329                                         subsink = gst_bin_get_by_name(GST_BIN(m_gst_playbin), "subtitle_sink");
1330                                         if (subsink)
1331                                         {
1332 #ifdef GSTREAMER_SUBTITLE_SYNC_MODE_BUG
1333                                                 g_object_set (G_OBJECT (subsink), "sync", FALSE, NULL);
1334 #endif
1335                                                 gst_object_unref(subsink);
1336                                         }
1337
1338                                         if (audioSink)
1339                                         {
1340                                                 gst_object_unref(GST_OBJECT(audioSink));
1341                                                 audioSink = NULL;
1342                                         }
1343                                         if (videoSink)
1344                                         {
1345                                                 gst_object_unref(GST_OBJECT(videoSink));
1346                                                 videoSink = NULL;
1347                                         }
1348
1349                                         children = gst_bin_iterate_recurse(GST_BIN(m_gst_playbin));
1350 #if GST_VERSION_MAJOR < 1
1351                                         audioSink = GST_ELEMENT_CAST(gst_iterator_find_custom(children, (GCompareFunc)match_sinktype, (gpointer)"GstDVBAudioSink"));
1352 #else
1353                                         if (gst_iterator_find_custom(children, (GCompareFunc)match_sinktype, &result, (gpointer)"GstDVBAudioSink"))
1354                                         {
1355                                                 audioSink = GST_ELEMENT_CAST(g_value_dup_object(&result));
1356                                                 g_value_unset(&result);
1357                                         }
1358 #endif
1359                                         gst_iterator_free(children);
1360
1361                                         children = gst_bin_iterate_recurse(GST_BIN(m_gst_playbin));
1362 #if GST_VERSION_MAJOR < 1
1363                                         videoSink = GST_ELEMENT_CAST(gst_iterator_find_custom(children, (GCompareFunc)match_sinktype, (gpointer)"GstDVBVideoSink"));
1364 #else
1365                                         if (gst_iterator_find_custom(children, (GCompareFunc)match_sinktype, &result, (gpointer)"GstDVBVideoSink"))
1366                                         {
1367                                                 videoSink = GST_ELEMENT_CAST(g_value_dup_object(&result));
1368                                                 g_value_unset(&result);
1369                                         }
1370 #endif
1371                                         gst_iterator_free(children);
1372
1373                                         setAC3Delay(ac3_delay);
1374                                         setPCMDelay(pcm_delay);
1375
1376                                 }       break;
1377                                 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1378                                 {
1379                                         if ( m_sourceinfo.is_streaming && m_streamingsrc_timeout )
1380                                                 m_streamingsrc_timeout->stop();
1381                                 }       break;
1382                                 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1383                                 {
1384                                 }       break;
1385                                 case GST_STATE_CHANGE_PAUSED_TO_READY:
1386                                 {
1387                                         if (audioSink)
1388                                         {
1389                                                 gst_object_unref(GST_OBJECT(audioSink));
1390                                                 audioSink = NULL;
1391                                         }
1392                                         if (videoSink)
1393                                         {
1394                                                 gst_object_unref(GST_OBJECT(videoSink));
1395                                                 videoSink = NULL;
1396                                         }
1397                                 }       break;
1398                                 case GST_STATE_CHANGE_READY_TO_NULL:
1399                                 {
1400                                 }       break;
1401                         }
1402                         break;
1403                 }
1404                 case GST_MESSAGE_ERROR:
1405                 {
1406                         gchar *debug;
1407                         GError *err;
1408                         gst_message_parse_error (msg, &err, &debug);
1409                         g_free (debug);
1410                         eWarning("Gstreamer error: %s (domain:%i, code:%i) from %s", err->message, err->domain, err->code, sourceName );
1411                         if ( err->domain == GST_STREAM_ERROR )
1412                         {
1413                                 if ( err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND )
1414                                 {
1415                                         if ( g_strrstr(sourceName, "videosink") )
1416                                                 m_event((iPlayableService*)this, evUser+11);
1417                                         else if ( g_strrstr(sourceName, "audiosink") )
1418                                                 m_event((iPlayableService*)this, evUser+10);
1419                                 }
1420                         }
1421                         else if ( err->domain == GST_RESOURCE_ERROR )
1422                         {
1423                                 if ( err->code == GST_RESOURCE_ERROR_OPEN_READ || err->code == GST_RESOURCE_ERROR_READ )
1424                                 {
1425                                         sourceTimeout();
1426                                 }
1427                         }
1428                         g_error_free(err);
1429                         break;
1430                 }
1431                 case GST_MESSAGE_INFO:
1432                 {
1433                         gchar *debug;
1434                         GError *inf;
1435         
1436                         gst_message_parse_info (msg, &inf, &debug);
1437                         g_free (debug);
1438                         if ( inf->domain == GST_STREAM_ERROR && inf->code == GST_STREAM_ERROR_DECODE )
1439                         {
1440                                 if ( g_strrstr(sourceName, "videosink") )
1441                                         m_event((iPlayableService*)this, evUser+14);
1442                         }
1443                         g_error_free(inf);
1444                         break;
1445                 }
1446                 case GST_MESSAGE_TAG:
1447                 {
1448                         GstTagList *tags, *result;
1449                         gst_message_parse_tag(msg, &tags);
1450         
1451                         result = gst_tag_list_merge(m_stream_tags, tags, GST_TAG_MERGE_REPLACE);
1452                         if (result)
1453                         {
1454                                 if (m_stream_tags)
1455                                         gst_tag_list_free(m_stream_tags);
1456                                 m_stream_tags = result;
1457                         }
1458         
1459                         const GValue *gv_image = gst_tag_list_get_value_index(tags, GST_TAG_IMAGE, 0);
1460                         if ( gv_image )
1461                         {
1462                                 GstBuffer *buf_image;
1463 #if GST_VERSION_MAJOR < 1
1464                                 buf_image = gst_value_get_buffer (gv_image);
1465 #else
1466                                 GstSample *sample;
1467                                 sample = (GstSample *)g_value_get_boxed(gv_image);
1468                                 buf_image = gst_sample_get_buffer(sample);
1469 #endif
1470                                 int fd = open("/tmp/.id3coverart", O_CREAT|O_WRONLY|O_TRUNC, 0644);
1471                                 if (fd >= 0)
1472                                 {
1473                                         guint8 *data;
1474                                         gsize size;
1475                                         int ret;
1476 #if GST_VERSION_MAJOR < 1
1477                                         data = GST_BUFFER_DATA(buf_image);
1478                                         size = GST_BUFFER_SIZE(buf_image);
1479 #else
1480                                         GstMapInfo map;
1481                                         gst_buffer_map(buf_image, &map, GST_MAP_READ);
1482                                         data = map.data;
1483                                         size = map.size;
1484 #endif
1485                                         ret = write(fd, data, size);
1486 #if GST_VERSION_MAJOR >= 1
1487                                         gst_buffer_unmap(buf_image, &map);
1488 #endif
1489                                         close(fd);
1490                                         eDebug("eServiceMP3::/tmp/.id3coverart %d bytes written ", ret);
1491                                 }
1492                                 m_event((iPlayableService*)this, evUser+13);
1493                         }
1494                         gst_tag_list_free(tags);
1495                         m_event((iPlayableService*)this, evUpdatedInfo);
1496                         break;
1497                 }
1498                 case GST_MESSAGE_ASYNC_DONE:
1499                 {
1500                         if(GST_MESSAGE_SRC(msg) != GST_OBJECT(m_gst_playbin))
1501                                 break;
1502
1503                         gint i, n_video = 0, n_audio = 0, n_text = 0;
1504
1505                         g_object_get (m_gst_playbin, "n-video", &n_video, NULL);
1506                         g_object_get (m_gst_playbin, "n-audio", &n_audio, NULL);
1507                         g_object_get (m_gst_playbin, "n-text", &n_text, NULL);
1508
1509                         eDebug("eServiceMP3::async-done - %d video, %d audio, %d subtitle", n_video, n_audio, n_text);
1510
1511                         if ( n_video + n_audio <= 0 )
1512                                 stop();
1513
1514                         m_audioStreams.clear();
1515                         m_subtitleStreams.clear();
1516
1517                         for (i = 0; i < n_audio; i++)
1518                         {
1519                                 audioStream audio;
1520                                 gchar *g_codec, *g_lang;
1521                                 GstTagList *tags = NULL;
1522                                 GstPad* pad = 0;
1523                                 g_signal_emit_by_name (m_gst_playbin, "get-audio-pad", i, &pad);
1524 #if GST_VERSION_MAJOR < 1
1525                                 GstCaps* caps = gst_pad_get_negotiated_caps(pad);
1526 #else
1527                                 GstCaps* caps = gst_pad_get_current_caps(pad);
1528 #endif
1529                                 if (!caps)
1530                                         continue;
1531                                 GstStructure* str = gst_caps_get_structure(caps, 0);
1532                                 const gchar *g_type = gst_structure_get_name(str);
1533                                 audio.type = gstCheckAudioPad(str);
1534                                 g_codec = g_strdup(g_type);
1535                                 g_lang = g_strdup_printf ("und");
1536                                 g_signal_emit_by_name (m_gst_playbin, "get-audio-tags", i, &tags);
1537 #if GST_VERSION_MAJOR < 1
1538                                 if ( tags && gst_is_tag_list(tags) )
1539 #else
1540                                 if ( tags && GST_IS_TAG_LIST(tags) )
1541 #endif
1542                                 {
1543                                         gst_tag_list_get_string(tags, GST_TAG_AUDIO_CODEC, &g_codec);
1544                                         gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &g_lang);
1545                                         gst_tag_list_free(tags);
1546                                 }
1547                                 audio.language_code = std::string(g_lang);
1548                                 audio.codec = std::string(g_codec);
1549                                 eDebug("eServiceMP3::audio stream=%i codec=%s language=%s", i, g_codec, g_lang);
1550                                 m_audioStreams.push_back(audio);
1551                                 g_free (g_lang);
1552                                 g_free (g_codec);
1553                                 gst_caps_unref(caps);
1554                         }
1555
1556                         for (i = 0; i < n_text; i++)
1557                         {
1558                                 gchar *g_codec = NULL, *g_lang = NULL;
1559                                 GstTagList *tags = NULL;
1560                                 g_signal_emit_by_name (m_gst_playbin, "get-text-tags", i, &tags);
1561                                 subtitleStream subs;
1562 //                              int ret;
1563
1564                                 g_lang = g_strdup_printf ("und");
1565 #if GST_VERSION_MAJOR < 1
1566                                 if ( tags && gst_is_tag_list(tags) )
1567 #else
1568                                 if ( tags && GST_IS_TAG_LIST(tags) )
1569 #endif
1570                                 {
1571                                         gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &g_lang);
1572                                         gst_tag_list_get_string(tags, GST_TAG_SUBTITLE_CODEC, &g_codec);
1573                                         gst_tag_list_free(tags);
1574                                 }
1575
1576                                 subs.language_code = std::string(g_lang);
1577                                 eDebug("eServiceMP3::subtitle stream=%i language=%s codec=%s", i, g_lang, g_codec);
1578                                 
1579                                 GstPad* pad = 0;
1580                                 g_signal_emit_by_name (m_gst_playbin, "get-text-pad", i, &pad);
1581                                 if ( pad )
1582                                         g_signal_connect (G_OBJECT (pad), "notify::caps", G_CALLBACK (gstTextpadHasCAPS), this);
1583                                 subs.type = getSubtitleType(pad, g_codec);
1584
1585                                 m_subtitleStreams.push_back(subs);
1586                                 g_free (g_lang);
1587                         }
1588                         m_event((iPlayableService*)this, evUpdatedInfo);
1589
1590                         if ( m_errorInfo.missing_codec != "" )
1591                         {
1592                                 if ( m_errorInfo.missing_codec.find("video/") == 0 || ( m_errorInfo.missing_codec.find("audio/") == 0 && getNumberOfTracks() == 0 ) )
1593                                         m_event((iPlayableService*)this, evUser+12);
1594                         }
1595                         break;
1596                 }
1597                 case GST_MESSAGE_ELEMENT:
1598                 {
1599                         if (const GstStructure *msgstruct = gst_message_get_structure(msg))
1600                         {
1601                                 if ( gst_is_missing_plugin_message(msg) )
1602                                 {
1603                                         GstCaps *caps= NULL;
1604                                         gboolean ret = gst_structure_get (msgstruct, "detail", GST_TYPE_CAPS, &caps, NULL);
1605                                         if (ret)
1606                                         {
1607                                                 std::string codec = (const char*) gst_caps_to_string(caps);
1608                                                 gchar *description = gst_missing_plugin_message_get_description(msg);
1609                                                 if ( description )
1610                                                 {
1611                                                         eDebug("eServiceMP3::m_errorInfo.missing_codec = %s", codec.c_str());
1612                                                         m_errorInfo.error_message = "GStreamer plugin " + (std::string)description + " not available!\n";
1613                                                         m_errorInfo.missing_codec = codec.substr(0,(codec.find_first_of(',')));
1614                                                         g_free(description);
1615                                                 }
1616                                                 gst_caps_unref(caps);
1617                                         }
1618                                 }
1619                                 else
1620                                 {
1621                                         const gchar *eventname = gst_structure_get_name(msgstruct);
1622                                         if ( eventname )
1623                                         {
1624                                                 if (!strcmp(eventname, "eventSizeChanged") || !strcmp(eventname, "eventSizeAvail"))
1625                                                 {
1626                                                         gst_structure_get_int (msgstruct, "aspect_ratio", &m_aspect);
1627                                                         gst_structure_get_int (msgstruct, "width", &m_width);
1628                                                         gst_structure_get_int (msgstruct, "height", &m_height);
1629                                                         if (strstr(eventname, "Changed"))
1630                                                                 m_event((iPlayableService*)this, evVideoSizeChanged);
1631                                                 }
1632                                                 else if (!strcmp(eventname, "eventFrameRateChanged") || !strcmp(eventname, "eventFrameRateAvail"))
1633                                                 {
1634                                                         gst_structure_get_int (msgstruct, "frame_rate", &m_framerate);
1635                                                         if (strstr(eventname, "Changed"))
1636                                                                 m_event((iPlayableService*)this, evVideoFramerateChanged);
1637                                                 }
1638                                                 else if (!strcmp(eventname, "eventProgressiveChanged") || !strcmp(eventname, "eventProgressiveAvail"))
1639                                                 {
1640                                                         gst_structure_get_int (msgstruct, "progressive", &m_progressive);
1641                                                         if (strstr(eventname, "Changed"))
1642                                                                 m_event((iPlayableService*)this, evVideoProgressiveChanged);
1643                                                 }
1644                                         }
1645                                 }
1646                         }
1647                         break;
1648                 }
1649                 case GST_MESSAGE_BUFFERING:
1650                 {
1651                         GstBufferingMode mode;
1652                         gst_message_parse_buffering(msg, &(m_bufferInfo.bufferPercent));
1653                         gst_message_parse_buffering_stats(msg, &mode, &(m_bufferInfo.avgInRate), &(m_bufferInfo.avgOutRate), &(m_bufferInfo.bufferingLeft));
1654                         m_event((iPlayableService*)this, evBuffering);
1655                         break;
1656                 }
1657                 case GST_MESSAGE_STREAM_STATUS:
1658                 {
1659                         GstStreamStatusType type;
1660                         GstElement *owner;
1661                         gst_message_parse_stream_status (msg, &type, &owner);
1662                         if ( type == GST_STREAM_STATUS_TYPE_CREATE && m_sourceinfo.is_streaming )
1663                         {
1664                                 if ( GST_IS_PAD(source) )
1665                                         owner = gst_pad_get_parent_element(GST_PAD(source));
1666                                 else if ( GST_IS_ELEMENT(source) )
1667                                         owner = GST_ELEMENT(source);
1668                                 else
1669                                         owner = 0;
1670                                 if ( owner )
1671                                 {
1672                                         GstElementFactory *factory = gst_element_get_factory(GST_ELEMENT(owner));
1673                                         const gchar *name = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory));
1674                                         if (!strcmp(name, "souphttpsrc"))
1675                                         {
1676                                                 if (m_streamingsrc_timeout) {
1677                                                         m_streamingsrc_timeout->start(HTTP_TIMEOUT*1000, true);
1678                                                 }
1679                                                 g_object_set (G_OBJECT (owner), "timeout", HTTP_TIMEOUT, NULL);
1680                                                 eDebug("eServiceMP3::GST_STREAM_STATUS_TYPE_CREATE -> setting timeout on %s to %is", name, HTTP_TIMEOUT);
1681                                         }
1682                                 }
1683                                 if ( GST_IS_PAD(source) )
1684                                         gst_object_unref(owner);
1685                         }
1686                         break;
1687                 }
1688                 default:
1689                         break;
1690         }
1691         g_free (sourceName);
1692 }
1693
1694 GstBusSyncReply eServiceMP3::gstBusSyncHandler(GstBus *bus, GstMessage *message, gpointer user_data)
1695 {
1696         eServiceMP3 *_this = (eServiceMP3*)user_data;
1697         _this->m_pump.send(Message(1));
1698                 /* wake */
1699         return GST_BUS_PASS;
1700 }
1701
1702 void eServiceMP3::gstHTTPSourceSetAgent(GObject *object, GParamSpec *unused, gpointer user_data)
1703 {
1704         eServiceMP3 *_this = (eServiceMP3*)user_data;
1705         GstElement *source = NULL;
1706         g_object_get(_this->m_gst_playbin, "source", &source, NULL);
1707         if (source)
1708         {
1709 #if GST_VERSION_MAJOR >= 1
1710                 if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "timeout") != 0)
1711                 {
1712                         GstElementFactory *factory = gst_element_get_factory(source);
1713                         if (factory)
1714                         {
1715                                 const gchar *sourcename = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory));
1716                                 if (!strcmp(sourcename, "souphttpsrc"))
1717                                 {
1718                                         g_object_set(G_OBJECT(source), "timeout", HTTP_TIMEOUT, NULL);
1719                                 }
1720                         }
1721                 }
1722                 if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "ssl-strict") != 0)
1723                 {
1724                         g_object_set(G_OBJECT(source), "ssl-strict", FALSE, NULL);
1725                 }
1726 #endif
1727                 g_object_set (G_OBJECT (source), "user-agent", _this->m_useragent.c_str(), NULL);
1728                 gst_object_unref(source);
1729         }
1730 }
1731
1732 audiotype_t eServiceMP3::gstCheckAudioPad(GstStructure* structure)
1733 {
1734         if (!structure)
1735                 return atUnknown;
1736
1737         if ( gst_structure_has_name (structure, "audio/mpeg"))
1738         {
1739                 gint mpegversion, layer = -1;
1740                 if (!gst_structure_get_int (structure, "mpegversion", &mpegversion))
1741                         return atUnknown;
1742
1743                 switch (mpegversion) {
1744                         case 1:
1745                                 {
1746                                         gst_structure_get_int (structure, "layer", &layer);
1747                                         if ( layer == 3 )
1748                                                 return atMP3;
1749                                         else
1750                                                 return atMPEG;
1751                                         break;
1752                                 }
1753                         case 2:
1754                                 return atAAC;
1755                         case 4:
1756                                 return atAAC;
1757                         default:
1758                                 return atUnknown;
1759                 }
1760         }
1761
1762         else if ( gst_structure_has_name (structure, "audio/x-ac3") || gst_structure_has_name (structure, "audio/ac3") )
1763                 return atAC3;
1764         else if ( gst_structure_has_name (structure, "audio/x-dts") || gst_structure_has_name (structure, "audio/dts") )
1765                 return atDTS;
1766 #if GST_VERSION_MAJOR < 1
1767         else if ( gst_structure_has_name (structure, "audio/x-raw-int") )
1768 #else
1769         else if ( gst_structure_has_name (structure, "audio/x-raw") )
1770 #endif
1771                 return atPCM;
1772
1773         return atUnknown;
1774 }
1775
1776 void eServiceMP3::gstPoll(const Message &msg)
1777 {
1778         switch(msg.type)
1779         {
1780                 case 1:
1781                 {
1782                         GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (m_gst_playbin));
1783                         GstMessage *message;
1784                         while ((message = gst_bus_pop(bus)))
1785                         {
1786                                 gstBusCall(bus, message);
1787                                 gst_message_unref (message);
1788                         }
1789                         gst_object_unref(bus);
1790                         break;
1791                 }
1792                 case 2:
1793                 {
1794                         pullSubtitle(msg.d.buffer);
1795                         gst_buffer_unref(msg.d.buffer);
1796                         break;
1797                 }
1798                 case 3:
1799                 {
1800                         gstTextpadHasCAPS_synced(msg.d.pad);
1801                         gst_object_unref(msg.d.pad);
1802                         break;
1803                 }
1804                 default:
1805                 {
1806                         eDebug("gstPoll unhandled Message %d\n", msg.type);
1807                         break;
1808                 }
1809         }
1810 }
1811
1812 eAutoInitPtr<eServiceFactoryMP3> init_eServiceFactoryMP3(eAutoInitNumbers::service+1, "eServiceFactoryMP3");
1813
1814 void eServiceMP3::gstCBsubtitleAvail(GstElement *subsink, GstBuffer *buffer, gpointer user_data)
1815 {
1816         eServiceMP3 *_this = (eServiceMP3*)user_data;
1817         if (_this->m_currentSubtitleStream < 0)
1818         {
1819                 if (buffer) gst_buffer_unref(buffer);
1820                 return;
1821         }
1822         _this->m_pump.send(Message(2, buffer));
1823 }
1824
1825 void eServiceMP3::gstTextpadHasCAPS(GstPad *pad, GParamSpec * unused, gpointer user_data)
1826 {
1827         eServiceMP3 *_this = (eServiceMP3*)user_data;
1828
1829         gst_object_ref (pad);
1830
1831         _this->m_pump.send(Message(3, pad));
1832 }
1833
1834 // after messagepump
1835 void eServiceMP3::gstTextpadHasCAPS_synced(GstPad *pad)
1836 {
1837         GstCaps *caps;
1838
1839         g_object_get (G_OBJECT (pad), "caps", &caps, NULL);
1840
1841         eDebug("gstTextpadHasCAPS:: signal::caps = %s", gst_caps_to_string(caps));
1842
1843         if (caps)
1844         {
1845                 subtitleStream subs;
1846
1847 //              eDebug("gstGhostpadHasCAPS_synced %p %d", pad, m_subtitleStreams.size());
1848
1849                 if (m_currentSubtitleStream >= 0 && m_currentSubtitleStream < (int)m_subtitleStreams.size())
1850                         subs = m_subtitleStreams[m_currentSubtitleStream];
1851                 else {
1852                         subs.type = stUnknown;
1853                         subs.pad = pad;
1854                 }
1855
1856                 if ( subs.type == stUnknown )
1857                 {
1858                         GstTagList *tags = NULL;
1859                         gchar *g_lang;
1860                         g_signal_emit_by_name (m_gst_playbin, "get-text-tags", m_currentSubtitleStream, &tags);
1861
1862                         g_lang = g_strdup_printf ("und");
1863 #if GST_VERSION_MAJOR < 1
1864                         if ( tags && gst_is_tag_list(tags) )
1865 #else
1866                         if ( tags && GST_IS_TAG_LIST(tags) )
1867 #endif
1868                                 gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &g_lang);
1869
1870                         subs.language_code = std::string(g_lang);
1871                         subs.type = getSubtitleType(pad);
1872
1873                         if (m_currentSubtitleStream >= 0 && m_currentSubtitleStream < (int)m_subtitleStreams.size())
1874                                 m_subtitleStreams[m_currentSubtitleStream] = subs;
1875                         else
1876                                 m_subtitleStreams.push_back(subs);
1877
1878                         g_free (g_lang);
1879                 }
1880
1881 //              eDebug("gstGhostpadHasCAPS:: m_gst_prev_subtitle_caps=%s equal=%i",gst_caps_to_string(m_gst_prev_subtitle_caps),gst_caps_is_equal(m_gst_prev_subtitle_caps, caps));
1882
1883                 gst_caps_unref (caps);
1884         }
1885 }
1886
1887 void eServiceMP3::pullSubtitle(GstBuffer *buffer)
1888 {
1889         if (buffer && m_currentSubtitleStream >= 0 && m_currentSubtitleStream < (int)m_subtitleStreams.size())
1890         {
1891 #if GST_VERSION_MAJOR < 1
1892                 gint64 buf_pos = GST_BUFFER_TIMESTAMP(buffer);
1893                 size_t len = GST_BUFFER_SIZE(buffer);
1894 #else
1895                 GstMapInfo map;
1896                 if(!gst_buffer_map(buffer, &map, GST_MAP_READ))
1897                 {
1898                         eDebug("eServiceMP3::pullSubtitle gst_buffer_map failed");
1899                         gst_buffer_unref(buffer);
1900                         return;
1901                 }
1902                 gint64 buf_pos = GST_BUFFER_PTS(buffer);
1903                 size_t len = map.size;
1904 //              eDebug("gst_buffer_get_size %zu map.size %zu", gst_buffer_get_size(buffer), len);
1905 #endif
1906                 gint64 duration_ns = GST_BUFFER_DURATION(buffer);
1907                 int subType = m_subtitleStreams[m_currentSubtitleStream].type;
1908 #ifdef SUBTITLE_DEBUG
1909                 eDebug("pullSubtitle type = %i" ,subType);
1910 #endif                          
1911                 if (subType)
1912                 {
1913                         if ( subType < stVOB )
1914                         {
1915                                 int delay = ePythonConfigQuery::getConfigIntValue("config.subtitles.pango_subtitles_delay");
1916                                 int subtitle_fps = ePythonConfigQuery::getConfigIntValue("config.subtitles.pango_subtitles_fps");
1917
1918                                 double convert_fps = 1.0;
1919                                 if (subtitle_fps > 1 && m_framerate > 0)
1920                                         convert_fps = subtitle_fps / (double)m_framerate;
1921
1922                                 unsigned char line[len+1];
1923                                 SubtitlePage page;
1924 #if GST_VERSION_MAJOR < 1
1925                                 memcpy(line, GST_BUFFER_DATA(buffer), len);
1926 #else
1927                                 memcpy(line, map.data, len);
1928 #endif
1929                                 line[len] = 0;
1930 #ifdef SUBTITLE_DEBUG
1931                                 eDebug("got new text subtitle @ buf_pos = %lld ns (in pts=%lld), dur=%lld: '%s' ", buf_pos, buf_pos/11111LL, duration_ns, line);
1932 #endif
1933                                 gRGB rgbcol(0xD0,0xD0,0xD0);
1934                                 page.type = SubtitlePage::Pango;
1935                                 page.pango_page.m_elements.push_back(ePangoSubtitlePageElement(rgbcol, (const char*)line));
1936                                 page.pango_page.m_show_pts = buf_pos / 11111LL + convert_fps + delay;
1937                                 page.pango_page.m_timeout = duration_ns / 1000000;
1938                                 m_subtitle_pages.push_back(page);
1939                                 m_subtitle_sync_timer->start(1, true);
1940                         }
1941                         else
1942                         {
1943                                 eDebug("unsupported subpicture... ignoring");
1944                         }
1945                 }
1946 #if GST_VERSION_MAJOR >= 1
1947                 gst_buffer_unmap(buffer, &map);
1948 #endif
1949         }
1950 }
1951
1952 void eServiceMP3::pushSubtitles()
1953 {
1954         long next_timer = 0;
1955         while ( !m_subtitle_pages.empty() )
1956         {
1957                 pts_t running_pts;
1958                 gint64 diff_ms = 0;
1959                 gint64 show_pts = 0;
1960
1961                 if (getPlayPosition(running_pts) < 0)
1962                         m_decoder_time_valid_state = 0;
1963
1964                 if (m_decoder_time_valid_state < 4) {
1965                         ++m_decoder_time_valid_state;
1966                         if (m_prev_decoder_time == running_pts)
1967                                 m_decoder_time_valid_state = 0;
1968                         if (m_decoder_time_valid_state < 4) {
1969 //                              if (m_decoder_time_valid_state)
1970 //                                      eDebug("%d: decoder time not valid! prev %lld, now %lld\n", m_decoder_time_valid_state, m_prev_decoder_time/90, running_pts/90);
1971 //                              else
1972 //                                      eDebug("%d: decoder time not valid! now %lld\n", m_decoder_time_valid_state, running_pts/90);
1973                                 next_timer = 25;
1974                                 m_prev_decoder_time = running_pts;
1975                                 break;
1976                         }
1977                 }
1978
1979                 SubtitlePage &frontpage = m_subtitle_pages.front();
1980                 if (frontpage.type == SubtitlePage::Pango)
1981                         show_pts = frontpage.pango_page.m_show_pts;
1982
1983                 diff_ms = ( show_pts - running_pts ) / 90;
1984
1985 #ifdef SUBTITLE_DEBUG
1986                 int32_t decoder_ms, start_ms, end_ms, diff_start_ms, diff_end_ms;
1987                 ePangoSubtitlePageElement &element = frontpage.pango_page.m_elements[0];
1988                 std::string text = element.m_pango_line;
1989                 decoder_ms = running_pts / 90;
1990                 start_ms = show_pts/ 90;
1991                 end_ms = start_ms + frontpage.pango_page.m_timeout;
1992                 diff_start_ms = start_ms - decoder_ms;
1993                 diff_end_ms = end_ms - decoder_ms;
1994
1995                 eDebug("*** next subtitle: decoder: %d, start: %d, end: %d, duration_ms: %d, diff_start: %d, diff_end: %d : %s",
1996                         decoder_ms, start_ms, end_ms, end_ms - start_ms, diff_start_ms, diff_end_ms, text.c_str());
1997 #endif
1998
1999                 if ( diff_ms < -100 )
2000                 {
2001 #ifdef SUBTITLE_DEBUG
2002                         eDebug("subtitle too late... drop");
2003 #endif
2004                         m_subtitle_pages.pop_front();
2005                 }
2006                 else if ( diff_ms > 20 )
2007                 {
2008 #ifdef SUBTITLE_DEBUG
2009                         eDebug("*** current sub in the future, start timer, %d\n", diff_start_ms);
2010 #endif
2011                         next_timer = diff_ms;
2012                         break;
2013                 }
2014                 else // immediate show
2015                 {
2016                         if ( m_subtitle_widget )
2017                         {
2018 #ifdef SUBTITLE_DEBUG
2019                                 eDebug("*** current sub actual, show!");
2020 #endif
2021                                 if ( frontpage.type == SubtitlePage::Pango)
2022                                         m_subtitle_widget->setPage(frontpage.pango_page);
2023                                 m_subtitle_widget->show();
2024                         }
2025                         m_subtitle_pages.pop_front();
2026                 }
2027         }
2028         if (!next_timer)
2029         {
2030                 next_timer = 1000;
2031         }
2032         m_subtitle_sync_timer->start(next_timer, true);
2033 }
2034
2035
2036 RESULT eServiceMP3::enableSubtitles(eWidget *parent, ePyObject tuple)
2037 {
2038         eDebug ("eServiceMP3::enableSubtitles m_currentSubtitleStream=%i this=%p",m_currentSubtitleStream, this);
2039         ePyObject entry;
2040         int tuplesize = PyTuple_Size(tuple);
2041         int pid, type;
2042         gint text_pid = 0;
2043
2044 //      GstPad *pad = 0;
2045 //      g_signal_emit_by_name (m_gst_playbin, "get-text-pad", m_currentSubtitleStream, &pad);
2046 //      gst_element_get_static_pad(m_gst_subtitlebin, "sink");
2047 //      gulong subprobe_handler_id = gst_pad_add_buffer_probe (pad, G_CALLBACK (gstCBsubtitleDrop), NULL);
2048
2049         if (!PyTuple_Check(tuple))
2050                 goto error_out;
2051         if (tuplesize < 1)
2052                 goto error_out;
2053         entry = PyTuple_GET_ITEM(tuple, 1);
2054         if (!PyInt_Check(entry))
2055                 goto error_out;
2056         pid = PyInt_AsLong(entry);
2057         entry = PyTuple_GET_ITEM(tuple, 2);
2058         if (!PyInt_Check(entry))
2059                 goto error_out;
2060         type = PyInt_AsLong(entry);
2061
2062         if (m_currentSubtitleStream != pid)
2063         {
2064                 g_object_set (G_OBJECT (m_gst_playbin), "current-text", -1, NULL);
2065                 m_subtitle_sync_timer->stop();
2066                 m_subtitle_pages.clear();
2067                 m_prev_decoder_time = -1;
2068                 m_decoder_time_valid_state = 0;
2069                 m_currentSubtitleStream = pid;
2070                 g_object_set (G_OBJECT (m_gst_playbin), "current-text", m_currentSubtitleStream, NULL);
2071                 eDebug ("eServiceMP3::enableSubtitles g_object_set current-text = %i", pid);
2072         }
2073
2074         m_subtitle_widget = 0;
2075         m_subtitle_widget = new eSubtitleWidget(parent);
2076         m_subtitle_widget->resize(parent->size()); /* full size */
2077
2078         g_object_get (G_OBJECT (m_gst_playbin), "current-text", &text_pid, NULL);
2079
2080         eDebug ("eServiceMP3::switched to subtitle stream %i", text_pid);
2081 //      gst_pad_remove_buffer_probe (pad, subprobe_handler_id);
2082
2083 #ifdef GSTREAMER_SUBTITLE_SYNC_MODE_BUG
2084                         /*
2085                          * when we're running the subsink in sync=false mode,
2086                          * we have to force a seek, before the new subtitle stream will start
2087                          */
2088                         seekRelative(-1, 90000);
2089 #endif
2090
2091         m_event((iPlayableService*)this, evUpdatedInfo);
2092
2093         return 0;
2094
2095 error_out:
2096         eDebug("eServiceMP3::enableSubtitles needs a tuple as 2nd argument!\n"
2097                 "for gst subtitles (2, subtitle_stream_count, subtitle_type)");
2098         return -1;
2099 }
2100
2101 RESULT eServiceMP3::disableSubtitles(eWidget *parent)
2102 {
2103         m_currentSubtitleStream = -1;
2104         g_object_set (G_OBJECT (m_gst_playbin), "current-text", m_currentSubtitleStream, NULL);
2105         eDebug("eServiceMP3::disableSubtitles");
2106         m_subtitle_sync_timer->stop();
2107         m_subtitle_pages.clear();
2108         m_prev_decoder_time = -1;
2109         m_decoder_time_valid_state = 0;
2110         if (m_subtitle_widget)
2111                 delete m_subtitle_widget;
2112         m_subtitle_widget = 0;
2113         return 0;
2114 }
2115
2116 PyObject *eServiceMP3::getCachedSubtitle()
2117 {
2118 //      eDebug("eServiceMP3::getCachedSubtitle");
2119         bool autoturnon = ePythonConfigQuery::getConfigBoolValue("config.subtitles.pango_autoturnon", true);
2120         if (!autoturnon)
2121                 Py_RETURN_NONE;
2122
2123         if (!m_subtitleStreams.empty())
2124         {
2125                 int index = 0;
2126                 if (m_currentSubtitleStream >= 0 && m_currentSubtitleStream < (int)m_subtitleStreams.size())
2127                 {
2128                         index = m_currentSubtitleStream;
2129                 }
2130                 ePyObject tuple = PyTuple_New(5);
2131                 PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(2));
2132                 PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(index));
2133                 PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(int(m_subtitleStreams[index].type)));
2134                 PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(0));
2135                 PyTuple_SET_ITEM(tuple, 4, PyString_FromString(m_subtitleStreams[index].language_code.c_str()));
2136                 return tuple;
2137         }
2138
2139         Py_RETURN_NONE;
2140 }
2141
2142 PyObject *eServiceMP3::getSubtitleList()
2143 {
2144 //      eDebug("eServiceMP3::getSubtitleList");
2145         ePyObject l = PyList_New(0);
2146         int stream_idx = 0;
2147         
2148         for (std::vector<subtitleStream>::iterator IterSubtitleStream(m_subtitleStreams.begin()); IterSubtitleStream != m_subtitleStreams.end(); ++IterSubtitleStream)
2149         {
2150                 subtype_t type = IterSubtitleStream->type;
2151                 switch(type)
2152                 {
2153                 case stUnknown:
2154                 case stVOB:
2155                 case stPGS:
2156                         break;
2157                 default:
2158                 {
2159                         ePyObject tuple = PyTuple_New(5);
2160 //                      eDebug("eServiceMP3::getSubtitleList idx=%i type=%i, code=%s", stream_idx, int(type), (IterSubtitleStream->language_code).c_str());
2161                         PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(2));
2162                         PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(stream_idx));
2163                         PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(int(type)));
2164                         PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(0));
2165                         PyTuple_SET_ITEM(tuple, 4, PyString_FromString((IterSubtitleStream->language_code).c_str()));
2166                         PyList_Append(l, tuple);
2167                         Py_DECREF(tuple);
2168                 }
2169                 }
2170                 stream_idx++;
2171         }
2172         eDebug("eServiceMP3::getSubtitleList finished");
2173         return l;
2174 }
2175
2176 RESULT eServiceMP3::streamed(ePtr<iStreamedService> &ptr)
2177 {
2178         ptr = this;
2179         return 0;
2180 }
2181
2182 PyObject *eServiceMP3::getBufferCharge()
2183 {
2184         ePyObject tuple = PyTuple_New(5);
2185         PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(m_bufferInfo.bufferPercent));
2186         PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(m_bufferInfo.avgInRate));
2187         PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(m_bufferInfo.avgOutRate));
2188         PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(m_bufferInfo.bufferingLeft));
2189         PyTuple_SET_ITEM(tuple, 4, PyInt_FromLong(m_buffer_size));
2190         return tuple;
2191 }
2192
2193 int eServiceMP3::setBufferSize(int size)
2194 {
2195         m_buffer_size = size;
2196         g_object_set (G_OBJECT (m_gst_playbin), "buffer-size", m_buffer_size, NULL);
2197         return 0;
2198 }
2199
2200 int eServiceMP3::getAC3Delay()
2201 {
2202         return ac3_delay;
2203 }
2204
2205 int eServiceMP3::getPCMDelay()
2206 {
2207         return pcm_delay;
2208 }
2209
2210 void eServiceMP3::setAC3Delay(int delay)
2211 {
2212         ac3_delay = delay;
2213         if (!m_gst_playbin || m_state != stRunning)
2214                 return;
2215         else
2216         {
2217                 int config_delay_int = delay;
2218                 if (videoSink)
2219                 {
2220                         std::string config_delay;
2221                         if(ePythonConfigQuery::getConfigValue("config.av.generalAC3delay", config_delay) == 0)
2222                                 config_delay_int += atoi(config_delay.c_str());
2223                 }
2224                 else
2225                 {
2226                         eDebug("dont apply ac3 delay when no video is running!");
2227                         config_delay_int = 0;
2228                 }
2229
2230                 if (audioSink)
2231                 {
2232                         eTSMPEGDecoder::setHwAC3Delay(config_delay_int);
2233                 }
2234         }
2235 }
2236
2237 void eServiceMP3::setPCMDelay(int delay)
2238 {
2239         pcm_delay = delay;
2240         if (!m_gst_playbin || m_state != stRunning)
2241                 return;
2242         else
2243         {
2244                 int config_delay_int = delay;
2245                 if (videoSink)
2246                 {
2247                         std::string config_delay;
2248                         if(ePythonConfigQuery::getConfigValue("config.av.generalPCMdelay", config_delay) == 0)
2249                                 config_delay_int += atoi(config_delay.c_str());
2250                 }
2251                 else
2252                 {
2253                         eDebug("dont apply pcm delay when no video is running!");
2254                         config_delay_int = 0;
2255                 }
2256
2257                 if (audioSink)
2258                 {
2259                         eTSMPEGDecoder::setHwPCMDelay(config_delay_int);
2260                 }
2261         }
2262 }
2263