]> git.sesse.net Git - vlc/blob - modules/access/imem.c
No functionnal changes.
[vlc] / modules / access / imem.c
1 /*****************************************************************************
2  * imem.c : Memory input for VLC
3  *****************************************************************************
4  * Copyright (C) 2009-2010 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  OpenDemux (vlc_object_t *);
43 static void CloseDemux(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 COOKIE_TEXT N_("Callback cookie string")
99 #define COOKIE_LONGTEXT N_(\
100     "Text identifier for the callback functions")
101
102 #define DATA_TEXT N_("Callback data")
103 #define DATA_LONGTEXT N_(\
104     "Data for the get and release functions")
105
106 #define GET_TEXT N_("Get function")
107 #define GET_LONGTEXT N_(\
108     "Address of the get callback function")
109
110 #define RELEASE_TEXT N_("Release function")
111 #define RELEASE_LONGTEXT N_(\
112     "Address of the release callback function")
113
114 vlc_module_begin()
115     set_shortname(N_("Memory input"))
116     set_description(N_("Memory input"))
117     set_category(CAT_INPUT)
118     set_subcategory(SUBCAT_INPUT_ACCESS)
119
120     add_integer("imem-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT, true)
121         change_private()
122     add_string ("imem-get", "0", NULL, GET_TEXT, GET_LONGTEXT, true)
123         change_volatile()
124     add_string ("imem-release", "0", NULL, RELEASE_TEXT, RELEASE_LONGTEXT, true)
125         change_volatile()
126     add_string ("imem-cookie", NULL, NULL, COOKIE_TEXT, COOKIE_LONGTEXT, true)
127         change_volatile()
128         change_safe()
129     add_string ("imem-data", "0", NULL, DATA_TEXT, DATA_LONGTEXT, true)
130         change_volatile()
131
132     add_integer("imem-id", -1, NULL, ID_TEXT, ID_LONGTEXT, true)
133         change_private()
134         change_safe()
135     add_integer("imem-group", 0, NULL, GROUP_TEXT, GROUP_LONGTEXT, true)
136         change_private()
137         change_safe()
138     add_integer("imem-cat", 0, NULL, CAT_TEXT, CAT_LONGTEXT, true)
139         change_integer_list(cat_values, cat_texts, NULL)
140         change_private()
141         change_safe()
142     add_string ("imem-codec", NULL, NULL, CODEC_TEXT, CODEC_LONGTEXT, true)
143         change_private()
144         change_safe()
145     add_string( "imem-language", NULL, NULL, LANGUAGE_TEXT, LANGUAGE_LONGTEXT, false)
146         change_private()
147         change_safe()
148
149     add_integer("imem-samplerate", 0, NULL, SAMPLERATE_TEXT, SAMPLERATE_LONGTEXT, true)
150         change_private()
151         change_safe()
152     add_integer("imem-channels", 0, NULL, CHANNELS_TEXT, CHANNELS_LONGTEXT, true)
153         change_private()
154         change_safe()
155
156     add_integer("imem-width", 0, NULL, WIDTH_TEXT, WIDTH_LONGTEXT, true)
157         change_private()
158         change_safe()
159     add_integer("imem-height", 0, NULL, HEIGHT_TEXT, HEIGHT_LONGTEXT, true)
160         change_private()
161         change_safe()
162     add_string ("imem-dar", NULL, NULL, DAR_TEXT, DAR_LONGTEXT, true)
163         change_private()
164         change_safe()
165     add_string ("imem-fps", NULL, NULL, FPS_TEXT, FPS_LONGTEXT, true)
166         change_private()
167         change_safe()
168
169     add_shortcut("imem")
170     set_capability("access_demux", 0)
171     set_callbacks(OpenDemux, CloseDemux)
172 vlc_module_end()
173
174 /*****************************************************************************
175  * Exported API
176  *****************************************************************************/
177
178 /* The clock origin for the DTS and PTS is assumed to be 0.
179  * A negative value means unknown.
180  *
181  * TODO define flags
182  */
183 typedef int  (*imem_get_t)(void *data, const char *cookie,
184                            int64_t *dts, int64_t *pts, unsigned *flags,
185                            size_t *, void **);
186 typedef void (*imem_release_t)(void *data, const char *cookie, size_t, void *);
187
188 /*****************************************************************************
189  * Local prototypes
190  *****************************************************************************/
191
192 /* */
193 static int Demux(demux_t *);
194 static int ControlDemux(demux_t *, int, va_list);
195
196 /* */
197 typedef struct {
198     struct {
199         imem_get_t      get;
200         imem_release_t  release;
201         void           *data;
202         char           *cookie;
203     } source;
204
205     es_out_id_t  *es;
206
207     mtime_t      pts_delay;
208
209     mtime_t      dts;
210
211     mtime_t      deadline;
212 } imem_sys_t;
213
214 static void ParseMRL(vlc_object_t *, const char *);
215 #define var_CreateGetRational(a,b,c,d) var_CreateGetRational(VLC_OBJECT(a),b,c,d)
216 static int (var_CreateGetRational)(vlc_object_t *,
217                                    unsigned *num, unsigned *den,
218                                    const char *var);
219
220 /**
221  * It closes the common part of the access and access_demux
222  */
223 static void CloseCommon(imem_sys_t *sys)
224 {
225     free(sys->source.cookie);
226     free(sys);
227 }
228
229 /**
230  * It initializes the common part for imem access/access_demux.
231  */
232 static int OpenCommon(vlc_object_t *object, imem_sys_t **sys_ptr, const char *psz_path)
233 {
234     char *tmp;
235
236         /* */
237     imem_sys_t *sys = calloc(1, sizeof(*sys));
238         if (!sys)
239                 return VLC_ENOMEM;
240
241     /* Read the user functions */
242     tmp = var_CreateGetString(object, "imem-get");
243     if (tmp)
244         sys->source.get = (imem_get_t)(intptr_t)strtoll(tmp, NULL, 0);
245     free(tmp);
246
247     tmp = var_CreateGetString(object, "imem-release");
248     if (tmp)
249         sys->source.release = (imem_release_t)(intptr_t)strtoll(tmp, NULL, 0);
250     free(tmp);
251
252     if (!sys->source.get || !sys->source.release) {
253         msg_Err(object, "Invalid get/release function pointers");
254         free(sys);
255         return VLC_EGENERIC;
256     }
257
258     tmp = var_CreateGetString(object, "imem-data");
259     if (tmp)
260         sys->source.data = (void *)(uintptr_t)strtoull(tmp, NULL, 0);
261     free(tmp);
262
263     /* Now we can parse the MRL (get/release must not be parsed to avoid
264      * security risks) */
265     if (*psz_path)
266         ParseMRL(object, psz_path);
267
268     sys->source.cookie = var_InheritString(object, "imem-cookie");
269
270     msg_Dbg(object, "Using get(%p), release(%p), data(%p), cookie(%s)",
271             sys->source.get, sys->source.release, sys->source.data,
272             sys->source.cookie ? sys->source.cookie : "(null)");
273
274     /* */
275     sys->pts_delay = var_CreateGetInteger(object, "imem-caching") * INT64_C(1000);
276     sys->dts       = 0;
277     sys->deadline  = VLC_TS_INVALID;
278
279     *sys_ptr = sys;
280     return VLC_SUCCESS;
281 }
282
283 /**
284  * It opens an imem access_demux.
285  */
286 static int OpenDemux(vlc_object_t *object)
287 {
288     demux_t    *demux = (demux_t *)object;
289     imem_sys_t *sys;
290
291     if (OpenCommon(VLC_OBJECT(demux), &sys, demux->psz_path))
292         return VLC_EGENERIC;
293
294         /* ES format */
295     es_format_t fmt;
296         es_format_Init(&fmt, UNKNOWN_ES, 0);
297
298     fmt.i_id = var_CreateGetInteger(object, "imem-id");
299     fmt.i_group = var_CreateGetInteger(object, "imem-group");
300
301     char *tmp = var_CreateGetString(object, "imem-codec");
302     if (tmp)
303         fmt.i_codec = vlc_fourcc_GetCodecFromString(UNKNOWN_ES, tmp);
304     free(tmp);
305
306     switch (var_CreateGetInteger(object, "imem-cat")) {
307     case 1: {
308         fmt.i_cat = AUDIO_ES;
309         fmt.audio.i_channels = var_CreateGetInteger(object, "imem-channels");
310         fmt.audio.i_rate = var_CreateGetInteger(object, "imem-samplerate");
311
312         msg_Dbg(object, "Audio %4.4s %d channels %d Hz",
313                 (const char *)&fmt.i_codec,
314                 fmt.audio.i_channels, fmt.audio.i_rate);
315         break;
316     }
317     case 2: {
318         fmt.i_cat = VIDEO_ES;
319         fmt.video.i_width  = var_CreateGetInteger(object, "imem-width");
320         fmt.video.i_height = var_CreateGetInteger(object, "imem-height");
321         unsigned num, den;
322         if (!var_CreateGetRational(object, &num, &den, "imem-dar") && num > 0 && den > 0) {
323             if (fmt.video.i_width > 0 && fmt.video.i_height > 0) {
324                 fmt.video.i_sar_num = num * fmt.video.i_height;
325                 fmt.video.i_sar_den = den * fmt.video.i_width;
326             }
327         }
328         if (!var_CreateGetRational(object, &num, &den, "imem-fps") && num > 0 && den > 0) {
329             fmt.video.i_frame_rate      = num;
330             fmt.video.i_frame_rate_base = den;
331         }
332
333         msg_Dbg(object, "Video %4.4s %dx%d  SAR %d:%d frame rate %u/%u",
334                 (const char *)&fmt.i_codec,
335                 fmt.video.i_width, fmt.video.i_height,
336                 fmt.video.i_sar_num, fmt.video.i_sar_den,
337                 fmt.video.i_frame_rate, fmt.video.i_frame_rate_base);
338         break;
339     }
340     case 3: {
341         fmt.i_cat = SPU_ES;
342         fmt.subs.spu.i_original_frame_width =
343             var_CreateGetInteger(object, "imem-width");
344         fmt.subs.spu.i_original_frame_height =
345             var_CreateGetInteger(object, "imem-height");
346
347         msg_Dbg(object, "Subtitle %4.4s",
348                 (const char *)&fmt.i_codec);
349         break;
350     }
351     default:
352         msg_Err(object, "Invalid ES category");
353         es_format_Clean(&fmt);
354         CloseCommon(sys);
355         return VLC_EGENERIC;
356     }
357
358     fmt.psz_language = var_CreateGetString(object, "imem-language");
359
360         sys->es = es_out_Add(demux->out, &fmt);
361     es_format_Clean(&fmt);
362
363     if (!sys->es) {
364         CloseCommon(sys);
365         return VLC_EGENERIC;
366     }
367
368     /* */
369     demux->pf_control = ControlDemux;
370     demux->pf_demux   = Demux;
371     demux->p_sys      = (demux_sys_t*)sys;
372
373     demux->info.i_update = 0;
374     demux->info.i_title = 0;
375     demux->info.i_seekpoint = 0;
376     return VLC_SUCCESS;
377 }
378
379 /**
380  * It closes an imem access_demux
381  */
382 static void CloseDemux(vlc_object_t *object)
383 {
384     demux_t *demux = (demux_t *)object;
385
386     CloseCommon((imem_sys_t*)demux->p_sys);
387 }
388
389 /**
390  * It controls imem
391  */
392 static int ControlDemux(demux_t *demux, int i_query, va_list args)
393 {
394     imem_sys_t *sys = (imem_sys_t*)demux->p_sys;
395
396     switch (i_query)
397     {
398     case DEMUX_CAN_PAUSE:
399     case DEMUX_CAN_CONTROL_PACE: {
400         bool *b = va_arg(args, bool *);
401         *b = true;
402         return VLC_SUCCESS;
403     }
404     case DEMUX_SET_PAUSE_STATE:
405         return VLC_SUCCESS;
406
407     case DEMUX_GET_PTS_DELAY: {
408         int64_t *delay = va_arg(args, int64_t *);
409         *delay = sys->pts_delay;
410         return VLC_SUCCESS;
411     }
412     case DEMUX_GET_POSITION: {
413         double *position = va_arg(args, double *);
414         *position = 0.0;
415         return VLC_SUCCESS;
416     }
417     case DEMUX_GET_TIME: {
418         int64_t *t = va_arg(args, int64_t *);
419         *t = sys->dts;
420         return VLC_SUCCESS;
421     }
422     case DEMUX_GET_LENGTH: {
423         int64_t *l = va_arg(args, int64_t *);
424         *l = 0;
425         return VLC_SUCCESS;
426     }
427     case DEMUX_SET_NEXT_DEMUX_TIME:
428         sys->deadline = va_arg(args, int64_t);
429         return VLC_SUCCESS;
430
431     /* */
432     case DEMUX_CAN_SEEK:
433     case DEMUX_SET_POSITION:
434     case DEMUX_SET_TIME:
435     default:
436         return VLC_EGENERIC;
437     }
438
439     return VLC_EGENERIC;
440 }
441
442 /**
443  * It retreives data using the get() callback, sends them to es_out
444  * and the release it using the release() callback.
445  */
446 static int Demux(demux_t *demux)
447 {
448     imem_sys_t *sys = (imem_sys_t*)demux->p_sys;
449
450     if (sys->deadline == VLC_TS_INVALID)
451         sys->deadline = sys->dts + 1;
452
453     for (;;) {
454         if (sys->deadline <= sys->dts)
455             break;
456
457         /* */
458         int64_t dts, pts;
459         unsigned flags;
460         size_t buffer_size;
461         void   *buffer;
462
463         if (sys->source.get(sys->source.data, sys->source.cookie,
464                             &dts, &pts, &flags, &buffer_size, &buffer))
465             return 0;
466
467         if (dts < 0)
468             dts = pts;
469
470         if (buffer_size > 0) {
471             block_t *block = block_New(demux, buffer_size);
472             if (block) {
473                 block->i_dts = dts >= 0 ? (1 + dts) : VLC_TS_INVALID;
474                 block->i_pts = pts >= 0 ? (1 + pts) : VLC_TS_INVALID;
475                 memcpy(block->p_buffer, buffer, buffer_size);
476
477                 es_out_Control(demux->out, ES_OUT_SET_PCR, block->i_dts);
478                 es_out_Send(demux->out, sys->es, block);
479             }
480         }
481
482         sys->dts = dts;
483
484         sys->source.release(sys->source.data, sys->source.cookie,
485                             buffer_size, buffer);
486     }
487     sys->deadline = VLC_TS_INVALID;
488     return 1;
489 }
490
491 /**
492  * It parses a rational number (it also accepts basic float number).
493  *
494  * It returns an error if the rational number cannot be parsed (0/0 is valid).
495  */
496 static int (var_CreateGetRational)(vlc_object_t *object,
497                                    unsigned *num, unsigned *den,
498                                    const char *var)
499 {
500     /* */
501     *num = 0;
502     *den = 0;
503
504     /* */
505     char *tmp = var_CreateGetString(object, var);
506     if (!tmp)
507         goto error;
508
509     char *next;
510     unsigned n = strtol(tmp,  &next, 0);
511     unsigned d = strtol(*next ? &next[1] : "0", NULL, 0);
512
513     if (*next == '.') {
514         /* Interpret as a float number */
515         double r = us_atof(tmp);
516         double c = ceil(r);
517         if (c >= UINT_MAX)
518             goto error;
519         unsigned m = c;
520         if (m > 0) {
521             d = UINT_MAX / m;
522             n = r * d;
523         } else {
524             n = 0;
525             d = 0;
526         }
527     }
528
529     if (n > 0 && d > 0)
530         vlc_ureduce(num, den, n, d, 0);
531
532     free(tmp);
533     return VLC_SUCCESS;
534
535 error:
536     free(tmp);
537     return VLC_EGENERIC;
538 }
539
540 /**
541  * Parse the MRL and extract configuration from it.
542  *
543  * Syntax: option1=value1[:option2=value2[...]]
544  *
545  * XXX get and release are not supported on purpose.
546  */
547 static void ParseMRL(vlc_object_t *object, const char *psz_path)
548 {
549     static const struct {
550         const char *name;
551         int        type;
552     } options[] = {
553         { "caching",    VLC_VAR_INTEGER },
554         { "id",         VLC_VAR_INTEGER },
555         { "group",      VLC_VAR_INTEGER },
556         { "cat",        VLC_VAR_INTEGER },
557         { "samplerate", VLC_VAR_INTEGER },
558         { "channels",   VLC_VAR_INTEGER },
559         { "width",      VLC_VAR_INTEGER },
560         { "height",     VLC_VAR_INTEGER },
561         { "cookie",     VLC_VAR_STRING },
562         { "codec",      VLC_VAR_STRING },
563         { "language",   VLC_VAR_STRING },
564         { "dar",        VLC_VAR_STRING },
565         { "fps",        VLC_VAR_STRING },
566         { NULL, -1 }
567     };
568
569     char *dup = strdup(psz_path);
570     if (!dup)
571         return;
572     char *current = dup;
573
574     while (current) {
575         char *next = strchr(current, ':');
576         if (next)
577             *next++ = '\0';
578
579         char *option = current;
580         char *value = strchr(current, '=');
581         if (value) {
582             *value++ = '\0';
583             msg_Dbg(object, "option '%s' value '%s'", option, value);
584         } else {
585             msg_Dbg(object, "option '%s' without value (unsupported)", option);
586         }
587
588         char *name;
589         if (asprintf(&name, "imem-%s", option) < 0)
590             name = NULL;
591         for (unsigned i = 0; name && options[i].name; i++) {
592             if (strcmp(options[i].name, option))
593                 continue;
594             /* */
595             var_Create(object, name, options[i].type | VLC_VAR_DOINHERIT);
596             if (options[i].type == VLC_VAR_INTEGER && value) {
597                 var_SetInteger(object, name, strtol(value, NULL, 0));
598             } else if (options[i].type == VLC_VAR_STRING && value) {
599                 var_SetString(object, name, value);
600             }
601             break;
602         }
603         free(name);
604
605         /* */
606         current = next;
607     }
608     free(dup);
609 }