1 /*******************************************************************************
2 VLC Player Plugin by A. Lätsch 2007
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
8 ********************************************************************************/
10 #include <sys/types.h>
14 #include <sys/socket.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>
28 #include <lib/dvb/pmt.h>
30 #define MAX(a,b) ((a) > (b) ? (a) : (b))
32 /********************************************************************/
33 /* eServiceFactoryTS */
34 /********************************************************************/
36 eServiceFactoryTS::eServiceFactoryTS()
38 ePtr<eServiceCenter> sc;
40 eServiceCenter::getPrivInstance(sc);
43 std::list<std::string> extensions;
44 sc->addServiceFactory(eServiceFactoryTS::id, this, extensions);
48 eServiceFactoryTS::~eServiceFactoryTS()
50 ePtr<eServiceCenter> sc;
52 eServiceCenter::getPrivInstance(sc);
54 sc->removeServiceFactory(eServiceFactoryTS::id);
57 DEFINE_REF(eServiceFactoryTS)
60 RESULT eServiceFactoryTS::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
62 ptr = new eServiceTS(ref);
66 RESULT eServiceFactoryTS::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr)
72 RESULT eServiceFactoryTS::list(const eServiceReference &, ePtr<iListableService> &ptr)
78 RESULT eServiceFactoryTS::info(const eServiceReference &ref, ePtr<iStaticServiceInformation> &ptr)
84 RESULT eServiceFactoryTS::offlineOperations(const eServiceReference &, ePtr<iServiceOfflineOperations> &ptr)
91 /********************************************************************/
93 /********************************************************************/
94 DEFINE_REF(TSAudioInfo);
96 void TSAudioInfo::addAudio(int pid, std::string lang, std::string desc, int type) {
98 as.description = desc;
102 audioStreams.push_back(as);
106 /********************************************************************/
108 /********************************************************************/
110 eServiceTS::eServiceTS(const eServiceReference &url): m_pump(eApp, 1)
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);
120 eServiceTS::~eServiceTS()
122 eDebug("ServiceTS destruct!");
123 if (m_state == stRunning)
127 DEFINE_REF(eServiceTS);
129 size_t crop(char *buf)
131 size_t len = strlen(buf) - 1;
132 while (len > 0 && (buf[len] == '\r' || buf[len] == '\n')) {
138 static int getline(char** pbuffer, size_t* pbufsize, int fd)
143 if (i >= *pbufsize) {
144 char *newbuf = (char*)realloc(*pbuffer, (*pbufsize)+1024);
148 *pbufsize = (*pbufsize)+1024;
150 rc = ::read(fd, (*pbuffer)+i, 1);
151 if (rc <= 0 || (*pbuffer)[i] == '\n')
153 (*pbuffer)[i] = '\0';
154 return rc <= 0 ? -1 : i;
156 if ((*pbuffer)[i] != '\r') i++;
160 int eServiceTS::openHttpConnection(std::string url)
166 int slash = url.find("/", 7);
168 host = url.substr(7, slash-7);
169 uri = url.substr(slash, url.length()-slash);
171 host = url.substr(7, url.length()-7);
174 int dp = host.find(":");
176 port = atoi(host.substr(1, host.length()-1).c_str());
179 port = atoi(host.substr(dp+1, host.length()-dp-1).c_str());
180 host = host.substr(0, dp);
183 struct hostent* h = gethostbyname(host.c_str());
184 if (h == NULL || h->h_addr_list == NULL)
186 int fd = socket(PF_INET, SOCK_STREAM, 0);
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);
195 eDebug("connecting to %s", url.c_str());
197 if (connect(fd, (sockaddr*)&addr, sizeof(addr)) == -1) {
198 std::string msg = "connect failed for: " + url;
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());
213 size_t buflen = 1000;
214 char* linebuf = (char*)malloc(1000);
216 rc = getline(&linebuf, &buflen, fd);
217 //eDebug("RECV(%d): %s", rc, linebuf);
228 rc = sscanf(linebuf, "%99s %d %99s", proto, &statuscode, statusmsg);
229 if (rc != 3 || statuscode != 200) {
230 eDebug("wrong response: \"200 OK\" expected.");
235 eDebug("proto=%s, code=%d, msg=%s", proto, statuscode, statusmsg);
238 rc = getline(&linebuf, &buflen, fd);
239 //eDebug("RECV(%d): %s", rc, linebuf);
246 RESULT eServiceTS::connectEvent(const Slot2<void,iPlayableService*,int> &event, ePtr<eConnection> &connection)
248 connection = new eConnection((iPlayableService*)this, m_event.connect(event));
252 RESULT eServiceTS::start()
254 ePtr<eDVBResourceManager> rmgr;
255 eDVBResourceManager::getInstance(rmgr);
256 if (rmgr->allocateDemux(NULL, m_decodedemux, iDVBChannel::capDecode) != 0) {
257 eDebug("Cannot allocate decode-demux");
260 if (m_decodedemux->get().getMPEGDecoder(m_decoder, 1) != 0) {
261 eDebug("Cannot allocate MPEGDecoder");
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;
273 m_event(this, evStart);
277 RESULT eServiceTS::stop()
279 if (m_state != stRunning)
281 printf("TS: %s stop\n", m_filename.c_str());
282 m_streamthread->stop();
283 m_decodedemux->get().flush();
289 void eServiceTS::recv_event(int evt)
291 eDebug("eServiceTS::recv_event: %d", evt);
293 case eStreamThread::evtEOS:
294 m_decodedemux->get().flush();
296 m_event((iPlayableService*)this, evEOF);
298 case eStreamThread::evtReadError:
299 case eStreamThread::evtWriteError:
300 m_decoder->freeze(0);
302 m_event((iPlayableService*)this, evEOF);
304 case eStreamThread::evtStreamInfo:
305 bool wasnull = !m_audioInfo;
306 m_streamthread->getAudioInfo(m_audioInfo);
308 eDebug("[servicets] %d audiostreams found", m_audioInfo->audioStreams.size());
309 if (m_audioInfo && wasnull) {
310 int sel = getCurrentTrack();
313 else if (m_audioInfo->audioStreams[sel].type != eDVBAudio::aMPEG)
320 RESULT eServiceTS::pause(ePtr<iPauseableService> &ptr)
327 RESULT eServiceTS::pause()
329 m_streamthread->stop();
330 m_decoder->freeze(0);
334 RESULT eServiceTS::unpause()
336 int is_streaming = !strncmp(m_filename.c_str(), "http://", 7);
339 srcfd = openHttpConnection(m_filename);
341 srcfd = ::open(m_filename.c_str(), O_RDONLY);
344 eDebug("Cannot open source stream: %s", m_filename.c_str());
348 int destfd = ::open("/dev/misc/pvr", O_WRONLY);
350 eDebug("Cannot open /dev/misc/pvr");
354 m_decodedemux->get().flush();
355 m_streamthread->start(srcfd, destfd);
356 m_decoder->unfreeze();
361 RESULT eServiceTS::seek(ePtr<iSeekableService> &ptr)
367 RESULT eServiceTS::getLength(pts_t &pts)
372 RESULT eServiceTS::seekTo(pts_t to)
377 RESULT eServiceTS::seekRelative(int direction, pts_t to)
382 RESULT eServiceTS::getPlayPosition(pts_t &pts)
387 RESULT eServiceTS::setTrickmode(int trick)
392 RESULT eServiceTS::isCurrentlySeekable()
397 RESULT eServiceTS::info(ePtr<iServiceInformation>&i)
403 RESULT eServiceTS::getName(std::string &name)
406 size_t n = name.rfind('/');
407 if (n != std::string::npos)
408 name = name.substr(n + 1);
412 int eServiceTS::getInfo(int w)
417 std::string eServiceTS::getInfoString(int w)
422 int eServiceTS::getNumberOfTracks() {
424 return (int)m_audioInfo->audioStreams.size();
429 RESULT eServiceTS::selectTrack(unsigned int i) {
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();
442 RESULT eServiceTS::getTrackInfo(struct iAudioTrackInfo &info, unsigned int n) {
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;
453 int eServiceTS::getCurrentTrack() {
455 for (size_t i = 0; i < m_audioInfo->audioStreams.size(); i++) {
456 if (m_apid == m_audioInfo->audioStreams[i].pid) {
464 /********************************************************************/
466 /********************************************************************/
468 DEFINE_REF(eStreamThread)
470 eStreamThread::eStreamThread(): m_messagepump(eApp, 0) {
471 CONNECT(m_messagepump.recv_msg, eStreamThread::recvEvent);
473 eStreamThread::~eStreamThread() {
476 void eStreamThread::start(int srcfd, int destfd) {
481 run(IOPRIO_CLASS_RT);
483 void eStreamThread::stop() {
488 void eStreamThread::recvEvent(const int &evt)
493 RESULT eStreamThread::getAudioInfo(ePtr<TSAudioInfo> &ptr)
499 #define REGISTRATION_DESCRIPTOR 5
500 #define LANGUAGE_DESCRIPTOR 10
502 std::string eStreamThread::getDescriptor(unsigned char buf[], int buflen, int type)
507 if (buf[0] == type) {
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);
514 buflen -= desc_len+2;
521 bool eStreamThread::scanAudioInfo(unsigned char buf[], int len)
526 int adaptfield, pmtpid, offset;
527 unsigned char pmt[1188];
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
534 if ((0x40 & buf[a + 1]) == 0) // start
537 if ((0xC0 & buf[a + 3]) != 0) // scrambling
540 adaptfield = (0x30 & buf[a + 3]) >> 4;
542 if ((adaptfield & 1) == 0) // adapt - no payload
545 offset = adaptfield == 3 ? 1 + (0xFF & buf[a + 4]) : 0; //adaptlength
547 if (buf[a + offset + 4] != 0 || buf[a + offset + 5] != 2 || (0xF0 & buf[a + offset + 6]) != 0xB0)
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;
561 if (pmtsize == 0) return false;
563 int pmtlen = (0x0F & pmt[2]) << 8 | (0xFF & pmt[3]);
566 ePtr<TSAudioInfo> ainfo = new TSAudioInfo();
568 for (int b=8; b < pmtlen-4 && b < pmtsize-6; b++)
570 if ( (0xe0 & pmt[b+1]) != 0xe0 )
573 int pid = (0x1F & pmt[b+1])<<8 | (0xFF & pmt[b+2]);
578 case 2: // MPEG Video
579 //addVideo(pid, "MPEG2");
582 case 0x1B: // H.264 Video
583 //addVideo(pid, "H.264");
587 case 4: // MPEG Audio
588 lang = getDescriptor(pmt+b+5, pmt[b+4], LANGUAGE_DESCRIPTOR);
589 ainfo->addAudio(pid, lang, "MPEG", eDVBAudio::aMPEG);
593 case 0x81: //private data of AC3 in ATSC
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);
605 if (ainfo->audioStreams.size() > 0) {
613 void eStreamThread::thread() {
614 const int bufsize = 40000;
615 unsigned char buf[bufsize];
619 struct timeval timeout;
621 time_t next_scantime = 0;
625 eDebug("eStreamThread started");
627 pthread_testcancel();
634 FD_SET(m_srcfd, &rfds);
635 maxfd = MAX(maxfd, m_srcfd);
638 FD_SET(m_destfd, &wfds);
639 maxfd = MAX(maxfd, m_destfd);
641 rc = select(maxfd+1, &rfds, &wfds, NULL, &timeout);
643 eDebug("eStreamThread::thread: timeout!");
647 eDebug("eStreamThread::thread: error in select (%d)", errno);
650 if (FD_ISSET(m_srcfd, &rfds)) {
651 rc = ::read(m_srcfd, buf+r, bufsize - r);
653 eDebug("eStreamThread::thread: error in read (%d)", errno);
654 m_messagepump.send(evtReadError);
656 } else if (rc == 0) {
660 if (r == bufsize) eDebug("eStreamThread::thread: buffer full");
663 if (FD_ISSET(m_destfd, &wfds) && (w < r) && (r > bufsize/4)) {
664 rc = ::write(m_destfd, buf+w, r-w);
666 eDebug("eStreamThread::thread: error in write (%d)", errno);
667 m_messagepump.send(evtWriteError);
671 //eDebug("eStreamThread::thread: buffer r=%d w=%d",r,w);
673 if (time(0) >= next_scantime) {
674 if (scanAudioInfo(buf, r)) {
675 m_messagepump.send(evtStreamInfo);
676 next_scantime = time(0) + 1;
687 m_messagepump.send(evtEOS);
691 eDebug("eStreamThread end");
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");
700 eAutoInitPtr<eServiceFactoryTS> init_eServiceFactoryTS(eAutoInitNumbers::service+1, "eServiceFactoryTS");
705 Py_InitModule("servicets", NULL);