whitespace fixes
[openblackhole/openblackhole-enigma2.git] / lib / service / servicem2ts.cpp
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <lib/base/init_num.h>
4 #include <lib/base/init.h>
5 #include <lib/dvb/metaparser.h>
6 #include <lib/service/servicem2ts.h>
7
8 DEFINE_REF(eServiceFactoryM2TS)
9
10 class eM2TSFile: public iTsSource
11 {
12         DECLARE_REF(eM2TSFile);
13         eSingleLock m_lock;
14 public:
15         eM2TSFile(const char *filename);
16         ~eM2TSFile();
17
18         // iTsSource
19         ssize_t read(off_t offset, void *buf, size_t count);
20         off_t length();
21         off_t offset();
22         int valid();
23 private:
24         int m_sync_offset;
25         int m_fd;
26         off_t m_current_offset;
27         off_t m_length;
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, bool simulate) { return 1; }
42         int getInfo(const eServiceReference &ref, int w);
43         std::string getInfoString(const eServiceReference &ref,int w);
44         long long getFileSize(const eServiceReference &ref);
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 long long eStaticServiceM2TSInformation::getFileSize(const eServiceReference &ref)
141 {
142         return m_parser.m_filesize;
143 }
144
145 RESULT eStaticServiceM2TSInformation::getEvent(const eServiceReference &ref, ePtr<eServiceEvent> &evt, time_t start_time)
146 {
147         if (!ref.path.empty())
148         {
149                 ePtr<eServiceEvent> event = new eServiceEvent;
150                 std::string filename = ref.path;
151                 filename.erase(filename.length()-4, 2);
152                 filename+="eit";
153                 if (!event->parseFrom(filename, (m_parser.m_ref.getTransportStreamID().get()<<16)|m_parser.m_ref.getOriginalNetworkID().get()))
154                 {
155                         evt = event;
156                         return 0;
157                 }
158         }
159         evt = 0;
160         return -1;
161 }
162
163 DEFINE_REF(eM2TSFile);
164
165 eM2TSFile::eM2TSFile(const char *filename):
166         m_lock(),
167         m_sync_offset(0),
168         m_fd(::open(filename, O_RDONLY | O_LARGEFILE)),
169         m_current_offset(0),
170         m_length(0)
171 {
172         if (m_fd != -1)
173                 m_current_offset = m_length = lseek_internal(0, SEEK_END);
174 }
175
176 eM2TSFile::~eM2TSFile()
177 {
178         if (m_fd != -1)
179                 ::close(m_fd);
180 }
181
182 off_t eM2TSFile::lseek_internal(off_t offset, int whence)
183 {
184         off_t ret;
185
186         ret = ::lseek(m_fd, offset, whence);
187         return ret <= 0 ? ret : (ret % 192) + (ret*188) / 192;
188 }
189
190 ssize_t eM2TSFile::read(off_t offset, void *b, size_t count)
191 {
192         eSingleLocker l(m_lock);
193         unsigned char tmp[192*3];
194         unsigned char *buf = (unsigned char*)b;
195
196         size_t rd=0;
197         offset = (offset % 188) + (offset * 192) / 188;
198
199 sync:
200         if ((offset+m_sync_offset) != m_current_offset)
201         {
202 //              eDebug("seekTo %lld", offset+m_sync_offset);
203                 m_current_offset = lseek_internal(offset+m_sync_offset, SEEK_SET);
204                 if (m_current_offset < 0)
205                         return m_current_offset;
206         }
207
208         while (rd < count) {
209                 size_t ret;
210                 ret = ::read(m_fd, tmp, 192);
211                 if (ret < 0 || ret < 192)
212                         return rd ? rd : ret;
213
214                 if (tmp[4] != 0x47)
215                 {
216                         if (rd > 0) {
217                                 eDebug("short read at pos %lld async!!", m_current_offset);
218                                 return rd;
219                         }
220                         else {
221                                 int x=0;
222                                 ret = ::read(m_fd, tmp+192, 384);
223
224 #if 0
225                                 eDebugNoNewLine("m2ts out of sync at pos %lld, real %lld:", offset + m_sync_offset, m_current_offset);
226                                 for (; x < 192; ++x)
227                                         eDebugNoNewLine(" %02x", tmp[x]);
228                                 eDebug("");
229                                 x=0;
230 #else
231                                 eDebug("m2ts out of sync at pos %lld, real %lld", offset + m_sync_offset, m_current_offset);
232 #endif
233                                 for (; x < 192; ++x)
234                                 {
235                                         if (tmp[x] == 0x47 && tmp[x+192] == 0x47)
236                                         {
237                                                 int add_offs = (x - 4);
238                                                 eDebug("sync found at pos %d, sync_offset is now %d, old was %d", x, add_offs + m_sync_offset, m_sync_offset);
239                                                 m_sync_offset += add_offs;
240                                                 goto sync;
241                                         }
242                                 }
243                         }
244                 }
245
246                 memcpy(buf+rd, tmp+4, 188);
247
248                 rd += 188;
249                 m_current_offset += 188;
250         }
251
252         m_sync_offset %= 188;
253
254         return rd;
255 }
256
257 int eM2TSFile::valid()
258 {
259         return m_fd != -1;
260 }
261
262 off_t eM2TSFile::length()
263 {
264         return m_length;
265 }
266
267 off_t eM2TSFile::offset()
268 {
269         return m_current_offset;
270 }
271
272 eServiceFactoryM2TS::eServiceFactoryM2TS()
273 {
274         ePtr<eServiceCenter> sc;
275         eServiceCenter::getPrivInstance(sc);
276         if (sc)
277         {
278                 std::list<std::string> extensions;
279                 extensions.push_back("m2ts");
280                 extensions.push_back("mts");
281                 sc->addServiceFactory(eServiceFactoryM2TS::id, this, extensions);
282         }
283 }
284
285 eServiceFactoryM2TS::~eServiceFactoryM2TS()
286 {
287         ePtr<eServiceCenter> sc;
288
289         eServiceCenter::getPrivInstance(sc);
290         if (sc)
291                 sc->removeServiceFactory(eServiceFactoryM2TS::id);
292 }
293
294 RESULT eServiceFactoryM2TS::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
295 {
296         ptr = new eServiceM2TS(ref);
297         return 0;
298 }
299
300 RESULT eServiceFactoryM2TS::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr)
301 {
302         ptr=0;
303         return -1;
304 }
305
306 RESULT eServiceFactoryM2TS::list(const eServiceReference &ref, ePtr<iListableService> &ptr)
307 {
308         ptr=0;
309         return -1;
310 }
311
312 RESULT eServiceFactoryM2TS::info(const eServiceReference &ref, ePtr<iStaticServiceInformation> &ptr)
313 {
314         ptr=new eStaticServiceM2TSInformation(ref);
315         return 0;
316 }
317
318 RESULT eServiceFactoryM2TS::offlineOperations(const eServiceReference &ref, ePtr<iServiceOfflineOperations> &ptr)
319 {
320         ptr = 0;
321         return -1;
322 }
323
324 eServiceM2TS::eServiceM2TS(const eServiceReference &ref)
325         :eDVBServicePlay(ref, NULL)
326 {
327 }
328
329 ePtr<iTsSource> eServiceM2TS::createTsSource(eServiceReferenceDVB &ref, int packetsize)
330 {
331         ePtr<iTsSource> source = new eM2TSFile(ref.path.c_str());
332         return source;
333 }
334
335 RESULT eServiceM2TS::isCurrentlySeekable()
336 {
337         return 1; // for fast winding we need index files... so only skip forward/backward yet
338 }
339
340 eAutoInitPtr<eServiceFactoryM2TS> init_eServiceFactoryM2TS(eAutoInitNumbers::service+1, "eServiceFactoryM2TS");