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