<!DOCTYPE html><html><head><title></title><style type="text/css">p.MsoNormal,p.MsoNoSpacing{margin:0}</style></head><body><div><br></div><div><br></div><div>On Wed, Aug 19, 2020, at 08:12, Kartik Ohri wrote:<br></div><blockquote type="cite" id="qt" style=""><div dir="ltr"><div dir="ltr">On Wed, Aug 19, 2020 at 10:45 AM Steve Lhomme <<a href="mailto:robux4@ycbcr.xyz">robux4@ycbcr.xyz</a>> wrote:<br></div><div class="qt-gmail_quote"><blockquote class="qt-gmail_quote" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0.8ex;border-left-color:rgb(204, 204, 204);border-left-style:solid;border-left-width:1px;padding-left:1ex;"><div>Hello,<br></div><div> <br></div><div> On 2020-08-18 22:37, Kartik Ohri wrote:<br></div><div> > The purpose of this module is to serve as a Proof Of Concept<br></div><div> <br></div><div> In general you should use [RFC] in the subject of your email, suggesting <br></div><div> it's not meant to be merged as such.<br></div><div> <br></div></blockquote><div>I will keep this in mind.<br></div><div><br></div><blockquote class="qt-gmail_quote" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0.8ex;border-left-color:rgb(204, 204, 204);border-left-style:solid;border-left-width:1px;padding-left:1ex;"><div>> and an example on how Rust code can be integrated inside VLC.<br></div><div> > ---<br></div><div> >   <a href="http://configure.ac" rel="noreferrer" target="_blank">configure.ac</a>                                  |  12 ++<br></div><div> >   modules/demux/Makefile.am                     |  25 +++<br></div><div> >   modules/demux/playlist/cuesheet.c             | 155 ++++++++++++++++++<br></div><div> >   modules/demux/playlist/cuesheet/.gitignore    |   2 +<br></div><div> >   modules/demux/playlist/cuesheet/Cargo.toml    |  10 ++<br></div><div> >   modules/demux/playlist/cuesheet/cbindgen.toml |  16 ++<br></div><div> >   modules/demux/playlist/cuesheet/cuesheet.h    |  44 +++++<br></div><div> >   modules/demux/playlist/cuesheet/src/<a href="http://capi.rs" rel="noreferrer" target="_blank">capi.rs</a>   | 115 +++++++++++++<br></div><div> >   modules/demux/playlist/cuesheet/src/<a href="http://config.rs" rel="noreferrer" target="_blank">config.rs</a> | 108 ++++++++++++<br></div><div> >   modules/demux/playlist/cuesheet/src/<a href="http://lib.rs" rel="noreferrer" target="_blank">lib.rs</a>    |  18 ++<br></div><div> >   modules/demux/playlist/cuesheet/src/<a href="http://parse.rs" rel="noreferrer" target="_blank">parse.rs</a>  |  69 ++++++++<br></div><div> >   modules/demux/playlist/playlist.c             |   4 +<br></div><div> >   modules/demux/playlist/playlist.h             |   2 +<br></div><div> >   13 files changed, 580 insertions(+)<br></div><div> >   create mode 100644 modules/demux/playlist/cuesheet.c<br></div><div> >   create mode 100644 modules/demux/playlist/cuesheet/.gitignore<br></div><div> >   create mode 100644 modules/demux/playlist/cuesheet/Cargo.toml<br></div><div> >   create mode 100644 modules/demux/playlist/cuesheet/cbindgen.toml<br></div><div> >   create mode 100644 modules/demux/playlist/cuesheet/cuesheet.h<br></div><div> >   create mode 100644 modules/demux/playlist/cuesheet/src/<a href="http://capi.rs" rel="noreferrer" target="_blank">capi.rs</a><br></div><div> >   create mode 100644 modules/demux/playlist/cuesheet/src/<a href="http://config.rs" rel="noreferrer" target="_blank">config.rs</a><br></div><div> >   create mode 100644 modules/demux/playlist/cuesheet/src/<a href="http://lib.rs" rel="noreferrer" target="_blank">lib.rs</a><br></div><div> >   create mode 100644 modules/demux/playlist/cuesheet/src/<a href="http://parse.rs" rel="noreferrer" target="_blank">parse.rs</a><br></div><div> > <br></div><div> > diff --git a/<a href="http://configure.ac" rel="noreferrer" target="_blank">configure.ac</a> b/<a href="http://configure.ac" rel="noreferrer" target="_blank">configure.ac</a><br></div><div> > index a30e86bf37..93a45d57d6 100644<br></div><div> > --- a/<a href="http://configure.ac" rel="noreferrer" target="_blank">configure.ac</a><br></div><div> > +++ b/<a href="http://configure.ac" rel="noreferrer" target="_blank">configure.ac</a><br></div><div> > @@ -1886,6 +1886,18 @@ AS_IF([test "${enable_sout}" != "no"], [<br></div><div> >   ])<br></div><div> >   AM_CONDITIONAL([ENABLE_SOUT], [test "${enable_sout}" != "no"])<br></div><div> > <br></div><div> > +dnl Rust Modules<br></div><div> > +AC_ARG_ENABLE([rust],<br></div><div> > +    AS_HELP_STRING([--enable-rust], [disable building Rust modules (default disabled)]))<br></div><div> > +AS_IF([test "${enable_rust}" = "yes"],<br></div><div> > +      [AC_DEFINE(ENABLE_RUST, 1, [Define to 1 for building rust modules.])])<br></div><div> > +AM_CONDITIONAL([BUILD_RUST], [test "${enable_rust}" = "yes"])<br></div><div> > +if test "${enable_rust}" = "yes"<br></div><div> > +then<br></div><div> > +    AC_CHECK_PROG(CARGO, [cargo], [yes], [no])<br></div><div> > +    AS_IF(test "x$CARGO" = "xno", [cargo not found. cargo is required to build rust modules])<br></div><div> > +fi<br></div><div> <br></div><div> You probably don't want to enable building Rust if cargo is not found. <br></div><div> So BUILD_RUST should also depend on this test.<br></div><div> <br></div></blockquote><div>Yes, right. Will fix it.<br></div><div> <br></div><blockquote class="qt-gmail_quote" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0.8ex;border-left-color:rgb(204, 204, 204);border-left-style:solid;border-left-width:1px;padding-left:1ex;"><div>> +<br></div><div> >   dnl Lua modules<br></div><div> >   AC_ARG_ENABLE([lua],<br></div><div> >     AS_HELP_STRING([--disable-lua],<br></div><div> > diff --git a/modules/demux/Makefile.am b/modules/demux/Makefile.am<br></div><div> > index 67ec086af9..a2c3018f71 100644<br></div><div> > --- a/modules/demux/Makefile.am<br></div><div> > +++ b/modules/demux/Makefile.am<br></div><div> > @@ -1,3 +1,4 @@<br></div><div> > +<br></div><div> >   demuxdir = $(pluginsdir)/demux<br></div><div> >   demux_LTLIBRARIES =<br></div><div> > <br></div><div> > @@ -254,6 +255,30 @@ libplaylist_plugin_la_SOURCES = \<br></div><div> >       demux/playlist/wpl.c \<br></div><div> >       demux/playlist/xspf.c \<br></div><div> >       demux/playlist/playlist.c demux/playlist/playlist.h<br></div><div> > +<br></div><div> > +if BUILD_RUST<br></div><div> > +CARGO_C = cargo capi install --release<br></div><div> > +RUST_RUNTIME_LIBS = -ldl -lrt -lpthread -lgcc_s -lc -lm -lrt -lpthread -lutil<br></div><div> <br></div><div> This should not be hardcoded. Some may come from the Rust+C linking, <br></div><div> some from the library you are using. This should be settled via <br></div><div> variables in <a href="http://configure.ac" rel="noreferrer" target="_blank">configure.ac</a>.<br></div><div> <br></div></blockquote><div>Understood.<br></div><div><br></div><blockquote class="qt-gmail_quote" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0.8ex;border-left-color:rgb(204, 204, 204);border-left-style:solid;border-left-width:1px;padding-left:1ex;"><div>> +<br></div><div> > +libcuesheet_rust_plugin_la_SOURCES = \<br></div><div> > +     demux/playlist/cuesheet/src/<a href="http://capi.rs" rel="noreferrer" target="_blank">capi.rs</a> \<br></div><div> > +     demux/playlist/cuesheet/src/<a href="http://config.rs" rel="noreferrer" target="_blank">config.rs</a> \<br></div><div> > +     demux/playlist/cuesheet/src/<a href="http://lib.rs" rel="noreferrer" target="_blank">lib.rs</a> \<br></div><div> > +     demux/playlist/cuesheet/src/<a href="http://parse.rs" rel="noreferrer" target="_blank">parse.rs</a><br></div><div> > +<br></div><div> > +abs_src_dir = $(shell readlink -f $(srcdir))<br></div><div> > +<br></div><div> > +@abs_top_builddir@/modules/demux/playlist/cuesheet/libcuesheet.a: $(libcuesheet_rust_plugin_la_SOURCES)<br></div><div> > +     mkdir -p demux/playlist/cuesheet && cd demux/playlist/cuesheet && \<br></div><div> > +     $(CARGO_C) --library-type=staticlib --destdir=. --includedir=.. --libdir=. \<br></div><div> > +     --manifest-path=$(abs_src_dir)/demux/playlist/cuesheet/Cargo.toml<br></div><div> > +<br></div><div> > +CUESHEET_LIBS = @abs_top_builddir@/modules/demux/playlist/cuesheet/libcuesheet.a<br></div><div> > +CUESHEET_LIBS += $(RUST_RUNTIME_LIBS)<br></div><div> > +libplaylist_plugin_la_SOURCES += demux/playlist/cuesheet.c demux/playlist/cuesheet/cuesheet.h<br></div><div> > +libplaylist_plugin_la_LIBADD = $(CUESHEET_LIBS)<br></div><div> > +endif<br></div><div> > +<br></div><div> >   demux_LTLIBRARIES += <a href="http://libplaylist_plugin.la" rel="noreferrer" target="_blank">libplaylist_plugin.la</a><br></div><div> > <br></div><div> >   libts_plugin_la_SOURCES = demux/mpeg/ts.c demux/mpeg/ts.h \<br></div><div> > diff --git a/modules/demux/playlist/cuesheet.c b/modules/demux/playlist/cuesheet.c<br></div><div> > new file mode 100644<br></div><div> > index 0000000000..9330b01079<br></div><div> > --- /dev/null<br></div><div> > +++ b/modules/demux/playlist/cuesheet.c<br></div><div> > @@ -0,0 +1,155 @@<br></div><div> > +/*****************************************************************************<br></div><div> > + * cuesheet.c : cue sheet playlist import format<br></div><div> > + *****************************************************************************<br></div><div> > + * Copyright (C) 2020 VLC authors and VideoLAN<br></div><div> > + *<br></div><div> > + * Authors: Kartik Ohri <<a href="mailto:kartikohri13@gmail.com" target="_blank">kartikohri13@gmail.com</a>><br></div><div> > + *<br></div><div> > + * This program is free software; you can redistribute it and/or modify it<br></div><div> > + * under the terms of the GNU Lesser General Public License as published by<br></div><div> > + * the Free Software Foundation; either version 2.1 of the License, or<br></div><div> > + * (at your option) any later version.<br></div><div> > + *<br></div><div> > + * This program is distributed in the hope that it will be useful,<br></div><div> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of<br></div><div> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br></div><div> > + * GNU Lesser General Public License for more details.<br></div><div> > + *<br></div><div> > + * You should have received a copy of the GNU Lesser General Public License<br></div><div> > + * along with this program; if not, write to the Free Software Foundation,<br></div><div> > + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.<br></div><div> > + *****************************************************************************/<br></div><div> > +<br></div><div> > +#ifdef HAVE_CONFIG_H<br></div><div> > +# include "config.h"<br></div><div> > +#endif<br></div><div> > +<br></div><div> > +#include <string.h><br></div><div> > +<br></div><div> > +#include <vlc_common.h><br></div><div> > +#include <vlc_access.h><br></div><div> > +#include <vlc_url.h><br></div><div> > +<br></div><div> > +#include "cuesheet/cuesheet.h"<br></div><div> > +#include "playlist.h"<br></div><div> > +<br></div><div> > +typedef struct {<br></div><div> > +    struct CCuesheet *cuesheet;<br></div><div> > +} cuesheet_sys_t;<br></div><div> > +<br></div><div> > +static int ReadDir(stream_t *, input_item_node_t *);<br></div><div> > +static char* get_string_property(CCuesheet *, const char*, int);<br></div><div> > +<br></div><div> > +int Import_Cue_Sheet( vlc_object_t *p_this )<br></div><div> > +{<br></div><div> > +    stream_t *p_demux = (stream_t *) p_this;<br></div><div> > +    CHECK_FILE ( p_demux );<br></div><div> > +<br></div><div> > +    if ( !stream_IsMimeType ( p_demux->s, "application/x-cue" )<br></div><div> > +    && !stream_HasExtension( p_demux->s, ".cue" ))<br></div><div> > +        return VLC_EGENERIC;<br></div><div> > +<br></div><div> > +    cuesheet_sys_t *sys = (cuesheet_sys_t *) malloc(sizeof(*sys));<br></div><div> > +    sys->cuesheet = cuesheet_default();<br></div><div> > +    p_demux->p_sys = sys;<br></div><div> > +<br></div><div> > +    p_demux->pf_control = access_vaDirectoryControlHelper;<br></div><div> > +    p_demux->pf_readdir = ReadDir;<br></div><div> > +    return VLC_SUCCESS;<br></div><div> > +}<br></div><div> > +<br></div><div> > +/**<br></div><div> > + * free should not be called on strings allocated by rust compiler. There is no<br></div><div> > + * way to ensure this without duplicating the string before passing it to<br></div><div> > + * input_item_t. To avoid redundancy and duplication, use this method instead<br></div><div> > + * of directly using cuesheet_get_property or cuesheet_get_track_property.<br></div><div> > + * For getting the property using cuesheet_get_property pass a negative number as<br></div><div> > + * track_num. For using cuesheet_get_track_property, pass the track number for<br></div><div> > + * which you the property as track_num.<br></div><div> > + */<br></div><div> > +static char* get_string_property(CCuesheet *cuesheet, const char* property, int track_num) {<br></div><div> > +    const char* psz_val;<br></div><div> > +    if(track_num < 0)<br></div><div> > +        psz_val = cuesheet_get_property(cuesheet, property);<br></div><div> > +    else<br></div><div> > +        psz_val = cuesheet_get_track_property(cuesheet, track_num, property);<br></div><div> > +    if(!psz_val)<br></div><div> > +        return NULL;<br></div><div> > +    char *psz_c_val = strdup(psz_val);<br></div><div> <br></div><div> It seems you use the return value to call other setter functions. You <br></div><div> can probably just return the const char * and use it directly.<br></div><div> <br></div></blockquote><div>I did this because the Rust documentation (<a href="https://doc.rust-lang.org/std/ffi/struct.CString.html#method.into_raw">https://doc.rust-lang.org/std/ffi/struct.CString.html#method.into_raw</a>) mentions that free() should not be called on a string allocated by rust. If it is guaranteed that this will not happen for the current usage, it is safe to remove this block of code.<br></div><div>  <br></div><blockquote class="qt-gmail_quote" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0.8ex;border-left-color:rgb(204, 204, 204);border-left-style:solid;border-left-width:1px;padding-left:1ex;"><div>> +    unref_cuesheet_string(psz_val);<br></div><div> > +    return psz_c_val;<br></div><div> > +}<br></div><div> > +<br></div><div> > +static int ReadDir(stream_t* p_demux, input_item_node_t* p_subitems)<br></div><div> > +{<br></div><div> > +    cuesheet_sys_t *sys = p_demux->p_sys;<br></div><div> > +    char *psz_line;<br></div><div> > +    while ((psz_line = vlc_stream_ReadLine(p_demux->s)) != NULL)<br></div><div> > +    {<br></div><div> > +        msg_Dbg(p_demux, "%s", psz_line);<br></div><div> <br></div><div> Should probably be commented out in the end.<br></div><div> <br></div></blockquote><div>Will fix this.<br></div><div> <br></div><blockquote class="qt-gmail_quote" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0.8ex;border-left-color:rgb(204, 204, 204);border-left-style:solid;border-left-width:1px;padding-left:1ex;"><div>> +        cuesheet_process_line(sys->cuesheet, psz_line);<br></div><div> > +        free(psz_line);<br></div><div> > +    }<br></div><div> > +    int tracks = cuesheet_get_tracks_number(sys->cuesheet);<br></div><div> > +    int idx = 0;<br></div><div> > +    const char *p_val = NULL;<br></div><div> > +    char *ppsz_option[2];<br></div><div> > +<br></div><div> > +    char *psz_audio_file = get_string_property(sys->cuesheet, "FILE_NAME", -1);<br></div><div> > +    if (!psz_audio_file)<br></div><div> > +        goto error;<br></div><div> > +<br></div><div> > +    char *psz_audio_file_uri = vlc_uri_encode(psz_audio_file);<br></div><div> > +    char *psz_url = vlc_uri_resolve(p_demux->psz_url, psz_audio_file_uri);<br></div><div> > +    free(psz_audio_file);<br></div><div> > +    free(psz_audio_file_uri);<br></div><div> > +<br></div><div> > +    while ( idx < tracks )<br></div><div> > +    {<br></div><div> > +        int i_options = 0;<br></div><div> > +        input_item_t *p_item;<br></div><div> > +<br></div><div> > +        p_val = get_string_property(sys->cuesheet, "TITLE", idx);<br></div><div> > +        if (p_val)<br></div><div> > +        {<br></div><div> > +            p_item = input_item_New(psz_url, p_val);<br></div><div> > +            p_item->i_type = ITEM_TYPE_FILE;<br></div><div> > +        }<br></div><div> > +        else<br></div><div> > +            goto error;<br></div><div> > +<br></div><div> > +        p_val = get_string_property(sys->cuesheet, "TITLE", -1);<br></div><div> > +        if (p_val)<br></div><div> > +            input_item_SetAlbum(p_item, p_val);<br></div><div> > +<br></div><div> > +        p_val = get_string_property(sys->cuesheet, "PERFORMER", -1);<br></div><div> > +        if (p_val)<br></div><div> > +            input_item_SetAlbumArtist(p_item, p_val);<br></div><div> > +<br></div><div> > +        p_val = get_string_property(sys->cuesheet, "PERFORMER", idx);<br></div><div> > +        if (p_val)<br></div><div> > +            input_item_SetArtist(p_item, p_val);<br></div><div> > +<br></div><div> > +        int i_time = cuesheet_get_track_start(sys->cuesheet, idx);<br></div><div> > +        if (asprintf(ppsz_option, ":start-time=%d", i_time))<br></div><div> > +            i_options++;<br></div><div> > +<br></div><div> > +        if (idx < tracks - 1)<br></div><div> > +        {<br></div><div> > +            i_time = cuesheet_get_track_start(sys->cuesheet, idx + 1);<br></div><div> > +            if (asprintf(ppsz_option + i_options, ":stop-time=%d", i_time))<br></div><div> > +                i_options++;<br></div><div> > +        }<br></div><div> > +<br></div><div> > +        input_item_AddOptions(p_item, i_options, (const char **)ppsz_option, VLC_INPUT_OPTION_TRUSTED);<br></div><div> > +        input_item_node_AppendItem(p_subitems, p_item);<br></div><div> > +        input_item_Release(p_item);<br></div><div> > +        idx++;<br></div><div> > +    }<br></div><div> > +    return VLC_SUCCESS;<br></div><div> > +error:<br></div><div> > +    return VLC_EGENERIC;<br></div><div> > +}<br></div><div> > +<br></div><div> > +<br></div><div> > +<br></div><div> > diff --git a/modules/demux/playlist/cuesheet/.gitignore b/modules/demux/playlist/cuesheet/.gitignore<br></div><div> <br></div><div> This should not be merged here. Either a general rule in the topmost <br></div><div> .gitignore or not at all.<br></div><div> <br></div></blockquote><div>Understood. Will fix it.<br></div><div> <br></div><blockquote class="qt-gmail_quote" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0.8ex;border-left-color:rgb(204, 204, 204);border-left-style:solid;border-left-width:1px;padding-left:1ex;"><div>> new file mode 100644<br></div><div> > index 0000000000..96ef6c0b94<br></div><div> > --- /dev/null<br></div><div> > +++ b/modules/demux/playlist/cuesheet/.gitignore<br></div><div> <br></div><div> Same here.<br></div><div> <br></div><div> > @@ -0,0 +1,2 @@<br></div><div> > +/target<br></div><div> > +Cargo.lock<br></div><div> > diff --git a/modules/demux/playlist/cuesheet/Cargo.toml b/modules/demux/playlist/cuesheet/Cargo.toml<br></div><div> > new file mode 100644<br></div><div> > index 0000000000..3ce20f6480<br></div><div> > --- /dev/null<br></div><div> > +++ b/modules/demux/playlist/cuesheet/Cargo.toml<br></div><div> > @@ -0,0 +1,10 @@<br></div><div> > +[package]<br></div><div> > +name = "cuesheet"<br></div><div> > +version = "0.1.0"<br></div><div> > +authors = ["Kartik Ohri <<a href="mailto:kartikohri13@gmail.com" target="_blank">kartikohri13@gmail.com</a>>"]<br></div><div> > +edition = "2018"<br></div><div> > +<br></div><div> > +[dependencies]<br></div><div> > +<br></div><div> > +[lib]<br></div><div> > +name = "cuesheet"<br></div><div> > \ No newline at end of file<br></div><div> > diff --git a/modules/demux/playlist/cuesheet/cbindgen.toml b/modules/demux/playlist/cuesheet/cbindgen.toml<br></div><div> > new file mode 100644<br></div><div> > index 0000000000..bc8fd14ae8<br></div><div> > --- /dev/null<br></div><div> > +++ b/modules/demux/playlist/cuesheet/cbindgen.toml<br></div><div> > @@ -0,0 +1,16 @@<br></div><div> > +header = "// SPDX-License-Identifier: LGPL-3.0-or-later"<br></div><div> > +sys_includes = ["stddef.h", "stdint.h", "stdlib.h"]<br></div><div> > +no_includes = true<br></div><div> > +include_guard = "CUESHEET_H"<br></div><div> > +tab_width = 4<br></div><div> <br></div><div> Is this file used for text editors or also for building ?<br></div><div> <br></div></blockquote><div>Yes, the file is required for building. This file is used to generate the C headers for the rust module in this case (cuesheet.h). I'd like to know the opinion on whether to commit the cuesheet.h file or not because it is auto-generated during the build time. If a cuesheet.h file (from a previous build or say from the git repository) already exists, it will be overridden.<br></div><div> <br></div><blockquote class="qt-gmail_quote" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0.8ex;border-left-color:rgb(204, 204, 204);border-left-style:solid;border-left-width:1px;padding-left:1ex;"><div>> +language = "C"<br></div><div> > +documentation_style = "C"<br></div><div> > +style = "type"<br></div><div> > +cpp_compat = true<br></div><div> > +<br></div><div> > +[export]<br></div><div> > +item_types = ["enums", "structs", "unions", "typedefs", "opaque", "functions"]<br></div><div> > +<br></div><div> > +[enum]<br></div><div> > +rename_variants = "ScreamingSnakeCase"<br></div><div> > +prefix_with_name = true<br></div><div> > \ No newline at end of file<br></div><div> > diff --git a/modules/demux/playlist/cuesheet/cuesheet.h b/modules/demux/playlist/cuesheet/cuesheet.h<br></div><div> > new file mode 100644<br></div><div> > index 0000000000..cb7dd970f3<br></div><div> > --- /dev/null<br></div><div> > +++ b/modules/demux/playlist/cuesheet/cuesheet.h<br></div><div> > @@ -0,0 +1,44 @@<br></div><div> > +// SPDX-License-Identifier: LGPL-3.0-or-later<br></div><div> > +<br></div><div> > +#ifndef CUESHEET_H<br></div><div> > +#define CUESHEET_H<br></div><div> > +<br></div><div> > +<br></div><div> > +#define CUESHEET_MAJOR 0<br></div><div> > +#define CUESHEET_MINOR 1<br></div><div> > +#define CUESHEET_PATCH 0<br></div><div> > +<br></div><div> > +<br></div><div> > +#include <stddef.h><br></div><div> > +#include <stdint.h><br></div><div> > +#include <stdlib.h><br></div><div> > +<br></div><div> > +typedef struct CCuesheet CCuesheet;<br></div><div> > +<br></div><div> > +#ifdef __cplusplus<br></div><div> > +extern "C" {<br></div><div> > +#endif // __cplusplus<br></div><div> > +<br></div><div> > +CCuesheet *cuesheet_default(void);<br></div><div> > +<br></div><div> > +const char *cuesheet_get_comment_value(CCuesheet *c_cuesheet, const char *line);<br></div><div> > +<br></div><div> > +const char *cuesheet_get_property(CCuesheet *c_cuesheet, const char *line);<br></div><div> > +<br></div><div> > +const char *cuesheet_get_track_property(CCuesheet *c_cuesheet, int index, const char *line);<br></div><div> > +<br></div><div> > +int cuesheet_get_track_start(CCuesheet *c_cuesheet, int index);<br></div><div> > +<br></div><div> > +int cuesheet_get_tracks_number(CCuesheet *c_cuesheet);<br></div><div> > +<br></div><div> > +void cuesheet_process_line(CCuesheet *c_cuesheet, const char *line);<br></div><div> > +<br></div><div> > +void unref_cuesheet(CCuesheet *cuesheet);<br></div><div> > +<br></div><div> > +void unref_cuesheet_string(char *text);<br></div><div> > +<br></div><div> > +#ifdef __cplusplus<br></div><div> > +} // extern "C"<br></div><div> > +#endif // __cplusplus<br></div><div> > +<br></div><div> > +#endif /* CUESHEET_H */<br></div><div> > diff --git a/modules/demux/playlist/cuesheet/src/<a href="http://capi.rs" rel="noreferrer" target="_blank">capi.rs</a> b/modules/demux/playlist/cuesheet/src/<a href="http://capi.rs" rel="noreferrer" target="_blank">capi.rs</a><br></div><div> > new file mode 100644<br></div><div> > index 0000000000..b5af473fe7<br></div><div> > --- /dev/null<br></div><div> > +++ b/modules/demux/playlist/cuesheet/src/<a href="http://capi.rs" rel="noreferrer" target="_blank">capi.rs</a><br></div><div> > @@ -0,0 +1,115 @@<br></div><div> > +use crate::config::Cuesheet;<br></div><div> > +use crate::parse::process_line;<br></div><div> > +use std::os::raw::{c_char, c_int};<br></div><div> > +use std::ffi::{CStr, CString};<br></div><div> > +use std::convert::TryFrom;<br></div><div> > +<br></div><div> > +pub struct CCuesheet {<br></div><div> > +    cuesheet : Cuesheet<br></div><div> > +}<br></div><div> > +<br></div><div> > +#[no_mangle]<br></div><div> > +pub unsafe extern fn cuesheet_default() -> *mut CCuesheet {<br></div><div> > +    let cuesheet = Cuesheet::default();<br></div><div> > +    let c_cuesheet = Box::new(CCuesheet {cuesheet});<br></div><div> > +    Box::into_raw(c_cuesheet)<br></div><div> > +}<br></div><div> > +<br></div><div> > +#[no_mangle]<br></div><div> > +pub unsafe extern fn cuesheet_process_line(c_cuesheet : *mut CCuesheet, line : *const c_char) {<br></div><div> > +    let rust_line = CStr::from_ptr(line).to_str().to_owned().unwrap();<br></div><div> > +    process_line(&mut (*c_cuesheet).cuesheet, rust_line);<br></div><div> > +}<br></div><div> > +<br></div><div> > +#[no_mangle]<br></div><div> > +pub unsafe extern fn cuesheet_get_property(c_cuesheet : *mut CCuesheet, line : *const c_char)<br></div><div> > +    -> *const c_char {<br></div><div> > +    let rust_line = match CStr::from_ptr(line).to_str() {<br></div><div> > +        Ok(value) => value,<br></div><div> > +        Err(_) => return std::ptr::null()<br></div><div> > +    };<br></div><div> > +<br></div><div> > +    let cuesheet = & (*c_cuesheet).cuesheet;<br></div><div> > +<br></div><div> > +    let property_value = cuesheet.get_property(rust_line);<br></div><div> > +    if property_value.is_empty() {<br></div><div> > +        return std::ptr::null()<br></div><div> > +    }<br></div><div> > +    convert_string_to_ptr(property_value.to_string())<br></div><div> > +}<br></div><div> > +<br></div><div> > +#[no_mangle]<br></div><div> > +pub unsafe extern fn cuesheet_get_comment_value(c_cuesheet : *mut CCuesheet,<br></div><div> > +                                                line : *const c_char) -> *const c_char {<br></div><div> > +    let rust_line = match CStr::from_ptr(line).to_str() {<br></div><div> > +        Ok(value) => value,<br></div><div> > +        Err(_) => return std::ptr::null()<br></div><div> > +    };<br></div><div> > +<br></div><div> > +    let cuesheet = & (*c_cuesheet).cuesheet;<br></div><div> > +<br></div><div> > +    match cuesheet.comments.get(rust_line) {<br></div><div> > +        Some(value) => convert_string_to_ptr(value.to_string()),<br></div><div> > +        _ => std::ptr::null()<br></div><div> > +    }<br></div><div> > +}<br></div><div> > +<br></div><div> > +#[no_mangle]<br></div><div> > +pub unsafe extern fn cuesheet_get_tracks_number(c_cuesheet : *mut CCuesheet) -> c_int {<br></div><div> > +    (*c_cuesheet).cuesheet.tracks.len() as i32<br></div><div> > +}<br></div><div> > +<br></div><div> > +#[no_mangle]<br></div><div> > +pub unsafe extern fn cuesheet_get_track_property(c_cuesheet : *mut CCuesheet, index : c_int,<br></div><div> > +                                                 line : *const c_char) -> *const c_char {<br></div><div> > +    let rust_line = match CStr::from_ptr(line).to_str() {<br></div><div> > +        Ok(value) => value,<br></div><div> > +        Err(_) => return std::ptr::null()<br></div><div> > +    };<br></div><div> > +<br></div><div> > +    let cuesheet = & (*c_cuesheet).cuesheet;<br></div><div> > +    let u_index = match usize::try_from(index) {<br></div><div> > +        Ok(value) => value,<br></div><div> > +        Err(_) => return std::ptr::null()<br></div><div> > +    };<br></div><div> > +<br></div><div> > +    let track = match cuesheet.get_track_at_index(u_index) {<br></div><div> > +        Ok(value) => value,<br></div><div> > +        Err(_) => return std::ptr::null()<br></div><div> > +    };<br></div><div> > +<br></div><div> > +    let val = track.get_property(rust_line);<br></div><div> > +    if val.is_empty() {<br></div><div> > +        return std::ptr::null();<br></div><div> > +    }<br></div><div> > +    convert_string_to_ptr(val.to_string())<br></div><div> > +}<br></div><div> > +<br></div><div> > +unsafe fn convert_string_to_ptr(string: String) -> *mut c_char {<br></div><div> > +    CString::new(string).unwrap().into_raw()<br></div><div> > +}<br></div><div> > +<br></div><div> > +#[no_mangle]<br></div><div> > +pub unsafe extern fn cuesheet_get_track_start(c_cuesheet: *mut CCuesheet, index : c_int) -> c_int {<br></div><div> > +    let cuesheet = &(*c_cuesheet).cuesheet;<br></div><div> > +<br></div><div> > +    let u_index = match usize::try_from(index){<br></div><div> > +        Ok(value) => value,<br></div><div> > +        Err(_e) => return -1<br></div><div> > +    };<br></div><div> > +<br></div><div> > +    match cuesheet.get_track_at_index(u_index) {<br></div><div> > +        Ok(t) => t.begin_time_in_seconds(),<br></div><div> > +        Err(_) => return -1<br></div><div> > +    }<br></div><div> > +}<br></div><div> > +<br></div><div> > +#[no_mangle]<br></div><div> > +pub unsafe extern fn unref_cuesheet_string(text : *mut c_char) {<br></div><div> > +    let _temp = CString::from_raw(text);<br></div><div> > +}<br></div><div> > +<br></div><div> > +#[no_mangle]<br></div><div> > +pub unsafe extern fn unref_cuesheet(cuesheet : *mut CCuesheet) {<br></div><div> > +    let _temp = Box::from_raw(cuesheet);<br></div><div> > +}<br></div><div> > diff --git a/modules/demux/playlist/cuesheet/src/<a href="http://config.rs" rel="noreferrer" target="_blank">config.rs</a> b/modules/demux/playlist/cuesheet/src/<a href="http://config.rs" rel="noreferrer" target="_blank">config.rs</a><br></div><div> > new file mode 100644<br></div><div> > index 0000000000..45292a3e3b<br></div><div> > --- /dev/null<br></div><div> > +++ b/modules/demux/playlist/cuesheet/src/<a href="http://config.rs" rel="noreferrer" target="_blank">config.rs</a><br></div><div> > @@ -0,0 +1,108 @@<br></div><div> > +use std::collections::HashMap;<br></div><div> > +use std::borrow::Borrow;<br></div><div> > +<br></div><div> > +#[derive(Clone, Debug)]<br></div><div> > +pub struct CuesheetTrack {<br></div><div> > +    pub item_type : String,<br></div><div> > +    pub item_position : u8,<br></div><div> > +    pub item_performer : String,<br></div><div> > +    pub item_title : String,<br></div><div> > +    pub item_begin_duration : String<br></div><div> > +}<br></div><div> > +<br></div><div> > +impl Default for CuesheetTrack {<br></div><div> > +    fn default() -> Self {<br></div><div> > +        CuesheetTrack {<br></div><div> > +            item_type: "".to_owned(),<br></div><div> > +            item_position: 0,<br></div><div> > +            item_performer: "".to_string(),<br></div><div> > +            item_title: "".to_string(),<br></div><div> > +            item_begin_duration: "".to_string()<br></div><div> > +        }<br></div><div> > +    }<br></div><div> > +}<br></div><div> > +<br></div><div> > +impl CuesheetTrack {<br></div><div> > +    pub fn begin_time_in_seconds(&self) -> i32 {<br></div><div> > +        let start: &str = &self.item_begin_duration;<br></div><div> > +        let mut duration_parts: Vec<&str> = start.split(":").collect();<br></div><div> > +<br></div><div> > +        let frame_rate = 75;<br></div><div> > +        let convert_min_to_s = 60;<br></div><div> > +        let mut start_time;<br></div><div> > +        let mut part;<br></div><div> > +<br></div><div> > +        part = match duration_parts.pop() {<br></div><div> > +            Some(t) => t.parse::<u32>().unwrap(),<br></div><div> > +            None => return -1<br></div><div> > +        };<br></div><div> > +        start_time = (part / frame_rate) as i32;<br></div><div> > +<br></div><div> > +        part = match duration_parts.pop() {<br></div><div> > +            Some(t) => t.parse::<u32>().unwrap(),<br></div><div> > +            None => return -1<br></div><div> > +        };<br></div><div> > +        start_time += part as i32;<br></div><div> > +<br></div><div> > +        part = match duration_parts.pop() {<br></div><div> > +            Some(t) => t.parse::<u32>().unwrap(),<br></div><div> > +            None => return -1<br></div><div> > +        };<br></div><div> > +        start_time += (part * convert_min_to_s) as i32;<br></div><div> > +        start_time<br></div><div> > +    }<br></div><div> > +<br></div><div> > +    pub fn get_property(&self, key : &str) -> &str {<br></div><div> > +        match key.to_ascii_lowercase().as_str() {<br></div><div> > +            "type" => self.item_type.as_str(),<br></div><div> > +            "begin_duration" => self.item_begin_duration.as_str(),<br></div><div> > +            "performer" => self.item_performer.as_str(),<br></div><div> > +            "title" => self.item_title.as_str(),<br></div><div> > +            _ => ""<br></div><div> > +        }<br></div><div> > +    }<br></div><div> > +}<br></div><div> > +<br></div><div> > +#[derive(Clone, Debug)]<br></div><div> > +pub struct Cuesheet {<br></div><div> > +    pub file_name : String,<br></div><div> > +    pub file_type : String,<br></div><div> > +    pub tracks : Vec<CuesheetTrack>,<br></div><div> > +    pub performer : String,<br></div><div> > +    pub title : String,<br></div><div> > +    pub comments : HashMap<String, String>,<br></div><div> > +    pub is_processing_tracks : bool<br></div><div> > +}<br></div><div> > +<br></div><div> > +impl Default for Cuesheet {<br></div><div> > +    fn default() -> Self {<br></div><div> > +        Cuesheet {<br></div><div> > +            file_name: "".to_string(),<br></div><div> > +            file_type: "".to_string(),<br></div><div> > +            tracks: Vec::new(),<br></div><div> > +            performer: "".to_string(),<br></div><div> > +            title: "".to_string(),<br></div><div> > +            comments: HashMap::new(),<br></div><div> > +            is_processing_tracks: false<br></div><div> > +        }<br></div><div> > +    }<br></div><div> > +}<br></div><div> > +<br></div><div> > +impl Cuesheet {<br></div><div> > +    pub fn get_track_at_index(&self, index : usize) -> Result<&CuesheetTrack, &'static str> {<br></div><div> > +        if index >= self.tracks.len() {<br></div><div> > +            return Err("Index Out of Bounds");<br></div><div> > +        }<br></div><div> > +        Ok(self.tracks[index].borrow())<br></div><div> > +    }<br></div><div> > +<br></div><div> > +    pub fn get_property(&self, key : &str) -> &str {<br></div><div> > +        match key.to_ascii_lowercase().as_str() {<br></div><div> > +            "file_name" => self.file_name.as_str(),<br></div><div> > +            "file_type" => self.file_type.as_str(),<br></div><div> > +            "performer" => self.performer.as_str(),<br></div><div> > +            "title" => self.title.as_str(),<br></div><div> > +            _ => ""<br></div><div> > +        }<br></div><div> > +    }<br></div><div> > +}<br></div><div> > \ No newline at end of file<br></div><div> > diff --git a/modules/demux/playlist/cuesheet/src/<a href="http://lib.rs" rel="noreferrer" target="_blank">lib.rs</a> b/modules/demux/playlist/cuesheet/src/<a href="http://lib.rs" rel="noreferrer" target="_blank">lib.rs</a><br></div><div> > new file mode 100644<br></div><div> > index 0000000000..c768d23825<br></div><div> > --- /dev/null<br></div><div> > +++ b/modules/demux/playlist/cuesheet/src/<a href="http://lib.rs" rel="noreferrer" target="_blank">lib.rs</a><br></div><div> > @@ -0,0 +1,18 @@<br></div><div> > +mod parse;<br></div><div> > +mod config;<br></div><div> > +<br></div><div> > +#[cfg(test)]<br></div><div> > +mod tests {<br></div><div> > +    use crate::parse::parse_line_alt;<br></div><div> > +    #[test]<br></div><div> > +    fn test_parse_line_alt() {<br></div><div> > +        let parts = parse_line_alt("FILE \"Live in Berlin\" MP3");<br></div><div> > +        assert_eq!(3, parts.len());<br></div><div> > +        assert_eq!("FILE", parts[0]);<br></div><div> > +        assert_eq!("Live in Berlin", parts[1]);<br></div><div> > +        assert_eq!("MP3", parts[2]);<br></div><div> > +    }<br></div><div> > +}<br></div><div> > +<br></div><div> > +#[cfg(cargo_c)]<br></div><div> > +mod capi;<br></div><div> > \ No newline at end of file<br></div><div> > diff --git a/modules/demux/playlist/cuesheet/src/<a href="http://parse.rs" rel="noreferrer" target="_blank">parse.rs</a> b/modules/demux/playlist/cuesheet/src/<a href="http://parse.rs" rel="noreferrer" target="_blank">parse.rs</a><br></div><div> > new file mode 100644<br></div><div> > index 0000000000..c1e6e35a2a<br></div><div> > --- /dev/null<br></div><div> > +++ b/modules/demux/playlist/cuesheet/src/<a href="http://parse.rs" rel="noreferrer" target="_blank">parse.rs</a><br></div><div> > @@ -0,0 +1,69 @@<br></div><div> > +use crate::config::*;<br></div><div> > +<br></div><div> > +pub(crate) fn parse_line_alt(line: &str) -> Vec<String> {<br></div><div> > +    let characters = line.trim().chars();<br></div><div> > +    let mut parts: Vec<String> = Vec::new();<br></div><div> > +    let mut inside_quotes: bool = false;<br></div><div> > +    let mut temp_str: String = String::new();<br></div><div> > +    for char in characters {<br></div><div> > +        if char == '"' {<br></div><div> > +            inside_quotes = !inside_quotes;<br></div><div> > +        } else if inside_quotes {<br></div><div> > +            temp_str.push(char);<br></div><div> > +        } else if char.is_whitespace() {<br></div><div> > +            parts.push(temp_str.clone());<br></div><div> > +            temp_str.clear();<br></div><div> > +        } else {<br></div><div> > +            temp_str.push(char);<br></div><div> > +        }<br></div><div> > +    }<br></div><div> > +    parts.push(temp_str.clone());<br></div><div> > +    parts<br></div><div> > +}<br></div><div> > +<br></div><div> > +pub fn process_line(cuesheet: &mut Cuesheet, line : &str) {<br></div><div> > +    let parts = parse_line_alt(line);<br></div><div> > +    let keyword = parts[0].as_str();<br></div><div> > +    if !cuesheet.is_processing_tracks {<br></div><div> > +        match keyword {<br></div><div> > +            "FILE" => {<br></div><div> > +                cuesheet.file_name = parts[1].to_owned();<br></div><div> > +                cuesheet.file_type = parts[2].to_owned();<br></div><div> > +            },<br></div><div> > +            "TITLE" => cuesheet.title = parts[1].to_owned(),<br></div><div> > +            "PERFORMER" => cuesheet.performer = parts[1].to_owned(),<br></div><div> > +            "REM" => {<br></div><div> > +                cuesheet.comments.insert(<br></div><div> > +                    parts[1].to_owned().to_lowercase(), parts[2].to_owned());<br></div><div> > +            },<br></div><div> > +            "TRACK" =>  cuesheet.is_processing_tracks = true,<br></div><div> > +            _ => {}<br></div><div> > +        };<br></div><div> > +    }<br></div><div> > +    if cuesheet.is_processing_tracks {<br></div><div> > +        if keyword == "TRACK" {<br></div><div> > +            let mut track = CuesheetTrack::default();<br></div><div> > +            track.item_position = parts[1].to_owned().parse::<u8>().unwrap();<br></div><div> > +            track.item_type = parts[2].to_owned();<br></div><div> > +            cuesheet.tracks.push(track);<br></div><div> > +        } else {<br></div><div> > +            process_track(cuesheet.tracks.last_mut().unwrap(), line);<br></div><div> > +        }<br></div><div> > +    }<br></div><div> > +}<br></div><div> > +<br></div><div> > +fn process_track(track: &mut CuesheetTrack, line : &str) {<br></div><div> > +    let parts = parse_line_alt(line);<br></div><div> > +    let keyword = parts[0].as_str();<br></div><div> > +    match keyword {<br></div><div> > +        "TITLE" => track.item_title = parts[1].to_owned(),<br></div><div> > +        "PERFORMER" => track.item_performer = parts[1].to_owned(),<br></div><div> > +        "INDEX" => {<br></div><div> > +            let index_position = parts[1].to_owned().parse::<u8>().unwrap();<br></div><div> > +            if index_position == 1 {<br></div><div> > +                track.item_begin_duration = parts[2].to_owned();<br></div><div> > +            }<br></div><div> > +        },<br></div><div> > +        _ => {}<br></div><div> > +    };<br></div><div> > +}<br></div><div> > \ No newline at end of file<br></div><div> > diff --git a/modules/demux/playlist/playlist.c b/modules/demux/playlist/playlist.c<br></div><div> > index d3cca7a502..d5142b7930 100644<br></div><div> > --- a/modules/demux/playlist/playlist.c<br></div><div> > +++ b/modules/demux/playlist/playlist.c<br></div><div> > @@ -130,6 +130,10 @@ vlc_module_begin ()<br></div><div> >           add_shortcut( "wpl" )<br></div><div> >           set_capability( "stream_filter", 310 )<br></div><div> >           set_callbacks( Import_WPL, Close_WPL )<br></div><div> > +    add_submodule ()<br></div><div> > +        set_description ( N_("Cue Sheet importer") )<br></div><div> > +        set_capability ( "stream_filter", 320 )<br></div><div> > +        set_callback ( Import_Cue_Sheet )<br></div><div> <br></div><div> I suppose this module requires some (large) Rust runtime to be linked <br></div><div> with it. I think it would be better in a separate module to avoid this <br></div><div> dependency on the regular module.<br></div><div> <br></div></blockquote><div>I understand the rationale but I did not completely understand what should be done. <br></div></div></div></blockquote><div><br></div><div>Remove your modification in playlist.c/.h<br></div><div><br></div><div>And create your separate module by adding in cuesheet.c<br></div><div><br></div><div>vlc_module_begin()<br></div><div>    add_shortcut( "playlist" )<br></div><div>    set_category( CAT_INPUT )<br></div><div>    set_subcategory( SUBCAT_INPUT_DEMUX )<br></div><div>    set_description ( N_("Cue Sheet importer") )<br></div><div>    set_capability ( "stream_filter", 320 )<br></div><div>    set_callback ( Import_Cue_Sheet )<br></div><div>vlc_module_end()<br></div><div><br></div><div>You will need also to fix the makefile:<br></div><div><br></div><div>libplaylist_cue_plugin_la_SOURCES += demux/playlist/cuesheet.c demux/playlist/cuesheet/cuesheet.h<br></div><div>libplaylist_cue_plugin_la_LIBADD = $(CUESHEET_LIBS)<br></div><div><br></div><blockquote type="cite" id="qt" style=""><div dir="ltr"><div class="qt-gmail_quote"><div><br></div><blockquote class="qt-gmail_quote" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0.8ex;border-left-color:rgb(204, 204, 204);border-left-style:solid;border-left-width:1px;padding-left:1ex;"><div>>   vlc_module_end ()<br></div><div> > <br></div><div> >   /**<br></div><div> > diff --git a/modules/demux/playlist/playlist.h b/modules/demux/playlist/playlist.h<br></div><div> > index 10a9135bbf..6daf77aebb 100644<br></div><div> > --- a/modules/demux/playlist/playlist.h<br></div><div> > +++ b/modules/demux/playlist/playlist.h<br></div><div> > @@ -61,6 +61,8 @@ int Import_WMS(vlc_object_t *);<br></div><div> >   int Import_WPL ( vlc_object_t * );<br></div><div> >   void Close_WPL ( vlc_object_t * );<br></div><div> > <br></div><div> > +int Import_Cue_Sheet ( vlc_object_t * );<br></div><div> > +<br></div><div> >   #define GetCurrentItem(obj) ((obj)->p_input_item)<br></div><div> >   #define GetSource(obj) ((obj)->s)<br></div><div> > <br></div><div> > --<br></div><div> > 2.25.1<br></div><div> > <br></div><div> > _______________________________________________<br></div><div> > vlc-devel mailing list<br></div><div> > To unsubscribe or modify your subscription options:<br></div><div> > <a href="https://mailman.videolan.org/listinfo/vlc-devel" rel="noreferrer" target="_blank">https://mailman.videolan.org/listinfo/vlc-devel</a><br></div><div> > <br></div><div> _______________________________________________<br></div><div> vlc-devel mailing list<br></div><div> To unsubscribe or modify your subscription options:<br></div><div> <a href="https://mailman.videolan.org/listinfo/vlc-devel" rel="noreferrer" target="_blank">https://mailman.videolan.org/listinfo/vlc-devel</a><br></div></blockquote></div></div><div>_______________________________________________<br></div><div>vlc-devel mailing list<br></div><div>To unsubscribe or modify your subscription options:<br></div><div><a href="https://mailman.videolan.org/listinfo/vlc-devel">https://mailman.videolan.org/listinfo/vlc-devel</a><br></div></blockquote><div><br></div></body></html>