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