follow enigma2 pvr channel allocation api change
[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.m_pid = m_audioInfo->audioStreams[n].pid;
491                 info.m_description = m_audioInfo->audioStreams[n].description;
492                 info.m_language = m_audioInfo->audioStreams[n].language;
493                 return 0;
494         } else {
495                 return -1;
496         }
497 }
498
499 int eServiceWebTS::getCurrentTrack() {
500         if (m_audioInfo) {
501                 for (size_t i = 0; i < m_audioInfo->audioStreams.size(); i++) {
502                         if (m_apid == m_audioInfo->audioStreams[i].pid) {
503                                 return i;
504                         }
505                 }
506         }
507         return -1;
508 }
509
510 /********************************************************************/
511 /* eStreamThreadWeb                                                       */
512 /********************************************************************/
513
514 DEFINE_REF(eStreamThreadWeb)
515
516 eStreamThreadWeb::eStreamThreadWeb(): m_messagepump(eApp, 0) {
517         CONNECT(m_messagepump.recv_msg, eStreamThreadWeb::recvEvent);
518         m_running = false;
519 }
520
521 eStreamThreadWeb::~eStreamThreadWeb() {
522 }
523
524 void eStreamThreadWeb::start(int srcfd, int destfd) {
525         m_srcfd = srcfd;
526         m_destfd = destfd;
527         m_stop = false;
528         m_audioInfo = 0;
529         run(IOPRIO_CLASS_RT);
530 }
531
532 void eStreamThreadWeb::stop() {
533         m_stop = true;
534         kill();
535 }
536
537 void eStreamThreadWeb::recvEvent(const int &evt)
538 {
539         m_event(evt);
540 }
541
542 RESULT eStreamThreadWeb::getAudioInfo(ePtr<TSAudioInfoWeb> &ptr)
543 {
544         ptr = m_audioInfo;
545         return 0;
546 }
547
548 #define REGISTRATION_DESCRIPTOR 5
549 #define LANGUAGE_DESCRIPTOR 10
550
551 std::string eStreamThreadWeb::getDescriptor(unsigned char buf[], int buflen, int type)
552 {
553         int desc_len;
554         while (buflen > 1) {
555                 desc_len = buf[1];
556                 if (buf[0] == type) {
557                         char str[21];
558                         if (desc_len > 20) desc_len = 20;
559                         strncpy(str, (char*)buf+2, desc_len);
560                         str[desc_len] = '\0';
561                         return std::string(str);
562                 } else {
563                         buflen -= desc_len+2;
564                         buf += desc_len+2;
565                 }
566         }
567         return "";
568 }
569
570 bool eStreamThreadWeb::scanAudioInfo(unsigned char buf[], int len)
571 {
572         if (len < 1880)
573                 return false;
574
575         int adaptfield, pmtpid, offset;
576         unsigned char pmt[1188];
577         int pmtsize = 0;
578
579         for (int a=0; a < len - 188*4; a++) {
580                 if ( buf[a] != 0x47 || buf[a + 188] != 0x47 || buf[a + 376] != 0x47 )
581                         continue; // TS Header
582
583                 if ((0x40 & buf[a + 1]) == 0) // start
584                         continue;
585
586                 if ((0xC0 & buf[a + 3]) != 0) // scrambling
587                         continue;
588
589                 adaptfield = (0x30 & buf[a + 3]) >> 4;
590
591                 if ((adaptfield & 1) == 0) // adapt - no payload
592                         continue;
593
594                 offset = adaptfield == 3 ? 1 + (0xFF & buf[a + 4]) : 0; //adaptlength
595
596                 if (buf[a + offset + 4] != 0 || buf[a + offset + 5] != 2 || (0xF0 & buf[a + offset + 6]) != 0xB0)
597                 {
598                         a += 187;
599                         continue;
600                 }
601
602                 pmtpid = (0x1F & buf[a + 1])<<8 | (0xFF & buf[a + 2]);
603                 memcpy(pmt + pmtsize, buf + a + 4 + offset, 184 - offset);
604                 pmtsize += 184 - offset;
605
606                 if (pmtsize >= 1000)
607                         break;
608         }
609
610         if (pmtsize == 0) return false;
611
612         int pmtlen = (0x0F & pmt[2]) << 8 | (0xFF & pmt[3]);
613         std::string lang;
614         std::string pd_type;
615         ePtr<TSAudioInfoWeb> ainfo = new TSAudioInfoWeb();
616
617         for (int b=8; b < pmtlen-4 && b < pmtsize-6; b++)
618         {
619                 if ( (0xe0 & pmt[b+1]) != 0xe0 )
620                         continue;
621
622                 int pid = (0x1F & pmt[b+1])<<8 | (0xFF & pmt[b+2]);
623                 switch(pmt[b])
624                 {
625                 case 1:
626                 case 2: // MPEG Video
627                         //addVideo(pid, "MPEG2");
628                         H264 = 0;
629                         if (VPID == 0)
630                                 VPID= pid;
631                         break;
632
633                 case 0x1B: // H.264 Video
634                         //addVideo(pid, "H.264");
635                         H264 = 1;
636                         if (VPID == 0)
637                                 VPID= pid;
638                         break;
639                 case 3:
640                 case 4: // MPEG Audio
641                         if (APID == 0)
642                                 APID =pid;
643                         lang = getDescriptor(pmt+b+5, pmt[b+4], LANGUAGE_DESCRIPTOR);
644                         ainfo->addAudio(pid, lang, "MPEG", eDVBAudio::aMPEG);
645                         break;
646
647                 case 0x80:
648                 case 0x81:  //private data of AC3 in ATSC
649                 case 0x82:
650                 case 0x83:
651                 case 6:
652                         lang = getDescriptor(pmt+b+5, pmt[b+4], LANGUAGE_DESCRIPTOR);
653                         pd_type = getDescriptor(pmt+b+5, pmt[b+4], REGISTRATION_DESCRIPTOR);
654                         //if (pd_type == "AC-3")
655                         // dirty dirty :-) Aber es funktioniert...
656                         if (lang.length() != 0)
657                         {
658                                 ainfo->addAudio(pid, lang, "AC-3", eDVBAudio::aAC3);
659                                 if (APID == 0)
660                                         APID =pid;
661                         }
662                         break;
663                 }
664                 b += 4 + pmt[b+4];
665         }
666         if (ainfo->audioStreams.size() > 0) {
667                 m_audioInfo = ainfo;
668                 return true;
669         } else {
670                 return false;
671         }
672 }
673
674 void eStreamThreadWeb::thread() {
675         const int bufsize = 40000;
676         unsigned char buf[bufsize];
677         bool eof = false;
678         fd_set rfds;
679         fd_set wfds;
680         struct timeval timeout;
681         int rc,r,w,maxfd;
682         time_t next_scantime = 0;
683         bool sosSend = false;
684         m_running = true;
685
686         r = w = 0;
687         hasStarted();
688         eDebug("eStreamThreadWeb started");
689         while (!m_stop) {
690                 pthread_testcancel();
691                 FD_ZERO(&rfds);
692                 FD_ZERO(&wfds);
693                 maxfd = 0;
694                 timeout.tv_sec = 1;
695                 timeout.tv_usec = 0;
696                 if (r < bufsize) {
697                         FD_SET(m_srcfd, &rfds);
698                         maxfd = MAX(maxfd, m_srcfd);
699                 }
700                 if (w < r) {
701                         FD_SET(m_destfd, &wfds);
702                         maxfd = MAX(maxfd, m_destfd);
703                 }
704                 rc = select(maxfd+1, &rfds, &wfds, NULL, &timeout);
705                 if (rc == 0) {
706                         eDebug("eStreamThreadWeb::thread: timeout!");
707                         continue;
708                 }
709                 if (rc < 0) {
710                         eDebug("eStreamThreadWeb::thread: error in select (%d)", errno);
711                         break;
712                 }
713                 if (FD_ISSET(m_srcfd, &rfds)) {
714                         rc = ::read(m_srcfd, buf+r, bufsize - r);
715                         if (rc < 0) {
716                                 eDebug("eStreamThreadWeb::thread: error in read (%d)", errno);
717                                 m_messagepump.send(evtReadError);
718                                 break;
719                         } else if (rc == 0) {
720                                 eof = true;
721                         } else {
722                                 if (!sosSend) {
723                                         sosSend = true;
724                                         m_messagepump.send(evtSOS);
725                                 }
726                                 r += rc;
727                                 if (r == bufsize) eDebug("eStreamThreadWeb::thread: buffer full");
728                         }
729                 }
730                 if (FD_ISSET(m_destfd, &wfds) && (w < r) && ((r > bufsize/4) || eof)) {
731                         rc = ::write(m_destfd, buf+w, r-w);
732                         if (rc < 0) {
733                                 eDebug("eStreamThreadWeb::thread: error in write (%d)", errno);
734                                 m_messagepump.send(evtWriteError);
735                                 break;
736                         }
737                         w += rc;
738                         //eDebug("eStreamThreadWeb::thread: buffer r=%d w=%d",r,w);
739                         if (w == r) {
740                                 if (time(0) >= next_scantime) {
741                                         if (scanAudioInfo(buf, r)) {
742                                                 m_messagepump.send(evtStreamInfo);
743                                                 next_scantime = time(0) + 1;
744                                         }
745                                 }
746                                 w = r = 0;
747                         }
748                 }
749                 if (eof && (r==w)) {
750                         m_messagepump.send(evtEOS);
751                         break;
752                 }
753         }
754         eDebug("eStreamThreadWeb end");
755 }
756
757 void eStreamThreadWeb::thread_finished() {
758         if (m_srcfd >= 0)
759                 ::close(m_srcfd);
760         eDebug("eStreamThreadWeb closed");
761         m_running = false;
762 }
763
764 eAutoInitPtr<eServiceFactoryWebTS> init_eServiceFactoryWebTS(eAutoInitNumbers::service+1, "eServiceFactoryWebTS");
765
766 PyMODINIT_FUNC
767 initservicewebts(void)
768 {
769         Py_InitModule("servicewebts", NULL);
770 }