]> git.sesse.net Git - vlc/blob - modules/access/imem.c
imem: don't pass data pointer through MRL either
[vlc] / modules / access / imem.c
1 /*****************************************************************************
2  * imem.c : Memory input for VLC
3  *****************************************************************************
4  * Copyright (C) 2009 Laurent Aimar
5  * $Id$
6  *
7  * Author: Laurent Aimar <fenrir _AT_ videolan _DOT org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31 #include <limits.h>
32 #include <math.h>
33
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_demux.h>
37 #include <vlc_charset.h>
38
39 /*****************************************************************************
40  * Module descriptior
41  *****************************************************************************/
42 static int  Open (vlc_object_t *);
43 static void Close(vlc_object_t *);
44
45 #define CACHING_TEXT N_("Caching value in ms")
46 #define CACHING_LONGTEXT N_(\
47     "Caching value for imem streams. This " \
48     "value should be set in milliseconds.")
49
50 #define ID_TEXT N_("ID")
51 #define ID_LONGTEXT N_(\
52     "Set the ID of the elementary stream")
53
54 #define GROUP_TEXT N_("Group")
55 #define GROUP_LONGTEXT N_(\
56     "Set the group of the elementary stream")
57
58 #define CAT_TEXT N_("Category")
59 #define CAT_LONGTEXT N_(\
60     "Set the category of the elementary stream")
61 static const int cat_values[] = {
62     0, 1, 2, 3
63 };
64 static const char *cat_texts[] = {
65     N_("Unknown"), N_("Audio"), N_("Video"), N_("Subtitle")
66 };
67
68 #define CODEC_TEXT N_("Codec")
69 #define CODEC_LONGTEXT N_(\
70     "Set the codec of the elementary stream")
71
72 #define LANGUAGE_TEXT N_("Language")
73 #define LANGUAGE_LONGTEXT N_(\
74     "Language of the elementary stream as described by ISO639")
75
76 #define SAMPLERATE_TEXT N_("Sample rate")
77 #define SAMPLERATE_LONGTEXT N_(\
78     "Sample rate of an audio elementary stream")
79
80 #define CHANNELS_TEXT N_("Channels count")
81 #define CHANNELS_LONGTEXT N_(\
82     "Channels count of an audio elementary stream")
83
84 #define WIDTH_TEXT N_("Width")
85 #define WIDTH_LONGTEXT N_("Width of video or subtitle elementary streams")
86
87 #define HEIGHT_TEXT N_("Height")
88 #define HEIGHT_LONGTEXT N_("Height of video or subtitle elementary streams")
89
90 #define DAR_TEXT N_("Display aspect ratio")
91 #define DAR_LONGTEXT N_(\
92     "Display aspect ratio of a video elementary stream")
93
94 #define FPS_TEXT N_("Frame rate")
95 #define FPS_LONGTEXT N_(\
96     "Frame rate of a video elementary stream")
97
98 #define DATA_TEXT N_("Callback data")
99 #define DATA_LONGTEXT N_(\
100     "Data for the get and release functions")
101
102 #define GET_TEXT N_("Get function")
103 #define GET_LONGTEXT N_(\
104     "Address of the get callback function")
105
106 #define RELEASE_TEXT N_("Release function")
107 #define RELEASE_LONGTEXT N_(\
108     "Address of the release callback function")
109
110 vlc_module_begin()
111     set_shortname(N_("Memory input"))
112     set_description(N_("Memory input"))
113     set_category(CAT_INPUT)
114     set_subcategory(SUBCAT_INPUT_ACCESS)
115
116     add_integer("imem-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT, true)
117         change_private()
118     add_string ("imem-get", "0", NULL, GET_TEXT, GET_LONGTEXT, true)
119         change_volatile()
120     add_string ("imem-release", "0", NULL, RELEASE_TEXT, RELEASE_LONGTEXT, true)
121         change_volatile()
122     add_string ("imem-data", "0", NULL, DATA_TEXT, DATA_LONGTEXT, true)
123         change_volatile()
124
125     add_integer("imem-id", -1, NULL, ID_TEXT, ID_LONGTEXT, true)
126         change_private()
127     add_integer("imem-group", 0, NULL, GROUP_TEXT, GROUP_LONGTEXT, true)
128         change_private()
129     add_integer("imem-cat", 0, NULL, CAT_TEXT, CAT_LONGTEXT, true)
130         change_integer_list(cat_values, cat_texts, NULL)
131         change_private()
132     add_string ("imeme-codec", NULL, NULL, CODEC_TEXT, CODEC_LONGTEXT, true)
133         change_private()
134     add_string( "imem-language", NULL, NULL, LANGUAGE_TEXT, LANGUAGE_LONGTEXT, false)
135         change_private()
136
137     add_integer("imem-samplerate", 0, NULL, SAMPLERATE_TEXT, SAMPLERATE_LONGTEXT, true)
138         change_private()
139     add_integer("imem-channels", 0, NULL, CHANNELS_TEXT, CHANNELS_LONGTEXT, true)
140         change_private()
141
142     add_integer("imem-width", 0, NULL, WIDTH_TEXT, WIDTH_LONGTEXT, true)
143         change_private()
144     add_integer("imem-height", 0, NULL, HEIGHT_TEXT, HEIGHT_LONGTEXT, true)
145         change_private()
146     add_string ("imem-dar", NULL, NULL, DAR_TEXT, DAR_LONGTEXT, true)
147         change_private()
148     add_string ("imem-fps", NULL, NULL, FPS_TEXT, FPS_LONGTEXT, true)
149         change_private()
150
151     add_shortcut("imem")
152     set_capability("access_demux", 0)
153     set_callbacks(Open, Close)
154 vlc_module_end()
155
156 /*****************************************************************************
157  * Exported API
158  *****************************************************************************/
159
160 /* The clock origin for the DTS and PTS is assumed to be 0.
161  * A negative value means unknown.
162  *
163  * TODO define flags
164  */
165 typedef int  (*imem_get_t)(void *data,
166                            int64_t *dts, int64_t *pts, unsigned *flags,
167                            size_t *, void **);
168 typedef void (*imem_release_t)(void *data, size_t, void *);
169
170 /*****************************************************************************
171  * Local prototypes
172  *****************************************************************************/
173
174 /* */
175 static int Demux(demux_t *);
176 static int Control(demux_t *, int, va_list);
177
178 /* */
179 struct demux_sys_t {
180     struct {
181         imem_get_t      get;
182         imem_release_t  release;
183         void            *data;
184     } source;
185
186     es_out_id_t  *es;
187
188     mtime_t      pts_delay;
189
190     mtime_t      dts;
191
192     mtime_t      deadline;
193 };
194
195 static void ParseMRL(demux_t *);
196 static int var_CreateGetRational(demux_t *,
197                                  unsigned *num, unsigned *den,
198                                  const char *var);
199
200 /**
201  * It opens an imem access_demux
202  */
203 static int Open(vlc_object_t *object)
204 {
205     demux_t     *demux = (demux_t*)object;
206     char *tmp;
207
208         /* */
209     demux_sys_t *sys = calloc(1, sizeof(*sys));
210         if (!sys)
211                 return VLC_ENOMEM;
212
213     /* Read the user functions */
214     tmp = var_CreateGetString(demux, "imem-get");
215     if (tmp)
216         sys->source.get = (imem_get_t)(intptr_t)strtoll(tmp, NULL, 0);
217     free(tmp);
218
219     tmp = var_CreateGetString(demux, "imem-release");
220     if (tmp)
221         sys->source.release = (imem_release_t)(intptr_t)strtoll(tmp, NULL, 0);
222     free(tmp);
223
224     if (!sys->source.get || !sys->source.release) {
225         msg_Err(demux, "Invalid get/release function pointers");
226         free(sys);
227         return VLC_EGENERIC;
228     }
229
230     tmp = var_CreateGetString(demux, "imem-data");
231     if (tmp)
232         sys->source.data = (void*)(intptr_t)strtoll(tmp, NULL, 0);
233     free(tmp);
234
235     /* Now we can parse the MRL (get/release must not be parsed to avoid
236      * security risks) */
237     if (*demux->psz_path)
238         ParseMRL(demux);
239
240     msg_Dbg(demux, "Using get(%p) release(%p) and data(%p)",
241             sys->source.get, sys->source.release, sys->source.data);
242
243         /* ES format */
244     es_format_t fmt;
245         es_format_Init(&fmt, UNKNOWN_ES, 0);
246
247     fmt.i_id = var_CreateGetInteger(demux, "imem-id");
248     fmt.i_group = var_CreateGetInteger(demux, "imem-group");
249
250     tmp = var_CreateGetString(demux, "imem-codec");
251     if (tmp)
252         fmt.i_codec = vlc_fourcc_GetCodecFromString(UNKNOWN_ES, tmp);
253     free(tmp);
254
255     switch (var_CreateGetInteger(demux, "imem-cat")) {
256     case 1: {
257         fmt.i_cat = AUDIO_ES;
258         fmt.audio.i_channels = var_CreateGetInteger(demux, "imem-channels");
259         fmt.audio.i_rate = var_CreateGetInteger(demux, "imem-samplerate");
260
261         msg_Dbg(demux, "Audio %4.4s %d channels %d Hz",
262                 (const char *)&fmt.i_codec,
263                 fmt.audio.i_channels, fmt.audio.i_rate);
264         break;
265     }
266     case 2: {
267         fmt.i_cat = VIDEO_ES;
268         fmt.video.i_width  = var_CreateGetInteger(demux, "imem-width");
269         fmt.video.i_height = var_CreateGetInteger(demux, "imem-height");
270         unsigned num, den;
271         if (!var_CreateGetRational(demux, &num, &den, "imem-dar") && num > 0 && den > 0) {
272             if (fmt.video.i_width > 0 && fmt.video.i_height > 0) {
273                 fmt.video.i_sar_num = num * fmt.video.i_height;
274                 fmt.video.i_sar_den = den * fmt.video.i_width;
275             }
276         }
277         if (!var_CreateGetRational(demux, &num, &den, "imem-fps") && num > 0 && den > 0) {
278             fmt.video.i_frame_rate      = num;
279             fmt.video.i_frame_rate_base = den;
280         }
281
282         msg_Dbg(demux, "Video %4.4s %dx%d  SAR %d:%d frame rate %u/%u",
283                 (const char *)&fmt.i_codec,
284                 fmt.video.i_width, fmt.video.i_height,
285                 fmt.video.i_sar_num, fmt.video.i_sar_den,
286                 fmt.video.i_frame_rate, fmt.video.i_frame_rate_base);
287         break;
288     }
289     case 3: {
290         fmt.i_cat = SPU_ES;
291         fmt.subs.spu.i_original_frame_width =
292             var_CreateGetInteger(demux, "imem-width");
293         fmt.subs.spu.i_original_frame_height =
294             var_CreateGetInteger(demux, "imem-height");
295
296         msg_Dbg(demux, "Subtitle %4.4s",
297                 (const char *)&fmt.i_codec);
298         break;
299     }
300     default:
301         msg_Err(demux, "Invalid ES category");
302         es_format_Clean(&fmt);
303         free(sys);
304         return VLC_EGENERIC;
305     }
306
307     fmt.psz_language = var_CreateGetString(demux, "imem-language");
308
309     /* */
310         sys->es = es_out_Add(demux->out, &fmt);
311     es_format_Clean(&fmt);
312
313     if (!sys->es) {
314         free(sys);
315         return VLC_EGENERIC;
316     }
317
318     /* */
319     sys->pts_delay = var_CreateGetInteger(demux, "imem-caching") * INT64_C(1000);
320     sys->dts       = 0;
321     sys->deadline  = VLC_TS_INVALID;
322
323     /* Set up demux */
324     demux->pf_control = Control;
325     demux->pf_demux   = Demux;
326     demux->p_sys      = sys;
327
328     demux->info.i_update = 0;
329     demux->info.i_title = 0;
330     demux->info.i_seekpoint = 0;
331     return VLC_SUCCESS;
332 }
333
334 /**
335  * It closes an imem access_demux
336  */
337 static void Close(vlc_object_t *object)
338 {
339     demux_t     *demux = (demux_t *)object;
340
341     free(demux->p_sys);
342 }
343
344 /**
345  * It controls imem
346  */
347 static int Control(demux_t *demux, int i_query, va_list args)
348 {
349     demux_sys_t *sys = demux->p_sys;
350
351     switch (i_query)
352     {
353     case DEMUX_CAN_PAUSE:
354     case DEMUX_CAN_CONTROL_PACE: {
355         bool *b = va_arg(args, bool *);
356         *b = true;
357         return VLC_SUCCESS;
358     }
359     case DEMUX_SET_PAUSE_STATE:
360         return VLC_SUCCESS;
361
362     case DEMUX_GET_PTS_DELAY: {
363         int64_t *delay = va_arg(args, int64_t *);
364         *delay = sys->pts_delay;
365         return VLC_SUCCESS;
366     }
367     case DEMUX_GET_POSITION: {
368         double *position = va_arg(args, double *);
369         *position = 0.0;
370         return VLC_SUCCESS;
371     }
372     case DEMUX_GET_TIME: {
373         int64_t *t = va_arg(args, int64_t *);
374         *t = sys->dts;
375         return VLC_SUCCESS;
376     }
377     case DEMUX_GET_LENGTH: {
378         int64_t *l = va_arg(args, int64_t *);
379         *l = 0;
380         return VLC_SUCCESS;
381     }
382     case DEMUX_SET_NEXT_DEMUX_TIME:
383         sys->deadline = va_arg(args, int64_t);
384         return VLC_SUCCESS;
385
386     /* */
387     case DEMUX_CAN_SEEK:
388     case DEMUX_SET_POSITION:
389     case DEMUX_SET_TIME:
390     default:
391         return VLC_EGENERIC;
392     }
393
394     return VLC_EGENERIC;
395 }
396
397 /**
398  * It retreives data using the get() callback, sends them to es_out
399  * and the release it using the release() callback.
400  */
401 static int Demux(demux_t *demux)
402 {
403     demux_sys_t *sys = demux->p_sys;
404
405     if (sys->deadline == VLC_TS_INVALID)
406         sys->deadline = sys->dts + 1;
407
408     for (;;) {
409         if (sys->deadline <= sys->dts)
410             break;
411
412         /* */
413         int64_t dts, pts;
414         unsigned flags;
415         size_t buffer_size;
416         void   *buffer;
417
418         if (sys->source.get(sys->source.data, &dts, &pts, &flags, &buffer_size, &buffer))
419             return 0;
420
421         if (dts < 0)
422             dts = pts;
423
424         if (buffer_size > 0) {
425             block_t *block = block_New(demux, buffer_size);
426             if (block) {
427                 block->i_dts = dts >= 0 ? (1 + dts) : VLC_TS_INVALID;
428                 block->i_pts = pts >= 0 ? (1 + pts) : VLC_TS_INVALID;
429                 memcpy(block->p_buffer, buffer, buffer_size);
430
431                 es_out_Control(demux->out, ES_OUT_SET_PCR, block->i_dts);
432                 es_out_Send(demux->out, sys->es, block);
433             }
434         }
435
436         sys->dts = dts;
437
438         sys->source.release(sys->source.data, buffer_size, buffer);
439     }
440     sys->deadline = VLC_TS_INVALID;
441     return 1;
442 }
443
444 /**
445  * It parses a rational number (it also accepts basic float number).
446  *
447  * It returns an error if the rational number cannot be parsed (0/0 is valid).
448  */
449 static int var_CreateGetRational(demux_t *demux,
450                                  unsigned *num, unsigned *den,
451                                  const char *var)
452 {
453     /* */
454     *num = 0;
455     *den = 0;
456
457     /* */
458     char *tmp = var_CreateGetString(demux, var);
459     if (!tmp)
460         goto error;
461
462     char *next;
463     unsigned n = strtol(tmp,  &next, 0);
464     unsigned d = strtol(*next ? &next[1] : "0", NULL, 0);
465
466     if (*next == '.') {
467         /* Interpret as a float number */
468         double r = us_atof(tmp);
469         double c = ceil(r);
470         if (c >= UINT_MAX)
471             goto error;
472         unsigned m = c;
473         if (m > 0) {
474             d = UINT_MAX / m;
475             n = r * d;
476         } else {
477             n = 0;
478             d = 0;
479         }
480     }
481
482     if (n > 0 && d > 0)
483         vlc_ureduce(num, den, n, d, 0);
484
485     free(tmp);
486     return VLC_SUCCESS;
487
488 error:
489     free(tmp);
490     return VLC_EGENERIC;
491 }
492
493 /**
494  * Parse the MRL and extract configuration from it.
495  *
496  * Syntax: option1=value1[:option2=value2[...]]
497  *
498  * XXX get and release are not supported on purpose.
499  */
500 static void ParseMRL(demux_t *demux)
501 {
502     static const struct {
503         const char *name;
504         int        type;
505     } options[] = {
506         { "caching",    VLC_VAR_INTEGER },
507         { "id",         VLC_VAR_INTEGER },
508         { "group",      VLC_VAR_INTEGER },
509         { "cat",        VLC_VAR_INTEGER },
510         { "samplerate", VLC_VAR_INTEGER },
511         { "channels",   VLC_VAR_INTEGER },
512         { "width",      VLC_VAR_INTEGER },
513         { "height",     VLC_VAR_INTEGER },
514         { "data",       VLC_VAR_STRING },
515         { "codec",      VLC_VAR_STRING },
516         { "language",   VLC_VAR_STRING },
517         { "dar",        VLC_VAR_STRING },
518         { "fps",        VLC_VAR_STRING },
519         { NULL, -1 }
520     };
521
522     char *dup = strdup(demux->psz_path);
523     if (!dup)
524         return;
525     char *current = dup;
526
527     while (current) {
528         char *next = strchr(current, ':');
529         if (next)
530             *next++ = '\0';
531
532         char *option = current;
533         char *value = strchr(current, '=');
534         if (value) {
535             *value++ = '\0';
536             msg_Dbg(demux, "option '%s' value '%s'", option, value);
537         } else {
538             msg_Dbg(demux, "option '%s' without value (unsupported)", option);
539         }
540
541         char *name;
542         if (asprintf(&name, "imem-%s", option) < 0)
543             name = NULL;
544         for (unsigned i = 0; name && options[i].name; i++) {
545             if (strcmp(options[i].name, option))
546                 continue;
547             /* */
548             var_Create(demux, name, options[i].type | VLC_VAR_DOINHERIT);
549             if (options[i].type == VLC_VAR_INTEGER && value) {
550                 var_SetInteger(demux, name, strtol(value, NULL, 0));
551             } else if (options[i].type == VLC_VAR_STRING && value) {
552                 var_SetString(demux, name, value);
553             }
554             break;
555         }
556         free(name);
557
558         /* */
559         current = next;
560     }
561     free(dup);
562 }