add servicets.so sources
[enigma2-plugins.git] / vlcplayer / src / servicets / servicets.cpp
1
2
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6 #include <string>
7 #include <sys/socket.h>
8 #include <netdb.h>
9 #include <signal.h>
10 #include "servicets.h"
11 #include <lib/base/eerror.h>
12 #include <lib/base/object.h>
13 #include <lib/base/ebase.h>
14 #include <servicets.h>
15 #include <lib/service/service.h>
16 #include <lib/base/init_num.h>
17 #include <lib/base/init.h>
18 #include <lib/dvb/decoder.h>
19
20 #define MAX(a,b) ((a) > (b) ? (a) : (b))
21
22 // eServiceFactoryTS
23
24 eServiceFactoryTS::eServiceFactoryTS()
25 {
26         ePtr<eServiceCenter> sc;
27         
28         eServiceCenter::getPrivInstance(sc);
29         if (sc)
30         {
31                 std::list<std::string> extensions;
32                 sc->addServiceFactory(eServiceFactoryTS::id, this, extensions);
33         }
34
35         m_service_info = new eStaticServiceTSInfo();
36 }
37
38 eServiceFactoryTS::~eServiceFactoryTS()
39 {
40         ePtr<eServiceCenter> sc;
41         
42         eServiceCenter::getPrivInstance(sc);
43         if (sc)
44                 sc->removeServiceFactory(eServiceFactoryTS::id);
45 }
46
47 DEFINE_REF(eServiceFactoryTS)
48
49         // iServiceHandler
50 RESULT eServiceFactoryTS::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
51 {
52                 // check resources...
53         ptr = new eServiceTS(ref);
54         return 0;
55 }
56
57 RESULT eServiceFactoryTS::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr)
58 {
59         ptr=0;
60         return -1;
61 }
62
63 RESULT eServiceFactoryTS::list(const eServiceReference &, ePtr<iListableService> &ptr)
64 {
65         ptr=0;
66         return -1;
67 }
68
69 RESULT eServiceFactoryTS::info(const eServiceReference &ref, ePtr<iStaticServiceInformation> &ptr)
70 {
71         ptr = m_service_info;
72         return 0;
73 }
74
75 RESULT eServiceFactoryTS::offlineOperations(const eServiceReference &, ePtr<iServiceOfflineOperations> &ptr)
76 {
77         ptr = 0;
78         return -1;
79 }
80
81
82 // eStaticServiceTSInfo
83 DEFINE_REF(eStaticServiceTSInfo)
84
85 eStaticServiceTSInfo::eStaticServiceTSInfo()
86 {
87 }
88
89 RESULT eStaticServiceTSInfo::getName(const eServiceReference &ref, std::string &name)
90 {
91         size_t last = ref.path.rfind('/');
92         if (last != std::string::npos)
93                 name = ref.path.substr(last+1);
94         else
95                 name = ref.path;
96         return 0;
97 }
98
99 int eStaticServiceTSInfo::getLength(const eServiceReference &ref)
100 {
101         return -1;
102 }
103
104 // eServiceTS
105
106 eServiceTS::eServiceTS(const eServiceReference &url): m_pump(eApp, 1)
107 {
108         eDebug("ServiceTS construct!");
109         m_filename = url.path.c_str();
110         m_vpid = url.getData(0) == 0 ? 0x44 : url.getData(0);
111         m_apid = url.getData(1) == 0 ? 0x45 : url.getData(1);
112         m_state = stIdle;
113 }
114
115 eServiceTS::~eServiceTS()
116 {
117         eDebug("ServiceTS destruct!");
118         if (m_state == stRunning)
119                 stop();
120 }
121
122 DEFINE_REF(eServiceTS); 
123
124 size_t crop(char *buf)
125 {
126         size_t len = strlen(buf) - 1;
127         while (len > 0 && (buf[len] == '\r' || buf[len] == '\n')) {
128                 buf[len--] = '\0';
129         }
130         return len;
131 }
132
133 static int getline(char** pbuffer, size_t* pbufsize, int fd) 
134 {
135         size_t i = 0;
136         int rc;
137         while (true) {
138                 if (i >= *pbufsize) {
139                         char *newbuf = (char*)realloc(*pbuffer, (*pbufsize)+1024);
140                         if (newbuf == NULL)
141                                 return -ENOMEM;
142                         *pbuffer = newbuf;
143                         *pbufsize = (*pbufsize)+1024;
144                 }
145                 rc = ::read(fd, (*pbuffer)+i, 1);
146                 if (rc <= 0 || (*pbuffer)[i] == '\n')
147                 {
148                         (*pbuffer)[i] = '\0';
149                         return rc <= 0 ? -1 : i;
150                 }
151                 if ((*pbuffer)[i] != '\r') i++;
152         }
153 }
154
155 int eServiceTS::openHttpConnection(std::string url)
156 {
157         std::string host;
158         int port = 80;
159         std::string uri;
160
161         int slash = url.find("/", 7);
162         if (slash > 0) {
163                 host = url.substr(7, slash-7);
164                 uri = url.substr(slash, url.length()-slash);
165         } else {
166                 host = url.substr(7, url.length()-7);
167                 uri = "";
168         }
169         int dp = host.find(":");
170         if (dp == 0) {
171                 port = atoi(host.substr(1, host.length()-1).c_str());
172                 host = "localhost";
173         } else if (dp > 0) {
174                 port = atoi(host.substr(dp+1, host.length()-dp-1).c_str());
175                 host = host.substr(0, dp);
176         }
177
178         struct hostent* h = gethostbyname(host.c_str());
179         if (h == NULL || h->h_addr_list == NULL)
180                 return -1;
181         int fd = socket(PF_INET, SOCK_STREAM, 0);
182         if (fd == -1)
183                 return -1;
184
185         struct sockaddr_in addr;
186         addr.sin_family = AF_INET;
187         addr.sin_addr.s_addr = *((in_addr_t*)h->h_addr_list[0]);
188         addr.sin_port = htons(port);
189
190         eDebug("connecting to %s", url.c_str());
191
192         if (connect(fd, (sockaddr*)&addr, sizeof(addr)) == -1) {
193                 std::string msg = "connect failed for: " + url;
194                 eDebug(msg.c_str());
195                 return -1;
196         }
197
198         std::string request = "GET ";
199         request.append(uri).append(" HTTP/1.1\n");
200         request.append("Host: ").append(host).append("\n");
201         request.append("Accept: */*\n");
202         request.append("Connection: close\n");
203         request.append("\n");
204         //eDebug(request.c_str());
205         write(fd, request.c_str(), request.length());
206
207         int rc;
208         size_t buflen = 1000;
209         char* linebuf = (char*)malloc(1000);
210
211         rc = getline(&linebuf, &buflen, fd);
212         //eDebug("RECV(%d): %s", rc, linebuf);
213         if (rc <= 0)
214         {
215                 close(fd);
216                 free(linebuf);
217                 return -1;
218         }
219
220         char proto[100];
221         int statuscode = 0;
222         char statusmsg[100];
223         rc = sscanf(linebuf, "%99s %d %99s", proto, &statuscode, statusmsg);
224         if (rc != 3 || statuscode != 200) {
225                 eDebug("wrong response: \"200 OK\" expected.");
226                 free(linebuf);
227                 close(fd);
228                 return -1;
229         }
230         eDebug("proto=%s, code=%d, msg=%s", proto, statuscode, statusmsg);
231         while (rc > 0)
232         {
233                 rc = getline(&linebuf, &buflen, fd);
234                 //eDebug("RECV(%d): %s", rc, linebuf);
235         }
236         free(linebuf);
237
238         return fd;
239 }
240
241 RESULT eServiceTS::connectEvent(const Slot2<void,iPlayableService*,int> &event, ePtr<eConnection> &connection)
242 {
243         connection = new eConnection((iPlayableService*)this, m_event.connect(event));
244         return 0;
245 }
246
247 RESULT eServiceTS::start()
248 {
249         ePtr<eDVBResourceManager> rmgr;
250         eDVBResourceManager::getInstance(rmgr);
251         if (rmgr->allocateDemux(NULL, m_decodedemux, iDVBChannel::capDecode) != 0) {
252                 eDebug("Cannot allocate decode-demux");
253                 return 1;
254         }
255         if (m_decodedemux->get().getMPEGDecoder(m_decoder, 1) != 0) {
256                 eDebug("Cannot allocate MPEGDecoder");
257                 return 1;
258         }
259         m_decodedemux->get().setSourcePVR(0);
260         m_decoder->setVideoPID(m_vpid, eDVBVideo::MPEG2);
261         m_decoder->setAudioPID(m_apid, eDVBAudio::aMPEG);
262         m_streamthread = new eStreamThread();
263         CONNECT(m_streamthread->m_event, eServiceTS::recv_event);
264         m_decoder->freeze(0);
265         m_decoder->preroll();
266         if (unpause() != 0) return -1;
267         m_state = stRunning;
268         m_event(this, evStart);
269         return 0;
270 }
271
272 RESULT eServiceTS::stop()
273 {
274         if (m_state != stRunning)
275                 return -1;
276         printf("TS: %s stop\n", m_filename.c_str());
277         m_streamthread->stop();
278         m_decodedemux->get().flush();
279         m_state = stStopped;
280         return 0;
281 }
282
283 void eServiceTS::recv_event(int evt)
284 {
285         eDebug("eServiceTS::recv_event: %d", evt);
286         switch (evt) {
287         case eStreamThread::evtEOS:
288                 m_decodedemux->get().flush();
289                 m_state = stStopped;
290                 m_event((iPlayableService*)this, evEOF);
291                 break;
292         case eStreamThread::evtReadError:
293         case eStreamThread::evtWriteError:
294                 m_decoder->freeze(0);
295                 m_state = stStopped;
296                 m_event((iPlayableService*)this, evEOF);
297         }
298 }
299
300 RESULT eServiceTS::setTarget(int target)
301 {
302         return -1;
303 }
304
305 RESULT eServiceTS::pause(ePtr<iPauseableService> &ptr)
306 {
307         ptr=this;
308         return 0;
309 }
310
311 RESULT eServiceTS::setSlowMotion(int ratio)
312 {
313         return -1;
314 }
315
316 RESULT eServiceTS::setFastForward(int ratio)
317 {
318         return -1;
319 }
320   
321                 // iPausableService
322 RESULT eServiceTS::pause()
323 {
324         m_streamthread->stop();
325         m_decoder->freeze(0);
326         return 0;
327 }
328
329 RESULT eServiceTS::unpause()
330 {
331         int is_streaming = !strncmp(m_filename.c_str(), "http://", 7);
332         int srcfd = -1;
333         if (is_streaming) {
334                 srcfd = openHttpConnection(m_filename);
335         } else {
336                 srcfd = ::open(m_filename.c_str(), O_RDONLY);
337         }
338         if (srcfd < 0) {
339                 eDebug("Cannot open source stream: %s", m_filename.c_str());
340                 return 1;
341         }
342         
343         int destfd = ::open("/dev/misc/pvr", O_WRONLY);
344         if (destfd < 0) {
345                 eDebug("Cannot open source stream: %s", m_filename.c_str());
346                 ::close(srcfd);
347                 return 1;
348         }
349         m_decodedemux->get().flush();
350         m_streamthread->start(srcfd, destfd);
351         // let the video buffer fill up a bit
352         usleep(200*1000);
353         m_decoder->unfreeze();
354         return 0;
355 }
356
357         /* iSeekableService */
358 RESULT eServiceTS::seek(ePtr<iSeekableService> &ptr)
359 {
360         ptr = this;
361         return 0;
362 }
363
364 RESULT eServiceTS::getLength(pts_t &pts)
365 {
366         return 0;
367 }
368
369 RESULT eServiceTS::seekTo(pts_t to)
370 {
371         return 0;
372 }
373
374 RESULT eServiceTS::seekRelative(int direction, pts_t to)
375 {
376         return 0;
377 }
378
379 RESULT eServiceTS::getPlayPosition(pts_t &pts)
380 {
381         return 0;
382 }
383
384 RESULT eServiceTS::setTrickmode(int trick)
385 {
386         return -1;
387 }
388
389
390 RESULT eServiceTS::isCurrentlySeekable()
391 {
392         return 1;
393 }
394
395 RESULT eServiceTS::info(ePtr<iServiceInformation>&i)
396 {
397         i = this;
398         return 0;
399 }
400
401 RESULT eServiceTS::getName(std::string &name)
402 {
403         name = m_filename;
404         size_t n = name.rfind('/');
405         if (n != std::string::npos)
406                 name = name.substr(n + 1);
407         return 0;
408 }
409
410 int eServiceTS::getInfo(int w)
411 {
412         return resNA;
413 }
414
415 std::string eServiceTS::getInfoString(int w)
416 {
417         return "";
418 }
419
420 DEFINE_REF(eStreamThread)
421
422 eStreamThread::eStreamThread(): m_messagepump(eApp, 0) {
423         CONNECT(m_messagepump.recv_msg, eStreamThread::recvEvent);
424 }
425 eStreamThread::~eStreamThread() {
426 }
427
428 void eStreamThread::start(int srcfd, int destfd) {
429         m_srcfd = srcfd;
430         m_destfd = destfd;
431         m_stop = false;
432         run(IOPRIO_CLASS_RT);
433 }
434 void eStreamThread::stop() {
435         m_stop = true;
436         kill();
437 }
438
439 void eStreamThread::recvEvent(const int &evt)
440 {
441         m_event(evt);
442 }
443
444 void eStreamThread::thread() {
445         const int bufsize = 60000;
446         unsigned char buf[bufsize];
447         bool eof = false;
448         fd_set rfds;
449         fd_set wfds;
450         struct timeval timeout;
451         int rc,r,w,maxfd;
452         
453         r = w = 0;
454         hasStarted();
455         eDebug("eStreamThread started");
456         while (!m_stop) {
457                 pthread_testcancel();
458                 FD_ZERO(&rfds);
459                 FD_ZERO(&wfds);
460                 maxfd = 0;
461                 timeout.tv_sec = 1;
462                 timeout.tv_usec = 0;
463                 if (r < bufsize) {
464                         FD_SET(m_srcfd, &rfds);
465                         maxfd = MAX(maxfd, m_srcfd);
466                 }
467                 if (w < r) {
468                         FD_SET(m_destfd, &wfds);
469                         maxfd = MAX(maxfd, m_destfd);
470                 }
471                 rc = select(maxfd+1, &rfds, &wfds, NULL, &timeout);
472                 if (rc == 0) {
473                         eDebug("eStreamThread::thread: timeout!");
474                         continue;
475                 }
476                 if (rc < 0) {
477                         eDebug("eStreamThread::thread: error in select (%d)", errno);
478                         break;
479                 }
480                 if (FD_ISSET(m_srcfd, &rfds)) {
481                         rc = ::read(m_srcfd, buf+r, bufsize - r);
482                         if (rc < 0) {
483                                 eDebug("eStreamThread::thread: error in read (%d)", errno);
484                                 m_messagepump.send(evtReadError);
485                                 break;
486                         } else if (rc == 0) {
487                                 eof = true;
488                         } else {
489                                 r += rc;
490                                 if (r == bufsize) eDebug("eStreamThread::thread: buffer full");
491                         }
492                 }
493                 if (FD_ISSET(m_destfd, &wfds) && (w < r)) {
494                         rc = ::write(m_destfd, buf+w, r-w);
495                         if (rc < 0) {
496                                 eDebug("eStreamThread::thread: error in write (%d)", errno);
497                                 m_messagepump.send(evtWriteError);
498                                 break;
499                         }
500                         w += rc;
501                         //eDebug("eStreamThread::thread: buffer r=%d w=%d",r,w);
502                         if (w == r) w = r = 0;
503                 }
504                 if (eof && (r==w)) {
505                         ::close(m_destfd);
506                         m_destfd = -1;
507                         ::close(m_srcfd);
508                         m_srcfd = -1;
509                         m_messagepump.send(evtEOS);
510                         break;
511                 }
512         }
513         eDebug("eStreamThread end");
514 }
515
516 void eStreamThread::thread_finished() {
517         if (m_srcfd >= 0) ::close(m_srcfd);
518         if (m_destfd >= 0) ::close(m_destfd);
519         eDebug("eStreamThread closed");
520 }
521
522 eAutoInitPtr<eServiceFactoryTS> init_eServiceFactoryTS(eAutoInitNumbers::service+1, "eServiceFactoryTS");
523
524 PyMODINIT_FUNC
525 initservicets(void)
526 {
527         Py_InitModule("servicets", NULL);
528 }