Support turbo2.
[vuplus_dvbapp] / lib / service / servicem2ts.cpp
1 #include <lib/base/init_num.h>
2 #include <lib/base/init.h>
3 #include <lib/dvb/metaparser.h>
4 #include <lib/service/servicem2ts.h>
5
6 DEFINE_REF(eServiceFactoryM2TS)
7
8 class eM2TSFile: public iTsSource
9 {
10         DECLARE_REF(eM2TSFile);
11         eSingleLock m_lock;
12 public:
13         eM2TSFile(const char *filename, bool cached=false);
14         ~eM2TSFile();
15
16         // iTsSource
17         off_t lseek(off_t offset, int whence);
18         ssize_t read(off_t offset, void *buf, size_t count);
19         off_t length();
20         off_t offset();
21         int valid();
22 private:
23         int m_sync_offset;
24         int m_fd;     /* for uncached */
25         FILE *m_file; /* for cached */
26         off_t m_current_offset, m_length;
27         bool m_cached;
28         off_t lseek_internal(off_t offset, int whence);
29 };
30
31 class eStaticServiceM2TSInformation: public iStaticServiceInformation
32 {
33         DECLARE_REF(eStaticServiceM2TSInformation);
34         eServiceReference m_ref;
35         eDVBMetaParser m_parser;
36 public:
37         eStaticServiceM2TSInformation(const eServiceReference &ref);
38         RESULT getName(const eServiceReference &ref, std::string &name);
39         int getLength(const eServiceReference &ref);
40         RESULT getEvent(const eServiceReference &ref, ePtr<eServiceEvent> &SWIG_OUTPUT, time_t start_time);
41         int isPlayable(const eServiceReference &ref, const eServiceReference &ignore) { return 1; }
42         int getInfo(const eServiceReference &ref, int w);
43         std::string getInfoString(const eServiceReference &ref,int w);
44         PyObject *getInfoObject(const eServiceReference &r, int what);
45 };
46
47 DEFINE_REF(eStaticServiceM2TSInformation);
48
49 eStaticServiceM2TSInformation::eStaticServiceM2TSInformation(const eServiceReference &ref)
50 {
51         m_ref = ref;
52         m_parser.parseFile(ref.path);
53 }
54
55 RESULT eStaticServiceM2TSInformation::getName(const eServiceReference &ref, std::string &name)
56 {
57         ASSERT(ref == m_ref);
58         if (m_parser.m_name.size())
59                 name = m_parser.m_name;
60         else
61         {
62                 name = ref.path;
63                 size_t n = name.rfind('/');
64                 if (n != std::string::npos)
65                         name = name.substr(n + 1);
66         }
67         return 0;
68 }
69
70 int eStaticServiceM2TSInformation::getLength(const eServiceReference &ref)
71 {
72         ASSERT(ref == m_ref);
73         
74         eDVBTSTools tstools;
75         
76         struct stat s;
77         stat(ref.path.c_str(), &s);
78
79         eM2TSFile *file = new eM2TSFile(ref.path.c_str());
80         ePtr<iTsSource> source = file;
81
82         if (!source->valid())
83                 return 0;
84
85         tstools.setSource(source);
86
87                         /* check if cached data is still valid */
88         if (m_parser.m_data_ok && (s.st_size == m_parser.m_filesize) && (m_parser.m_length))
89                 return m_parser.m_length / 90000;
90
91         /* open again, this time with stream info */
92         tstools.setSource(source, ref.path.c_str());
93
94                         /* otherwise, re-calc length and update meta file */
95         pts_t len;
96         if (tstools.calcLen(len))
97                 return 0;
98
99         m_parser.m_length = len;
100         m_parser.m_filesize = s.st_size;
101         m_parser.updateMeta(ref.path);
102         return m_parser.m_length / 90000;
103 }
104
105 int eStaticServiceM2TSInformation::getInfo(const eServiceReference &ref, int w)
106 {
107         switch (w)
108         {
109         case iServiceInformation::sDescription:
110                 return iServiceInformation::resIsString;
111         case iServiceInformation::sServiceref:
112                 return iServiceInformation::resIsString;
113         case iServiceInformation::sFileSize:
114                 return m_parser.m_filesize;
115         case iServiceInformation::sTimeCreate:
116                 if (m_parser.m_time_create)
117                         return m_parser.m_time_create;
118                 else
119                         return iServiceInformation::resNA;
120         default:
121                 return iServiceInformation::resNA;
122         }
123 }
124
125 std::string eStaticServiceM2TSInformation::getInfoString(const eServiceReference &ref,int w)
126 {
127         switch (w)
128         {
129         case iServiceInformation::sDescription:
130                 return m_parser.m_description;
131         case iServiceInformation::sServiceref:
132                 return m_parser.m_ref.toString();
133         case iServiceInformation::sTags:
134                 return m_parser.m_tags;
135         default:
136                 return "";
137         }
138 }
139
140 PyObject *eStaticServiceM2TSInformation::getInfoObject(const eServiceReference &r, int what)
141 {
142         switch (what)
143         {
144         case iServiceInformation::sFileSize:
145                 return PyLong_FromLongLong(m_parser.m_filesize);
146         default:
147                 Py_RETURN_NONE;
148         }
149 }
150
151 RESULT eStaticServiceM2TSInformation::getEvent(const eServiceReference &ref, ePtr<eServiceEvent> &evt, time_t start_time)
152 {
153         if (!ref.path.empty())
154         {
155                 ePtr<eServiceEvent> event = new eServiceEvent;
156                 std::string filename = ref.path;
157                 filename.erase(filename.length()-4, 2);
158                 filename+="eit";
159                 if (!event->parseFrom(filename, (m_parser.m_ref.getTransportStreamID().get()<<16)|m_parser.m_ref.getOriginalNetworkID().get()))
160                 {
161                         evt = event;
162                         return 0;
163                 }
164         }
165         evt = 0;
166         return -1;
167 }
168
169 DEFINE_REF(eM2TSFile);
170
171 eM2TSFile::eM2TSFile(const char *filename, bool cached)
172         :m_lock(false), m_sync_offset(0), m_fd(-1), m_file(NULL), m_current_offset(0), m_length(0), m_cached(cached)
173 {
174         if (!m_cached)
175                 m_fd = ::open(filename, O_RDONLY | O_LARGEFILE);
176         else
177                 m_file = ::fopen64(filename, "rb");
178         if (valid())
179                 m_current_offset = m_length = lseek_internal(0, SEEK_END);
180 }
181
182 eM2TSFile::~eM2TSFile()
183 {
184         if (m_cached)
185         {
186                 if (m_file)
187                 {
188                         ::fclose(m_file);
189                         m_file = 0;
190                 }
191         }
192         else
193         {
194                 if (m_fd >= 0)
195                         ::close(m_fd);
196                 m_fd = -1;
197         }
198 }
199
200 off_t eM2TSFile::lseek(off_t offset, int whence)
201 {
202         eSingleLocker l(m_lock);
203
204         offset = (offset % 188) + (offset * 192) / 188;
205
206         if (offset != m_current_offset)
207                 m_current_offset = lseek_internal(offset, whence);
208
209         return m_current_offset;
210 }
211
212 off_t eM2TSFile::lseek_internal(off_t offset, int whence)
213 {
214         off_t ret;
215
216         if (!m_cached)
217                 ret = ::lseek(m_fd, offset, whence);
218         else
219         {
220                 if (::fseeko(m_file, offset, whence) < 0)
221                         perror("fseeko");
222                 ret = ::ftello(m_file);
223         }
224         return ret <= 0 ? ret : (ret % 192) + (ret*188) / 192;
225 }
226
227 ssize_t eM2TSFile::read(off_t offset, void *b, size_t count)
228 {
229         eSingleLocker l(m_lock);
230         unsigned char tmp[192*3];
231         unsigned char *buf = (unsigned char*)b;
232
233         size_t rd=0;
234         offset = (offset % 188) + (offset * 192) / 188;
235
236 sync:
237         if ((offset+m_sync_offset) != m_current_offset)
238         {
239 //              eDebug("seekTo %lld", offset+m_sync_offset);
240                 m_current_offset = lseek_internal(offset+m_sync_offset, SEEK_SET);
241                 if (m_current_offset < 0)
242                         return m_current_offset;
243         }
244
245         while (rd < count) {
246                 size_t ret;
247                 if (!m_cached)
248                         ret = ::read(m_fd, tmp, 192);
249                 else
250                         ret = ::fread(tmp, 1, 192, m_file);
251                 if (ret < 0 || ret < 192)
252                         return rd ? rd : ret;
253
254                 if (tmp[4] != 0x47)
255                 {
256                         if (rd > 0) {
257                                 eDebug("short read at pos %lld async!!", m_current_offset);
258                                 return rd;
259                         }
260                         else {
261                                 int x=0;
262                                 if (!m_cached)
263                                         ret = ::read(m_fd, tmp+192, 384);
264                                 else
265                                         ret = ::fread(tmp+192, 1, 384, m_file);
266
267 #if 0
268                                 eDebugNoNewLine("m2ts out of sync at pos %lld, real %lld:", offset + m_sync_offset, m_current_offset);
269                                 for (; x < 192; ++x)
270                                         eDebugNoNewLine(" %02x", tmp[x]);
271                                 eDebug("");
272                                 x=0;
273 #else
274                                 eDebug("m2ts out of sync at pos %lld, real %lld", offset + m_sync_offset, m_current_offset);
275 #endif
276                                 for (; x < 192; ++x)
277                                 {
278                                         if (tmp[x] == 0x47 && tmp[x+192] == 0x47)
279                                         {
280                                                 int add_offs = (x - 4);
281                                                 eDebug("sync found at pos %d, sync_offset is now %d, old was %d", x, add_offs + m_sync_offset, m_sync_offset);
282                                                 m_sync_offset += add_offs;
283                                                 goto sync;
284                                         }
285                                 }
286                         }
287                 }
288
289                 memcpy(buf+rd, tmp+4, 188);
290
291                 rd += 188;
292                 m_current_offset += 188;
293         }
294
295         m_sync_offset %= 188;
296
297         return rd;
298 }
299
300 int eM2TSFile::valid()
301 {
302         if (!m_cached)
303                 return m_fd != -1;
304         else
305                 return !!m_file;
306 }
307
308 off_t eM2TSFile::length()
309 {
310         return m_length;
311 }
312
313 off_t eM2TSFile::offset()
314 {
315         return m_current_offset;
316 }
317
318 eServiceFactoryM2TS::eServiceFactoryM2TS()
319 {
320         ePtr<eServiceCenter> sc;
321         eServiceCenter::getPrivInstance(sc);
322         if (sc)
323         {
324                 std::list<std::string> extensions;
325                 extensions.push_back("m2ts");
326                 extensions.push_back("mts");
327                 sc->addServiceFactory(eServiceFactoryM2TS::id, this, extensions);
328         }
329 }
330
331 eServiceFactoryM2TS::~eServiceFactoryM2TS()
332 {
333         ePtr<eServiceCenter> sc;
334         
335         eServiceCenter::getPrivInstance(sc);
336         if (sc)
337                 sc->removeServiceFactory(eServiceFactoryM2TS::id);
338 }
339
340 RESULT eServiceFactoryM2TS::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
341 {
342         ptr = new eServiceM2TS(ref);
343         return 0;
344 }
345
346 RESULT eServiceFactoryM2TS::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr)
347 {
348         ptr=0;
349         return -1;
350 }
351
352 RESULT eServiceFactoryM2TS::list(const eServiceReference &ref, ePtr<iListableService> &ptr)
353 {
354         ptr=0;
355         return -1;
356 }
357
358 RESULT eServiceFactoryM2TS::info(const eServiceReference &ref, ePtr<iStaticServiceInformation> &ptr)
359 {
360         ptr=new eStaticServiceM2TSInformation(ref);
361         return 0;
362 }
363
364 RESULT eServiceFactoryM2TS::offlineOperations(const eServiceReference &ref, ePtr<iServiceOfflineOperations> &ptr)
365 {
366         ptr = 0;
367         return -1;
368 }
369
370 eServiceM2TS::eServiceM2TS(const eServiceReference &ref)
371         :eDVBServicePlay(ref, NULL)
372 {
373 }
374
375 ePtr<iTsSource> eServiceM2TS::createTsSource(eServiceReferenceDVB &ref)
376 {
377         ePtr<iTsSource> source = new eM2TSFile(ref.path.c_str());
378         return source;
379 }
380
381 RESULT eServiceM2TS::isCurrentlySeekable()
382 {
383         return 1; // for fast winding we need index files... so only skip forward/backward yet
384 }
385
386 eAutoInitPtr<eServiceFactoryM2TS> init_eServiceFactoryM2TS(eAutoInitNumbers::service+1, "eServiceFactoryM2TS");