]> git.sesse.net Git - vlc/blob - modules/demux/asademux.c
dvb_scan: fix memleak.
[vlc] / modules / demux / asademux.c
1 /*****************************************************************************
2  * asademux.c: asa demuxer VM
3  *****************************************************************************
4  * Copyright (C) 2007 the VideoLAN team
5  *
6  * Originated from asa: portable digital subtitle renderer
7  *
8  * Authors: David Lamparter <equinox at diac24 dot net>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23  ****************************************************************************/
24
25 /****************************************************************************
26  * Changes from asa version:
27  *  - headers adapted
28  *  - external definition file support dropped
29  *  - integer timestamps
30  ****************************************************************************
31  * Please retain Linux kernel CodingStyle for sync.
32  * base commit d8c269b0fae9a8f8904e16e92313da165d664c74
33  ****************************************************************************/
34 #include "config.h"
35 #include <vlc_common.h>
36 #include <vlc_input.h>
37 #include <vlc_demux.h>
38
39 #include <limits.h>
40 #include <string.h>
41
42 #include "asademux.h"
43
44 #define MAXDELTA        4       /**< nr of times kept for delta backref */
45 #define MAXGROUP        24      /**< maximum number of regex match groups */
46
47 #define xfree free
48 static inline char *xstrdup(const char *str)
49 {
50     char *ret = strdup (str);
51     if (unlikely(ret == NULL))
52         abort();
53     return ret;
54 }
55
56 /** state of a running import */
57 struct asa_import_state {
58         demux_t *demux;                 /**< demuxer for msg* funcs */
59         const char *line;               /**< beginning of current line */
60         size_t remain;                  /**< remaining data at line */
61
62         char **matches;                 /**< active matchgroups */
63         unsigned nmatches;              /**< number of matchgroups */
64         char *selstr;                   /**< buffer for currently selected */
65         size_t sellen;                  /**< strlen of selstr */
66         char *out;                      /**< output buffer (NULL if empty) */
67         size_t outlen;                  /**< length of output string w/o \0 */
68
69         int64_t usecperf,               /**< microseconds per frame, active */
70                 origusecperf,           /**< microseconds per frame, from app */
71                 start,                  /**< start time */
72                 end;                    /**< end time */
73         int64_t delta[MAXDELTA];                /**< hist of last times for delta */
74
75         asa_import_callback *cb;        /**< commit callback */
76         void *cb_arg;                   /**< callback argument */
77 };
78
79 #define iargs struct asa_import_state *state, struct asa_import_insn *insn
80 /** asa instruction function.
81  * @param state import state
82  * @param insn instruction to execute
83  * @return status code.\n
84  *   0: continue
85  *   -1: restart from beginning
86  *   >0: break level
87  */
88 typedef int (*asa_import_func)(iargs);
89
90 static int asai_commit (iargs);
91 static int asai_discard(iargs);
92 static int asai_break  (iargs);
93 static int asai_select (iargs);
94 static int asai_sg     (iargs);
95 static int asai_sgu    (iargs);
96 static int asai_append (iargs);
97 static int asai_fps    (iargs);
98 static int asai_show   (iargs);
99 static int asai_hide   (iargs);
100 static int asai_child  (iargs);
101 #undef iargs
102
103 /** vm functions. KEEP IN SYNC WITH imports.h! */
104 static const asa_import_func importfuncs[] = {
105         asai_commit,
106         asai_discard,
107         asai_break,
108         asai_select,
109         asai_sg,
110         asai_sgu,
111         asai_append,
112         asai_fps,
113         asai_show,
114         asai_hide,
115         asai_child
116 };
117 #define ASAI_MAX (unsigned)(sizeof(importfuncs) / sizeof(importfuncs[0]))
118
119 struct asa_import_detect *asa_det_first = NULL,
120         **asa_det_last = &asa_det_first;
121 struct asa_import_format *asa_fmt_first = NULL,
122         **asa_fmt_last = &asa_fmt_first;
123
124 /** asa_imports_crosslink - resolve references in imports file.
125  * updates asa_import_format.prevtgt, asa_import_format.nexttgt,
126  * asa_import_detect.fmt
127  */
128 static void asa_imports_crosslink(void)
129 {
130         struct asa_import_format *fmt, *fmt2;
131         struct asa_import_detect *det;
132
133         for (fmt = asa_fmt_first; fmt; fmt = fmt->next) {
134                 for (fmt2 = fmt->next; fmt2; fmt2 = fmt2->next)
135                         if (!strcmp(fmt->name, fmt2->name)) {
136                                 fmt->nexttgt = fmt2;
137                                 fmt2->prevtgt = fmt;
138                                 break;
139                         }
140         }
141
142         for (det = asa_det_first; det; det = det->next) {
143                 det->fmt = NULL;
144                 for (fmt = asa_fmt_first; fmt; fmt = fmt->next)
145                         if (!strcmp(det->name, fmt->name)) {
146                                 det->fmt = fmt;
147                                 break;
148                         }
149         }
150 }
151
152 /** asa_imports_detect - autodetect subtitle format.
153  * @param data pointer to subtitle byte data
154  * @param datalen byte length of data
155  * @return the detect structure that hit or NULL if detection failed
156  */
157 struct asa_import_detect *asa_imports_detect(const void *data, size_t dlen)
158 {
159         struct asa_import_detect *det;
160         const char *d = (const char *)data;
161         int v[64];
162
163         if (dlen > 2048)
164                 dlen = 2048;
165         for (det = asa_det_first; det; det = det->next)
166                 if (pcre_exec(det->re.pcre, NULL, d, dlen, 0, 0, v, 64) >= 0)
167                         return det;
168         return NULL;
169 }
170
171 /** asai_run_insns - execute a list of instructions.
172  * @see asa_import_func */
173 static int asai_run_insns(struct asa_import_state *state,
174         struct asa_import_insn *insn)
175 {
176         struct asa_import_insn *inow = insn, preload;
177         int rv, repeating = 0;
178
179         preload.next = insn;
180         for (; inow; inow = inow->next) {
181                 if (repeating && inow->insn != ASAI_CHILD)
182                         continue;
183                 if (inow->insn >= ASAI_MAX)
184                         continue;
185                 rv = importfuncs[inow->insn](state, inow);
186                 if (rv == -1) {
187                         inow = &preload;
188                         continue;
189                 }
190                 if (rv > 0)
191                         return rv - 1;
192         }
193         /* ran through everything, let's try another round */
194         return -1;
195 }
196
197 /** asai_commit - commit a block to the user.
198  * @see asa_import_func */
199 static int asai_commit(struct asa_import_state *state,
200         struct asa_import_insn *insn)
201 {
202         int rv = 0;
203
204         if (!state->out)
205                 return 0;
206         if (state->outlen > 0)
207                 rv = state->cb(state->demux, state->cb_arg,
208                         state->start, state->end,
209                         state->out, state->outlen);
210         xfree(state->out);
211         state->out = NULL;
212         state->outlen = 0;
213         if (rv)
214                 return INT_MAX;
215         return 0;
216 }
217
218 /** asai_discard - clear the buffer without committing.
219  * @see asa_import_func */
220 static int asai_discard(struct asa_import_state *state,
221         struct asa_import_insn *insn)
222 {
223         if (!state->out)
224                 return 0;
225         xfree(state->out);
226         state->out = NULL;
227         state->outlen = 0;
228         return 0;
229 }
230
231 /** asai_break - jump out of child.
232  * @see asa_import_func */
233 static int asai_break(struct asa_import_state *state,
234         struct asa_import_insn *insn)
235 {
236         return insn->v.break_depth;
237 }
238
239 /** asai_select - choose a match group to be the active one.
240  * @see asa_import_func */
241 static int asai_select (struct asa_import_state *state,
242         struct asa_import_insn *insn)
243 {
244         if (insn->v.select < 0
245                 || (unsigned)insn->v.select >= state->nmatches) {
246                 msg_Err(state->demux, "import script trying to "
247                         "reference group %d, maximum is %d",
248                         insn->v.select, state->nmatches);
249                 return 0;
250         }
251         if (state->selstr)
252                 xfree(state->selstr);
253         state->selstr = xstrdup(state->matches[insn->v.select]);
254         state->sellen = strlen(state->selstr);
255         return 0;
256 }
257
258 #include <stddef.h>
259 static ptrdiff_t asai_process_replace(struct asa_import_state *state,
260         struct asa_import_insn *insn, int *v, int rv)
261 {
262         struct asa_repl *r;
263         char *newstr;
264         ptrdiff_t newpos, firstold;
265         size_t newstr_size;
266
267         newstr_size = v[0] * 2;
268         newstr = (char *)xmalloc(newstr_size);
269         memcpy(newstr, state->selstr, v[0]);
270         newpos = v[0];
271
272         for (r = insn->v.sg.repl; r; r = r->next) {
273                 size_t avail = newstr_size - newpos, need;
274                 const char *src;
275
276                 if (r->group >= rv) {
277                         msg_Err(state->demux,
278                                 "import script trying to replace by "
279                                 "reference group %d, maximum is %d",
280                                 r->group, rv);
281                         continue;
282                 }
283                 if (r->group >= 0) {
284                         need = v[r->group * 2 + 1] - v[r->group * 2];
285                         src = state->selstr + v[r->group * 2];
286                 } else {
287                         need = strlen(r->text);
288                         src = r->text;
289                 }
290                 if (need > avail) {
291                         newstr_size += need - avail + 256;
292                         newstr = (char *)xrealloc(newstr, newstr_size);
293                 }
294                 memcpy(newstr + newpos, src, need);
295                 newpos += need;
296         }
297         firstold = newpos;
298         newstr_size = newpos + state->sellen - v[1];
299         newstr = (char *)xrealloc(newstr, newstr_size + 1);
300         memcpy(newstr + newpos, state->selstr + v[1],
301                 state->sellen - v[1] + 1);
302         state->selstr = newstr;
303         state->sellen = newstr_size;
304         return firstold;
305 }
306
307 /** asai_sg - search and replace.
308  * @see asa_import_func */
309 static int asai_sg(struct asa_import_state *state,
310         struct asa_import_insn *insn)
311 {
312         int rv, v[MAXGROUP * 2];
313         char *oldstr;
314         ptrdiff_t s = 0;
315
316         if (!state->selstr)
317                 return 0;
318         while ((unsigned)s < state->sellen &&
319                 (rv = pcre_exec(insn->v.sg.regex.pcre, NULL, state->selstr,
320                         state->sellen, s, 0, v, MAXGROUP * 2)) >= 0) {
321                 oldstr = state->selstr;
322                 s = asai_process_replace(state, insn, v, rv);
323                 xfree(oldstr);
324         }
325         return 0;
326 }
327
328 /** asai_chunk_alloc - allocate composite chunk.
329  * @see asa_import_func */
330 static inline char **asai_chunk_alloc(char **old, int *v, int rv)
331 {
332         size_t s = rv * sizeof(char *);
333         int i;
334         for (i = 0; i < rv; i++)
335                 s += v[i * 2 + 1] - v[i * 2] + 1;
336         return (char **)xrealloc(old, s);
337 }
338
339 /** asai_set_matches - load result from pcre_exec into matches */
340 static void asai_set_matches(struct asa_import_state *state,
341         const char *src, int *v, int rv)
342 {
343         unsigned i;
344         char *dst;
345
346         state->matches = asai_chunk_alloc(state->matches, v, rv);
347         state->nmatches = rv;
348         dst = (char *)(state->matches + rv);
349         for (i = 0; i < state->nmatches; i++) {
350                 size_t len = v[2 * i + 1] - v[2 * i];
351                 state->matches[i] = dst;
352                 memcpy(dst, src + v[2 * i], len);
353                 dst[len] = '\0';
354                 dst += len + 1;
355         }
356         if (state->selstr)
357                 xfree(state->selstr);
358         state->selstr = xstrdup(state->matches[0]);
359         state->sellen = strlen(state->selstr);
360 }
361
362 /** asai_sgu - replace one time and update matches.
363  * @see asa_import_func */
364 static int asai_sgu(struct asa_import_state *state,
365         struct asa_import_insn *insn)
366 {
367         int rv, v[MAXGROUP * 2];
368         char *oldstr;
369
370         if (!state->selstr)
371                 return 0;
372         if ((rv = pcre_exec(insn->v.sg.regex.pcre, NULL, state->selstr,
373                         state->sellen, 0, 0, v, MAXGROUP * 2)) >= 0) {
374                 oldstr = state->selstr;
375                 asai_process_replace(state, insn, v, rv);
376
377                 asai_set_matches(state, oldstr, v, rv);
378                 xfree(oldstr);
379         }
380         return 0;
381 }
382
383 /** asai_append - append selected string to output buffer.
384  * @see asa_import_func */
385 static int asai_append (struct asa_import_state *state,
386         struct asa_import_insn *insn)
387 {
388         state->out = (char *)xrealloc(state->out,
389                 state->outlen + state->sellen + 1);
390         memcpy(state->out + state->outlen, state->selstr, state->sellen);
391         state->outlen += state->sellen;
392         state->out[state->outlen] = '\0';
393         return 0;
394 }
395
396 /** asai_fps - override fps.
397  * @see asa_import_func */
398 static int asai_fps(struct asa_import_state *state,
399         struct asa_import_insn *insn)
400 {
401         if (insn->v.fps_value == 0)
402                 state->usecperf = state->origusecperf;
403         else
404                 state->usecperf = (int64_t)(1000000. / insn->v.fps_value);
405         return 0;
406 }
407
408 /** asai_gettime - walk asa_tspec and sum up the time.
409  * @see asa_import_func
410  * @return the calculated time, delta[0] on error
411  * also updates the delta history.
412  */
413 static int64_t asai_gettime(struct asa_import_state *state,
414         struct asa_import_insn *insn)
415 {
416         struct asa_tspec *tsp;
417         int64_t t = 0;
418         if (insn->v.tspec.delta_select != -1) {
419                 if (insn->v.tspec.delta_select < MAXDELTA)
420                         t += state->delta[insn->v.tspec.delta_select];
421                 else
422                         msg_Err(state->demux, "imports: tspec "
423                                 "delta %d exceeds compiled-in maximum of %d",
424                                 insn->v.tspec.delta_select, MAXDELTA);
425         }
426         for (tsp = insn->v.tspec.tsp; tsp; tsp = tsp->next) {
427                 char *errptr;
428                 double src;
429
430                 if ((unsigned)tsp->group >= state->nmatches) {
431                         msg_Err(state->demux, "imports: tspec "
432                                 "tries to access group %d, but only "
433                                 "%d groups exist",
434                                 tsp->group, state->nmatches);
435                         continue;
436                 }
437                 if (!*state->matches[tsp->group])
438                         continue;
439                 src = strtod(state->matches[tsp->group], &errptr);
440                 if (*errptr)
441                         msg_Warn(state->demux, "imports: invalid tspec '%s'",
442                                 state->matches[tsp->group]);
443                 t += (src * tsp->mult * 1000000)
444                         + src * tsp->fps_mult * state->usecperf;
445         }
446         memmove(state->delta + 1, state->delta,
447                 sizeof(state->delta[0]) * (MAXDELTA - 1));
448         state->delta[0] = t;
449         return t;
450 }
451
452 /** asai_show - set start time.
453  * @see asa_import_func */
454 static int asai_show(struct asa_import_state *state,
455         struct asa_import_insn *insn)
456 {
457         state->start = asai_gettime(state, insn);
458         return 0;
459 }
460
461 /** asai_hide - set end time.
462  * @see asa_import_func */
463 static int asai_hide(struct asa_import_state *state,
464         struct asa_import_insn *insn)
465 {
466         state->end = asai_gettime(state, insn);
467         return 0;
468 }
469
470 /** asai_child - execute childs if we match.
471  * @see asa_import_func */
472 static int asai_child(struct asa_import_state *state,
473         struct asa_import_insn *insn)
474 {
475         int rv, v[MAXGROUP * 2];
476         if ((rv = pcre_exec(insn->v.child.regex.pcre, NULL, state->line,
477                         state->remain, 0, 0, v, MAXGROUP * 2)) >= 0) {
478                 asai_set_matches(state, state->line, v, rv);
479                 state->line += v[1];
480                 state->remain -= v[1];
481                 rv = asai_run_insns(state, insn->v.child.insns);
482                 return rv;
483         }
484         return 0;
485 }
486
487 int asa_import(demux_t *d, const void *data, size_t dlen,
488         int64_t usecperframe, struct asa_import_detect *det,
489         asa_import_callback *callback, void *arg)
490 {
491         struct asa_import_format *fmt = det->fmt;
492         struct asa_import_state state;
493         int rv;
494
495         memset(&state, 0, sizeof(state));
496         state.demux = d;
497         state.usecperf = state.origusecperf = usecperframe;
498         state.line = (const char *)data;
499         state.remain = dlen;
500         state.cb = callback;
501         state.cb_arg = arg;
502
503         rv = asai_run_insns(&state, fmt->insns);
504         if (state.matches)
505                 xfree(state.matches);
506         if (state.out) {
507                 callback(d, arg, state.start, state.end,
508                         state.out, state.outlen);
509                 xfree(state.out);
510         }
511         if (state.selstr)
512                 xfree(state.selstr);
513         return rv;
514 }
515
516 int asa_pcre_compile(asa_pcre *out, const char *str)
517 {
518         const char *err;
519         int ec, eo;
520
521         out->pcre = pcre_compile2(str, 0, &ec, &err, &eo, NULL);
522         if (out->pcre)
523                 return 0;
524         return 1;
525 }
526
527 #include "asademux_defs.h"
528
529 void asa_init_import()
530 {
531         static int setup = 0;
532         if (setup)
533                 return;
534
535         preparse_add();
536         asa_imports_crosslink();
537         setup = 1;
538 }