small fix
[vuplus_dvbapp-plugin] / 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         m_destfd = -1;
119 }
120
121 eServiceTS::~eServiceTS()
122 {
123         eDebug("ServiceTS destruct!");
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 (m_destfd == -1)
258         {
259                 m_destfd = ::open("/dev/misc/pvr", O_WRONLY);
260                 if (m_destfd < 0)
261                 {
262                         eDebug("Cannot open /dev/misc/pvr");
263                         return -1;
264                 }
265         }
266         if (dvbChannel.getDemux(m_decodedemux, iDVBChannel::capDecode) != 0) {
267                 eDebug("Cannot allocate decode-demux");
268                 return -1;
269         }
270         if (m_decodedemux->getMPEGDecoder(m_decoder, 1) != 0) {
271                 eDebug("Cannot allocate MPEGDecoder");
272                 return -1;
273         }
274         m_decoder->setVideoPID(m_vpid, eDVBVideo::MPEG2);
275         m_decoder->setAudioPID(m_apid, eDVBAudio::aMPEG);
276         m_streamthread = new eStreamThread();
277         CONNECT(m_streamthread->m_event, eServiceTS::recv_event);
278         m_decoder->pause();
279         if (unpause() != 0) return -1;
280         m_state = stRunning;
281         m_event(this, evStart);
282         return 0;
283 }
284
285 RESULT eServiceTS::stop()
286 {
287         if (m_destfd >= 0)
288         {
289                 ::close(m_destfd);
290                 m_destfd = -1;
291         }
292         if (m_state != stRunning)
293                 return -1;
294         printf("TS: %s stop\n", m_filename.c_str());
295         m_streamthread->stop();
296         m_decodedemux->flush();
297         m_state = stStopped;
298         m_audioInfo = 0;
299         return 0;
300 }
301
302 void eServiceTS::recv_event(int evt)
303 {
304         eDebug("eServiceTS::recv_event: %d", evt);
305         switch (evt) {
306         case eStreamThread::evtEOS:
307                 m_decodedemux->flush();
308                 m_state = stStopped;
309                 m_event((iPlayableService*)this, evEOF);
310                 break;
311         case eStreamThread::evtReadError:
312         case eStreamThread::evtWriteError:
313                 m_decoder->pause();
314                 m_state = stStopped;
315                 m_event((iPlayableService*)this, evEOF);
316                 break;
317         case eStreamThread::evtSOS:
318                 m_event((iPlayableService*)this, evSOF);
319                 break;
320         case eStreamThread::evtStreamInfo:
321                 bool wasnull = !m_audioInfo;
322                 m_streamthread->getAudioInfo(m_audioInfo);
323                 if (m_audioInfo)
324                         eDebug("[servicets] %d audiostreams found", m_audioInfo->audioStreams.size());
325                 if (m_audioInfo && wasnull) {
326                         int sel = getCurrentTrack();
327                         if (sel < 0)
328                                 selectTrack(0);
329                         else if (m_audioInfo->audioStreams[sel].type != eDVBAudio::aMPEG)
330                                 selectTrack(sel);
331                 }
332                 break;
333         }
334 }
335
336 RESULT eServiceTS::pause(ePtr<iPauseableService> &ptr)
337 {
338         ptr = this;
339         return 0;
340 }
341
342 // iPausableService
343 RESULT eServiceTS::pause()
344 {
345         m_streamthread->stop();
346         m_decoder->pause();
347         return 0;
348 }
349
350 RESULT eServiceTS::unpause()
351 {
352         int is_streaming = !strncmp(m_filename.c_str(), "http://", 7);
353         int srcfd = -1;
354         if (is_streaming) {
355                 srcfd = openHttpConnection(m_filename);
356         } else {
357                 srcfd = ::open(m_filename.c_str(), O_RDONLY);
358         }
359         if (srcfd < 0) {
360                 eDebug("Cannot open source stream: %s", m_filename.c_str());
361                 return 1;
362         }
363         m_decodedemux->flush();
364         m_streamthread->start(srcfd, m_destfd);
365         m_decoder->play();
366         return 0;
367 }
368
369 // iSeekableService
370 RESULT eServiceTS::seek(ePtr<iSeekableService> &ptr)
371 {
372         ptr = this;
373         return 0;
374 }
375
376 RESULT eServiceTS::getLength(pts_t &pts)
377 {
378         return 0;
379 }
380
381 RESULT eServiceTS::seekTo(pts_t to)
382 {
383         return 0;
384 }
385
386 RESULT eServiceTS::seekRelative(int direction, pts_t to)
387 {
388         return 0;
389 }
390
391 RESULT eServiceTS::getPlayPosition(pts_t &pts)
392 {
393         return 0;
394 }
395
396 RESULT eServiceTS::setTrickmode(int trick)
397 {
398         return -1;
399 }
400
401 RESULT eServiceTS::isCurrentlySeekable()
402 {
403         return 1;
404 }
405
406 RESULT eServiceTS::info(ePtr<iServiceInformation>&i)
407 {
408         i = this;
409         return 0;
410 }
411
412 RESULT eServiceTS::getName(std::string &name)
413 {
414         name = m_filename;
415         size_t n = name.rfind('/');
416         if (n != std::string::npos)
417                 name = name.substr(n + 1);
418         return 0;
419 }
420
421 int eServiceTS::getInfo(int w)
422 {
423         return resNA;
424 }
425
426 std::string eServiceTS::getInfoString(int w)
427 {
428         return "";
429 }
430
431 int eServiceTS::getNumberOfTracks() {
432         if (m_audioInfo)
433                 return (int)m_audioInfo->audioStreams.size();
434         else
435                 return 0;
436 }
437
438 RESULT eServiceTS::selectTrack(unsigned int i) {
439         if (m_audioInfo) {
440                 m_apid = m_audioInfo->audioStreams[i].pid;
441                 eDebug("[servicets] audio track %d PID 0x%02x type %d\n", i, m_apid, m_audioInfo->audioStreams[i].type);
442                 m_decoder->setAudioPID(m_apid, m_audioInfo->audioStreams[i].type);
443                 if (m_state == stRunning)
444                         m_decoder->set();
445                 return 0;
446         } else {
447                 return -1;
448         }
449 }
450
451 RESULT eServiceTS::getTrackInfo(struct iAudioTrackInfo &info, unsigned int n) {
452         if (m_audioInfo) {
453                 info.m_pid = m_audioInfo->audioStreams[n].pid;
454                 info.m_description = m_audioInfo->audioStreams[n].description;
455                 info.m_language = m_audioInfo->audioStreams[n].language;
456                 return 0;
457         } else {
458                 return -1;
459         }
460 }
461
462 int eServiceTS::getCurrentTrack() {
463         if (m_audioInfo) {
464                 for (size_t i = 0; i < m_audioInfo->audioStreams.size(); i++) {
465                         if (m_apid == m_audioInfo->audioStreams[i].pid) {
466                                 return i;
467                         }
468                 }
469         }
470         return -1;
471 }
472
473 /********************************************************************/
474 /* eStreamThread                                                       */
475 /********************************************************************/
476
477 DEFINE_REF(eStreamThread)
478
479 eStreamThread::eStreamThread(): m_messagepump(eApp, 0) {
480         CONNECT(m_messagepump.recv_msg, eStreamThread::recvEvent);
481 }
482 eStreamThread::~eStreamThread() {
483 }
484
485 void eStreamThread::start(int srcfd, int destfd) {
486         m_srcfd = srcfd;
487         m_destfd = destfd;
488         m_stop = false;
489         m_audioInfo = 0;
490         run(IOPRIO_CLASS_RT);
491 }
492
493 void eStreamThread::stop() {
494         m_stop = true;
495         kill();
496 }
497
498 void eStreamThread::recvEvent(const int &evt)
499 {
500         m_event(evt);
501 }
502
503 RESULT eStreamThread::getAudioInfo(ePtr<TSAudioInfo> &ptr)
504 {
505         ptr = m_audioInfo;
506         return 0;
507 }
508
509 #define REGISTRATION_DESCRIPTOR 5
510 #define LANGUAGE_DESCRIPTOR 10
511
512 std::string eStreamThread::getDescriptor(unsigned char buf[], int buflen, int type)
513 {
514         int desc_len;
515         while (buflen > 1) {
516                 desc_len = buf[1];
517                 if (buf[0] == type) {
518                         char str[21];
519                         if (desc_len > 20) desc_len = 20;
520                         strncpy(str, (char*)buf+2, desc_len);
521                         str[desc_len] = '\0';
522                         return std::string(str);
523                 } else {
524                         buflen -= desc_len+2;
525                         buf += desc_len+2;
526                 }
527         }
528         return "";
529 }
530
531 bool eStreamThread::scanAudioInfo(unsigned char buf[], int len)
532 {
533         if (len < 1880)
534                 return false;
535
536         int adaptfield, pmtpid, offset;
537         unsigned char pmt[1188];
538         int pmtsize = 0;
539
540         for (int a=0; a < len - 188*4; a++) {
541                 if ( buf[a] != 0x47 || buf[a + 188] != 0x47 || buf[a + 376] != 0x47 )
542                         continue; // TS Header
543
544                 if ((0x40 & buf[a + 1]) == 0) // start
545                         continue;
546
547                 if ((0xC0 & buf[a + 3]) != 0) // scrambling
548                         continue;
549
550                 adaptfield = (0x30 & buf[a + 3]) >> 4;
551
552                 if ((adaptfield & 1) == 0) // adapt - no payload
553                         continue;
554
555                 offset = adaptfield == 3 ? 1 + (0xFF & buf[a + 4]) : 0; //adaptlength
556
557                 if (buf[a + offset + 4] != 0 || buf[a + offset + 5] != 2 || (0xF0 & buf[a + offset + 6]) != 0xB0)
558                 {
559                         a += 187;
560                         continue;
561                 }
562
563                 pmtpid = (0x1F & buf[a + 1])<<8 | (0xFF & buf[a + 2]);
564                 memcpy(pmt + pmtsize, buf + a + 4 + offset, 184 - offset);
565                 pmtsize += 184 - offset;
566
567                 if (pmtsize >= 1000)
568                         break;
569         }
570
571         if (pmtsize == 0) return false;
572
573         int pmtlen = (0x0F & pmt[2]) << 8 | (0xFF & pmt[3]);
574         std::string lang;
575         std::string pd_type;
576         ePtr<TSAudioInfo> ainfo = new TSAudioInfo();
577
578         for (int b=8; b < pmtlen-4 && b < pmtsize-6; b++)
579         {
580                 if ( (0xe0 & pmt[b+1]) != 0xe0 )
581                         continue;
582
583                 int pid = (0x1F & pmt[b+1])<<8 | (0xFF & pmt[b+2]);
584
585                 switch(pmt[b])
586                 {
587                 case 1:
588                 case 2: // MPEG Video
589                         //addVideo(pid, "MPEG2");
590                         break;
591
592                 case 0x1B: // H.264 Video
593                         //addVideo(pid, "H.264");
594                         break;
595
596                 case 3:
597                 case 4: // MPEG Audio
598                         lang = getDescriptor(pmt+b+5, pmt[b+4], LANGUAGE_DESCRIPTOR);
599                         ainfo->addAudio(pid, lang, "MPEG", eDVBAudio::aMPEG);
600                         break;
601
602                 case 0x80:
603                 case 0x81:  //private data of AC3 in ATSC
604                 case 0x82:
605                 case 0x83:
606                 case 6:
607                         lang = getDescriptor(pmt+b+5, pmt[b+4], LANGUAGE_DESCRIPTOR);
608                         pd_type = getDescriptor(pmt+b+5, pmt[b+4], REGISTRATION_DESCRIPTOR);
609                         if (pd_type == "AC-3")
610                                 ainfo->addAudio(pid, lang, pd_type, eDVBAudio::aAC3);
611                         break;
612                 }
613                 b += 4 + pmt[b+4];
614         }
615         if (ainfo->audioStreams.size() > 0) {
616                 m_audioInfo = ainfo;
617                 return true;
618         } else {
619                 return false;
620         }
621 }
622
623 void eStreamThread::thread() {
624         const int bufsize = 40000;
625         unsigned char buf[bufsize];
626         bool eof = false;
627         fd_set rfds;
628         fd_set wfds;
629         struct timeval timeout;
630         int rc,r,w,maxfd;
631         time_t next_scantime = 0;
632         bool sosSend = false;
633
634         r = w = 0;
635         hasStarted();
636         eDebug("eStreamThread started");
637         while (!m_stop) {
638                 pthread_testcancel();
639                 FD_ZERO(&rfds);
640                 FD_ZERO(&wfds);
641                 maxfd = 0;
642                 timeout.tv_sec = 1;
643                 timeout.tv_usec = 0;
644                 if (r < bufsize) {
645                         FD_SET(m_srcfd, &rfds);
646                         maxfd = MAX(maxfd, m_srcfd);
647                 }
648                 if (w < r) {
649                         FD_SET(m_destfd, &wfds);
650                         maxfd = MAX(maxfd, m_destfd);
651                 }
652                 rc = select(maxfd+1, &rfds, &wfds, NULL, &timeout);
653                 if (rc == 0) {
654                         eDebug("eStreamThread::thread: timeout!");
655                         continue;
656                 }
657                 if (rc < 0) {
658                         eDebug("eStreamThread::thread: error in select (%d)", errno);
659                         break;
660                 }
661                 if (FD_ISSET(m_srcfd, &rfds)) {
662                         rc = ::read(m_srcfd, buf+r, bufsize - r);
663                         if (rc < 0) {
664                                 eDebug("eStreamThread::thread: error in read (%d)", errno);
665                                 m_messagepump.send(evtReadError);
666                                 break;
667                         } else if (rc == 0) {
668                                 eof = true;
669                         } else {
670                                 if (!sosSend) {
671                                         sosSend = true;
672                                         m_messagepump.send(evtSOS);
673                                 }
674                                 r += rc;
675                                 if (r == bufsize) eDebug("eStreamThread::thread: buffer full");
676                         }
677                 }
678                 if (FD_ISSET(m_destfd, &wfds) && (w < r) && ((r > bufsize/4) || eof)) {
679                         rc = ::write(m_destfd, buf+w, r-w);
680                         if (rc < 0) {
681                                 eDebug("eStreamThread::thread: error in write (%d)", errno);
682                                 m_messagepump.send(evtWriteError);
683                                 break;
684                         }
685                         w += rc;
686                         //eDebug("eStreamThread::thread: buffer r=%d w=%d",r,w);
687                         if (w == r) {
688                                 if (time(0) >= next_scantime) {
689                                         if (scanAudioInfo(buf, r)) {
690                                                 m_messagepump.send(evtStreamInfo);
691                                                 next_scantime = time(0) + 1;
692                                         }
693                                 }
694                                 w = r = 0;
695                         }
696                 }
697                 if (eof && (r==w)) {
698                         ::close(m_srcfd);
699                         m_srcfd = -1;
700                         m_messagepump.send(evtEOS);
701                         break;
702                 }
703         }
704         eDebug("eStreamThread end");
705 }
706
707 void eStreamThread::thread_finished() {
708         if (m_srcfd >= 0) ::close(m_srcfd);
709         eDebug("eStreamThread closed");
710 }
711
712 eAutoInitPtr<eServiceFactoryTS> init_eServiceFactoryTS(eAutoInitNumbers::service+1, "eServiceFactoryTS");
713
714 PyMODINIT_FUNC
715 initservicets(void)
716 {
717         Py_InitModule("servicets", NULL);
718 }