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