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