more work on servicem2ts
[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 iDataSource
9 {
10         DECLARE_REF(eM2TSFile);
11         eSingleLock m_lock;
12 public:
13         eM2TSFile(const char *filename, bool cached=false);
14         ~eM2TSFile();
15
16         // iDataSource
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_fd;     /* for uncached */
23         FILE *m_file; /* for cached */
24         off_t m_current_offset, m_length;
25         bool m_cached;
26         off_t lseek_internal(off_t offset, int whence);
27 };
28
29 class eStaticServiceM2TSInformation: public iStaticServiceInformation
30 {
31         DECLARE_REF(eStaticServiceM2TSInformation);
32         eServiceReference m_ref;
33         eDVBMetaParser m_parser;
34 public:
35         eStaticServiceM2TSInformation(const eServiceReference &ref);
36         RESULT getName(const eServiceReference &ref, std::string &name);
37         int getLength(const eServiceReference &ref);
38         RESULT getEvent(const eServiceReference &ref, ePtr<eServiceEvent> &SWIG_OUTPUT, time_t start_time);
39         int isPlayable(const eServiceReference &ref, const eServiceReference &ignore) { return 1; }
40         int getInfo(const eServiceReference &ref, int w);
41         std::string getInfoString(const eServiceReference &ref,int w);
42         PyObject *getInfoObject(const eServiceReference &r, int what);
43 };
44
45 DEFINE_REF(eStaticServiceM2TSInformation);
46
47 eStaticServiceM2TSInformation::eStaticServiceM2TSInformation(const eServiceReference &ref)
48 {
49         m_ref = ref;
50         m_parser.parseFile(ref.path);
51 }
52
53 RESULT eStaticServiceM2TSInformation::getName(const eServiceReference &ref, std::string &name)
54 {
55         ASSERT(ref == m_ref);
56         if (m_parser.m_name.size())
57                 name = m_parser.m_name;
58         else
59         {
60                 name = ref.path;
61                 size_t n = name.rfind('/');
62                 if (n != std::string::npos)
63                         name = name.substr(n + 1);
64         }
65         return 0;
66 }
67
68 int eStaticServiceM2TSInformation::getLength(const eServiceReference &ref)
69 {
70         ASSERT(ref == m_ref);
71         
72         eDVBTSTools tstools;
73         
74         struct stat s;
75         stat(ref.path.c_str(), &s);
76
77         eM2TSFile *file = new eM2TSFile(ref.path.c_str());
78         ePtr<iDataSource> source = file;
79
80         if (!source->valid())
81                 return 0;
82
83         tstools.setSource(source);
84
85                         /* check if cached data is still valid */
86         if (m_parser.m_data_ok && (s.st_size == m_parser.m_filesize) && (m_parser.m_length))
87                 return m_parser.m_length / 90000;
88
89         /* open again, this time with stream info */
90         tstools.setSource(source, ref.path.c_str());
91
92                         /* otherwise, re-calc length and update meta file */
93         pts_t len;
94         if (tstools.calcLen(len))
95                 return 0;
96
97         m_parser.m_length = len;
98         m_parser.m_filesize = s.st_size;
99         m_parser.updateMeta(ref.path);
100         return m_parser.m_length / 90000;
101 }
102
103 int eStaticServiceM2TSInformation::getInfo(const eServiceReference &ref, int w)
104 {
105         switch (w)
106         {
107         case iServiceInformation::sDescription:
108                 return iServiceInformation::resIsString;
109         case iServiceInformation::sServiceref:
110                 return iServiceInformation::resIsString;
111         case iServiceInformation::sFileSize:
112                 return m_parser.m_filesize;
113         case iServiceInformation::sTimeCreate:
114                 if (m_parser.m_time_create)
115                         return m_parser.m_time_create;
116                 else
117                         return iServiceInformation::resNA;
118         default:
119                 return iServiceInformation::resNA;
120         }
121 }
122
123 std::string eStaticServiceM2TSInformation::getInfoString(const eServiceReference &ref,int w)
124 {
125         switch (w)
126         {
127         case iServiceInformation::sDescription:
128                 return m_parser.m_description;
129         case iServiceInformation::sServiceref:
130                 return m_parser.m_ref.toString();
131         case iServiceInformation::sTags:
132                 return m_parser.m_tags;
133         default:
134                 return "";
135         }
136 }
137
138 PyObject *eStaticServiceM2TSInformation::getInfoObject(const eServiceReference &r, int what)
139 {
140         switch (what)
141         {
142         case iServiceInformation::sFileSize:
143                 return PyLong_FromLongLong(m_parser.m_filesize);
144         default:
145                 Py_RETURN_NONE;
146         }
147 }
148
149 RESULT eStaticServiceM2TSInformation::getEvent(const eServiceReference &ref, ePtr<eServiceEvent> &evt, time_t start_time)
150 {
151         if (!ref.path.empty())
152         {
153                 ePtr<eServiceEvent> event = new eServiceEvent;
154                 std::string filename = ref.path;
155                 filename.erase(filename.length()-4, 2);
156                 filename+="eit";
157                 if (!event->parseFrom(filename, (m_parser.m_ref.getTransportStreamID().get()<<16)|m_parser.m_ref.getOriginalNetworkID().get()))
158                 {
159                         evt = event;
160                         return 0;
161                 }
162         }
163         evt = 0;
164         return -1;
165 }
166
167 DEFINE_REF(eM2TSFile);
168
169 eM2TSFile::eM2TSFile(const char *filename, bool cached)
170         :m_lock(false), m_fd(-1), m_file(NULL), m_current_offset(0), m_length(0), m_cached(cached)
171 {
172         if (!m_cached)
173                 m_fd = ::open(filename, O_RDONLY | O_LARGEFILE);
174         else
175                 m_file = ::fopen64(filename, "rb");
176         if (valid())
177                 m_current_offset = m_length = lseek_internal(0, SEEK_END);
178 }
179
180 eM2TSFile::~eM2TSFile()
181 {
182         if (m_cached)
183         {
184                 if (m_file)
185                 {
186                         ::fclose(m_file);
187                         m_file = 0;
188                 }
189         }
190         else
191         {
192                 if (m_fd >= 0)
193                         ::close(m_fd);
194                 m_fd = -1;
195         }
196 }
197
198 off_t eM2TSFile::lseek(off_t offset, int whence)
199 {
200         eSingleLocker l(m_lock);
201
202         offset = (offset * 192) / 188;
203         ASSERT(!(offset % 192));
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*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];
230         unsigned char *buf = (unsigned char*)b;
231         size_t rd=0;
232
233         offset = (offset * 192) / 188;
234         ASSERT(!(offset % 192));
235         ASSERT(!(count % 188));
236
237         if (offset != m_current_offset)
238         {
239                 m_current_offset = lseek_internal(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                 memcpy(buf+rd, tmp+4, 188);
253
254                 ASSERT(buf[rd] == 0x47);
255
256                 rd += 188;
257                 m_current_offset += 188;
258         }
259
260         return rd;
261 }
262
263 int eM2TSFile::valid()
264 {
265         if (!m_cached)
266                 return m_fd != -1;
267         else
268                 return !!m_file;
269 }
270
271 off_t eM2TSFile::length()
272 {
273         return m_length;
274 }
275
276 eServiceFactoryM2TS::eServiceFactoryM2TS()
277 {
278         ePtr<eServiceCenter> sc;
279         eServiceCenter::getPrivInstance(sc);
280         if (sc)
281         {
282                 std::list<std::string> extensions;
283                 extensions.push_back("m2ts");
284                 extensions.push_back("mts");
285                 sc->addServiceFactory(eServiceFactoryM2TS::id, this, extensions);
286         }
287 }
288
289 eServiceFactoryM2TS::~eServiceFactoryM2TS()
290 {
291         ePtr<eServiceCenter> sc;
292         
293         eServiceCenter::getPrivInstance(sc);
294         if (sc)
295                 sc->removeServiceFactory(eServiceFactoryM2TS::id);
296 }
297
298 RESULT eServiceFactoryM2TS::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
299 {
300         ptr = new eServiceM2TS(ref);
301         return 0;
302 }
303
304 RESULT eServiceFactoryM2TS::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr)
305 {
306         ptr=0;
307         return -1;
308 }
309
310 RESULT eServiceFactoryM2TS::list(const eServiceReference &ref, ePtr<iListableService> &ptr)
311 {
312         ptr=0;
313         return -1;
314 }
315
316 RESULT eServiceFactoryM2TS::info(const eServiceReference &ref, ePtr<iStaticServiceInformation> &ptr)
317 {
318         ptr=new eStaticServiceM2TSInformation(ref);
319         return 0;
320 }
321
322 RESULT eServiceFactoryM2TS::offlineOperations(const eServiceReference &ref, ePtr<iServiceOfflineOperations> &ptr)
323 {
324         ptr = 0;
325         return -1;
326 }
327
328 eServiceM2TS::eServiceM2TS(const eServiceReference &ref)
329         :eDVBServicePlay(ref, NULL)
330 {
331 }
332
333 ePtr<iDataSource> eServiceM2TS::createDataSource(eServiceReferenceDVB &ref)
334 {
335         ePtr<iDataSource> source = new eM2TSFile(ref.path.c_str());
336         return source;
337 }
338
339 RESULT eServiceM2TS::isCurrentlySeekable()
340 {
341         return 1; // for fast winding we need index files... so only skip forward/backward yet
342 }
343
344 eAutoInitPtr<eServiceFactoryM2TS> init_eServiceFactoryM2TS(eAutoInitNumbers::service+1, "eServiceFactoryM2TS");