Merge commit 'origin/bug_597_crashlog_show_skinname' into experimental
[vuplus_dvbapp] / lib / service / servicemp3.cpp
1 #ifdef HAVE_GSTREAMER
2
3         /* note: this requires gstreamer 0.10.x and a big list of plugins. */
4         /* it's currently hardcoded to use a big-endian alsasink as sink. */
5 #include <lib/base/ebase.h>
6 #include <lib/base/eerror.h>
7 #include <lib/base/init_num.h>
8 #include <lib/base/init.h>
9 #include <lib/base/nconfig.h>
10 #include <lib/base/object.h>
11 #include <lib/dvb/decoder.h>
12 #include <lib/components/file_eraser.h>
13 #include <lib/gui/esubtitle.h>
14 #include <lib/service/servicemp3.h>
15 #include <lib/service/service.h>
16
17 #include <string>
18
19 #include <gst/gst.h>
20 #include <gst/pbutils/missing-plugins.h>
21 #include <sys/stat.h>
22
23 #define HTTP_TIMEOUT 10
24
25 // eServiceFactoryMP3
26
27 eServiceFactoryMP3::eServiceFactoryMP3()
28 {
29         ePtr<eServiceCenter> sc;
30         
31         eServiceCenter::getPrivInstance(sc);
32         if (sc)
33         {
34                 std::list<std::string> extensions;
35                 extensions.push_back("mp2");
36                 extensions.push_back("mp3");
37                 extensions.push_back("ogg");
38                 extensions.push_back("mpg");
39                 extensions.push_back("vob");
40                 extensions.push_back("wav");
41                 extensions.push_back("wave");
42                 extensions.push_back("m4v");
43                 extensions.push_back("mkv");
44                 extensions.push_back("avi");
45                 extensions.push_back("divx");
46                 extensions.push_back("dat");
47                 extensions.push_back("flac");
48                 extensions.push_back("mp4");
49                 extensions.push_back("mov");
50                 extensions.push_back("m4a");
51                 extensions.push_back("m2ts");
52                 sc->addServiceFactory(eServiceFactoryMP3::id, this, extensions);
53         }
54
55         m_service_info = new eStaticServiceMP3Info();
56 }
57
58 eServiceFactoryMP3::~eServiceFactoryMP3()
59 {
60         ePtr<eServiceCenter> sc;
61         
62         eServiceCenter::getPrivInstance(sc);
63         if (sc)
64                 sc->removeServiceFactory(eServiceFactoryMP3::id);
65 }
66
67 DEFINE_REF(eServiceFactoryMP3)
68
69         // iServiceHandler
70 RESULT eServiceFactoryMP3::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
71 {
72                 // check resources...
73         ptr = new eServiceMP3(ref);
74         return 0;
75 }
76
77 RESULT eServiceFactoryMP3::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr)
78 {
79         ptr=0;
80         return -1;
81 }
82
83 RESULT eServiceFactoryMP3::list(const eServiceReference &, ePtr<iListableService> &ptr)
84 {
85         ptr=0;
86         return -1;
87 }
88
89 RESULT eServiceFactoryMP3::info(const eServiceReference &ref, ePtr<iStaticServiceInformation> &ptr)
90 {
91         ptr = m_service_info;
92         return 0;
93 }
94
95 class eMP3ServiceOfflineOperations: public iServiceOfflineOperations
96 {
97         DECLARE_REF(eMP3ServiceOfflineOperations);
98         eServiceReference m_ref;
99 public:
100         eMP3ServiceOfflineOperations(const eServiceReference &ref);
101         
102         RESULT deleteFromDisk(int simulate);
103         RESULT getListOfFilenames(std::list<std::string> &);
104         RESULT reindex();
105 };
106
107 DEFINE_REF(eMP3ServiceOfflineOperations);
108
109 eMP3ServiceOfflineOperations::eMP3ServiceOfflineOperations(const eServiceReference &ref): m_ref((const eServiceReference&)ref)
110 {
111 }
112
113 RESULT eMP3ServiceOfflineOperations::deleteFromDisk(int simulate)
114 {
115         if (simulate)
116                 return 0;
117         else
118         {
119                 std::list<std::string> res;
120                 if (getListOfFilenames(res))
121                         return -1;
122                 
123                 eBackgroundFileEraser *eraser = eBackgroundFileEraser::getInstance();
124                 if (!eraser)
125                         eDebug("FATAL !! can't get background file eraser");
126                 
127                 for (std::list<std::string>::iterator i(res.begin()); i != res.end(); ++i)
128                 {
129                         eDebug("Removing %s...", i->c_str());
130                         if (eraser)
131                                 eraser->erase(i->c_str());
132                         else
133                                 ::unlink(i->c_str());
134                 }
135                 
136                 return 0;
137         }
138 }
139
140 RESULT eMP3ServiceOfflineOperations::getListOfFilenames(std::list<std::string> &res)
141 {
142         res.clear();
143         res.push_back(m_ref.path);
144         return 0;
145 }
146
147 RESULT eMP3ServiceOfflineOperations::reindex()
148 {
149         return -1;
150 }
151
152
153 RESULT eServiceFactoryMP3::offlineOperations(const eServiceReference &ref, ePtr<iServiceOfflineOperations> &ptr)
154 {
155         ptr = new eMP3ServiceOfflineOperations(ref);
156         return 0;
157 }
158
159 // eStaticServiceMP3Info
160
161
162 // eStaticServiceMP3Info is seperated from eServiceMP3 to give information
163 // about unopened files.
164
165 // probably eServiceMP3 should use this class as well, and eStaticServiceMP3Info
166 // should have a database backend where ID3-files etc. are cached.
167 // this would allow listing the mp3 database based on certain filters.
168
169 DEFINE_REF(eStaticServiceMP3Info)
170
171 eStaticServiceMP3Info::eStaticServiceMP3Info()
172 {
173 }
174
175 RESULT eStaticServiceMP3Info::getName(const eServiceReference &ref, std::string &name)
176 {
177         if ( ref.name.length() )
178                 name = ref.name;
179         else
180         {
181                 size_t last = ref.path.rfind('/');
182                 if (last != std::string::npos)
183                         name = ref.path.substr(last+1);
184                 else
185                         name = ref.path;
186         }
187         return 0;
188 }
189
190 int eStaticServiceMP3Info::getLength(const eServiceReference &ref)
191 {
192         return -1;
193 }
194
195 int eStaticServiceMP3Info::getInfo(const eServiceReference &ref, int w)
196 {
197         switch (w)
198         {
199         case iServiceInformation::sTimeCreate:
200         {
201                 struct stat s;
202                 if(stat(ref.path.c_str(), &s) == 0)
203                 {
204                   return s.st_mtime;
205                 }
206                 return iServiceInformation::resNA;
207         }
208         default: break;
209         }
210         return iServiceInformation::resNA;
211 }
212  
213
214 // eServiceMP3
215 int eServiceMP3::ac3_delay,
216     eServiceMP3::pcm_delay;
217
218 eServiceMP3::eServiceMP3(eServiceReference ref)
219         :m_ref(ref), m_pump(eApp, 1)
220 {
221         m_seekTimeout = eTimer::create(eApp);
222         m_subtitle_sync_timer = eTimer::create(eApp);
223         m_streamingsrc_timeout = 0;
224         m_stream_tags = 0;
225         m_currentAudioStream = -1;
226         m_currentSubtitleStream = 0;
227         m_subtitle_widget = 0;
228         m_currentTrickRatio = 0;
229         m_subs_to_pull = 0;
230         m_buffer_size = 1*1024*1024;
231         CONNECT(m_seekTimeout->timeout, eServiceMP3::seekTimeoutCB);
232         CONNECT(m_subtitle_sync_timer->timeout, eServiceMP3::pushSubtitles);
233         CONNECT(m_pump.recv_msg, eServiceMP3::gstPoll);
234         m_aspect = m_width = m_height = m_framerate = m_progressive = -1;
235
236         m_state = stIdle;
237         eDebug("eServiceMP3::construct!");
238
239         const char *filename = m_ref.path.c_str();
240         const char *ext = strrchr(filename, '.');
241         if (!ext)
242                 ext = filename;
243
244         m_sourceinfo.is_video = FALSE;
245         m_sourceinfo.audiotype = atUnknown;
246         if ( (strcasecmp(ext, ".mpeg") && strcasecmp(ext, ".mpg") && strcasecmp(ext, ".vob") && strcasecmp(ext, ".bin") && strcasecmp(ext, ".dat") ) == 0 )
247         {
248                 m_sourceinfo.containertype = ctMPEGPS;
249                 m_sourceinfo.is_video = TRUE;
250         }
251         else if ( strcasecmp(ext, ".ts") == 0 )
252         {
253                 m_sourceinfo.containertype = ctMPEGTS;
254                 m_sourceinfo.is_video = TRUE;
255         }
256         else if ( strcasecmp(ext, ".mkv") == 0 )
257         {
258                 m_sourceinfo.containertype = ctMKV;
259                 m_sourceinfo.is_video = TRUE;
260         }
261         else if ( strcasecmp(ext, ".avi") == 0 || strcasecmp(ext, ".divx") == 0)
262         {
263                 m_sourceinfo.containertype = ctAVI;
264                 m_sourceinfo.is_video = TRUE;
265         }
266         else if ( strcasecmp(ext, ".mp4") == 0 || strcasecmp(ext, ".mov") == 0 || strcasecmp(ext, ".m4v") == 0)
267         {
268                 m_sourceinfo.containertype = ctMP4;
269                 m_sourceinfo.is_video = TRUE;
270         }
271         else if ( strcasecmp(ext, ".m4a") == 0 )
272         {
273                 m_sourceinfo.containertype = ctMP4;
274                 m_sourceinfo.audiotype = atAAC;
275         }
276         else if ( strcasecmp(ext, ".mp3") == 0 )
277                 m_sourceinfo.audiotype = atMP3;
278         else if ( (strncmp(filename, "/autofs/", 8) || strncmp(filename+strlen(filename)-13, "/track-", 7) || strcasecmp(ext, ".wav")) == 0 )
279                 m_sourceinfo.containertype = ctCDA;
280         if ( strcasecmp(ext, ".dat") == 0 )
281         {
282                 m_sourceinfo.containertype = ctVCD;
283                 m_sourceinfo.is_video = TRUE;
284         }
285         if ( (strncmp(filename, "http://", 7)) == 0 || (strncmp(filename, "udp://", 6)) == 0 || (strncmp(filename, "rtp://", 6)) == 0  || (strncmp(filename, "https://", 8)) == 0 || (strncmp(filename, "mms://", 6)) == 0 || (strncmp(filename, "rtsp://", 7)) == 0 || (strncmp(filename, "rtspt://", 7)) == 0 )
286                 m_sourceinfo.is_streaming = TRUE;
287
288         gchar *uri;
289
290         if ( m_sourceinfo.is_streaming )
291         {
292                 uri = g_strdup_printf ("%s", filename);
293                 m_streamingsrc_timeout = eTimer::create(eApp);;
294                 CONNECT(m_streamingsrc_timeout->timeout, eServiceMP3::sourceTimeout);
295
296                 std::string config_str;
297                 if( ePythonConfigQuery::getConfigValue("config.mediaplayer.useAlternateUserAgent", config_str) == 0 )
298                 {
299                         if ( config_str == "True" )
300                                 ePythonConfigQuery::getConfigValue("config.mediaplayer.alternateUserAgent", m_useragent);
301                 }
302                 if ( m_useragent.length() == 0 )
303                         m_useragent = "Dream Multimedia Dreambox Enigma2 Mediaplayer";
304         }
305         else if ( m_sourceinfo.containertype == ctCDA )
306         {
307                 int i_track = atoi(filename+18);
308                 uri = g_strdup_printf ("cdda://%i", i_track);
309         }
310         else if ( m_sourceinfo.containertype == ctVCD )
311         {
312                 int fd = open(filename,O_RDONLY);
313                 char tmp[128*1024];
314                 int ret = read(fd, tmp, 128*1024);
315                 close(fd);
316                 if ( ret == -1 ) // this is a "REAL" VCD
317                         uri = g_strdup_printf ("vcd://");
318                 else
319                         uri = g_filename_to_uri(filename, NULL, NULL);
320         }
321         else
322
323                 uri = g_filename_to_uri(filename, NULL, NULL);
324
325         eDebug("eServiceMP3::playbin2 uri=%s", uri);
326
327         m_gst_playbin = gst_element_factory_make("playbin2", "playbin");
328         if (!m_gst_playbin)
329                 m_error_message = "failed to create GStreamer pipeline!\n";
330
331         g_object_set (G_OBJECT (m_gst_playbin), "uri", uri, NULL);
332
333         int flags = 0x47; // ( == GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_TEXT )
334         g_object_set (G_OBJECT (m_gst_playbin), "flags", flags, NULL);
335
336         g_free(uri);
337
338         GstElement *subsink = gst_element_factory_make("appsink", "subtitle_sink");
339         if (!subsink)
340                 eDebug("eServiceMP3::sorry, can't play: missing gst-plugin-appsink");
341         else
342         {
343                 m_subs_to_pull_handler_id = g_signal_connect (subsink, "new-buffer", G_CALLBACK (gstCBsubtitleAvail), this);
344                 g_object_set (G_OBJECT (subsink), "caps", gst_caps_from_string("text/plain; text/x-plain; text/x-pango-markup"), NULL);
345                 g_object_set (G_OBJECT (m_gst_playbin), "text-sink", subsink, NULL);
346         }
347
348         if ( m_gst_playbin )
349         {
350                 gst_bus_set_sync_handler(gst_pipeline_get_bus (GST_PIPELINE (m_gst_playbin)), gstBusSyncHandler, this);
351                 char srt_filename[strlen(filename)+1];
352                 strncpy(srt_filename,filename,strlen(filename)-3);
353                 srt_filename[strlen(filename)-3]='\0';
354                 strcat(srt_filename, "srt");
355                 struct stat buffer;
356                 if (stat(srt_filename, &buffer) == 0)
357                 {
358                         eDebug("eServiceMP3::subtitle uri: %s", g_filename_to_uri(srt_filename, NULL, NULL));
359                         g_object_set (G_OBJECT (m_gst_playbin), "suburi", g_filename_to_uri(srt_filename, NULL, NULL), NULL);
360                         subtitleStream subs;
361                         subs.type = stSRT;
362                         subs.language_code = std::string("und");
363                         m_subtitleStreams.push_back(subs);
364                 }
365                 if ( m_sourceinfo.is_streaming )
366                 {
367                         g_signal_connect (G_OBJECT (m_gst_playbin), "notify::source", G_CALLBACK (gstHTTPSourceSetAgent), this);
368                 }
369         } else
370         {
371                 m_event((iPlayableService*)this, evUser+12);
372
373                 if (m_gst_playbin)
374                         gst_object_unref(GST_OBJECT(m_gst_playbin));
375
376                 eDebug("eServiceMP3::sorry, can't play: %s",m_error_message.c_str());
377                 m_gst_playbin = 0;
378         }
379
380         setBufferSize(m_buffer_size);
381 }
382
383 eServiceMP3::~eServiceMP3()
384 {
385         // disconnect subtitle callback
386         GstElement *sink;
387         g_object_get (G_OBJECT (m_gst_playbin), "text-sink", &sink, NULL);
388         if (sink)
389         {
390                 g_signal_handler_disconnect (sink, m_subs_to_pull_handler_id);
391                 gst_object_unref(sink);
392         }
393
394         delete m_subtitle_widget;
395
396         // disconnect sync handler callback
397         gst_bus_set_sync_handler(gst_pipeline_get_bus (GST_PIPELINE (m_gst_playbin)), NULL, NULL);
398
399         if (m_state == stRunning)
400                 stop();
401
402         if (m_stream_tags)
403                 gst_tag_list_free(m_stream_tags);
404         
405         if (m_gst_playbin)
406         {
407                 gst_object_unref (GST_OBJECT (m_gst_playbin));
408                 eDebug("eServiceMP3::destruct!");
409         }
410 }
411
412 DEFINE_REF(eServiceMP3);
413
414 RESULT eServiceMP3::connectEvent(const Slot2<void,iPlayableService*,int> &event, ePtr<eConnection> &connection)
415 {
416         connection = new eConnection((iPlayableService*)this, m_event.connect(event));
417         return 0;
418 }
419
420 RESULT eServiceMP3::start()
421 {
422         ASSERT(m_state == stIdle);
423
424         m_state = stRunning;
425         if (m_gst_playbin)
426         {
427                 eDebug("eServiceMP3::starting pipeline");
428                 gst_element_set_state (m_gst_playbin, GST_STATE_PLAYING);
429         }
430
431         m_event(this, evStart);
432
433         return 0;
434 }
435
436 void eServiceMP3::sourceTimeout()
437 {
438         eDebug("eServiceMP3::http source timeout! issuing eof...");
439         m_event((iPlayableService*)this, evEOF);
440 }
441
442 RESULT eServiceMP3::stop()
443 {
444         ASSERT(m_state != stIdle);
445
446         if (m_state == stStopped)
447                 return -1;
448
449         eDebug("eServiceMP3::stop %s", m_ref.path.c_str());
450         gst_element_set_state(m_gst_playbin, GST_STATE_NULL);
451         m_state = stStopped;
452
453         return 0;
454 }
455
456 RESULT eServiceMP3::setTarget(int target)
457 {
458         return -1;
459 }
460
461 RESULT eServiceMP3::pause(ePtr<iPauseableService> &ptr)
462 {
463         ptr=this;
464         return 0;
465 }
466
467 RESULT eServiceMP3::setSlowMotion(int ratio)
468 {
469         if (!ratio)
470                 return 0;
471         eDebug("eServiceMP3::setSlowMotion ratio=%f",1/(float)ratio);
472         return trickSeek(1/(float)ratio);
473 }
474
475 RESULT eServiceMP3::setFastForward(int ratio)
476 {
477         eDebug("eServiceMP3::setFastForward ratio=%i",ratio);
478         return trickSeek(ratio);
479 }
480
481 void eServiceMP3::seekTimeoutCB()
482 {
483         pts_t ppos, len;
484         getPlayPosition(ppos);
485         getLength(len);
486         ppos += 90000*m_currentTrickRatio;
487         
488         if (ppos < 0)
489         {
490                 ppos = 0;
491                 m_seekTimeout->stop();
492         }
493         if (ppos > len)
494         {
495                 ppos = 0;
496                 stop();
497                 m_seekTimeout->stop();
498                 return;
499         }
500         seekTo(ppos);
501 }
502
503                 // iPausableService
504 RESULT eServiceMP3::pause()
505 {
506         if (!m_gst_playbin || m_state != stRunning)
507                 return -1;
508
509         gst_element_set_state(m_gst_playbin, GST_STATE_PAUSED);
510
511         return 0;
512 }
513
514 RESULT eServiceMP3::unpause()
515 {
516         if (!m_gst_playbin || m_state != stRunning)
517                 return -1;
518
519         gst_element_set_state(m_gst_playbin, GST_STATE_PLAYING);
520
521         return 0;
522 }
523
524         /* iSeekableService */
525 RESULT eServiceMP3::seek(ePtr<iSeekableService> &ptr)
526 {
527         ptr = this;
528         return 0;
529 }
530
531 RESULT eServiceMP3::getLength(pts_t &pts)
532 {
533         if (!m_gst_playbin)
534                 return -1;
535
536         if (m_state != stRunning)
537                 return -1;
538
539         GstFormat fmt = GST_FORMAT_TIME;
540         gint64 len;
541         
542         if (!gst_element_query_duration(m_gst_playbin, &fmt, &len))
543                 return -1;
544                 /* len is in nanoseconds. we have 90 000 pts per second. */
545         
546         pts = len / 11111;
547         return 0;
548 }
549
550 RESULT eServiceMP3::seekToImpl(pts_t to)
551 {
552                 /* convert pts to nanoseconds */
553         gint64 time_nanoseconds = to * 11111LL;
554         if (!gst_element_seek (m_gst_playbin, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
555                 GST_SEEK_TYPE_SET, time_nanoseconds,
556                 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
557         {
558                 eDebug("eServiceMP3::seekTo failed");
559                 return -1;
560         }
561
562         return 0;
563 }
564
565 RESULT eServiceMP3::seekTo(pts_t to)
566 {
567         RESULT ret = -1;
568
569         if (m_gst_playbin) {
570                 eSingleLocker l(m_subs_to_pull_lock); // this is needed to dont handle incomming subtitles during seek!
571                 if (!(ret = seekToImpl(to)))
572                 {
573                         m_subtitle_pages.clear();
574                         m_subs_to_pull = 0;
575                 }
576         }
577
578         return ret;
579 }
580
581
582 RESULT eServiceMP3::trickSeek(gdouble ratio)
583 {
584         if (!m_gst_playbin)
585                 return -1;
586         if (!ratio)
587                 return seekRelative(0, 0);
588
589         GstEvent *s_event;
590         int flags;
591         flags = GST_SEEK_FLAG_NONE;
592         flags |= GST_SEEK_FLAG_FLUSH;
593 //      flags |= GstSeekFlags (GST_SEEK_FLAG_ACCURATE);
594         flags |= GST_SEEK_FLAG_KEY_UNIT;
595 //      flags |= GstSeekFlags (GST_SEEK_FLAG_SEGMENT);
596 //      flags |= GstSeekFlags (GST_SEEK_FLAG_SKIP);
597
598         GstFormat fmt = GST_FORMAT_TIME;
599         gint64 pos, len;
600         gst_element_query_duration(m_gst_playbin, &fmt, &len);
601         gst_element_query_position(m_gst_playbin, &fmt, &pos);
602
603         if ( ratio >= 0 )
604         {
605                 s_event = gst_event_new_seek (ratio, GST_FORMAT_TIME, (GstSeekFlags)flags, GST_SEEK_TYPE_SET, pos, GST_SEEK_TYPE_SET, len);
606
607                 eDebug("eServiceMP3::trickSeek with rate %lf to %" GST_TIME_FORMAT " ", ratio, GST_TIME_ARGS (pos));
608         }
609         else
610         {
611                 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);
612         }
613
614         if (!gst_element_send_event ( GST_ELEMENT (m_gst_playbin), s_event))
615         {
616                 eDebug("eServiceMP3::trickSeek failed");
617                 return -1;
618         }
619
620         return 0;
621 }
622
623
624 RESULT eServiceMP3::seekRelative(int direction, pts_t to)
625 {
626         if (!m_gst_playbin)
627                 return -1;
628
629         pts_t ppos;
630         getPlayPosition(ppos);
631         ppos += to * direction;
632         if (ppos < 0)
633                 ppos = 0;
634         seekTo(ppos);
635         
636         return 0;
637 }
638
639 RESULT eServiceMP3::getPlayPosition(pts_t &pts)
640 {
641         GstFormat fmt = GST_FORMAT_TIME;
642         gint64 pos;
643         GstElement *sink;
644         pts = 0;
645
646         if (!m_gst_playbin)
647                 return -1;
648         if (m_state != stRunning)
649                 return -1;
650
651         g_object_get (G_OBJECT (m_gst_playbin), "audio-sink", &sink, NULL);
652
653         if (!sink)
654                 g_object_get (G_OBJECT (m_gst_playbin), "video-sink", &sink, NULL);
655
656         if (!sink)
657                 return -1;
658
659         gchar *name = gst_element_get_name(sink);
660         gboolean use_get_decoder_time = strstr(name, "dvbaudiosink") || strstr(name, "dvbvideosink");
661         g_free(name);
662
663         if (use_get_decoder_time)
664                 g_signal_emit_by_name(sink, "get-decoder-time", &pos);
665
666         gst_object_unref(sink);
667
668         if (!use_get_decoder_time && !gst_element_query_position(m_gst_playbin, &fmt, &pos)) {
669                 eDebug("gst_element_query_position failed in getPlayPosition");
670                 return -1;
671         }
672
673         /* pos is in nanoseconds. we have 90 000 pts per second. */
674         pts = pos / 11111;
675         return 0;
676 }
677
678 RESULT eServiceMP3::setTrickmode(int trick)
679 {
680                 /* trickmode is not yet supported by our dvbmediasinks. */
681         return -1;
682 }
683
684 RESULT eServiceMP3::isCurrentlySeekable()
685 {
686         int ret = 3; // seeking and fast/slow winding possible
687         GstElement *sink;
688
689         if (!m_gst_playbin)
690                 return 0;
691         if (m_state != stRunning)
692                 return 0;
693
694         g_object_get (G_OBJECT (m_gst_playbin), "video-sink", &sink, NULL);
695
696         // disable fast winding yet when a dvbvideosink or dvbaudiosink is used
697         // for this we must do some changes on different places.. (gstreamer.. our sinks.. enigma2)
698         if (sink) {
699                 ret &= ~2; // only seeking possible
700                 gst_object_unref(sink);
701         }
702         else {
703                 g_object_get (G_OBJECT (m_gst_playbin), "audio-sink", &sink, NULL);
704                 if (sink) {
705                         ret &= ~2; // only seeking possible
706                         gst_object_unref(sink);
707                 }
708         }
709
710         return ret;
711 }
712
713 RESULT eServiceMP3::info(ePtr<iServiceInformation>&i)
714 {
715         i = this;
716         return 0;
717 }
718
719 RESULT eServiceMP3::getName(std::string &name)
720 {
721         std::string title = m_ref.getName();
722         if (title.empty())
723         {
724                 name = m_ref.path;
725                 size_t n = name.rfind('/');
726                 if (n != std::string::npos)
727                         name = name.substr(n + 1);
728         }
729         else
730                 name = title;
731         return 0;
732 }
733
734 int eServiceMP3::getInfo(int w)
735 {
736         const gchar *tag = 0;
737
738         switch (w)
739         {
740         case sServiceref: return m_ref;
741         case sVideoHeight: return m_height;
742         case sVideoWidth: return m_width;
743         case sFrameRate: return m_framerate;
744         case sProgressive: return m_progressive;
745         case sAspect: return m_aspect;
746         case sTagTitle:
747         case sTagArtist:
748         case sTagAlbum:
749         case sTagTitleSortname:
750         case sTagArtistSortname:
751         case sTagAlbumSortname:
752         case sTagDate:
753         case sTagComposer:
754         case sTagGenre:
755         case sTagComment:
756         case sTagExtendedComment:
757         case sTagLocation:
758         case sTagHomepage:
759         case sTagDescription:
760         case sTagVersion:
761         case sTagISRC:
762         case sTagOrganization:
763         case sTagCopyright:
764         case sTagCopyrightURI:
765         case sTagContact:
766         case sTagLicense:
767         case sTagLicenseURI:
768         case sTagCodec:
769         case sTagAudioCodec:
770         case sTagVideoCodec:
771         case sTagEncoder:
772         case sTagLanguageCode:
773         case sTagKeywords:
774         case sTagChannelMode:
775         case sUser+12:
776                 return resIsString;
777         case sTagTrackGain:
778         case sTagTrackPeak:
779         case sTagAlbumGain:
780         case sTagAlbumPeak:
781         case sTagReferenceLevel:
782         case sTagBeatsPerMinute:
783         case sTagImage:
784         case sTagPreviewImage:
785         case sTagAttachment:
786                 return resIsPyObject;
787         case sTagTrackNumber:
788                 tag = GST_TAG_TRACK_NUMBER;
789                 break;
790         case sTagTrackCount:
791                 tag = GST_TAG_TRACK_COUNT;
792                 break;
793         case sTagAlbumVolumeNumber:
794                 tag = GST_TAG_ALBUM_VOLUME_NUMBER;
795                 break;
796         case sTagAlbumVolumeCount:
797                 tag = GST_TAG_ALBUM_VOLUME_COUNT;
798                 break;
799         case sTagBitrate:
800                 tag = GST_TAG_BITRATE;
801                 break;
802         case sTagNominalBitrate:
803                 tag = GST_TAG_NOMINAL_BITRATE;
804                 break;
805         case sTagMinimumBitrate:
806                 tag = GST_TAG_MINIMUM_BITRATE;
807                 break;
808         case sTagMaximumBitrate:
809                 tag = GST_TAG_MAXIMUM_BITRATE;
810                 break;
811         case sTagSerial:
812                 tag = GST_TAG_SERIAL;
813                 break;
814         case sTagEncoderVersion:
815                 tag = GST_TAG_ENCODER_VERSION;
816                 break;
817         case sTagCRC:
818                 tag = "has-crc";
819                 break;
820         default:
821                 return resNA;
822         }
823
824         if (!m_stream_tags || !tag)
825                 return 0;
826         
827         guint value;
828         if (gst_tag_list_get_uint(m_stream_tags, tag, &value))
829                 return (int) value;
830
831         return 0;
832 }
833
834 std::string eServiceMP3::getInfoString(int w)
835 {
836         if ( !m_stream_tags && w < sUser && w > 26 )
837                 return "";
838         const gchar *tag = 0;
839         switch (w)
840         {
841         case sTagTitle:
842                 tag = GST_TAG_TITLE;
843                 break;
844         case sTagArtist:
845                 tag = GST_TAG_ARTIST;
846                 break;
847         case sTagAlbum:
848                 tag = GST_TAG_ALBUM;
849                 break;
850         case sTagTitleSortname:
851                 tag = GST_TAG_TITLE_SORTNAME;
852                 break;
853         case sTagArtistSortname:
854                 tag = GST_TAG_ARTIST_SORTNAME;
855                 break;
856         case sTagAlbumSortname:
857                 tag = GST_TAG_ALBUM_SORTNAME;
858                 break;
859         case sTagDate:
860                 GDate *date;
861                 if (gst_tag_list_get_date(m_stream_tags, GST_TAG_DATE, &date))
862                 {
863                         gchar res[5];
864                         g_date_strftime (res, sizeof(res), "%Y-%M-%D", date); 
865                         return (std::string)res;
866                 }
867                 break;
868         case sTagComposer:
869                 tag = GST_TAG_COMPOSER;
870                 break;
871         case sTagGenre:
872                 tag = GST_TAG_GENRE;
873                 break;
874         case sTagComment:
875                 tag = GST_TAG_COMMENT;
876                 break;
877         case sTagExtendedComment:
878                 tag = GST_TAG_EXTENDED_COMMENT;
879                 break;
880         case sTagLocation:
881                 tag = GST_TAG_LOCATION;
882                 break;
883         case sTagHomepage:
884                 tag = GST_TAG_HOMEPAGE;
885                 break;
886         case sTagDescription:
887                 tag = GST_TAG_DESCRIPTION;
888                 break;
889         case sTagVersion:
890                 tag = GST_TAG_VERSION;
891                 break;
892         case sTagISRC:
893                 tag = GST_TAG_ISRC;
894                 break;
895         case sTagOrganization:
896                 tag = GST_TAG_ORGANIZATION;
897                 break;
898         case sTagCopyright:
899                 tag = GST_TAG_COPYRIGHT;
900                 break;
901         case sTagCopyrightURI:
902                 tag = GST_TAG_COPYRIGHT_URI;
903                 break;
904         case sTagContact:
905                 tag = GST_TAG_CONTACT;
906                 break;
907         case sTagLicense:
908                 tag = GST_TAG_LICENSE;
909                 break;
910         case sTagLicenseURI:
911                 tag = GST_TAG_LICENSE_URI;
912                 break;
913         case sTagCodec:
914                 tag = GST_TAG_CODEC;
915                 break;
916         case sTagAudioCodec:
917                 tag = GST_TAG_AUDIO_CODEC;
918                 break;
919         case sTagVideoCodec:
920                 tag = GST_TAG_VIDEO_CODEC;
921                 break;
922         case sTagEncoder:
923                 tag = GST_TAG_ENCODER;
924                 break;
925         case sTagLanguageCode:
926                 tag = GST_TAG_LANGUAGE_CODE;
927                 break;
928         case sTagKeywords:
929                 tag = GST_TAG_KEYWORDS;
930                 break;
931         case sTagChannelMode:
932                 tag = "channel-mode";
933                 break;
934         case sUser+12:
935                 return m_error_message;
936         default:
937                 return "";
938         }
939         if ( !tag )
940                 return "";
941         gchar *value;
942         if (gst_tag_list_get_string(m_stream_tags, tag, &value))
943         {
944                 std::string res = value;
945                 g_free(value);
946                 return res;
947         }
948         return "";
949 }
950
951 PyObject *eServiceMP3::getInfoObject(int w)
952 {
953         const gchar *tag = 0;
954         bool isBuffer = false;
955         switch (w)
956         {
957                 case sTagTrackGain:
958                         tag = GST_TAG_TRACK_GAIN;
959                         break;
960                 case sTagTrackPeak:
961                         tag = GST_TAG_TRACK_PEAK;
962                         break;
963                 case sTagAlbumGain:
964                         tag = GST_TAG_ALBUM_GAIN;
965                         break;
966                 case sTagAlbumPeak:
967                         tag = GST_TAG_ALBUM_PEAK;
968                         break;
969                 case sTagReferenceLevel:
970                         tag = GST_TAG_REFERENCE_LEVEL;
971                         break;
972                 case sTagBeatsPerMinute:
973                         tag = GST_TAG_BEATS_PER_MINUTE;
974                         break;
975                 case sTagImage:
976                         tag = GST_TAG_IMAGE;
977                         isBuffer = true;
978                         break;
979                 case sTagPreviewImage:
980                         tag = GST_TAG_PREVIEW_IMAGE;
981                         isBuffer = true;
982                         break;
983                 case sTagAttachment:
984                         tag = GST_TAG_ATTACHMENT;
985                         isBuffer = true;
986                         break;
987                 default:
988                         break;
989         }
990
991         if ( isBuffer )
992         {
993                 const GValue *gv_buffer = gst_tag_list_get_value_index(m_stream_tags, tag, 0);
994                 if ( gv_buffer )
995                 {
996                         GstBuffer *buffer;
997                         buffer = gst_value_get_buffer (gv_buffer);
998                         return PyBuffer_FromMemory(GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer));
999                 }
1000         }
1001         else
1002         {
1003                 gdouble value = 0.0;
1004                 gst_tag_list_get_double(m_stream_tags, tag, &value);
1005                 return PyFloat_FromDouble(value);
1006         }
1007
1008         return 0;
1009 }
1010
1011 RESULT eServiceMP3::audioChannel(ePtr<iAudioChannelSelection> &ptr)
1012 {
1013         ptr = this;
1014         return 0;
1015 }
1016
1017 RESULT eServiceMP3::audioTracks(ePtr<iAudioTrackSelection> &ptr)
1018 {
1019         ptr = this;
1020         return 0;
1021 }
1022
1023 RESULT eServiceMP3::subtitle(ePtr<iSubtitleOutput> &ptr)
1024 {
1025         ptr = this;
1026         return 0;
1027 }
1028
1029 RESULT eServiceMP3::audioDelay(ePtr<iAudioDelay> &ptr)
1030 {
1031         ptr = this;
1032         return 0;
1033 }
1034
1035 int eServiceMP3::getNumberOfTracks()
1036 {
1037         return m_audioStreams.size();
1038 }
1039
1040 int eServiceMP3::getCurrentTrack()
1041 {
1042         if (m_currentAudioStream == -1)
1043                 g_object_get (G_OBJECT (m_gst_playbin), "current-audio", &m_currentAudioStream, NULL);
1044         return m_currentAudioStream;
1045 }
1046
1047 RESULT eServiceMP3::selectTrack(unsigned int i)
1048 {
1049         pts_t ppos;
1050         getPlayPosition(ppos);
1051         ppos -= 90000;
1052         if (ppos < 0)
1053                 ppos = 0;
1054
1055         int ret = selectAudioStream(i);
1056         if (!ret) {
1057                 /* flush */
1058                 seekTo(ppos);
1059         }
1060
1061         return ret;
1062 }
1063
1064 int eServiceMP3::selectAudioStream(int i)
1065 {
1066         int current_audio;
1067         g_object_set (G_OBJECT (m_gst_playbin), "current-audio", i, NULL);
1068         g_object_get (G_OBJECT (m_gst_playbin), "current-audio", &current_audio, NULL);
1069         if ( current_audio == i )
1070         {
1071                 eDebug ("eServiceMP3::switched to audio stream %i", current_audio);
1072                 m_currentAudioStream = i;
1073                 return 0;
1074         }
1075         return -1;
1076 }
1077
1078 int eServiceMP3::getCurrentChannel()
1079 {
1080         return STEREO;
1081 }
1082
1083 RESULT eServiceMP3::selectChannel(int i)
1084 {
1085         eDebug("eServiceMP3::selectChannel(%i)",i);
1086         return 0;
1087 }
1088
1089 RESULT eServiceMP3::getTrackInfo(struct iAudioTrackInfo &info, unsigned int i)
1090 {
1091         if (i >= m_audioStreams.size())
1092                 return -2;
1093                 info.m_description = m_audioStreams[i].codec;
1094 /*      if (m_audioStreams[i].type == atMPEG)
1095                 info.m_description = "MPEG";
1096         else if (m_audioStreams[i].type == atMP3)
1097                 info.m_description = "MP3";
1098         else if (m_audioStreams[i].type == atAC3)
1099                 info.m_description = "AC3";
1100         else if (m_audioStreams[i].type == atAAC)
1101                 info.m_description = "AAC";
1102         else if (m_audioStreams[i].type == atDTS)
1103                 info.m_description = "DTS";
1104         else if (m_audioStreams[i].type == atPCM)
1105                 info.m_description = "PCM";
1106         else if (m_audioStreams[i].type == atOGG)
1107                 info.m_description = "OGG";
1108         else if (m_audioStreams[i].type == atFLAC)
1109                 info.m_description = "FLAC";
1110         else
1111                 info.m_description = "???";*/
1112         if (info.m_language.empty())
1113                 info.m_language = m_audioStreams[i].language_code;
1114         return 0;
1115 }
1116
1117 void eServiceMP3::gstBusCall(GstBus *bus, GstMessage *msg)
1118 {
1119         if (!msg)
1120                 return;
1121         gchar *sourceName;
1122         GstObject *source;
1123
1124         source = GST_MESSAGE_SRC(msg);
1125         sourceName = gst_object_get_name(source);
1126 #if 0
1127         if (gst_message_get_structure(msg))
1128         {
1129                 gchar *string = gst_structure_to_string(gst_message_get_structure(msg));
1130                 eDebug("eServiceMP3::gst_message from %s: %s", sourceName, string);
1131                 g_free(string);
1132         }
1133         else
1134                 eDebug("eServiceMP3::gst_message from %s: %s (without structure)", sourceName, GST_MESSAGE_TYPE_NAME(msg));
1135 #endif
1136         switch (GST_MESSAGE_TYPE (msg))
1137         {
1138                 case GST_MESSAGE_EOS:
1139                         m_event((iPlayableService*)this, evEOF);
1140                         break;
1141                 case GST_MESSAGE_STATE_CHANGED:
1142                 {
1143                         if(GST_MESSAGE_SRC(msg) != GST_OBJECT(m_gst_playbin))
1144                                 break;
1145
1146                         GstState old_state, new_state;
1147                         gst_message_parse_state_changed(msg, &old_state, &new_state, NULL);
1148                 
1149                         if(old_state == new_state)
1150                                 break;
1151         
1152                         eDebug("eServiceMP3::state transition %s -> %s", gst_element_state_get_name(old_state), gst_element_state_get_name(new_state));
1153         
1154                         GstStateChange transition = (GstStateChange)GST_STATE_TRANSITION(old_state, new_state);
1155         
1156                         switch(transition)
1157                         {
1158                                 case GST_STATE_CHANGE_NULL_TO_READY:
1159                                 {
1160                                 }       break;
1161                                 case GST_STATE_CHANGE_READY_TO_PAUSED:
1162                                 {
1163                                         GstElement *sink;
1164                                         g_object_get (G_OBJECT (m_gst_playbin), "text-sink", &sink, NULL);
1165                                         if (sink)
1166                                         {
1167                                                 g_object_set (G_OBJECT (sink), "max-buffers", 2, NULL);
1168                                                 g_object_set (G_OBJECT (sink), "sync", FALSE, NULL);
1169                                                 g_object_set (G_OBJECT (sink), "async", FALSE, NULL);
1170                                                 g_object_set (G_OBJECT (sink), "emit-signals", TRUE, NULL);
1171                                                 gst_object_unref(sink);
1172                                         }
1173                                         setAC3Delay(ac3_delay);
1174                                         setPCMDelay(pcm_delay);
1175                                 }       break;
1176                                 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1177                                 {
1178                                         if ( m_sourceinfo.is_streaming && m_streamingsrc_timeout )
1179                                                 m_streamingsrc_timeout->stop();
1180                                 }       break;
1181                                 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1182                                 {
1183                                 }       break;
1184                                 case GST_STATE_CHANGE_PAUSED_TO_READY:
1185                                 {
1186                                 }       break;
1187                                 case GST_STATE_CHANGE_READY_TO_NULL:
1188                                 {
1189                                 }       break;
1190                         }
1191                         break;
1192                 }
1193                 case GST_MESSAGE_ERROR:
1194                 {
1195                         gchar *debug;
1196                         GError *err;
1197                         gst_message_parse_error (msg, &err, &debug);
1198                         g_free (debug);
1199                         eWarning("Gstreamer error: %s (%i) from %s", err->message, err->code, sourceName );
1200                         if ( err->domain == GST_STREAM_ERROR )
1201                         {
1202                                 if ( err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND )
1203                                 {
1204                                         if ( g_strrstr(sourceName, "videosink") )
1205                                                 m_event((iPlayableService*)this, evUser+11);
1206                                         else if ( g_strrstr(sourceName, "audiosink") )
1207                                                 m_event((iPlayableService*)this, evUser+10);
1208                                 }
1209                         }
1210                         g_error_free(err);
1211                         break;
1212                 }
1213                 case GST_MESSAGE_INFO:
1214                 {
1215                         gchar *debug;
1216                         GError *inf;
1217         
1218                         gst_message_parse_info (msg, &inf, &debug);
1219                         g_free (debug);
1220                         if ( inf->domain == GST_STREAM_ERROR && inf->code == GST_STREAM_ERROR_DECODE )
1221                         {
1222                                 if ( g_strrstr(sourceName, "videosink") )
1223                                         m_event((iPlayableService*)this, evUser+14);
1224                         }
1225                         g_error_free(inf);
1226                         break;
1227                 }
1228                 case GST_MESSAGE_TAG:
1229                 {
1230                         GstTagList *tags, *result;
1231                         gst_message_parse_tag(msg, &tags);
1232         
1233                         result = gst_tag_list_merge(m_stream_tags, tags, GST_TAG_MERGE_REPLACE);
1234                         if (result)
1235                         {
1236                                 if (m_stream_tags)
1237                                         gst_tag_list_free(m_stream_tags);
1238                                 m_stream_tags = result;
1239                         }
1240         
1241                         const GValue *gv_image = gst_tag_list_get_value_index(tags, GST_TAG_IMAGE, 0);
1242                         if ( gv_image )
1243                         {
1244                                 GstBuffer *buf_image;
1245                                 buf_image = gst_value_get_buffer (gv_image);
1246                                 int fd = open("/tmp/.id3coverart", O_CREAT|O_WRONLY|O_TRUNC, 0644);
1247                                 int ret = write(fd, GST_BUFFER_DATA(buf_image), GST_BUFFER_SIZE(buf_image));
1248                                 close(fd);
1249                                 eDebug("eServiceMP3::/tmp/.id3coverart %d bytes written ", ret);
1250                                 m_event((iPlayableService*)this, evUser+13);
1251                         }
1252                         gst_tag_list_free(tags);
1253                         m_event((iPlayableService*)this, evUpdatedInfo);
1254                         break;
1255                 }
1256                 case GST_MESSAGE_ASYNC_DONE:
1257                 {
1258                         if(GST_MESSAGE_SRC(msg) != GST_OBJECT(m_gst_playbin))
1259                                 break;
1260
1261                         GstTagList *tags;
1262                         gint i, active_idx, n_video = 0, n_audio = 0, n_text = 0;
1263
1264                         g_object_get (m_gst_playbin, "n-video", &n_video, NULL);
1265                         g_object_get (m_gst_playbin, "n-audio", &n_audio, NULL);
1266                         g_object_get (m_gst_playbin, "n-text", &n_text, NULL);
1267
1268                         eDebug("eServiceMP3::async-done - %d video, %d audio, %d subtitle", n_video, n_audio, n_text);
1269
1270                         if ( n_video + n_audio <= 0 )
1271                                 stop();
1272
1273                         active_idx = 0;
1274
1275                         m_audioStreams.clear();
1276                         m_subtitleStreams.clear();
1277
1278                         for (i = 0; i < n_audio; i++)
1279                         {
1280                                 audioStream audio;
1281                                 gchar *g_codec, *g_lang;
1282                                 GstPad* pad = 0;
1283                                 g_signal_emit_by_name (m_gst_playbin, "get-audio-pad", i, &pad);
1284                                 GstCaps* caps = gst_pad_get_negotiated_caps(pad);
1285                                 if (!caps)
1286                                         continue;
1287                                 GstStructure* str = gst_caps_get_structure(caps, 0);
1288                                 const gchar *g_type = gst_structure_get_name(str);
1289                                 eDebug("AUDIO STRUCT=%s", g_type);
1290                                 audio.type = gstCheckAudioPad(str);
1291                                 g_codec = g_strdup(g_type);
1292                                 g_lang = g_strdup_printf ("und");
1293                                 g_signal_emit_by_name (m_gst_playbin, "get-audio-tags", i, &tags);
1294                                 if ( tags && gst_is_tag_list(tags) )
1295                                 {
1296                                         gst_tag_list_get_string(tags, GST_TAG_AUDIO_CODEC, &g_codec);
1297                                         gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &g_lang);
1298                                         gst_tag_list_free(tags);
1299                                 }
1300                                 audio.language_code = std::string(g_lang);
1301                                 audio.codec = std::string(g_codec);
1302                                 eDebug("eServiceMP3::audio stream=%i codec=%s language=%s", i, g_codec, g_lang);
1303                                 m_audioStreams.push_back(audio);
1304                                 g_free (g_lang);
1305                                 g_free (g_codec);
1306                                 gst_caps_unref(caps);
1307                         }
1308
1309                         for (i = 0; i < n_text; i++)
1310                         {       
1311                                 gchar *g_lang;
1312 //                              gchar *g_type;
1313 //                              GstPad* pad = 0;
1314 //                              g_signal_emit_by_name (m_gst_playbin, "get-text-pad", i, &pad);
1315 //                              GstCaps* caps = gst_pad_get_negotiated_caps(pad);
1316 //                              GstStructure* str = gst_caps_get_structure(caps, 0);
1317 //                              g_type = gst_structure_get_name(str);
1318 //                              g_signal_emit_by_name (m_gst_playbin, "get-text-tags", i, &tags);
1319                                 subtitleStream subs;
1320                                 subs.type = stPlainText;
1321                                 g_lang = g_strdup_printf ("und");
1322                                 if ( tags && gst_is_tag_list(tags) )
1323                                         gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &g_lang);
1324                                 subs.language_code = std::string(g_lang);
1325                                 eDebug("eServiceMP3::subtitle stream=%i language=%s"/* type=%s*/, i, g_lang/*, g_type*/);
1326                                 m_subtitleStreams.push_back(subs);
1327                                 g_free (g_lang);
1328 //                              g_free (g_type);
1329                         }
1330                         m_event((iPlayableService*)this, evUpdatedEventInfo);
1331                         break;
1332                 }
1333                 case GST_MESSAGE_ELEMENT:
1334                 {
1335                         if ( gst_is_missing_plugin_message(msg) )
1336                         {
1337                                 gchar *description = gst_missing_plugin_message_get_description(msg);
1338                                 if ( description )
1339                                 {
1340                                         m_error_message = "GStreamer plugin " + (std::string)description + " not available!\n";
1341                                         g_free(description);
1342                                         m_event((iPlayableService*)this, evUser+12);
1343                                 }
1344                         }
1345                         else if (const GstStructure *msgstruct = gst_message_get_structure(msg))
1346                         {
1347                                 const gchar *eventname = gst_structure_get_name(msgstruct);
1348                                 if ( eventname )
1349                                 {
1350                                         if (!strcmp(eventname, "eventSizeChanged") || !strcmp(eventname, "eventSizeAvail"))
1351                                         {
1352                                                 gst_structure_get_int (msgstruct, "aspect_ratio", &m_aspect);
1353                                                 gst_structure_get_int (msgstruct, "width", &m_width);
1354                                                 gst_structure_get_int (msgstruct, "height", &m_height);
1355                                                 if (strstr(eventname, "Changed"))
1356                                                         m_event((iPlayableService*)this, evVideoSizeChanged);
1357                                         }
1358                                         else if (!strcmp(eventname, "eventFrameRateChanged") || !strcmp(eventname, "eventFrameRateAvail"))
1359                                         {
1360                                                 gst_structure_get_int (msgstruct, "frame_rate", &m_framerate);
1361                                                 if (strstr(eventname, "Changed"))
1362                                                         m_event((iPlayableService*)this, evVideoFramerateChanged);
1363                                         }
1364                                         else if (!strcmp(eventname, "eventProgressiveChanged") || !strcmp(eventname, "eventProgressiveAvail"))
1365                                         {
1366                                                 gst_structure_get_int (msgstruct, "progressive", &m_progressive);
1367                                                 if (strstr(eventname, "Changed"))
1368                                                         m_event((iPlayableService*)this, evVideoProgressiveChanged);
1369                                         }
1370                                 }
1371                         }
1372                         break;
1373                 }
1374                 case GST_MESSAGE_BUFFERING:
1375                 {
1376                         GstBufferingMode mode;
1377                         gst_message_parse_buffering(msg, &(m_bufferInfo.bufferPercent));
1378                         gst_message_parse_buffering_stats(msg, &mode, &(m_bufferInfo.avgInRate), &(m_bufferInfo.avgOutRate), &(m_bufferInfo.bufferingLeft));
1379                         m_event((iPlayableService*)this, evBuffering);
1380                         break;
1381                 }
1382                 case GST_MESSAGE_STREAM_STATUS:
1383                 {
1384                         GstStreamStatusType type;
1385                         GstElement *owner;
1386                         gst_message_parse_stream_status (msg, &type, &owner);
1387                         if ( type == GST_STREAM_STATUS_TYPE_CREATE && m_sourceinfo.is_streaming )
1388                         {
1389                                 if ( GST_IS_PAD(source) )
1390                                         owner = gst_pad_get_parent_element(GST_PAD(source));
1391                                 else if ( GST_IS_ELEMENT(source) )
1392                                         owner = GST_ELEMENT(source);
1393                                 else
1394                                         owner = 0;
1395                                 if ( owner )
1396                                 {
1397                                         GstElementFactory *factory = gst_element_get_factory(GST_ELEMENT(owner));
1398                                         const gchar *name = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory));
1399                                         if (!strcmp(name, "souphttpsrc"))
1400                                         {
1401                                                 m_streamingsrc_timeout->start(HTTP_TIMEOUT*1000, true);
1402                                                 g_object_set (G_OBJECT (owner), "timeout", HTTP_TIMEOUT, NULL);
1403                                                 eDebug("eServiceMP3::GST_STREAM_STATUS_TYPE_CREATE -> setting timeout on %s to %is", name, HTTP_TIMEOUT);
1404                                         }
1405                                         
1406                                 }
1407                                 if ( GST_IS_PAD(source) )
1408                                         gst_object_unref(owner);
1409                         }
1410                         break;
1411                 }
1412                 default:
1413                         break;
1414         }
1415         g_free (sourceName);
1416 }
1417
1418 GstBusSyncReply eServiceMP3::gstBusSyncHandler(GstBus *bus, GstMessage *message, gpointer user_data)
1419 {
1420         eServiceMP3 *_this = (eServiceMP3*)user_data;
1421         _this->m_pump.send(1);
1422                 /* wake */
1423         return GST_BUS_PASS;
1424 }
1425
1426 void eServiceMP3::gstHTTPSourceSetAgent(GObject *object, GParamSpec *unused, gpointer user_data)
1427 {
1428         eServiceMP3 *_this = (eServiceMP3*)user_data;
1429         GstElement *source;
1430         g_object_get(_this->m_gst_playbin, "source", &source, NULL);
1431         g_object_set (G_OBJECT (source), "user-agent", _this->m_useragent.c_str(), NULL);
1432         gst_object_unref(source);
1433 }
1434
1435 audiotype_t eServiceMP3::gstCheckAudioPad(GstStructure* structure)
1436 {
1437         if (!structure)
1438                 return atUnknown;
1439
1440         if ( gst_structure_has_name (structure, "audio/mpeg"))
1441         {
1442                 gint mpegversion, layer = -1;
1443                 if (!gst_structure_get_int (structure, "mpegversion", &mpegversion))
1444                         return atUnknown;
1445
1446                 switch (mpegversion) {
1447                         case 1:
1448                                 {
1449                                         gst_structure_get_int (structure, "layer", &layer);
1450                                         if ( layer == 3 )
1451                                                 return atMP3;
1452                                         else
1453                                                 return atMPEG;
1454                                         break;
1455                                 }
1456                         case 2:
1457                                 return atAAC;
1458                         case 4:
1459                                 return atAAC;
1460                         default:
1461                                 return atUnknown;
1462                 }
1463         }
1464
1465         else if ( gst_structure_has_name (structure, "audio/x-ac3") || gst_structure_has_name (structure, "audio/ac3") )
1466                 return atAC3;
1467         else if ( gst_structure_has_name (structure, "audio/x-dts") || gst_structure_has_name (structure, "audio/dts") )
1468                 return atDTS;
1469         else if ( gst_structure_has_name (structure, "audio/x-raw-int") )
1470                 return atPCM;
1471
1472         return atUnknown;
1473 }
1474
1475 void eServiceMP3::gstPoll(const int &msg)
1476 {
1477                 /* ok, we have a serious problem here. gstBusSyncHandler sends 
1478                    us the wakup signal, but likely before it was posted.
1479                    the usleep, an EVIL HACK (DON'T DO THAT!!!) works around this.
1480                    
1481                    I need to understand the API a bit more to make this work 
1482                    proplerly. */
1483         if (msg == 1)
1484         {
1485                 GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (m_gst_playbin));
1486                 GstMessage *message;
1487                 usleep(1);
1488                 while ((message = gst_bus_pop (bus)))
1489                 {
1490                         gstBusCall(bus, message);
1491                         gst_message_unref (message);
1492                 }
1493         }
1494         else
1495                 pullSubtitle();
1496 }
1497
1498 eAutoInitPtr<eServiceFactoryMP3> init_eServiceFactoryMP3(eAutoInitNumbers::service+1, "eServiceFactoryMP3");
1499
1500 void eServiceMP3::gstCBsubtitleAvail(GstElement *appsink, gpointer user_data)
1501 {
1502         eServiceMP3 *_this = (eServiceMP3*)user_data;
1503         eSingleLocker l(_this->m_subs_to_pull_lock);
1504         ++_this->m_subs_to_pull;
1505         _this->m_pump.send(2);
1506 }
1507
1508 void eServiceMP3::pullSubtitle()
1509 {
1510         GstElement *sink;
1511         g_object_get (G_OBJECT (m_gst_playbin), "text-sink", &sink, NULL);
1512         if (sink)
1513         {
1514                 while (m_subs_to_pull && m_subtitle_pages.size() < 2)
1515                 {
1516                         GstBuffer *buffer;
1517                         {
1518                                 eSingleLocker l(m_subs_to_pull_lock);
1519                                 --m_subs_to_pull;
1520                                 g_signal_emit_by_name (sink, "pull-buffer", &buffer);
1521                         }
1522                         if (buffer)
1523                         {
1524                                 gint64 buf_pos = GST_BUFFER_TIMESTAMP(buffer);
1525                                 gint64 duration_ns = GST_BUFFER_DURATION(buffer);
1526                                 size_t len = GST_BUFFER_SIZE(buffer);
1527                                 unsigned char line[len+1];
1528                                 memcpy(line, GST_BUFFER_DATA(buffer), len);
1529                                 line[len] = 0;
1530                                 eDebug("got new subtitle @ buf_pos = %lld ns (in pts=%lld): '%s' ", buf_pos, buf_pos/11111, line);
1531                                 ePangoSubtitlePage page;
1532                                 gRGB rgbcol(0xD0,0xD0,0xD0);
1533                                 page.m_elements.push_back(ePangoSubtitlePageElement(rgbcol, (const char*)line));
1534                                 page.show_pts = buf_pos / 11111L;
1535                                 page.m_timeout = duration_ns / 1000000;
1536                                 m_subtitle_pages.push_back(page);
1537                                 pushSubtitles();
1538                                 gst_buffer_unref(buffer);
1539                         }
1540                 }
1541                 gst_object_unref(sink);
1542         }
1543         else
1544                 eDebug("no subtitle sink!");
1545 }
1546
1547 void eServiceMP3::pushSubtitles()
1548 {
1549         ePangoSubtitlePage page;
1550         pts_t running_pts;
1551         while ( !m_subtitle_pages.empty() )
1552         {
1553                 getPlayPosition(running_pts);
1554                 page = m_subtitle_pages.front();
1555                 gint64 diff_ms = ( page.show_pts - running_pts ) / 90;
1556                 eDebug("eServiceMP3::pushSubtitles show_pts = %lld  running_pts = %lld  diff = %lld", page.show_pts, running_pts, diff_ms);
1557                 if (diff_ms < -100)
1558                 {
1559                         GstFormat fmt = GST_FORMAT_TIME;
1560                         gint64 now;
1561                         if (gst_element_query_position(m_gst_playbin, &fmt, &now) != -1)
1562                         {
1563                                 now /= 11111;
1564                                 diff_ms = abs((now - running_pts) / 90);
1565                                 eDebug("diff < -100ms check decoder/pipeline diff: decoder: %lld, pipeline: %lld, diff: %lld", running_pts, now, diff_ms);
1566                                 if (diff_ms > 100000)
1567                                 {
1568                                         eDebug("high decoder/pipeline difference.. assume decoder has now started yet.. check again in 1sec");
1569                                         m_subtitle_sync_timer->start(1000, true);
1570                                         break;
1571                                 }
1572                         }
1573                         else
1574                                 eDebug("query position for decoder/pipeline check failed!");
1575                         eDebug("subtitle to late... drop");
1576                         m_subtitle_pages.pop_front();
1577                 }
1578                 else if ( diff_ms > 20 )
1579                 {
1580 //                      eDebug("start recheck timer");
1581                         m_subtitle_sync_timer->start(diff_ms > 1000 ? 1000 : diff_ms, true);
1582                         break;
1583                 }
1584                 else // immediate show
1585                 {
1586                         if (m_subtitle_widget)
1587                                 m_subtitle_widget->setPage(page);
1588                         m_subtitle_pages.pop_front();
1589                 }
1590         }
1591         if (m_subtitle_pages.empty())
1592                 pullSubtitle();
1593 }
1594
1595 RESULT eServiceMP3::enableSubtitles(eWidget *parent, ePyObject tuple)
1596 {
1597         ePyObject entry;
1598         int tuplesize = PyTuple_Size(tuple);
1599         int pid, type;
1600         gint text_pid = 0;
1601
1602         if (!PyTuple_Check(tuple))
1603                 goto error_out;
1604         if (tuplesize < 1)
1605                 goto error_out;
1606         entry = PyTuple_GET_ITEM(tuple, 1);
1607         if (!PyInt_Check(entry))
1608                 goto error_out;
1609         pid = PyInt_AsLong(entry);
1610         entry = PyTuple_GET_ITEM(tuple, 2);
1611         if (!PyInt_Check(entry))
1612                 goto error_out;
1613         type = PyInt_AsLong(entry);
1614
1615         if (m_currentSubtitleStream != pid)
1616         {
1617                 eSingleLocker l(m_subs_to_pull_lock);
1618                 g_object_set (G_OBJECT (m_gst_playbin), "current-text", pid, NULL);
1619                 m_currentSubtitleStream = pid;
1620                 m_subs_to_pull = 0;
1621                 m_subtitle_pages.clear();
1622         }
1623
1624         m_subtitle_widget = 0;
1625         m_subtitle_widget = new eSubtitleWidget(parent);
1626         m_subtitle_widget->resize(parent->size()); /* full size */
1627
1628         g_object_get (G_OBJECT (m_gst_playbin), "current-text", &text_pid, NULL);
1629
1630         eDebug ("eServiceMP3::switched to subtitle stream %i", text_pid);
1631
1632         return 0;
1633
1634 error_out:
1635         eDebug("eServiceMP3::enableSubtitles needs a tuple as 2nd argument!\n"
1636                 "for gst subtitles (2, subtitle_stream_count, subtitle_type)");
1637         return -1;
1638 }
1639
1640 RESULT eServiceMP3::disableSubtitles(eWidget *parent)
1641 {
1642         eDebug("eServiceMP3::disableSubtitles");
1643         m_subtitle_pages.clear();
1644         delete m_subtitle_widget;
1645         m_subtitle_widget = 0;
1646         return 0;
1647 }
1648
1649 PyObject *eServiceMP3::getCachedSubtitle()
1650 {
1651 //      eDebug("eServiceMP3::getCachedSubtitle");
1652         Py_RETURN_NONE;
1653 }
1654
1655 PyObject *eServiceMP3::getSubtitleList()
1656 {
1657         eDebug("eServiceMP3::getSubtitleList");
1658
1659         ePyObject l = PyList_New(0);
1660         int stream_count[sizeof(subtype_t)];
1661         for ( unsigned int i = 0; i < sizeof(subtype_t); i++ )
1662                 stream_count[i] = 0;
1663
1664         for (std::vector<subtitleStream>::iterator IterSubtitleStream(m_subtitleStreams.begin()); IterSubtitleStream != m_subtitleStreams.end(); ++IterSubtitleStream)
1665         {
1666                 subtype_t type = IterSubtitleStream->type;
1667                 ePyObject tuple = PyTuple_New(5);
1668                 PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(2));
1669                 PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(stream_count[type]));
1670                 PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(int(type)));
1671                 PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(0));
1672                 PyTuple_SET_ITEM(tuple, 4, PyString_FromString((IterSubtitleStream->language_code).c_str()));
1673                 PyList_Append(l, tuple);
1674                 Py_DECREF(tuple);
1675                 stream_count[type]++;
1676         }
1677         return l;
1678 }
1679
1680 RESULT eServiceMP3::streamed(ePtr<iStreamedService> &ptr)
1681 {
1682         ptr = this;
1683         return 0;
1684 }
1685
1686 PyObject *eServiceMP3::getBufferCharge()
1687 {
1688         ePyObject tuple = PyTuple_New(5);
1689         PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(m_bufferInfo.bufferPercent));
1690         PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(m_bufferInfo.avgInRate));
1691         PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(m_bufferInfo.avgOutRate));
1692         PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(m_bufferInfo.bufferingLeft));
1693         PyTuple_SET_ITEM(tuple, 4, PyInt_FromLong(m_buffer_size));
1694         return tuple;
1695 }
1696
1697 int eServiceMP3::setBufferSize(int size)
1698 {
1699         m_buffer_size = size;
1700         g_object_set (G_OBJECT (m_gst_playbin), "buffer-size", m_buffer_size, NULL);
1701         return 0;
1702 }
1703
1704 int eServiceMP3::getAC3Delay()
1705 {
1706         return ac3_delay;
1707 }
1708
1709 int eServiceMP3::getPCMDelay()
1710 {
1711         return pcm_delay;
1712 }
1713
1714 void eServiceMP3::setAC3Delay(int delay)
1715 {
1716         ac3_delay = delay;
1717         if (!m_gst_playbin || m_state != stRunning)
1718                 return;
1719         else
1720         {
1721                 GstElement *sink;
1722                 int config_delay_int = delay;
1723                 g_object_get (G_OBJECT (m_gst_playbin), "video-sink", &sink, NULL);
1724
1725                 if (sink)
1726                 {
1727                         std::string config_delay;
1728                         if(ePythonConfigQuery::getConfigValue("config.av.generalAC3delay", config_delay) == 0)
1729                                 config_delay_int += atoi(config_delay.c_str());
1730                         gst_object_unref(sink);
1731                 }
1732                 else
1733                 {
1734                         eDebug("dont apply ac3 delay when no video is running!");
1735                         config_delay_int = 0;
1736                 }
1737
1738                 g_object_get (G_OBJECT (m_gst_playbin), "audio-sink", &sink, NULL);
1739
1740                 if (sink)
1741                 {
1742                         gchar *name = gst_element_get_name(sink);
1743                         if (strstr(name, "dvbaudiosink"))
1744                                 eTSMPEGDecoder::setHwAC3Delay(config_delay_int);
1745                         g_free(name);
1746                         gst_object_unref(sink);
1747                 }
1748         }
1749 }
1750
1751 void eServiceMP3::setPCMDelay(int delay)
1752 {
1753         pcm_delay = delay;
1754         if (!m_gst_playbin || m_state != stRunning)
1755                 return;
1756         else
1757         {
1758                 GstElement *sink;
1759                 int config_delay_int = delay;
1760                 g_object_get (G_OBJECT (m_gst_playbin), "video-sink", &sink, NULL);
1761
1762                 if (sink)
1763                 {
1764                         std::string config_delay;
1765                         if(ePythonConfigQuery::getConfigValue("config.av.generalPCMdelay", config_delay) == 0)
1766                                 config_delay_int += atoi(config_delay.c_str());
1767                         gst_object_unref(sink);
1768                 }
1769                 else
1770                 {
1771                         eDebug("dont apply pcm delay when no video is running!");
1772                         config_delay_int = 0;
1773                 }
1774
1775                 g_object_get (G_OBJECT (m_gst_playbin), "audio-sink", &sink, NULL);
1776
1777                 if (sink)
1778                 {
1779                         gchar *name = gst_element_get_name(sink);
1780                         if (strstr(name, "dvbaudiosink"))
1781                                 eTSMPEGDecoder::setHwPCMDelay(config_delay_int);
1782                         else
1783                         {
1784                                 // this is realy untested..and not used yet
1785                                 gint64 offset = config_delay_int;
1786                                 offset *= 1000000; // milli to nano
1787                                 g_object_set (G_OBJECT (m_gst_playbin), "ts-offset", offset, NULL);
1788                         }
1789                         g_free(name);
1790                         gst_object_unref(sink);
1791                 }
1792         }
1793 }
1794
1795 #else
1796 #warning gstreamer not available, not building media player
1797 #endif