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