]> git.sesse.net Git - vlc/blob - modules/access/imem.c
Update LGPL license blurb, choosing v2.1+.
[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_access.h>
37 #include <vlc_demux.h>
38 #include <vlc_charset.h>
39
40 /*****************************************************************************
41  * Module descriptior
42  *****************************************************************************/
43 static int  OpenAccess (vlc_object_t *);
44 static void CloseAccess(vlc_object_t *);
45
46 static int  OpenDemux (vlc_object_t *);
47 static void CloseDemux(vlc_object_t *);
48
49 #define CACHING_TEXT N_("Caching value in ms")
50 #define CACHING_LONGTEXT N_(\
51     "Caching value for imem streams. This " \
52     "value should be set in milliseconds.")
53
54 #define ID_TEXT N_("ID")
55 #define ID_LONGTEXT N_(\
56     "Set the ID of the elementary stream")
57
58 #define GROUP_TEXT N_("Group")
59 #define GROUP_LONGTEXT N_(\
60     "Set the group of the elementary stream")
61
62 #define CAT_TEXT N_("Category")
63 #define CAT_LONGTEXT N_(\
64     "Set the category of the elementary stream")
65 static const int cat_values[] = {
66     0, 1, 2, 3, 4,
67 };
68 static const char *cat_texts[] = {
69     N_("Unknown"), N_("Audio"), N_("Video"), N_("Subtitle"), N_("Data")
70 };
71
72 #define CODEC_TEXT N_("Codec")
73 #define CODEC_LONGTEXT N_(\
74     "Set the codec of the elementary stream")
75
76 #define LANGUAGE_TEXT N_("Language")
77 #define LANGUAGE_LONGTEXT N_(\
78     "Language of the elementary stream as described by ISO639")
79
80 #define SAMPLERATE_TEXT N_("Sample rate")
81 #define SAMPLERATE_LONGTEXT N_(\
82     "Sample rate of an audio elementary stream")
83
84 #define CHANNELS_TEXT N_("Channels count")
85 #define CHANNELS_LONGTEXT N_(\
86     "Channels count of an audio elementary stream")
87
88 #define WIDTH_TEXT N_("Width")
89 #define WIDTH_LONGTEXT N_("Width of video or subtitle elementary streams")
90
91 #define HEIGHT_TEXT N_("Height")
92 #define HEIGHT_LONGTEXT N_("Height of video or subtitle elementary streams")
93
94 #define DAR_TEXT N_("Display aspect ratio")
95 #define DAR_LONGTEXT N_(\
96     "Display aspect ratio of a video elementary stream")
97
98 #define FPS_TEXT N_("Frame rate")
99 #define FPS_LONGTEXT N_(\
100     "Frame rate of a video elementary stream")
101
102 #define COOKIE_TEXT N_("Callback cookie string")
103 #define COOKIE_LONGTEXT N_(\
104     "Text identifier for the callback functions")
105
106 #define DATA_TEXT N_("Callback data")
107 #define DATA_LONGTEXT N_(\
108     "Data for the get and release functions")
109
110 #define GET_TEXT N_("Get function")
111 #define GET_LONGTEXT N_(\
112     "Address of the get callback function")
113
114 #define RELEASE_TEXT N_("Release function")
115 #define RELEASE_LONGTEXT N_(\
116     "Address of the release callback function")
117
118 #define SIZE_TEXT N_("Size")
119 #define SIZE_LONGTEXT N_(\
120     "Size of stream in bytes")
121
122 vlc_module_begin()
123     set_shortname(N_("Memory input"))
124     set_description(N_("Memory input"))
125     set_category(CAT_INPUT)
126     set_subcategory(SUBCAT_INPUT_ACCESS)
127
128     add_integer("imem-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT, true)
129         change_private()
130     add_string ("imem-get", "0", NULL, GET_TEXT, GET_LONGTEXT, true)
131         change_volatile()
132     add_string ("imem-release", "0", NULL, RELEASE_TEXT, RELEASE_LONGTEXT, true)
133         change_volatile()
134     add_string ("imem-cookie", NULL, NULL, COOKIE_TEXT, COOKIE_LONGTEXT, true)
135         change_volatile()
136         change_safe()
137     add_string ("imem-data", "0", NULL, DATA_TEXT, DATA_LONGTEXT, true)
138         change_volatile()
139
140     add_integer("imem-id", -1, NULL, ID_TEXT, ID_LONGTEXT, true)
141         change_private()
142         change_safe()
143     add_integer("imem-group", 0, NULL, GROUP_TEXT, GROUP_LONGTEXT, true)
144         change_private()
145         change_safe()
146     add_integer("imem-cat", 0, NULL, CAT_TEXT, CAT_LONGTEXT, true)
147         change_integer_list(cat_values, cat_texts, NULL)
148         change_private()
149         change_safe()
150     add_string ("imem-codec", NULL, NULL, CODEC_TEXT, CODEC_LONGTEXT, true)
151         change_private()
152         change_safe()
153     add_string( "imem-language", NULL, NULL, LANGUAGE_TEXT, LANGUAGE_LONGTEXT, false)
154         change_private()
155         change_safe()
156
157     add_integer("imem-samplerate", 0, NULL, SAMPLERATE_TEXT, SAMPLERATE_LONGTEXT, true)
158         change_private()
159         change_safe()
160     add_integer("imem-channels", 0, NULL, CHANNELS_TEXT, CHANNELS_LONGTEXT, true)
161         change_private()
162         change_safe()
163
164     add_integer("imem-width", 0, NULL, WIDTH_TEXT, WIDTH_LONGTEXT, true)
165         change_private()
166         change_safe()
167     add_integer("imem-height", 0, NULL, HEIGHT_TEXT, HEIGHT_LONGTEXT, true)
168         change_private()
169         change_safe()
170     add_string ("imem-dar", NULL, NULL, DAR_TEXT, DAR_LONGTEXT, true)
171         change_private()
172         change_safe()
173     add_string ("imem-fps", NULL, NULL, FPS_TEXT, FPS_LONGTEXT, true)
174         change_private()
175         change_safe()
176
177     add_integer ("imem-size", 0, NULL, SIZE_TEXT, SIZE_LONGTEXT, true)
178         change_private()
179         change_safe()
180
181     add_shortcut("imem")
182     set_capability("access_demux", 0)
183     set_callbacks(OpenDemux, CloseDemux)
184
185     add_submodule()
186         add_shortcut("imem")
187         set_capability("access", 0)
188         set_callbacks(OpenAccess, CloseAccess)
189 vlc_module_end()
190
191 /*****************************************************************************
192  * Exported API
193  *****************************************************************************/
194
195 /* The clock origin for the DTS and PTS is assumed to be 0.
196  * A negative value means unknown.
197  *
198  * TODO define flags
199  */
200 typedef int  (*imem_get_t)(void *data, const char *cookie,
201                            int64_t *dts, int64_t *pts, unsigned *flags,
202                            size_t *, void **);
203 typedef void (*imem_release_t)(void *data, const char *cookie, size_t, void *);
204
205 /*****************************************************************************
206  * Local prototypes
207  *****************************************************************************/
208
209 /* */
210 static block_t *Block(access_t *);
211 static int ControlAccess(access_t *, int, va_list);
212
213 static int Demux(demux_t *);
214 static int ControlDemux(demux_t *, int, va_list);
215
216 /* */
217 typedef struct {
218     struct {
219         imem_get_t      get;
220         imem_release_t  release;
221         void           *data;
222         char           *cookie;
223     } source;
224
225     es_out_id_t  *es;
226
227     mtime_t      pts_delay;
228
229     mtime_t      dts;
230
231     mtime_t      deadline;
232 } imem_sys_t;
233
234 static void ParseMRL(vlc_object_t *, const char *);
235
236 /**
237  * It closes the common part of the access and access_demux
238  */
239 static void CloseCommon(imem_sys_t *sys)
240 {
241     free(sys->source.cookie);
242     free(sys);
243 }
244
245 /**
246  * It initializes the common part for imem access/access_demux.
247  */
248 static int OpenCommon(vlc_object_t *object, imem_sys_t **sys_ptr, const char *psz_path)
249 {
250     char *tmp;
251
252     /* */
253     imem_sys_t *sys = calloc(1, sizeof(*sys));
254     if (!sys)
255         return VLC_ENOMEM;
256
257     /* Read the user functions */
258     tmp = var_InheritString(object, "imem-get");
259     if (tmp)
260         sys->source.get = (imem_get_t)(intptr_t)strtoll(tmp, NULL, 0);
261     free(tmp);
262
263     tmp = var_InheritString(object, "imem-release");
264     if (tmp)
265         sys->source.release = (imem_release_t)(intptr_t)strtoll(tmp, NULL, 0);
266     free(tmp);
267
268     if (!sys->source.get || !sys->source.release) {
269         msg_Err(object, "Invalid get/release function pointers");
270         free(sys);
271         return VLC_EGENERIC;
272     }
273
274     tmp = var_InheritString(object, "imem-data");
275     if (tmp)
276         sys->source.data = (void *)(uintptr_t)strtoull(tmp, NULL, 0);
277     free(tmp);
278
279     /* Now we can parse the MRL (get/release must not be parsed to avoid
280      * security risks) */
281     if (*psz_path)
282         ParseMRL(object, psz_path);
283
284     sys->source.cookie = var_InheritString(object, "imem-cookie");
285
286     msg_Dbg(object, "Using get(%p), release(%p), data(%p), cookie(%s)",
287             sys->source.get, sys->source.release, sys->source.data,
288             sys->source.cookie ? sys->source.cookie : "(null)");
289
290     /* */
291     sys->pts_delay = var_InheritInteger(object, "imem-caching") * INT64_C(1000);
292     sys->dts       = 0;
293     sys->deadline  = VLC_TS_INVALID;
294
295     *sys_ptr = sys;
296     return VLC_SUCCESS;
297 }
298
299 /**
300  * It opens an imem access.
301  */
302 static int OpenAccess(vlc_object_t *object)
303 {
304     access_t   *access = (access_t *)object;
305     imem_sys_t *sys;
306
307     if (OpenCommon(object, &sys, access->psz_location))
308         return VLC_EGENERIC;
309
310     if (var_InheritInteger(object, "imem-cat") != 4) {
311         CloseCommon(sys);
312         return VLC_EGENERIC;
313     }
314
315     /* */
316     access_InitFields(access);
317     access->pf_control = ControlAccess;
318     access->pf_read    = NULL;
319     access->pf_block   = Block;
320     access->pf_seek    = NULL;
321     access->p_sys      = (access_sys_t*)sys;
322     access->info.i_size = var_InheritInteger(object, "imem-size");
323
324     return VLC_SUCCESS;
325 }
326
327 /**
328  * It closes an imem access
329  */
330 static void CloseAccess(vlc_object_t *object)
331 {
332     access_t *access = (access_t *)object;
333
334     CloseCommon((imem_sys_t*)access->p_sys);
335 }
336
337 /**
338  * It controls an imem access
339  */
340 static int ControlAccess(access_t *access, int i_query, va_list args)
341 {
342     imem_sys_t *sys = (imem_sys_t*)access->p_sys;
343
344     switch (i_query)
345     {
346     case ACCESS_CAN_SEEK:
347     case ACCESS_CAN_FASTSEEK: {
348         bool *b = va_arg( args, bool* );
349         *b = false;
350         return VLC_SUCCESS;
351     }
352     case ACCESS_CAN_PAUSE:
353     case ACCESS_CAN_CONTROL_PACE: {
354         bool *b = va_arg( args, bool* );
355         *b = true;
356         return VLC_SUCCESS;
357     }
358     case ACCESS_GET_PTS_DELAY: {
359         int64_t *delay = va_arg(args, int64_t *);
360         *delay = sys->pts_delay;
361         return VLC_SUCCESS;
362     }
363     case ACCESS_SET_PAUSE_STATE:
364         return VLC_SUCCESS;
365
366     case ACCESS_GET_TITLE_INFO:
367     case ACCESS_SET_TITLE:
368     case ACCESS_SET_SEEKPOINT:
369     case ACCESS_SET_PRIVATE_ID_STATE:
370     case ACCESS_GET_META:
371     case ACCESS_GET_PRIVATE_ID_STATE:
372     case ACCESS_GET_CONTENT_TYPE:
373     default:
374         return VLC_EGENERIC;
375     }
376 }
377
378 /**
379  * It retreives data using the get() callback, copies them,
380  * and then release them using the release() callback.
381  */
382 static block_t *Block(access_t *access)
383 {
384     imem_sys_t *sys = (imem_sys_t*)access->p_sys;
385
386     unsigned flags;
387     size_t buffer_size;
388     void   *buffer;
389
390     if (sys->source.get(sys->source.data, sys->source.cookie,
391                         NULL, NULL, &flags, &buffer_size, &buffer)) {
392         access->info.b_eof = true;
393         return NULL;
394     }
395
396     block_t *block = NULL;
397     if (buffer_size > 0) {
398         block = block_New(access, buffer_size);
399         if (block)
400             memcpy(block->p_buffer, buffer, buffer_size);
401     }
402
403     sys->source.release(sys->source.data, sys->source.cookie,
404                         buffer_size, buffer);
405     return block;
406 }
407
408 /**
409  * It opens an imem access_demux.
410  */
411 static int OpenDemux(vlc_object_t *object)
412 {
413     demux_t    *demux = (demux_t *)object;
414     imem_sys_t *sys;
415
416     if (OpenCommon(object, &sys, demux->psz_location))
417         return VLC_EGENERIC;
418
419     /* ES format */
420     es_format_t fmt;
421     es_format_Init(&fmt, UNKNOWN_ES, 0);
422
423     fmt.i_id = var_InheritInteger(object, "imem-id");
424     fmt.i_group = var_InheritInteger(object, "imem-group");
425
426     char *tmp = var_InheritString(object, "imem-codec");
427     if (tmp)
428         fmt.i_codec = vlc_fourcc_GetCodecFromString(UNKNOWN_ES, tmp);
429     free(tmp);
430
431     const int cat = var_InheritInteger(object, "imem-cat");
432     switch (cat) {
433     case 1: {
434         fmt.i_cat = AUDIO_ES;
435         fmt.audio.i_channels = var_InheritInteger(object, "imem-channels");
436         fmt.audio.i_rate = var_InheritInteger(object, "imem-samplerate");
437
438         msg_Dbg(object, "Audio %4.4s %d channels %d Hz",
439                 (const char *)&fmt.i_codec,
440                 fmt.audio.i_channels, fmt.audio.i_rate);
441         break;
442     }
443     case 2: {
444         fmt.i_cat = VIDEO_ES;
445         fmt.video.i_width  = var_InheritInteger(object, "imem-width");
446         fmt.video.i_height = var_InheritInteger(object, "imem-height");
447         unsigned num, den;
448         if (!var_InheritURational(object, &num, &den, "imem-dar") && num > 0 && den > 0) {
449             if (fmt.video.i_width > 0 && fmt.video.i_height > 0) {
450                 fmt.video.i_sar_num = num * fmt.video.i_height;
451                 fmt.video.i_sar_den = den * fmt.video.i_width;
452             }
453         }
454         if (!var_InheritURational(object, &num, &den, "imem-fps") && num > 0 && den > 0) {
455             fmt.video.i_frame_rate      = num;
456             fmt.video.i_frame_rate_base = den;
457         }
458
459         msg_Dbg(object, "Video %4.4s %dx%d  SAR %d:%d frame rate %u/%u",
460                 (const char *)&fmt.i_codec,
461                 fmt.video.i_width, fmt.video.i_height,
462                 fmt.video.i_sar_num, fmt.video.i_sar_den,
463                 fmt.video.i_frame_rate, fmt.video.i_frame_rate_base);
464         break;
465     }
466     case 3: {
467         fmt.i_cat = SPU_ES;
468         fmt.subs.spu.i_original_frame_width =
469             var_InheritInteger(object, "imem-width");
470         fmt.subs.spu.i_original_frame_height =
471             var_InheritInteger(object, "imem-height");
472
473         msg_Dbg(object, "Subtitle %4.4s",
474                 (const char *)&fmt.i_codec);
475         break;
476     }
477     default:
478         if (cat != 4)
479             msg_Err(object, "Invalid ES category");
480         es_format_Clean(&fmt);
481         CloseCommon(sys);
482         return VLC_EGENERIC;
483     }
484
485     fmt.psz_language = var_InheritString(object, "imem-language");
486
487     sys->es = es_out_Add(demux->out, &fmt);
488     es_format_Clean(&fmt);
489
490     if (!sys->es) {
491         CloseCommon(sys);
492         return VLC_EGENERIC;
493     }
494
495     /* */
496     demux->pf_control = ControlDemux;
497     demux->pf_demux   = Demux;
498     demux->p_sys      = (demux_sys_t*)sys;
499
500     demux->info.i_update = 0;
501     demux->info.i_title = 0;
502     demux->info.i_seekpoint = 0;
503     return VLC_SUCCESS;
504 }
505
506 /**
507  * It closes an imem access_demux
508  */
509 static void CloseDemux(vlc_object_t *object)
510 {
511     demux_t *demux = (demux_t *)object;
512
513     CloseCommon((imem_sys_t*)demux->p_sys);
514 }
515
516 /**
517  * It controls an imem access_demux
518  */
519 static int ControlDemux(demux_t *demux, int i_query, va_list args)
520 {
521     imem_sys_t *sys = (imem_sys_t*)demux->p_sys;
522
523     switch (i_query)
524     {
525     case DEMUX_CAN_PAUSE:
526     case DEMUX_CAN_CONTROL_PACE: {
527         bool *b = va_arg(args, bool *);
528         *b = true;
529         return VLC_SUCCESS;
530     }
531     case DEMUX_SET_PAUSE_STATE:
532         return VLC_SUCCESS;
533
534     case DEMUX_GET_PTS_DELAY: {
535         int64_t *delay = va_arg(args, int64_t *);
536         *delay = sys->pts_delay;
537         return VLC_SUCCESS;
538     }
539     case DEMUX_GET_POSITION: {
540         double *position = va_arg(args, double *);
541         *position = 0.0;
542         return VLC_SUCCESS;
543     }
544     case DEMUX_GET_TIME: {
545         int64_t *t = va_arg(args, int64_t *);
546         *t = sys->dts;
547         return VLC_SUCCESS;
548     }
549     case DEMUX_GET_LENGTH: {
550         int64_t *l = va_arg(args, int64_t *);
551         *l = 0;
552         return VLC_SUCCESS;
553     }
554     case DEMUX_SET_NEXT_DEMUX_TIME:
555         sys->deadline = va_arg(args, int64_t);
556         return VLC_SUCCESS;
557
558     /* */
559     case DEMUX_CAN_SEEK:
560     case DEMUX_SET_POSITION:
561     case DEMUX_SET_TIME:
562     default:
563         return VLC_EGENERIC;
564     }
565
566     return VLC_EGENERIC;
567 }
568
569 /**
570  * It retreives data using the get() callback, sends them to es_out
571  * and the release it using the release() callback.
572  */
573 static int Demux(demux_t *demux)
574 {
575     imem_sys_t *sys = (imem_sys_t*)demux->p_sys;
576
577     if (sys->deadline == VLC_TS_INVALID)
578         sys->deadline = sys->dts + 1;
579
580     for (;;) {
581         if (sys->deadline <= sys->dts)
582             break;
583
584         /* */
585         int64_t dts, pts;
586         unsigned flags;
587         size_t buffer_size;
588         void   *buffer;
589
590         if (sys->source.get(sys->source.data, sys->source.cookie,
591                             &dts, &pts, &flags, &buffer_size, &buffer))
592             return 0;
593
594         if (dts < 0)
595             dts = pts;
596
597         if (buffer_size > 0) {
598             block_t *block = block_New(demux, buffer_size);
599             if (block) {
600                 block->i_dts = dts >= 0 ? (1 + dts) : VLC_TS_INVALID;
601                 block->i_pts = pts >= 0 ? (1 + pts) : VLC_TS_INVALID;
602                 memcpy(block->p_buffer, buffer, buffer_size);
603
604                 es_out_Control(demux->out, ES_OUT_SET_PCR, block->i_dts);
605                 es_out_Send(demux->out, sys->es, block);
606             }
607         }
608
609         sys->dts = dts;
610
611         sys->source.release(sys->source.data, sys->source.cookie,
612                             buffer_size, buffer);
613     }
614     sys->deadline = VLC_TS_INVALID;
615     return 1;
616 }
617
618 /**
619  * Parse the MRL and extract configuration from it.
620  *
621  * Syntax: option1=value1[:option2=value2[...]]
622  *
623  * XXX get and release are not supported on purpose.
624  */
625 static void ParseMRL(vlc_object_t *object, const char *psz_path)
626 {
627     static const struct {
628         const char *name;
629         int        type;
630     } options[] = {
631         { "caching",    VLC_VAR_INTEGER },
632         { "id",         VLC_VAR_INTEGER },
633         { "group",      VLC_VAR_INTEGER },
634         { "cat",        VLC_VAR_INTEGER },
635         { "samplerate", VLC_VAR_INTEGER },
636         { "channels",   VLC_VAR_INTEGER },
637         { "width",      VLC_VAR_INTEGER },
638         { "height",     VLC_VAR_INTEGER },
639         { "cookie",     VLC_VAR_STRING },
640         { "codec",      VLC_VAR_STRING },
641         { "language",   VLC_VAR_STRING },
642         { "dar",        VLC_VAR_STRING },
643         { "fps",        VLC_VAR_STRING },
644         { NULL, -1 }
645     };
646
647     char *dup = strdup(psz_path);
648     if (!dup)
649         return;
650     char *current = dup;
651
652     while (current) {
653         char *next = strchr(current, ':');
654         if (next)
655             *next++ = '\0';
656
657         char *option = current;
658         char *value = strchr(current, '=');
659         if (value) {
660             *value++ = '\0';
661             msg_Dbg(object, "option '%s' value '%s'", option, value);
662         } else {
663             msg_Dbg(object, "option '%s' without value (unsupported)", option);
664         }
665
666         char *name;
667         if (asprintf(&name, "imem-%s", option) < 0)
668             name = NULL;
669         for (unsigned i = 0; name && options[i].name; i++) {
670             if (strcmp(options[i].name, option))
671                 continue;
672             /* */
673             var_Create(object, name, options[i].type | VLC_VAR_DOINHERIT);
674             if (options[i].type == VLC_VAR_INTEGER && value) {
675                 var_SetInteger(object, name, strtol(value, NULL, 0));
676             } else if (options[i].type == VLC_VAR_STRING && value) {
677                 var_SetString(object, name, value);
678             }
679             break;
680         }
681         free(name);
682
683         /* */
684         current = next;
685     }
686     free(dup);
687 }