Merlin Music Player and iDream - initial checkin
[enigma2-plugins.git] / merlinmusicplayer / src / merlinmp3player / merlinmp3player.cpp
1 /*
2   MerlinMP3Player E2
3
4   (c) 2010 by Dr. Best  <dr.best@dreambox-tools.info>
5   Support: www.dreambox-tools.info
6
7 */
8
9 #include <lib/base/eerror.h>
10 #include <lib/base/object.h>
11 #include <lib/base/ebase.h>
12 #include <string>
13 #include "merlinmp3player.h"
14 #include <lib/service/service.h>
15 #include <lib/base/init_num.h>
16 #include <lib/base/init.h>
17 #include <gst/gst.h>
18
19 // eServiceFactoryMerlinMP3Player
20
21 eServiceFactoryMerlinMP3Player::eServiceFactoryMerlinMP3Player()
22 {
23         ePtr<eServiceCenter> sc;
24         
25         eServiceCenter::getPrivInstance(sc);
26         if (sc)
27         {
28                 std::list<std::string> extensions;
29                 extensions.push_back("mp3");
30                 sc->addServiceFactory(eServiceFactoryMerlinMP3Player::id, this, extensions);
31         }
32         m_service_info = new eStaticServiceMP3Info();
33
34 }
35
36 eServiceFactoryMerlinMP3Player::~eServiceFactoryMerlinMP3Player()
37 {
38         ePtr<eServiceCenter> sc;
39         
40         eServiceCenter::getPrivInstance(sc);
41         if (sc)
42                 sc->removeServiceFactory(eServiceFactoryMerlinMP3Player::id);
43 }
44
45 DEFINE_REF(eServiceFactoryMerlinMP3Player)
46
47         // iServiceHandler
48 RESULT eServiceFactoryMerlinMP3Player::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
49 {
50                 // check resources...
51         ptr = new eServiceMerlinMP3Player(ref);
52         return 0;
53 }
54
55 RESULT eServiceFactoryMerlinMP3Player::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr)
56 {
57         ptr=0;
58         return -1;
59 }
60
61 RESULT eServiceFactoryMerlinMP3Player::list(const eServiceReference &, ePtr<iListableService> &ptr)
62 {
63         ptr=0;
64         return -1;
65 }
66
67 RESULT eServiceFactoryMerlinMP3Player::info(const eServiceReference &ref, ePtr<iStaticServiceInformation> &ptr)
68 {
69         ptr = m_service_info;
70         return 0;
71 }
72
73 RESULT eServiceFactoryMerlinMP3Player::offlineOperations(const eServiceReference &, ePtr<iServiceOfflineOperations> &ptr)
74 {
75         ptr = 0;
76         return -1;
77 }
78
79 DEFINE_REF(eStaticServiceMP3Info)
80
81 eStaticServiceMP3Info::eStaticServiceMP3Info()
82 {
83         // nothing to to here...
84 }
85
86 RESULT eStaticServiceMP3Info::getName(const eServiceReference &ref, std::string &name)
87 {
88         size_t last = ref.path.rfind('/');
89         if (last != std::string::npos)
90                 name = ref.path.substr(last+1);
91         else
92                 name = ref.path;
93         return 0;
94 }
95
96 int eStaticServiceMP3Info::getLength(const eServiceReference &ref)
97 {
98         return -1;
99 }
100
101 // eServiceMerlinMP3Player
102
103 eServiceMerlinMP3Player::eServiceMerlinMP3Player(eServiceReference ref):  m_ref(ref), m_pump(eApp, 1)
104 {
105         m_filename = m_ref.path.c_str();
106         CONNECT(m_pump.recv_msg, eServiceMerlinMP3Player::gstPoll);
107         m_state = stIdle;
108         eDebug("eServiceMerlinMP3Player construct!");
109
110         GstElement *sink;
111         GstElement *source;
112         GstElement *decoder;
113
114         m_gst_pipeline = gst_pipeline_new ("audio-player");
115         if (!m_gst_pipeline)
116                 eWarning("failed to create pipeline");
117
118         source   = gst_element_factory_make ("filesrc", "file reader");
119         decoder   = gst_element_factory_make ("mad", "MP3 decoder");
120         sink     = gst_element_factory_make ("alsasink", "ALSA output");
121         if (m_gst_pipeline && source && decoder && sink)
122         {
123                 g_object_set (G_OBJECT (source), "location", m_filename.c_str(), NULL);
124                 gst_bin_add_many (GST_BIN (m_gst_pipeline), source, decoder, sink, NULL);
125                 gst_element_link_many (source, decoder, sink, NULL);
126                 gst_bus_set_sync_handler(gst_pipeline_get_bus (GST_PIPELINE (m_gst_pipeline)), gstBusSyncHandler, this);
127                 gst_element_set_state (m_gst_pipeline, GST_STATE_PLAYING);
128         }
129         else
130         {
131                 if (m_gst_pipeline)
132                         gst_object_unref(GST_OBJECT(m_gst_pipeline));
133                 if (source)
134                         gst_object_unref(GST_OBJECT(source));
135                 if (decoder)
136                         gst_object_unref(GST_OBJECT(decoder));
137                 if (sink)
138                         gst_object_unref(GST_OBJECT(sink));
139                 eDebug("no playing...!");
140         }
141         eDebug("eServiceMerlinMP3Player::using gstreamer with location=%s", m_filename.c_str());
142 }
143
144 eServiceMerlinMP3Player::~eServiceMerlinMP3Player()
145 {
146         if (m_state == stRunning)
147                 stop();
148
149         if (m_gst_pipeline)
150         {
151                 gst_object_unref (GST_OBJECT (m_gst_pipeline));
152                 eDebug("eServiceMerlinMP3Player destruct!");
153         }
154 }
155
156 DEFINE_REF(eServiceMerlinMP3Player);    
157
158 RESULT eServiceMerlinMP3Player::connectEvent(const Slot2<void,iPlayableService*,int> &event, ePtr<eConnection> &connection)
159 {
160         connection = new eConnection((iPlayableService*)this, m_event.connect(event));
161         return 0;
162 }
163
164 RESULT eServiceMerlinMP3Player::start()
165 {
166         assert(m_state == stIdle);
167         
168         m_state = stRunning;
169         if (m_gst_pipeline)
170         {
171                 eDebug("eServiceMerlinMP3Player::starting pipeline");
172                 gst_element_set_state (m_gst_pipeline, GST_STATE_PLAYING);
173         }
174         m_event(this, evStart);
175         return 0;
176 }
177
178 RESULT eServiceMerlinMP3Player::stop()
179 {
180         assert(m_state != stIdle);
181         if (m_state == stStopped)
182                 return -1;
183         eDebug("eServiceMerlinMP3Player::stop %s", m_filename.c_str());
184         gst_element_set_state(m_gst_pipeline, GST_STATE_NULL);
185         m_state = stStopped;
186         return 0;
187 }
188
189 RESULT eServiceMerlinMP3Player::setTarget(int target)
190 {
191         return -1;
192 }
193
194 RESULT eServiceMerlinMP3Player::pause(ePtr<iPauseableService> &ptr)
195 {
196         ptr=this;
197         return 0;
198 }
199
200 RESULT eServiceMerlinMP3Player::setSlowMotion(int ratio)
201 {
202         return -1;
203 }
204
205 RESULT eServiceMerlinMP3Player::setFastForward(int ratio)
206 {
207         return -1;
208 }
209   
210                 // iPausableService
211 RESULT eServiceMerlinMP3Player::pause()
212 {
213         if (!m_gst_pipeline)
214                 return -1;
215         gst_element_set_state(m_gst_pipeline, GST_STATE_PAUSED);
216         return 0;
217 }
218
219 RESULT eServiceMerlinMP3Player::unpause()
220 {
221         if (!m_gst_pipeline)
222                 return -1;
223         gst_element_set_state(m_gst_pipeline, GST_STATE_PLAYING);
224         return 0;
225 }
226
227         /* iSeekableService */
228 RESULT eServiceMerlinMP3Player::seek(ePtr<iSeekableService> &ptr)
229 {
230         ptr = this;
231         return 0;
232 }
233
234 RESULT eServiceMerlinMP3Player::getLength(pts_t &pts)
235 {
236         if (!m_gst_pipeline)
237                 return -1;
238         if (m_state != stRunning)
239                 return -1;
240         
241         GstFormat fmt = GST_FORMAT_TIME;
242         gint64 len;
243         
244         if (!gst_element_query_duration(m_gst_pipeline, &fmt, &len))
245                 return -1;
246         
247                 /* len is in nanoseconds. we have 90 000 pts per second. */
248         
249         pts = len / 11111;
250         return 0;
251 }
252
253 RESULT eServiceMerlinMP3Player::seekTo(pts_t to)
254 {
255         if (!m_gst_pipeline)
256                 return -1;
257
258                 /* convert pts to nanoseconds */
259         gint64 time_nanoseconds = to * 11111LL;
260         if (!gst_element_seek (m_gst_pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
261                 GST_SEEK_TYPE_SET, time_nanoseconds,
262                 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
263         {
264                 eDebug("eServiceMerlinMP3Player::SEEK failed");
265                 return -1;
266         }
267         return 0;
268 }
269
270 RESULT eServiceMerlinMP3Player::seekRelative(int direction, pts_t to)
271 {
272         if (!m_gst_pipeline)
273                 return -1;
274
275         pause();
276
277         pts_t ppos;
278         getPlayPosition(ppos);
279         ppos += to * direction;
280         if (ppos < 0)
281                 ppos = 0;
282         seekTo(ppos);
283         
284         unpause();
285
286         return 0;
287 }
288
289 RESULT eServiceMerlinMP3Player::getPlayPosition(pts_t &pts)
290 {
291         if (!m_gst_pipeline)
292                 return -1;
293         if (m_state != stRunning)
294                 return -1;
295         
296         GstFormat fmt = GST_FORMAT_TIME;
297         gint64 len;
298         
299         if (!gst_element_query_position(m_gst_pipeline, &fmt, &len))
300                 return -1;
301         
302                 /* len is in nanoseconds. we have 90 000 pts per second. */
303         pts = len / 11111;
304         return 0;
305 }
306
307 RESULT eServiceMerlinMP3Player::setTrickmode(int trick)
308 {
309                 /* trickmode currently doesn't make any sense for us. */
310         return -1;
311 }
312
313 RESULT eServiceMerlinMP3Player::isCurrentlySeekable()
314 {
315         return 1;
316 }
317
318 RESULT eServiceMerlinMP3Player::info(ePtr<iServiceInformation>&i)
319 {
320         i = this;
321         return 0;
322 }
323
324 RESULT eServiceMerlinMP3Player::getName(std::string &name)
325 {
326         name = m_filename;
327         size_t n = name.rfind('/');
328         if (n != std::string::npos)
329                 name = name.substr(n + 1);
330         return 0;
331 }
332
333 int eServiceMerlinMP3Player::getInfo(int w)
334 {
335         return resNA;
336 }
337
338 std::string eServiceMerlinMP3Player::getInfoString(int w)
339 {
340         return "";
341 }
342
343 void eServiceMerlinMP3Player::gstBusCall(GstBus *bus, GstMessage *msg)
344 {
345         switch (GST_MESSAGE_TYPE (msg))
346         {
347                 case GST_MESSAGE_EOS:
348                         m_event((iPlayableService*)this, evEOF);
349                         break;
350                 case GST_MESSAGE_STATE_CHANGED:
351                 {
352                         if(GST_MESSAGE_SRC(msg) != GST_OBJECT(m_gst_pipeline))
353                                 break;
354                         GstState old_state, new_state;
355                         gst_message_parse_state_changed(msg, &old_state, &new_state, NULL);
356                         if(old_state == new_state)
357                                 break;
358                         eDebug("eServiceMerlinMP3Player::state transition %s -> %s", gst_element_state_get_name(old_state), gst_element_state_get_name(new_state));
359                         break;
360                 }
361                 case GST_MESSAGE_ERROR:
362                 {
363                         gchar *debug;
364                         GError *err;
365                         gst_message_parse_error (msg, &err, &debug);
366                         g_free (debug);
367                         eWarning("Gstreamer error: %s", err->message);
368                         g_error_free(err);
369                         break;
370                 }
371                 default:
372                         break;
373         }
374 }
375
376 GstBusSyncReply eServiceMerlinMP3Player::gstBusSyncHandler(GstBus *bus, GstMessage *message, gpointer user_data)
377 {
378         eServiceMerlinMP3Player *_this = (eServiceMerlinMP3Player*)user_data;
379         _this->m_pump.send(1);
380                 /* wake */
381         return GST_BUS_PASS;
382 }
383
384 void eServiceMerlinMP3Player::gstPoll(const int&)
385 {
386         usleep(1);
387         GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (m_gst_pipeline));
388         GstMessage *message;
389         while ((message = gst_bus_pop (bus)))
390         {
391                 gstBusCall(bus, message);
392                 gst_message_unref (message);
393         }
394 }
395
396
397 eAutoInitPtr<eServiceFactoryMerlinMP3Player> init_eServiceFactoryMerlinMP3Player(eAutoInitNumbers::service+1, "eServiceFactoryMerlinMP3Player");
398
399 PyMODINIT_FUNC
400 initmerlinmp3player(void)
401 {
402         Py_InitModule("merlinmp3player", NULL);
403 }
404