remove unused import
[enigma2-plugins.git] / vlcplayer / src / servicets / servicets.cpp
1 /*******************************************************************************
2  VLC Player Plugin by A. Lätsch 2007
3
4  This is free software; you can redistribute it and/or modify it under
5  the terms of the GNU General Public License as published by the Free
6  Software Foundation; either version 2, or (at your option) any later
7  version.
8 ********************************************************************************/
9
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <fcntl.h>
13 #include <string>
14 #include <sys/socket.h>
15 #include <netdb.h>
16 #include <signal.h>
17 #include <time.h>
18 #include "servicets.h"
19 #include <lib/base/eerror.h>
20 #include <lib/base/object.h>
21 #include <lib/base/ebase.h>
22 #include <servicets.h>
23 #include <lib/service/service.h>
24 #include <lib/base/init_num.h>
25 #include <lib/base/init.h>
26 #include <lib/dvb/decoder.h>
27
28 #include <lib/dvb/pmt.h>
29
30 #define MAX(a,b) ((a) > (b) ? (a) : (b))
31
32 /********************************************************************/
33 /* eServiceFactoryTS                                                */
34 /********************************************************************/
35
36 eServiceFactoryTS::eServiceFactoryTS()
37 {
38         ePtr<eServiceCenter> sc;
39
40         eServiceCenter::getPrivInstance(sc);
41         if (sc)
42         {
43                 std::list<std::string> extensions;
44                 sc->addServiceFactory(eServiceFactoryTS::id, this, extensions);
45         }
46 }
47
48 eServiceFactoryTS::~eServiceFactoryTS()
49 {
50         ePtr<eServiceCenter> sc;
51
52         eServiceCenter::getPrivInstance(sc);
53         if (sc)
54                 sc->removeServiceFactory(eServiceFactoryTS::id);
55 }
56
57 DEFINE_REF(eServiceFactoryTS)
58
59 // iServiceHandler
60 RESULT eServiceFactoryTS::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
61 {
62         ptr = new eServiceTS(ref);
63         return 0;
64 }
65
66 RESULT eServiceFactoryTS::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr)
67 {
68         ptr=0;
69         return -1;
70 }
71
72 RESULT eServiceFactoryTS::list(const eServiceReference &, ePtr<iListableService> &ptr)
73 {
74         ptr=0;
75         return -1;
76 }
77
78 RESULT eServiceFactoryTS::info(const eServiceReference &ref, ePtr<iStaticServiceInformation> &ptr)
79 {
80         ptr = 0;
81         return -1;
82 }
83
84 RESULT eServiceFactoryTS::offlineOperations(const eServiceReference &, ePtr<iServiceOfflineOperations> &ptr)
85 {
86         ptr = 0;
87         return -1;
88 }
89
90
91 /********************************************************************/
92 /* TSAudioInfo                                            */
93 /********************************************************************/
94 DEFINE_REF(TSAudioInfo);
95
96 void TSAudioInfo::addAudio(int pid, std::string lang, std::string desc, int type) {
97         StreamInfo as;
98         as.description = desc;
99         as.language = lang;
100         as.pid = pid;
101         as.type = type;
102         audioStreams.push_back(as);
103 }
104
105
106 /********************************************************************/
107 /* eServiceTS                                                       */
108 /********************************************************************/
109
110 eServiceTS::eServiceTS(const eServiceReference &url): m_pump(eApp, 1)
111 {
112         eDebug("ServiceTS construct!");
113         m_filename = url.path.c_str();
114         m_vpid = url.getData(0) == 0 ? 0x44 : url.getData(0);
115         m_apid = url.getData(1) == 0 ? 0x45 : url.getData(1);
116         m_state = stIdle;
117         m_audioInfo = 0;
118 }
119
120 eServiceTS::~eServiceTS()
121 {
122         eDebug("ServiceTS destruct!");
123         if (m_state == stRunning)
124                 stop();
125 }
126
127 DEFINE_REF(eServiceTS);
128
129 size_t crop(char *buf)
130 {
131         size_t len = strlen(buf) - 1;
132         while (len > 0 && (buf[len] == '\r' || buf[len] == '\n')) {
133                 buf[len--] = '\0';
134         }
135         return len;
136 }
137
138 static int getline(char** pbuffer, size_t* pbufsize, int fd)
139 {
140         size_t i = 0;
141         int rc;
142         while (true) {
143                 if (i >= *pbufsize) {
144                         char *newbuf = (char*)realloc(*pbuffer, (*pbufsize)+1024);
145                         if (newbuf == NULL)
146                                 return -ENOMEM;
147                         *pbuffer = newbuf;
148                         *pbufsize = (*pbufsize)+1024;
149                 }
150                 rc = ::read(fd, (*pbuffer)+i, 1);
151                 if (rc <= 0 || (*pbuffer)[i] == '\n')
152                 {
153                         (*pbuffer)[i] = '\0';
154                         return rc <= 0 ? -1 : i;
155                 }
156                 if ((*pbuffer)[i] != '\r') i++;
157         }
158 }
159
160 int eServiceTS::openHttpConnection(std::string url)
161 {
162         std::string host;
163         int port = 80;
164         std::string uri;
165
166         int slash = url.find("/", 7);
167         if (slash > 0) {
168                 host = url.substr(7, slash-7);
169                 uri = url.substr(slash, url.length()-slash);
170         } else {
171                 host = url.substr(7, url.length()-7);
172                 uri = "";
173         }
174         int dp = host.find(":");
175         if (dp == 0) {
176                 port = atoi(host.substr(1, host.length()-1).c_str());
177                 host = "localhost";
178         } else if (dp > 0) {
179                 port = atoi(host.substr(dp+1, host.length()-dp-1).c_str());
180                 host = host.substr(0, dp);
181         }
182
183         struct hostent* h = gethostbyname(host.c_str());
184         if (h == NULL || h->h_addr_list == NULL)
185                 return -1;
186         int fd = socket(PF_INET, SOCK_STREAM, 0);
187         if (fd == -1)
188                 return -1;
189
190         struct sockaddr_in addr;
191         addr.sin_family = AF_INET;
192         addr.sin_addr.s_addr = *((in_addr_t*)h->h_addr_list[0]);
193         addr.sin_port = htons(port);
194
195         eDebug("connecting to %s", url.c_str());
196
197         if (connect(fd, (sockaddr*)&addr, sizeof(addr)) == -1) {
198                 std::string msg = "connect failed for: " + url;
199                 eDebug(msg.c_str());
200                 return -1;
201         }
202
203         std::string request = "GET ";
204         request.append(uri).append(" HTTP/1.1\n");
205         request.append("Host: ").append(host).append("\n");
206         request.append("Accept: */*\n");
207         request.append("Connection: close\n");
208         request.append("\n");
209         //eDebug(request.c_str());
210         write(fd, request.c_str(), request.length());
211
212         int rc;
213         size_t buflen = 1000;
214         char* linebuf = (char*)malloc(1000);
215
216         rc = getline(&linebuf, &buflen, fd);
217         //eDebug("RECV(%d): %s", rc, linebuf);
218         if (rc <= 0)
219         {
220                 close(fd);
221                 free(linebuf);
222                 return -1;
223         }
224
225         char proto[100];
226         int statuscode = 0;
227         char statusmsg[100];
228         rc = sscanf(linebuf, "%99s %d %99s", proto, &statuscode, statusmsg);
229         if (rc != 3 || statuscode != 200) {
230                 eDebug("wrong response: \"200 OK\" expected.");
231                 free(linebuf);
232                 close(fd);
233                 return -1;
234         }
235         eDebug("proto=%s, code=%d, msg=%s", proto, statuscode, statusmsg);
236         while (rc > 0)
237         {
238                 rc = getline(&linebuf, &buflen, fd);
239                 //eDebug("RECV(%d): %s", rc, linebuf);
240         }
241         free(linebuf);
242
243         return fd;
244 }
245
246 RESULT eServiceTS::connectEvent(const Slot2<void,iPlayableService*,int> &event, ePtr<eConnection> &connection)
247 {
248         connection = new eConnection((iPlayableService*)this, m_event.connect(event));
249         return 0;
250 }
251
252 RESULT eServiceTS::start()
253 {
254         ePtr<eDVBResourceManager> rmgr;
255         eDVBResourceManager::getInstance(rmgr);
256         eDVBChannel dvbChannel(rmgr, 0);
257         if (dvbChannel.getDemux(m_decodedemux, iDVBChannel::capDecode) != 0) {
258                 eDebug("Cannot allocate decode-demux");
259                 return 1;
260         }
261         if (m_decodedemux->getMPEGDecoder(m_decoder, 1) != 0) {
262                 eDebug("Cannot allocate MPEGDecoder");
263                 return 1;
264         }
265         m_decoder->setVideoPID(m_vpid, eDVBVideo::MPEG2);
266         m_decoder->setAudioPID(m_apid, eDVBAudio::aMPEG);
267         m_streamthread = new eStreamThread();
268         CONNECT(m_streamthread->m_event, eServiceTS::recv_event);
269         m_decoder->freeze(0);
270         m_decoder->preroll();
271         if (unpause() != 0) return -1;
272         m_state = stRunning;
273         m_event(this, evStart);
274         return 0;
275 }
276
277 RESULT eServiceTS::stop()
278 {
279         if (m_state != stRunning)
280                 return -1;
281         printf("TS: %s stop\n", m_filename.c_str());
282         m_streamthread->stop();
283         m_decodedemux->flush();
284         m_state = stStopped;
285         m_audioInfo = 0;
286         return 0;
287 }
288
289 void eServiceTS::recv_event(int evt)
290 {
291         eDebug("eServiceTS::recv_event: %d", evt);
292         switch (evt) {
293         case eStreamThread::evtEOS:
294                 m_decodedemux->flush();
295                 m_state = stStopped;
296                 m_event((iPlayableService*)this, evEOF);
297                 break;
298         case eStreamThread::evtReadError:
299         case eStreamThread::evtWriteError:
300                 m_decoder->freeze(0);
301                 m_state = stStopped;
302                 m_event((iPlayableService*)this, evEOF);
303                 break;
304         case eStreamThread::evtSOS:
305                 m_event((iPlayableService*)this, evSOF);
306                 break;
307         case eStreamThread::evtStreamInfo:
308                 bool wasnull = !m_audioInfo;
309                 m_streamthread->getAudioInfo(m_audioInfo);
310                 if (m_audioInfo)
311                         eDebug("[servicets] %d audiostreams found", m_audioInfo->audioStreams.size());
312                 if (m_audioInfo && wasnull) {
313                         int sel = getCurrentTrack();
314                         if (sel < 0)
315                                 selectTrack(0);
316                         else if (m_audioInfo->audioStreams[sel].type != eDVBAudio::aMPEG)
317                                 selectTrack(sel);
318                 }
319                 break;
320         }
321 }
322
323 RESULT eServiceTS::pause(ePtr<iPauseableService> &ptr)
324 {
325         ptr = this;
326         return 0;
327 }
328
329 // iPausableService
330 RESULT eServiceTS::pause()
331 {
332         m_streamthread->stop();
333         m_decoder->freeze(0);
334         return 0;
335 }
336
337 RESULT eServiceTS::unpause()
338 {
339         int is_streaming = !strncmp(m_filename.c_str(), "http://", 7);
340         int srcfd = -1;
341         if (is_streaming) {
342                 srcfd = openHttpConnection(m_filename);
343         } else {
344                 srcfd = ::open(m_filename.c_str(), O_RDONLY);
345         }
346         if (srcfd < 0) {
347                 eDebug("Cannot open source stream: %s", m_filename.c_str());
348                 return 1;
349         }
350
351         int destfd = ::open("/dev/misc/pvr", O_WRONLY);
352         if (destfd < 0) {
353                 eDebug("Cannot open /dev/misc/pvr");
354                 ::close(srcfd);
355                 return 1;
356         }
357         m_decodedemux->flush();
358         m_streamthread->start(srcfd, destfd);
359         m_decoder->unfreeze();
360         return 0;
361 }
362
363 // iSeekableService
364 RESULT eServiceTS::seek(ePtr<iSeekableService> &ptr)
365 {
366         ptr = this;
367         return 0;
368 }
369
370 RESULT eServiceTS::getLength(pts_t &pts)
371 {
372         return 0;
373 }
374
375 RESULT eServiceTS::seekTo(pts_t to)
376 {
377         return 0;
378 }
379
380 RESULT eServiceTS::seekRelative(int direction, pts_t to)
381 {
382         return 0;
383 }
384
385 RESULT eServiceTS::getPlayPosition(pts_t &pts)
386 {
387         return 0;
388 }
389
390 RESULT eServiceTS::setTrickmode(int trick)
391 {
392         return -1;
393 }
394
395 RESULT eServiceTS::isCurrentlySeekable()
396 {
397         return 1;
398 }
399
400 RESULT eServiceTS::info(ePtr<iServiceInformation>&i)
401 {
402         i = this;
403         return 0;
404 }
405
406 RESULT eServiceTS::getName(std::string &name)
407 {
408         name = m_filename;
409         size_t n = name.rfind('/');
410         if (n != std::string::npos)
411                 name = name.substr(n + 1);
412         return 0;
413 }
414
415 int eServiceTS::getInfo(int w)
416 {
417         return resNA;
418 }
419
420 std::string eServiceTS::getInfoString(int w)
421 {
422         return "";
423 }
424
425 int eServiceTS::getNumberOfTracks() {
426         if (m_audioInfo)
427                 return (int)m_audioInfo->audioStreams.size();
428         else
429                 return 0;
430 }
431
432 RESULT eServiceTS::selectTrack(unsigned int i) {
433         if (m_audioInfo) {
434                 m_apid = m_audioInfo->audioStreams[i].pid;
435                 eDebug("[servicets] audio track %d PID 0x%02x type %d\n", i, m_apid, m_audioInfo->audioStreams[i].type);
436                 m_decoder->setAudioPID(m_apid, m_audioInfo->audioStreams[i].type);
437                 if (m_state == stRunning)
438                         m_decoder->preroll();
439                 return 0;
440         } else {
441                 return -1;
442         }
443 }
444
445 RESULT eServiceTS::getTrackInfo(struct iAudioTrackInfo &info, unsigned int n) {
446         if (m_audioInfo) {
447                 info.m_pid = m_audioInfo->audioStreams[n].pid;
448                 info.m_description = m_audioInfo->audioStreams[n].description;
449                 info.m_language = m_audioInfo->audioStreams[n].language;
450                 return 0;
451         } else {
452                 return -1;
453         }
454 }
455
456 int eServiceTS::getCurrentTrack() {
457         if (m_audioInfo) {
458                 for (size_t i = 0; i < m_audioInfo->audioStreams.size(); i++) {
459                         if (m_apid == m_audioInfo->audioStreams[i].pid) {
460                                 return i;
461                         }
462                 }
463         }
464         return -1;
465 }
466
467 /********************************************************************/
468 /* eStreamThread                                                       */
469 /********************************************************************/
470
471 DEFINE_REF(eStreamThread)
472
473 eStreamThread::eStreamThread(): m_messagepump(eApp, 0) {
474         CONNECT(m_messagepump.recv_msg, eStreamThread::recvEvent);
475 }
476 eStreamThread::~eStreamThread() {
477 }
478
479 void eStreamThread::start(int srcfd, int destfd) {
480         m_srcfd = srcfd;
481         m_destfd = destfd;
482         m_stop = false;
483         m_audioInfo = 0;
484         run(IOPRIO_CLASS_RT);
485 }
486 void eStreamThread::stop() {
487         m_stop = true;
488         kill();
489 }
490
491 void eStreamThread::recvEvent(const int &evt)
492 {
493         m_event(evt);
494 }
495
496 RESULT eStreamThread::getAudioInfo(ePtr<TSAudioInfo> &ptr)
497 {
498         ptr = m_audioInfo;
499         return 0;
500 }
501
502 #define REGISTRATION_DESCRIPTOR 5
503 #define LANGUAGE_DESCRIPTOR 10
504
505 std::string eStreamThread::getDescriptor(unsigned char buf[], int buflen, int type)
506 {
507         int desc_len;
508         while (buflen > 1) {
509                 desc_len = buf[1];
510                 if (buf[0] == type) {
511                         char str[21];
512                         if (desc_len > 20) desc_len = 20;
513                         strncpy(str, (char*)buf+2, desc_len);
514                         str[desc_len] = '\0';
515                         return std::string(str);
516                 } else {
517                         buflen -= desc_len+2;
518                         buf += desc_len+2;
519                 }
520         }
521         return "";
522 }
523
524 bool eStreamThread::scanAudioInfo(unsigned char buf[], int len)
525 {
526         if (len < 1880)
527                 return false;
528
529         int adaptfield, pmtpid, offset;
530         unsigned char pmt[1188];
531         int pmtsize = 0;
532
533         for (int a=0; a < len - 188*4; a++) {
534                 if ( buf[a] != 0x47 || buf[a + 188] != 0x47 || buf[a + 376] != 0x47 )
535                         continue; // TS Header
536
537                 if ((0x40 & buf[a + 1]) == 0) // start
538                         continue;
539
540                 if ((0xC0 & buf[a + 3]) != 0) // scrambling
541                         continue;
542
543                 adaptfield = (0x30 & buf[a + 3]) >> 4;
544
545                 if ((adaptfield & 1) == 0) // adapt - no payload
546                         continue;
547
548                 offset = adaptfield == 3 ? 1 + (0xFF & buf[a + 4]) : 0; //adaptlength
549
550                 if (buf[a + offset + 4] != 0 || buf[a + offset + 5] != 2 || (0xF0 & buf[a + offset + 6]) != 0xB0)
551                 {
552                         a += 187;
553                         continue;
554                 }
555
556                 pmtpid = (0x1F & buf[a + 1])<<8 | (0xFF & buf[a + 2]);
557                 memcpy(pmt + pmtsize, buf + a + 4 + offset, 184 - offset);
558                 pmtsize += 184 - offset;
559
560                 if (pmtsize >= 1000)
561                         break;
562         }
563
564         if (pmtsize == 0) return false;
565
566         int pmtlen = (0x0F & pmt[2]) << 8 | (0xFF & pmt[3]);
567         std::string lang;
568         std::string pd_type;
569         ePtr<TSAudioInfo> ainfo = new TSAudioInfo();
570
571         for (int b=8; b < pmtlen-4 && b < pmtsize-6; b++)
572         {
573                 if ( (0xe0 & pmt[b+1]) != 0xe0 )
574                         continue;
575
576                 int pid = (0x1F & pmt[b+1])<<8 | (0xFF & pmt[b+2]);
577
578                 switch(pmt[b])
579                 {
580                 case 1:
581                 case 2: // MPEG Video
582                         //addVideo(pid, "MPEG2");
583                         break;
584
585                 case 0x1B: // H.264 Video
586                         //addVideo(pid, "H.264");
587                         break;
588
589                 case 3:
590                 case 4: // MPEG Audio
591                         lang = getDescriptor(pmt+b+5, pmt[b+4], LANGUAGE_DESCRIPTOR);
592                         ainfo->addAudio(pid, lang, "MPEG", eDVBAudio::aMPEG);
593                         break;
594
595                 case 0x80:
596                 case 0x81:  //private data of AC3 in ATSC
597                 case 0x82:
598                 case 0x83:
599                 case 6:
600                         lang = getDescriptor(pmt+b+5, pmt[b+4], LANGUAGE_DESCRIPTOR);
601                         pd_type = getDescriptor(pmt+b+5, pmt[b+4], REGISTRATION_DESCRIPTOR);
602                         if (pd_type == "AC-3")
603                                 ainfo->addAudio(pid, lang, pd_type, eDVBAudio::aAC3);
604                         break;
605                 }
606                 b += 4 + pmt[b+4];
607         }
608         if (ainfo->audioStreams.size() > 0) {
609                 m_audioInfo = ainfo;
610                 return true;
611         } else {
612                 return false;
613         }
614 }
615
616 void eStreamThread::thread() {
617         const int bufsize = 40000;
618         unsigned char buf[bufsize];
619         bool eof = false;
620         fd_set rfds;
621         fd_set wfds;
622         struct timeval timeout;
623         int rc,r,w,maxfd;
624         time_t next_scantime = 0;
625         bool sosSend = false;
626
627         r = w = 0;
628         hasStarted();
629         eDebug("eStreamThread started");
630         while (!m_stop) {
631                 pthread_testcancel();
632                 FD_ZERO(&rfds);
633                 FD_ZERO(&wfds);
634                 maxfd = 0;
635                 timeout.tv_sec = 1;
636                 timeout.tv_usec = 0;
637                 if (r < bufsize) {
638                         FD_SET(m_srcfd, &rfds);
639                         maxfd = MAX(maxfd, m_srcfd);
640                 }
641                 if (w < r) {
642                         FD_SET(m_destfd, &wfds);
643                         maxfd = MAX(maxfd, m_destfd);
644                 }
645                 rc = select(maxfd+1, &rfds, &wfds, NULL, &timeout);
646                 if (rc == 0) {
647                         eDebug("eStreamThread::thread: timeout!");
648                         continue;
649                 }
650                 if (rc < 0) {
651                         eDebug("eStreamThread::thread: error in select (%d)", errno);
652                         break;
653                 }
654                 if (FD_ISSET(m_srcfd, &rfds)) {
655                         rc = ::read(m_srcfd, buf+r, bufsize - r);
656                         if (rc < 0) {
657                                 eDebug("eStreamThread::thread: error in read (%d)", errno);
658                                 m_messagepump.send(evtReadError);
659                                 break;
660                         } else if (rc == 0) {
661                                 eof = true;
662                         } else {
663                                 if (!sosSend) {
664                                         sosSend = true;
665                                         m_messagepump.send(evtSOS);
666                                 }
667                                 r += rc;
668                                 if (r == bufsize) eDebug("eStreamThread::thread: buffer full");
669                         }
670                 }
671                 if (FD_ISSET(m_destfd, &wfds) && (w < r) && ((r > bufsize/4) || eof)) {
672                         rc = ::write(m_destfd, buf+w, r-w);
673                         if (rc < 0) {
674                                 eDebug("eStreamThread::thread: error in write (%d)", errno);
675                                 m_messagepump.send(evtWriteError);
676                                 break;
677                         }
678                         w += rc;
679                         //eDebug("eStreamThread::thread: buffer r=%d w=%d",r,w);
680                         if (w == r) {
681                                 if (time(0) >= next_scantime) {
682                                         if (scanAudioInfo(buf, r)) {
683                                                 m_messagepump.send(evtStreamInfo);
684                                                 next_scantime = time(0) + 1;
685                                         }
686                                 }
687                                 w = r = 0;
688                         }
689                 }
690                 if (eof && (r==w)) {
691                         ::close(m_destfd);
692                         m_destfd = -1;
693                         ::close(m_srcfd);
694                         m_srcfd = -1;
695                         m_messagepump.send(evtEOS);
696                         break;
697                 }
698         }
699         eDebug("eStreamThread end");
700 }
701
702 void eStreamThread::thread_finished() {
703         if (m_srcfd >= 0) ::close(m_srcfd);
704         if (m_destfd >= 0) ::close(m_destfd);
705         eDebug("eStreamThread closed");
706 }
707
708 eAutoInitPtr<eServiceFactoryTS> init_eServiceFactoryTS(eAutoInitNumbers::service+1, "eServiceFactoryTS");
709
710 PyMODINIT_FUNC
711 initservicets(void)
712 {
713         Py_InitModule("servicets", NULL);
714 }