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