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