2 /*******************************************************************************
3 VLC Player Plugin by A. Lätsch 2007
7 This is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
11 ********************************************************************************/
13 #include <sys/types.h>
17 #include <sys/socket.h>
21 #include "servicewebts.h"
22 #include <lib/base/eerror.h>
23 #include <lib/base/object.h>
24 #include <lib/base/ebase.h>
25 #include <lib/service/service.h>
26 #include <lib/base/init_num.h>
27 #include <lib/base/init.h>
28 #include <lib/dvb/decoder.h>
30 #include <lib/dvb/pmt.h>
32 #define MAX(a,b) ((a) > (b) ? (a) : (b))
39 /********************************************************************/
40 /* eServiceFactoryWebTS */
41 /********************************************************************/
43 eServiceFactoryWebTS::eServiceFactoryWebTS()
45 ePtr<eServiceCenter> sc;
47 eServiceCenter::getPrivInstance(sc);
50 std::list<std::string> extensions;
51 sc->addServiceFactory(eServiceFactoryWebTS::id, this, extensions);
55 eServiceFactoryWebTS::~eServiceFactoryWebTS()
57 ePtr<eServiceCenter> sc;
59 eServiceCenter::getPrivInstance(sc);
61 sc->removeServiceFactory(eServiceFactoryWebTS::id);
64 DEFINE_REF(eServiceFactoryWebTS)
67 RESULT eServiceFactoryWebTS::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
69 ptr = new eServiceWebTS(ref);
73 RESULT eServiceFactoryWebTS::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr)
79 RESULT eServiceFactoryWebTS::list(const eServiceReference &, ePtr<iListableService> &ptr)
85 RESULT eServiceFactoryWebTS::info(const eServiceReference &ref, ePtr<iStaticServiceInformation> &ptr)
91 RESULT eServiceFactoryWebTS::offlineOperations(const eServiceReference &, ePtr<iServiceOfflineOperations> &ptr)
98 /********************************************************************/
100 /********************************************************************/
101 DEFINE_REF(TSAudioInfoWeb);
103 void TSAudioInfoWeb::addAudio(int pid, std::string lang, std::string desc, int type) {
105 as.description = desc;
109 audioStreams.push_back(as);
113 /********************************************************************/
115 /********************************************************************/
117 eServiceWebTS::eServiceWebTS(const eServiceReference &url): m_pump(eApp, 1)
119 eDebug("ServiceWebTS construct!");
120 m_filename = url.path.c_str();
121 m_vpid = url.getData(0) == 0 ? 0x44 : url.getData(0);
122 m_apid = url.getData(1) == 0 ? 0x45 : url.getData(1);
127 eServiceWebTS::~eServiceWebTS()
129 eDebug("ServiceWebTS destruct!");
130 if (m_state == stRunning)
134 DEFINE_REF(eServiceWebTS);
136 size_t crop(char *buf)
138 size_t len = strlen(buf) - 1;
139 while (len > 0 && (buf[len] == '\r' || buf[len] == '\n')) {
145 static int getline(char** pbuffer, size_t* pbufsize, int fd)
150 if (i >= *pbufsize) {
151 char *newbuf = (char*)realloc(*pbuffer, (*pbufsize)+1024);
155 *pbufsize = (*pbufsize)+1024;
157 rc = ::read(fd, (*pbuffer)+i, 1);
158 if (rc <= 0 || (*pbuffer)[i] == '\n')
160 (*pbuffer)[i] = '\0';
161 return rc <= 0 ? -1 : i;
163 if ((*pbuffer)[i] != '\r') i++;
169 int eServiceWebTS::openHttpConnection(std::string url)
175 int slash = url.find("/", 7);
177 host = url.substr(7, slash-7);
178 uri = url.substr(slash, url.length()-slash);
180 host = url.substr(7, url.length()-7);
183 int dp = host.find(":");
185 port = atoi(host.substr(1, host.length()-1).c_str());
188 port = atoi(host.substr(dp+1, host.length()-dp-1).c_str());
189 host = host.substr(0, dp);
192 struct hostent* h = gethostbyname(host.c_str());
193 if (h == NULL || h->h_addr_list == NULL)
195 int fd = socket(PF_INET, SOCK_STREAM, 0);
199 struct sockaddr_in addr;
200 addr.sin_family = AF_INET;
201 addr.sin_addr.s_addr = *((in_addr_t*)h->h_addr_list[0]);
202 addr.sin_port = htons(port);
204 eDebug("connecting to %s", url.c_str());
206 if (connect(fd, (sockaddr*)&addr, sizeof(addr)) == -1) {
207 std::string msg = "connect failed for: " + url;
212 std::string request = "GET ";
213 request.append(uri).append(" HTTP/1.1\n");
214 request.append("Host: ").append(host).append("\n");
215 request.append("Accept: */*\n");
216 request.append("Connection: close\n");
217 request.append("\n");
218 //eDebug(request.c_str());
219 write(fd, request.c_str(), request.length());
222 size_t buflen = 1000;
223 char* linebuf = (char*)malloc(1000);
225 rc = getline(&linebuf, &buflen, fd);
226 //eDebug("RECV(%d): %s", rc, linebuf);
237 rc = sscanf(linebuf, "%99s %d %99s", proto, &statuscode, statusmsg);
238 if (rc != 3 || statuscode != 200) {
239 eDebug("wrong response: \"200 OK\" expected.");
244 eDebug("proto=%s, code=%d, msg=%s", proto, statuscode, statusmsg);
247 rc = getline(&linebuf, &buflen, fd);
248 //eDebug("RECV(%d): %s", rc, linebuf);
255 RESULT eServiceWebTS::connectEvent(const Slot2<void,iPlayableService*,int> &event, ePtr<eConnection> &connection)
257 connection = new eConnection((iPlayableService*)this, m_event.connect(event));
261 RESULT eServiceWebTS::start()
263 ePtr<eDVBResourceManager> rmgr;
264 eDVBResourceManager::getInstance(rmgr);
265 eDVBChannel dvbChannel(rmgr, 0);
266 if (dvbChannel.getDemux(m_decodedemux, iDVBChannel::capDecode) != 0) {
267 eDebug("Cannot allocate decode-demux");
270 if (m_decodedemux->getMPEGDecoder(m_decoder, 1) != 0) {
271 eDebug("Cannot allocate MPEGDecoder");
274 //m_decoder->setVideoPID(m_vpid, eDVBVideo::MPEG2);
275 //m_decoder->setAudioPID(m_apid, eDVBAudio::aMPEG);
276 m_streamthread = new eStreamThreadWeb();
277 CONNECT(m_streamthread->m_event, eServiceWebTS::recv_event);
278 //m_decoder->freeze(0);
279 //m_decoder->preroll();
280 if (unpause() != 0) return -1;
281 //m_state = stRunning;
282 //m_event(this, evStart);
286 RESULT eServiceWebTS::stop()
288 if (m_state != stRunning)
290 printf("TS: %s stop\n", m_filename.c_str());
291 m_streamthread->stop();
292 m_decodedemux->flush();
301 void eServiceWebTS::recv_event(int evt)
303 eDebug("eServiceWebTS::recv_event: %d", evt);
305 case eStreamThreadWeb::evtEOS:
306 m_decodedemux->flush();
308 m_event((iPlayableService*)this, evEOF);
310 case eStreamThreadWeb::evtReadError:
311 case eStreamThreadWeb::evtWriteError:
314 m_event((iPlayableService*)this, evEOF);
316 case eStreamThreadWeb::evtSOS:
317 m_event((iPlayableService*)this, evSOF);
319 case eStreamThreadWeb::evtStreamInfo:
320 if (VPID != 0 && PID_SET == 0 && APID != 0)
323 m_decodedemux->flush();
324 m_decoder->setVideoPID(VPID, eDVBVideo::MPEG2);
325 m_decoder->setAudioPID(APID, eDVBAudio::aMPEG);
328 m_event(this, evStart);
332 bool wasnull = !m_audioInfo;
333 m_streamthread->getAudioInfo(m_audioInfo);
335 // eDebug("[ServiceWebTS] %d audiostreams found", m_audioInfo->audioStreams.size());
336 if (m_audioInfo && wasnull) {
337 eDebug("[ServiceWebTS] %d audiostreams found", m_audioInfo->audioStreams.size());
338 int sel = getCurrentTrack();
341 else if (m_audioInfo->audioStreams[sel].type != eDVBAudio::aMPEG)
348 RESULT eServiceWebTS::pause(ePtr<iPauseableService> &ptr)
355 RESULT eServiceWebTS::pause()
357 m_streamthread->stop();
362 RESULT eServiceWebTS::unpause()
364 int is_streaming = !strncmp(m_filename.c_str(), "http://", 7);
367 srcfd = openHttpConnection(m_filename);
369 srcfd = ::open(m_filename.c_str(), O_RDONLY);
372 eDebug("Cannot open source stream: %s", m_filename.c_str());
376 int destfd = ::open("/dev/misc/pvr", O_WRONLY);
378 eDebug("Cannot open /dev/misc/pvr");
382 //m_decodedemux->flush();
383 m_streamthread->start(srcfd, destfd);
384 //m_decoder->unfreeze();
389 RESULT eServiceWebTS::seek(ePtr<iSeekableService> &ptr)
395 RESULT eServiceWebTS::getLength(pts_t &pts)
400 RESULT eServiceWebTS::seekTo(pts_t to)
405 RESULT eServiceWebTS::seekRelative(int direction, pts_t to)
410 RESULT eServiceWebTS::getPlayPosition(pts_t &pts)
415 RESULT eServiceWebTS::setTrickmode(int trick)
420 RESULT eServiceWebTS::isCurrentlySeekable()
425 RESULT eServiceWebTS::info(ePtr<iServiceInformation>&i)
431 RESULT eServiceWebTS::getName(std::string &name)
434 size_t n = name.rfind('/');
435 if (n != std::string::npos)
436 name = name.substr(n + 1);
440 int eServiceWebTS::getInfo(int w)
445 std::string eServiceWebTS::getInfoString(int w)
450 int eServiceWebTS::getNumberOfTracks() {
452 return (int)m_audioInfo->audioStreams.size();
457 RESULT eServiceWebTS::selectTrack(unsigned int i) {
459 m_apid = m_audioInfo->audioStreams[i].pid;
460 eDebug("[ServiceWebTS] audio track %d PID 0x%02x type %d\n", i, m_apid, m_audioInfo->audioStreams[i].type);
461 m_decoder->setAudioPID(m_apid, m_audioInfo->audioStreams[i].type);
462 if (m_state == stRunning)
470 RESULT eServiceWebTS::getTrackInfo(struct iAudioTrackInfo &info, unsigned int n) {
472 info.m_pid = m_audioInfo->audioStreams[n].pid;
473 info.m_description = m_audioInfo->audioStreams[n].description;
474 info.m_language = m_audioInfo->audioStreams[n].language;
481 int eServiceWebTS::getCurrentTrack() {
483 for (size_t i = 0; i < m_audioInfo->audioStreams.size(); i++) {
484 if (m_apid == m_audioInfo->audioStreams[i].pid) {
492 /********************************************************************/
493 /* eStreamThreadWeb */
494 /********************************************************************/
496 DEFINE_REF(eStreamThreadWeb)
498 eStreamThreadWeb::eStreamThreadWeb(): m_messagepump(eApp, 0) {
499 CONNECT(m_messagepump.recv_msg, eStreamThreadWeb::recvEvent);
501 eStreamThreadWeb::~eStreamThreadWeb() {
504 void eStreamThreadWeb::start(int srcfd, int destfd) {
509 run(IOPRIO_CLASS_RT);
511 void eStreamThreadWeb::stop() {
516 void eStreamThreadWeb::recvEvent(const int &evt)
521 RESULT eStreamThreadWeb::getAudioInfo(ePtr<TSAudioInfoWeb> &ptr)
527 #define REGISTRATION_DESCRIPTOR 5
528 #define LANGUAGE_DESCRIPTOR 10
530 std::string eStreamThreadWeb::getDescriptor(unsigned char buf[], int buflen, int type)
535 if (buf[0] == type) {
537 if (desc_len > 20) desc_len = 20;
538 strncpy(str, (char*)buf+2, desc_len);
539 str[desc_len] = '\0';
540 return std::string(str);
542 buflen -= desc_len+2;
549 bool eStreamThreadWeb::scanAudioInfo(unsigned char buf[], int len)
554 int adaptfield, pmtpid, offset;
555 unsigned char pmt[1188];
558 for (int a=0; a < len - 188*4; a++) {
559 if ( buf[a] != 0x47 || buf[a + 188] != 0x47 || buf[a + 376] != 0x47 )
560 continue; // TS Header
562 if ((0x40 & buf[a + 1]) == 0) // start
565 if ((0xC0 & buf[a + 3]) != 0) // scrambling
568 adaptfield = (0x30 & buf[a + 3]) >> 4;
570 if ((adaptfield & 1) == 0) // adapt - no payload
573 offset = adaptfield == 3 ? 1 + (0xFF & buf[a + 4]) : 0; //adaptlength
575 if (buf[a + offset + 4] != 0 || buf[a + offset + 5] != 2 || (0xF0 & buf[a + offset + 6]) != 0xB0)
581 pmtpid = (0x1F & buf[a + 1])<<8 | (0xFF & buf[a + 2]);
582 memcpy(pmt + pmtsize, buf + a + 4 + offset, 184 - offset);
583 pmtsize += 184 - offset;
589 if (pmtsize == 0) return false;
591 int pmtlen = (0x0F & pmt[2]) << 8 | (0xFF & pmt[3]);
594 ePtr<TSAudioInfoWeb> ainfo = new TSAudioInfoWeb();
596 for (int b=8; b < pmtlen-4 && b < pmtsize-6; b++)
598 if ( (0xe0 & pmt[b+1]) != 0xe0 )
601 int pid = (0x1F & pmt[b+1])<<8 | (0xFF & pmt[b+2]);
605 case 2: // MPEG Video
606 //addVideo(pid, "MPEG2");
611 case 0x1B: // H.264 Video
612 //addVideo(pid, "H.264");
615 case 4: // MPEG Audio
618 lang = getDescriptor(pmt+b+5, pmt[b+4], LANGUAGE_DESCRIPTOR);
619 ainfo->addAudio(pid, lang, "MPEG", eDVBAudio::aMPEG);
623 case 0x81: //private data of AC3 in ATSC
627 lang = getDescriptor(pmt+b+5, pmt[b+4], LANGUAGE_DESCRIPTOR);
628 pd_type = getDescriptor(pmt+b+5, pmt[b+4], REGISTRATION_DESCRIPTOR);
629 //if (pd_type == "AC-3")
630 // dirty dirty :-) Aber es funktioniert...
631 if (lang.length() != 0)
632 ainfo->addAudio(pid, lang, "AC-3", eDVBAudio::aAC3);
637 if (ainfo->audioStreams.size() > 0) {
645 void eStreamThreadWeb::thread() {
646 const int bufsize = 40000;
647 unsigned char buf[bufsize];
651 struct timeval timeout;
653 time_t next_scantime = 0;
654 bool sosSend = false;
658 eDebug("eStreamThreadWeb started");
660 pthread_testcancel();
667 FD_SET(m_srcfd, &rfds);
668 maxfd = MAX(maxfd, m_srcfd);
671 FD_SET(m_destfd, &wfds);
672 maxfd = MAX(maxfd, m_destfd);
674 rc = select(maxfd+1, &rfds, &wfds, NULL, &timeout);
676 eDebug("eStreamThreadWeb::thread: timeout!");
680 eDebug("eStreamThreadWeb::thread: error in select (%d)", errno);
683 if (FD_ISSET(m_srcfd, &rfds)) {
684 rc = ::read(m_srcfd, buf+r, bufsize - r);
686 eDebug("eStreamThreadWeb::thread: error in read (%d)", errno);
687 m_messagepump.send(evtReadError);
689 } else if (rc == 0) {
694 m_messagepump.send(evtSOS);
697 if (r == bufsize) eDebug("eStreamThreadWeb::thread: buffer full");
700 if (FD_ISSET(m_destfd, &wfds) && (w < r) && ((r > bufsize/4) || eof)) {
701 rc = ::write(m_destfd, buf+w, r-w);
703 eDebug("eStreamThreadWeb::thread: error in write (%d)", errno);
704 m_messagepump.send(evtWriteError);
708 //eDebug("eStreamThreadWeb::thread: buffer r=%d w=%d",r,w);
710 if (time(0) >= next_scantime) {
711 if (scanAudioInfo(buf, r)) {
712 m_messagepump.send(evtStreamInfo);
713 next_scantime = time(0) + 1;
724 m_messagepump.send(evtEOS);
728 eDebug("eStreamThreadWeb end");
731 void eStreamThreadWeb::thread_finished() {
732 if (m_srcfd >= 0) ::close(m_srcfd);
733 if (m_destfd >= 0) ::close(m_destfd);
734 eDebug("eStreamThreadWeb closed");
737 eAutoInitPtr<eServiceFactoryWebTS> init_eServiceFactoryWebTS(eAutoInitNumbers::service+1, "eServiceFactoryWebTS");
740 initservicewebts(void)
742 Py_InitModule("servicewebts", NULL);