[vlc-devel] [PATCH] WIP: demux: add mock module
Thomas Guillem
thomas at gllm.fr
Tue Oct 2 18:54:31 CEST 2018
This new demuxer will be used for various VLC tests. It won't be installed
(noinst_LTLIBRARIES) but will be always be built. It will be mainly used to
test the new vlc_player API.
Usage: ./vlc mock://length=10000000,audio_track_count=7
Why putting the parameters inside the URL ? In order to test input slaves for
example:
./vlc mock://length=10000000,audio_track_count=0 \
--input-slave mock://length=10000000,video_track_count=0,audio_track_count=1
TODO: handle more parameters like subtitle track count, video size, video
chromas (maybe), audio rates/channels, demux capabilities, etc...
What do you think?
---
modules/demux/Makefile.am | 4 +
modules/demux/mock.c | 470 ++++++++++++++++++++++++++++++++++++++
2 files changed, 474 insertions(+)
create mode 100644 modules/demux/mock.c
diff --git a/modules/demux/Makefile.am b/modules/demux/Makefile.am
index fbc23229b3..078d87f6be 100644
--- a/modules/demux/Makefile.am
+++ b/modules/demux/Makefile.am
@@ -474,3 +474,7 @@ demux_LTLIBRARIES += libadaptive_plugin.la
libnoseek_plugin_la_SOURCES = demux/filter/noseek.c
demux_LTLIBRARIES += libnoseek_plugin.la
+
+libdemux_mock_plugin_la_SOURCES = demux/mock.c
+libdemux_mock_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(demuxdir)'
+noinst_LTLIBRARIES += libdemux_mock_plugin.la
diff --git a/modules/demux/mock.c b/modules/demux/mock.c
new file mode 100644
index 0000000000..041d0819cf
--- /dev/null
+++ b/modules/demux/mock.c
@@ -0,0 +1,470 @@
+/*****************************************************************************
+ * mock.c : mock demux module for vlc
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <ctype.h>
+#include <limits.h>
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_aout.h>
+#include <vlc_picture.h>
+#include <vlc_demux.h>
+#include <vlc_vector.h>
+
+struct mock_track
+{
+ es_format_t fmt;
+ es_out_id_t *id;
+};
+typedef struct VLC_VECTOR(struct mock_track *) mock_track_vector;
+
+struct demux_sys
+{
+ mock_track_vector tracks;
+
+ size_t audio_track_count;
+ vlc_tick_t block_length;
+ vlc_tick_t length;
+ size_t video_track_count;
+
+ vlc_tick_t pts;
+};
+
+static int
+Control(demux_t *demux, int query, va_list args)
+{
+ struct demux_sys *sys = demux->p_sys;
+
+ switch (query)
+ {
+ case DEMUX_CAN_SEEK:
+ *va_arg(args, bool *) = true;
+ return VLC_SUCCESS;
+ case DEMUX_CAN_PAUSE:
+ *va_arg(args, bool *) = true;
+ return VLC_SUCCESS;
+ case DEMUX_CAN_CONTROL_PACE:
+ *va_arg(args, bool *) = true;
+ return VLC_SUCCESS;
+ case DEMUX_GET_PTS_DELAY:
+ return VLC_EGENERIC;
+ case DEMUX_GET_META:
+ return VLC_EGENERIC;
+ case DEMUX_GET_SIGNAL:
+ return VLC_EGENERIC;
+ case DEMUX_SET_PAUSE_STATE:
+ return VLC_SUCCESS;
+ case DEMUX_SET_TITLE:
+ return VLC_EGENERIC;
+ case DEMUX_SET_SEEKPOINT:
+ return VLC_EGENERIC;
+ case DEMUX_TEST_AND_CLEAR_FLAGS:
+ return VLC_EGENERIC;
+ case DEMUX_GET_TITLE:
+ return VLC_EGENERIC;
+ case DEMUX_GET_SEEKPOINT:
+ return VLC_EGENERIC;
+ case DEMUX_GET_POSITION:
+ *va_arg(args, double *) = sys->pts / (double) sys->length;
+ return VLC_SUCCESS;
+ case DEMUX_SET_POSITION:
+ sys->pts = va_arg(args, double) * sys->length;
+ return VLC_SUCCESS;
+ case DEMUX_GET_LENGTH:
+ *va_arg(args, vlc_tick_t *) = sys->length;
+ return VLC_SUCCESS;
+ case DEMUX_GET_TIME:
+ *va_arg(args, vlc_tick_t *) = sys->pts;
+ return VLC_SUCCESS;
+ case DEMUX_SET_TIME:
+ sys->pts = va_arg(args, vlc_tick_t);
+ return VLC_SUCCESS;
+ case DEMUX_GET_TITLE_INFO:
+ return VLC_EGENERIC;
+ case DEMUX_SET_GROUP_DEFAULT:
+ return VLC_EGENERIC;
+ case DEMUX_SET_GROUP_ALL:
+ return VLC_EGENERIC;
+ case DEMUX_SET_GROUP_LIST:
+ return VLC_EGENERIC;
+ case DEMUX_SET_ES:
+ return VLC_EGENERIC;
+ case DEMUX_SET_NEXT_DEMUX_TIME:
+ return VLC_EGENERIC;
+ case DEMUX_GET_FPS:
+ return VLC_EGENERIC;
+ case DEMUX_HAS_UNSUPPORTED_META:
+ return VLC_EGENERIC;
+ case DEMUX_GET_ATTACHMENTS:
+ return VLC_EGENERIC;
+ case DEMUX_CAN_RECORD:
+ return VLC_EGENERIC;
+ case DEMUX_SET_RECORD_STATE:
+ return VLC_EGENERIC;
+ case DEMUX_CAN_CONTROL_RATE:
+ return VLC_EGENERIC;
+ case DEMUX_SET_RATE:
+ return VLC_EGENERIC;
+ case DEMUX_IS_PLAYLIST:
+ return VLC_EGENERIC;
+ case DEMUX_NAV_ACTIVATE:
+ return VLC_EGENERIC;
+ case DEMUX_NAV_UP:
+ return VLC_EGENERIC;
+ case DEMUX_NAV_DOWN:
+ return VLC_EGENERIC;
+ case DEMUX_NAV_LEFT:
+ return VLC_EGENERIC;
+ case DEMUX_NAV_RIGHT:
+ return VLC_EGENERIC;
+ case DEMUX_NAV_POPUP:
+ return VLC_EGENERIC;
+ case DEMUX_NAV_MENU:
+ return VLC_EGENERIC;
+ default:
+ return VLC_EGENERIC;
+ }
+}
+
+static block_t *
+CreateAudioBlock(demux_t *demux, struct mock_track *track)
+{
+ struct demux_sys *sys = demux->p_sys;
+ const int64_t samples =
+ samples_from_vlc_tick(sys->block_length, track->fmt.audio.i_rate);
+ const int64_t bytes = samples / track->fmt.audio.i_frame_length
+ * track->fmt.audio.i_bytes_per_frame;
+ block_t *b = block_Alloc(bytes);
+ if (!b)
+ return NULL;
+ memset(b->p_buffer, 0, b->i_buffer);
+ return b;
+}
+
+struct video_block
+{
+ block_t b;
+ picture_t *pic;
+};
+
+static void
+video_block_free_cb(block_t *b)
+{
+ struct video_block *video = container_of(b, struct video_block, b);
+ picture_Release(video->pic);
+ free(video);
+}
+
+static block_t *
+CreateVideoBlock(demux_t *demux, struct mock_track *track)
+{
+ picture_t *pic = picture_NewFromFormat(&track->fmt.video);
+ if (!pic)
+ return NULL;
+
+ struct video_block *video = malloc(sizeof(*video));
+ if (!video)
+ {
+ picture_Release(pic);
+ return NULL;
+ }
+ video->pic = pic;
+
+ static const struct vlc_block_callbacks cbs =
+ {
+ .free = video_block_free_cb
+ };
+
+ size_t block_len = 0;
+ for (int i = 0; i < pic->i_planes; ++i)
+ block_len += pic->p[i].i_lines * pic->p[i].i_pitch;
+ memset(pic->p[0].p_pixels, 0, block_len);
+ return block_Init(&video->b, &cbs, pic->p[0].p_pixels, block_len);
+ (void) demux;
+}
+
+static int
+Demux(demux_t *demux)
+{
+ struct demux_sys *sys = demux->p_sys;
+ struct mock_track *track;
+ vlc_vector_foreach(track, &sys->tracks)
+ {
+ block_t *block;
+ switch (track->fmt.i_cat)
+ {
+ case AUDIO_ES:
+ block = CreateAudioBlock(demux, track);
+ break;
+ case VIDEO_ES:
+ block = CreateVideoBlock(demux, track);
+ break;
+ case SPU_ES: /* TODO */
+ default:
+ vlc_assert_unreachable();
+ }
+ if (!block)
+ return VLC_DEMUXER_EGENERIC;
+ block->i_length = sys->block_length;
+ block->i_pts = block->i_dts = sys->pts;
+ int ret = es_out_Send(demux->out, track->id, block);
+ if (ret != VLC_SUCCESS)
+ return VLC_DEMUXER_EGENERIC;
+ }
+ sys->pts += sys->block_length;
+ es_out_SetPCR(demux->out, sys->pts);
+ return sys->pts >= sys->length ? VLC_DEMUXER_EOF : VLC_DEMUXER_SUCCESS;
+}
+
+static int
+AppendMockTrack(demux_t *demux, const es_format_t *fmt)
+{
+ struct demux_sys *sys = demux->p_sys;
+ struct mock_track *mock_track = malloc(sizeof(*mock_track));
+ if (!mock_track)
+ return VLC_EGENERIC;
+ mock_track->fmt = *fmt;
+ mock_track->id = es_out_Add(demux->out, & mock_track->fmt);
+ if (!mock_track->id)
+ {
+ free(mock_track);
+ return VLC_ENOMEM;
+ }
+ bool success = vlc_vector_push(&sys->tracks, mock_track);
+ assert(success); (void) success; /* checked by reserve() */
+ return VLC_SUCCESS;
+}
+
+
+static void
+Close(vlc_object_t *obj)
+{
+ demux_t *demux = (demux_t*)obj;
+ struct demux_sys *sys = demux->p_sys;
+
+ struct mock_track *track;
+ vlc_vector_foreach(track, &sys->tracks)
+ {
+ es_out_Del(demux->out, track->id);
+ free(track);
+ }
+ vlc_vector_clear(&sys->tracks);
+}
+
+struct parse_entry
+{
+ const char *name;
+ enum
+ {
+ ENTRY_TYPE_TICK,
+ ENTRY_TYPE_INT64,
+ ENTRY_TYPE_SIZE,
+ ENTRY_TYPE_DOUBLE,
+ } type;
+ size_t offset;
+ union {
+ vlc_tick_t tick;
+ int64_t int64;
+ double dbl;
+ size_t size;
+ } default_val;
+};
+
+static int
+entry_cmp(const void *key, const void *entry)
+{
+ const struct parse_entry *parse_entry = entry;
+ const char *name = key;
+ const char *next = strchr(name, '=');
+ const size_t name_len = next - name;
+
+ return strncmp(name, parse_entry->name, name_len);
+}
+
+static void
+ParseParams(demux_t *demux)
+{
+ struct demux_sys *sys = demux->p_sys;
+#define OFF(x) offsetof(struct demux_sys, x)
+ static const struct parse_entry entries[] =
+ {
+ /* XXX: Alphabetical order */
+ {"audio_track_count", ENTRY_TYPE_SIZE, OFF(audio_track_count), { .size = 2 } },
+ {"block_length", ENTRY_TYPE_TICK, OFF(block_length), { .tick = VLC_TICK_FROM_MS(100) } },
+ {"length", ENTRY_TYPE_TICK, OFF(length), { .tick = VLC_TICK_FROM_MS(5000) } },
+ {"video_track_count", ENTRY_TYPE_SIZE, OFF(video_track_count), { .size = 1 } },
+ };
+
+ /* Initialize default values */
+ for (size_t i = 0; i < ARRAY_SIZE(entries); ++i)
+ {
+ const struct parse_entry *entry = &entries[i];
+ void *sys_value = (char *) sys + entry->offset;
+ switch (entry->type)
+ {
+ case ENTRY_TYPE_TICK:
+ *(vlc_tick_t *)sys_value = entry->default_val.tick;
+ break;
+ case ENTRY_TYPE_INT64:
+ *(int64_t *)sys_value = entry->default_val.int64;
+ break;
+ case ENTRY_TYPE_DOUBLE:
+ *(double *)sys_value = entry->default_val.dbl;
+ break;
+ case ENTRY_TYPE_SIZE:
+ *(size_t *)sys_value = entry->default_val.size;
+ break;
+ };
+ }
+
+ /* Parse the url to fill the demux_sys struct with initial values */
+ const char *start = demux->psz_location;
+ if (!start)
+ return;
+ for (;;)
+ {
+ while (*start != 0 && !isalpha(*start))
+ start++;
+ char *next = strchr(start, '=');
+ if (!next)
+ break;
+
+ struct parse_entry *entry = bsearch(start, entries, ARRAY_SIZE(entries),
+ sizeof(entries[0]), entry_cmp);
+ if (entry)
+ {
+ start = next + 1;
+ void *sys_value = (char *) sys + entry->offset;
+
+ switch (entry->type)
+ {
+ case ENTRY_TYPE_TICK:
+ case ENTRY_TYPE_INT64:
+ case ENTRY_TYPE_SIZE:
+ {
+ long long int val = strtoll(start, &next, 10);
+ if (val == LLONG_MAX || val == LLONG_MIN)
+ break;
+ switch (entry->type)
+ {
+ case ENTRY_TYPE_TICK:
+ *(vlc_tick_t *)sys_value = val;
+ break;
+ case ENTRY_TYPE_INT64:
+ *(int64_t *)sys_value = val;
+ break;
+ case ENTRY_TYPE_SIZE:
+ if (val >= 0)
+ *(size_t *)sys_value = val;
+ break;
+ default:
+ vlc_assert_unreachable();
+ }
+ break;
+ }
+ case ENTRY_TYPE_DOUBLE:
+ {
+ double val = strtod(start, &next);
+ *(double *)sys_value = val;
+ break;
+ }
+ default:
+ vlc_assert_unreachable();
+ }
+ }
+ else
+ next++;
+ start = next;
+ }
+}
+
+static int
+Open(vlc_object_t *obj)
+{
+ demux_t *demux = (demux_t*)obj;
+
+ if (demux->out == NULL)
+ return VLC_EGENERIC;
+ struct demux_sys *sys = vlc_obj_malloc(obj, sizeof(*sys));
+ if (!sys)
+ return VLC_ENOMEM;
+
+ demux->p_sys = sys;
+
+ ParseParams(demux);
+
+ vlc_vector_init(&sys->tracks);
+ bool success = vlc_vector_reserve(&sys->tracks, sys->video_track_count +
+ sys->audio_track_count);
+ if (!success)
+ {
+ vlc_vector_clear(&sys->tracks);
+ return VLC_ENOMEM;
+ }
+
+ for (size_t i = 0; i < sys->video_track_count; ++i)
+ {
+ es_format_t fmt;
+ es_format_Init(&fmt, VIDEO_ES, VLC_CODEC_NV12);
+ fmt.video.i_chroma = fmt.i_codec;
+ fmt.video.i_width = fmt.video.i_visible_width = 640;
+ fmt.video.i_height = fmt.video.i_visible_height = 480;
+
+ if (AppendMockTrack(demux, &fmt) != VLC_SUCCESS)
+ goto error;
+ }
+
+ for (size_t i = 0; i < sys->audio_track_count; ++i)
+ {
+ es_format_t fmt;
+ es_format_Init(&fmt, AUDIO_ES, VLC_CODEC_U8);
+ fmt.audio.i_format = fmt.i_codec;
+ fmt.audio.i_rate = 8000;
+ fmt.audio.i_physical_channels = AOUT_CHAN_LEFT;
+ aout_FormatPrepare(&fmt.audio);
+
+ if (AppendMockTrack(demux, &fmt) != VLC_SUCCESS)
+ goto error;
+ }
+ sys->pts = VLC_TICK_0;
+
+ demux->pf_control = Control;
+ demux->pf_demux = Demux;
+
+ return VLC_SUCCESS;
+error:
+ demux->p_sys = NULL;
+ Close(obj);
+ return VLC_ENOMEM;
+}
+
+
+vlc_module_begin()
+ set_description("mock access demux")
+ set_capability("access", 0)
+ set_category(CAT_INPUT)
+ set_subcategory(SUBCAT_INPUT_ACCESS)
+ set_callbacks(Open, Close)
+ add_shortcut("vlcmock")
+vlc_module_end()
--
2.19.0
More information about the vlc-devel
mailing list