[vlc-commits] [Git][videolan/vlc][master] 5 commits: json: add getter functions to the json API
Steve Lhomme (@robUx4)
gitlab at videolan.org
Sun Oct 6 07:50:37 UTC 2024
Steve Lhomme pushed to branch master at VideoLAN / VLC
Commits:
7e5d3b86 by Gabriel Lafond-Thenaille at 2024-10-06T07:34:42+00:00
json: add getter functions to the json API
Add wrapper functions arround json_get for json arrays and objects.
Co-authored-by: Mehdi Sabwat <mehdi at videolabs.io>
- - - - -
b7620213 by Gabriel Lafond-Thenaille at 2024-10-06T07:34:42+00:00
meson: make the json parser available as a static library in meson
- - - - -
5002a695 by Gabriel Lafond-Thenaille at 2024-10-06T07:34:42+00:00
webservices: use demux/json parser in webservices
Co-authored-by: Mehdi Sabwat <mehdi at videolabs.io>
- - - - -
d85ecc2d by Gabriel Lafond-Thenaille at 2024-10-06T07:34:42+00:00
chromecast: use demux/json parser in chromecast
Co-authored-by: Mehdi Sabwat <mehdi at videolabs.io>
- - - - -
5cb606cf by Gabriel Lafond-Thenaille at 2024-10-06T07:34:42+00:00
json: remove webservices json parser implementation
- - - - -
19 changed files:
- modules/access/Makefile.am
- modules/access/meson.build
- modules/demux/Makefile.am
- modules/demux/json/grammar.y
- modules/demux/json/json.c
- modules/demux/json/json.h
- modules/demux/json/lexicon.l
- modules/meson.build
- modules/misc/Makefile.am
- modules/misc/meson.build
- modules/misc/webservices/acoustid.c
- − modules/misc/webservices/json.c
- − modules/misc/webservices/json.h
- modules/misc/webservices/json_helper.h
- modules/misc/webservices/musicbrainz.c
- modules/stream_out/Makefile.am
- modules/stream_out/chromecast/chromecast.h
- modules/stream_out/chromecast/chromecast_ctrl.cpp
- modules/stream_out/chromecast/meson.build
Changes:
=====================================
modules/access/Makefile.am
=====================================
@@ -236,11 +236,11 @@ EXTRA_LTLIBRARIES += libvnc_plugin.la
### Optical media ###
libcdda_plugin_la_SOURCES = access/cdda.c access/disc_helper.h access/vcd/cdrom.c access/vcd/cdrom.h \
- access/vcd/cdrom_internals.h misc/webservices/json.c misc/webservices/json.h \
+ access/vcd/cdrom_internals.h \
misc/webservices/json_helper.h misc/webservices/musicbrainz.c \
misc/webservices/musicbrainz.h
libcdda_plugin_la_CFLAGS = $(AM_CFLAGS) $(LIBCDDB_CFLAGS)
-libcdda_plugin_la_LIBADD = $(LIBCDDB_LIBS) $(LIBM)
+libcdda_plugin_la_LIBADD = libvlc_json.la $(LIBCDDB_LIBS) $(LIBM)
libcdda_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(accessdir)'
if HAVE_DARWIN
libcdda_plugin_la_LIBADD += -liconv
=====================================
modules/access/meson.build
=====================================
@@ -202,6 +202,7 @@ endif
## Optical media
+#
# VCD and CD-DA access module
if get_option('vcd_module')
@@ -238,10 +239,10 @@ if get_option('vcd_module')
'sources' : files(
'cdda.c',
'vcd/cdrom.c',
- '../misc/webservices/json.c',
'../misc/webservices/musicbrainz.c',
),
'c_args' : vcd_cdda_flags,
+ 'link_with' : vlc_json_lib,
'dependencies' : [libcddb_dep, vcd_cdda_darwin_deps]
}
=====================================
modules/demux/Makefile.am
=====================================
@@ -558,4 +558,5 @@ libvlc_json_la_SOURCES = \
libvlc_json_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/demux/json
libvlc_json_la_LIBADD = $(LTLIBVLCCORE) ../compat/libcompat.la $(LIBM)
libvlc_json_la_LDFLAGS = -static
+libvlc_json_la_LFLAGS = $(AM_LFLAGS) --outfile=lex.yy.c
noinst_LTLIBRARIES += libvlc_json.la
=====================================
modules/demux/json/grammar.y
=====================================
@@ -22,7 +22,7 @@
%define api.prefix {json}
%lex-param { void *scanner }
-%parse-param { void *log }
+%parse-param { void *opaque }
%parse-param { void *scanner }
%parse-param { struct json_object *result }
@@ -144,14 +144,14 @@ static void json_append(struct json_object *o, struct json_member m)
%{
-static void yyerror(void *log, void *scanner, struct json_object *result,
- const char *msg)
+static void yyerror(void *opaque, void *scanner, struct json_object *result,
+ const char *msg)
{
- json_parse_error(log, msg);
+ json_parse_error(opaque, msg);
(void) scanner; (void) result;
}
-extern int jsonlex_init_extra(void *, void **);
+extern int jsonlex_init_extra(const void *, void **);
extern int yylex(YYSTYPE *value, void *scanner);
extern int jsonlex_destroy(void *);
=====================================
modules/demux/json/json.c
=====================================
@@ -134,6 +134,21 @@ const struct json_value *json_get(const struct json_object *obj,
return NULL;
}
+const struct json_object *json_get_object(const struct json_object *obj,
+ const char *name)
+{
+ const struct json_value *v = json_get(obj, name);
+ return (v != NULL && v->type == JSON_OBJECT) ? &v->object : NULL;
+}
+
+const struct json_array *json_get_array(const struct json_object *obj,
+ const char *name)
+{
+ const struct json_value *v = json_get(obj, name);
+ return (v != NULL && v->type == JSON_ARRAY) ? &v->array : NULL;
+}
+
+
const char *json_get_str(const struct json_object *obj, const char *name)
{
const struct json_value *v = json_get(obj, name);
=====================================
modules/demux/json/json.h
=====================================
@@ -61,15 +61,19 @@ struct json_member {
struct json_value value;
};
-size_t json_read(void *data, void *buf, size_t max);
-void json_parse_error(void *log, const char *msg);
+size_t json_read(void *opaque, void *buf, size_t max);
+void json_parse_error(void *opaque, const char *msg);
char *json_unescape(const char *, size_t);
-int json_parse(void *log, struct json_object *result);
+int json_parse(void *opaque, struct json_object *result);
void json_free(struct json_object *);
const struct json_value *json_get(const struct json_object *obj,
const char *name);
+const struct json_object *json_get_object(const struct json_object *obj,
+ const char *name);
+const struct json_array *json_get_array(const struct json_object *obj,
+ const char *name);
const char *json_get_str(const struct json_object *obj, const char *name);
double json_get_num(const struct json_object *obj, const char *name);
=====================================
modules/demux/json/lexicon.l
=====================================
@@ -26,7 +26,7 @@
%option nostdinit
%option nounput
%option noyywrap
-%option prefix="json" outfile="lex.yy.c"
+%option prefix="json"
%{
=====================================
modules/meson.build
=====================================
@@ -201,6 +201,21 @@ libgcrypt_dep = dependency('libgcrypt',
# Rust support
cargo_bin = find_program('cargo', required: get_option('rust'))
+# JSON library
+json_bison_files = bison_gen.process('demux/json/grammar.y')
+json_lex_files = flex_gen.process('demux/json/lexicon.l')
+
+vlc_json_lib = static_library('vlc_json',
+ sources: [
+ json_bison_files,
+ json_lex_files,
+ 'demux/json/json.c',
+ ],
+ include_directories: [vlc_include_dirs, 'demux/json/'],
+ install: false,
+ pic: true
+)
+
# Array that holds all enabled VLC module dicts
vlc_modules = []
vlc_rust_modules = []
=====================================
modules/misc/Makefile.am
=====================================
@@ -17,11 +17,10 @@ misc_LTLIBRARIES += libexport_plugin.la
libfingerprinter_plugin_la_SOURCES = \
misc/webservices/acoustid.c misc/webservices/acoustid.h \
- misc/webservices/json.c misc/webservices/json.h \
misc/webservices/json_helper.h \
misc/fingerprinter.c
libfingerprinter_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/misc
-libfingerprinter_plugin_la_LIBADD = $(LIBM)
+libfingerprinter_plugin_la_LIBADD = libvlc_json.la $(LIBM)
misc_LTLIBRARIES += libfingerprinter_plugin.la
libgnutls_plugin_la_SOURCES = misc/gnutls.c
=====================================
modules/misc/meson.build
=====================================
@@ -27,10 +27,10 @@ vlc_modules += {
'name' : 'fingerprinter',
'sources' : files(
'fingerprinter.c',
- 'webservices/json.c',
'webservices/acoustid.c',
),
- 'dependencies' : [m_lib]
+ 'dependencies' : [m_lib],
+ 'link_with' : vlc_json_lib,
}
# libxml2 module
=====================================
modules/misc/webservices/acoustid.c
=====================================
@@ -22,6 +22,9 @@
# include "config.h"
#endif
+#include <vlc_common.h>
+#include <vlc_messages.h>
+
#include "json_helper.h"
#include "acoustid.h"
@@ -39,36 +42,43 @@ void acoustid_result_release( acoustid_result_t * r )
free( r->recordings.p_recordings );
}
-static void parse_artists( const json_value *node, acoustid_mb_result_t *record )
+static void parse_artists( const struct json_array *artists, acoustid_mb_result_t *record )
{
/* take only main */
- if ( !node || node->type != json_array || node->u.array.length < 1 )
+ if ( artists->size < 1)
return;
- record->psz_artist = json_dupstring( node->u.array.values[ 0 ], "name" );
+ if (artists->entries[0].type == JSON_OBJECT) {
+ record->psz_artist = json_dupstring( &artists->entries[ 0 ].object, "name" );
+ }
}
-static void parse_recordings( vlc_object_t *p_obj, const json_value *node, acoustid_result_t *p_result )
+static void parse_recordings( vlc_object_t *p_obj, const struct json_object *object,
+ acoustid_result_t *p_result )
{
- if ( !node || node->type != json_array ) return;
- p_result->recordings.p_recordings = calloc( node->u.array.length, sizeof(acoustid_mb_result_t) );
+ const struct json_array *recordings = json_get_array(object, "recordings");
+ if (recordings == NULL)
+ return;
+ p_result->recordings.p_recordings = calloc( recordings->size, sizeof(acoustid_mb_result_t) );
if ( ! p_result->recordings.p_recordings ) return;
- p_result->recordings.count = node->u.array.length;
+ p_result->recordings.count = recordings->size;
- for( unsigned int i=0; i<node->u.array.length; i++ )
+ for( unsigned int i = 0; i < recordings->size; i++ )
{
acoustid_mb_result_t *record = & p_result->recordings.p_recordings[ i ];
- const json_value *recordnode = node->u.array.values[ i ];
- if ( !recordnode || recordnode->type != json_object )
+ struct json_value *recordnode = &recordings->entries[ i ];
+ if ( recordnode->type != JSON_OBJECT )
break;
- record->psz_title = json_dupstring( recordnode, "title" );
- const json_value *value = json_getbyname( recordnode, "id" );
- if ( value && value->type == json_string )
+ record->psz_title = json_dupstring( &recordnode->object, "title" );
+ const char *id = json_get_str( &recordnode->object, "id" );
+ if ( id != NULL )
{
- size_t i_len = strlen( value->u.string.ptr );
+ size_t i_len = strlen( id );
i_len = __MIN( i_len, MB_ID_SIZE );
- memcpy( record->s_musicbrainz_id, value->u.string.ptr, i_len );
+ memcpy( record->s_musicbrainz_id, id, i_len );
}
- parse_artists( json_getbyname( recordnode, "artists" ), record );
+ const struct json_array *artists = json_get_array(&recordnode->object,
+ "artists");
+ parse_artists( artists, record );
msg_Dbg( p_obj, "recording %d title %s %36s %s", i, record->psz_title,
record->s_musicbrainz_id, record->psz_artist );
}
@@ -77,49 +87,59 @@ static void parse_recordings( vlc_object_t *p_obj, const json_value *node, acous
static bool ParseJson( vlc_object_t *p_obj, const void *p_buffer, size_t i_buffer,
acoustid_results_t *p_results )
{
- json_value *root = json_parse_document( p_obj, p_buffer, i_buffer );
- if( !root )
+ struct json_helper_sys sys;
+ sys.logger = p_obj->logger;
+ sys.buffer = p_buffer;
+ sys.size = i_buffer;
+
+ struct json_object json;
+ int val = json_parse(&sys, &json);
+ if (val) {
+ msg_Dbg( p_obj, "error: could not parse json!");
return false;
+ }
- const json_value *node = json_getbyname( root, "status" );
- if ( !node || node->type != json_string )
+ const char *status = json_get_str(&json, "status");
+ if (status == NULL)
{
msg_Warn( p_obj, "status node not found or invalid" );
- goto error;
+ json_free(&json);
+ return false;
}
- if ( strcmp( node->u.string.ptr, "ok" ) != 0 )
+ if (strcmp(status, "ok") != 0)
{
msg_Warn( p_obj, "Bad request status" );
- goto error;
+ json_free(&json);
+ return false;
}
- node = json_getbyname( root, "results" );
- if ( !node || node->type != json_array )
+ const struct json_array *results = json_get_array(&json, "results");
+ if (results == NULL)
{
msg_Warn( p_obj, "Bad results array or no results" );
- goto error;
+ json_free(&json);
+ return false;
}
- p_results->p_results = calloc( node->u.array.length, sizeof(acoustid_result_t) );
- if ( ! p_results->p_results ) goto error;
- p_results->count = node->u.array.length;
- for( unsigned int i=0; i<node->u.array.length; i++ )
+ p_results->p_results = calloc(results->size, sizeof(acoustid_result_t));
+ if ( ! p_results->p_results ) {
+ json_free(&json);
+ return false;
+ }
+ p_results->count = results->size;
+ for( unsigned int i=0; i<results->size; i++ )
{
- const json_value *resultnode = node->u.array.values[i];
- if ( resultnode && resultnode->type == json_object )
+ const struct json_value *resultnode = &results->entries[i];
+ if ( resultnode->type == JSON_OBJECT )
{
acoustid_result_t *p_result = & p_results->p_results[i];
- const json_value *value = json_getbyname( resultnode, "score" );
- if ( value && value->type == json_double )
- p_result->d_score = value->u.dbl;
- p_result->psz_id = json_dupstring( resultnode, "id" );
- parse_recordings( p_obj, json_getbyname( resultnode, "recordings" ), p_result );
+ double score = json_get_num(&resultnode->object, "score");
+ if (score != NAN)
+ p_result->d_score = score;
+ p_result->psz_id = json_dupstring(&resultnode->object, "id");
+ parse_recordings( p_obj, &resultnode->object, p_result );
}
}
- json_value_free( root );
+ json_free(&json);
return true;
-
-error:
- if ( root ) json_value_free( root );
- return false;
}
int acoustid_lookup_fingerprint( const acoustid_config_t *p_cfg, acoustid_fingerprint_t *p_data )
=====================================
modules/misc/webservices/json.c deleted
=====================================
@@ -1,1005 +0,0 @@
-/* vim: set et ts=3 sw=3 sts=3 ft=c:
- *
- * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved.
- * https://github.com/udp/json-parser
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include "json.h"
-
-#ifdef _MSC_VER
- #ifndef _CRT_SECURE_NO_WARNINGS
- #define _CRT_SECURE_NO_WARNINGS
- #endif
-#endif
-
-const struct _json_value json_value_none;
-
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <math.h>
-
-typedef unsigned int json_uchar;
-
-static unsigned char hex_value (json_char c)
-{
- if (isdigit(c))
- return c - '0';
-
- switch (c) {
- case 'a': case 'A': return 0x0A;
- case 'b': case 'B': return 0x0B;
- case 'c': case 'C': return 0x0C;
- case 'd': case 'D': return 0x0D;
- case 'e': case 'E': return 0x0E;
- case 'f': case 'F': return 0x0F;
- default: return 0xFF;
- }
-}
-
-typedef struct
-{
- unsigned long used_memory;
-
- unsigned int uint_max;
- unsigned long ulong_max;
-
- json_settings settings;
- int first_pass;
-
- const json_char * ptr;
- unsigned int cur_line, cur_col;
-
-} json_state;
-
-static void * default_alloc (size_t size, int zero, void * user_data)
-{
- return zero ? calloc (1, size) : malloc (size);
-}
-
-static void default_free (void * ptr, void * user_data)
-{
- free (ptr);
-}
-
-static void * json_alloc (json_state * state, unsigned long size, int zero)
-{
- if ((state->ulong_max - state->used_memory) < size)
- return 0;
-
- if (state->settings.max_memory
- && (state->used_memory += size) > state->settings.max_memory)
- {
- return 0;
- }
-
- return state->settings.mem_alloc (size, zero, state->settings.user_data);
-}
-
-static int new_value (json_state * state,
- json_value ** top, json_value ** root, json_value ** alloc,
- json_type type)
-{
- json_value * value;
- int values_size;
-
- if (!state->first_pass)
- {
- value = *top = *alloc;
- *alloc = (*alloc)->_reserved.next_alloc;
-
- if (!*root)
- *root = value;
-
- switch (value->type)
- {
- case json_array:
-
- if (! (value->u.array.values = (json_value **) json_alloc
- (state, value->u.array.length * sizeof (json_value *), 0)) )
- {
- return 0;
- }
-
- value->u.array.length = 0;
- break;
-
- case json_object:
-
- values_size = sizeof (*value->u.object.values) * value->u.object.length;
-
- if (! ((*(void **) &value->u.object.values) = json_alloc
- (state, values_size + ((unsigned long) value->u.object.values), 0)) )
- {
- return 0;
- }
-
- value->_reserved.object_mem = (*(char **) &value->u.object.values) + values_size;
-
- value->u.object.length = 0;
- break;
-
- case json_string:
-
- if (! (value->u.string.ptr = (json_char *) json_alloc
- (state, (value->u.string.length + 1) * sizeof (json_char), 0)) )
- {
- return 0;
- }
-
- value->u.string.length = 0;
- break;
-
- default:
- break;
- };
-
- return 1;
- }
-
- if (! (value = (json_value *) json_alloc
- (state, sizeof (json_value) + state->settings.value_extra, 1)))
- {
- return 0;
- }
-
- if (!*root)
- *root = value;
-
- value->type = type;
- value->parent = *top;
-
- #ifdef JSON_TRACK_SOURCE
- value->line = state->cur_line;
- value->col = state->cur_col;
- #endif
-
- if (*alloc)
- (*alloc)->_reserved.next_alloc = value;
-
- *alloc = *top = value;
-
- return 1;
-}
-
-#define whitespace \
- case '\n': ++ state.cur_line; state.cur_col = 0; \
- case ' ': case '\t': case '\r'
-
-#define string_add(b) \
- do { if (!state.first_pass) string [string_length] = b; ++ string_length; } while (0);
-
-#define line_and_col \
- state.cur_line, state.cur_col
-
-static const long
- flag_next = 1 << 0,
- flag_reproc = 1 << 1,
- flag_need_comma = 1 << 2,
- flag_seek_value = 1 << 3,
- flag_escaped = 1 << 4,
- flag_string = 1 << 5,
- flag_need_colon = 1 << 6,
- flag_done = 1 << 7,
- flag_num_negative = 1 << 8,
- flag_num_zero = 1 << 9,
- flag_num_e = 1 << 10,
- flag_num_e_got_sign = 1 << 11,
- flag_num_e_negative = 1 << 12,
- flag_line_comment = 1 << 13,
- flag_block_comment = 1 << 14;
-
-json_value * json_parse_ex (json_settings * settings,
- const json_char * json,
- size_t length,
- char * error_buf)
-{
- json_char error [json_error_max];
- const json_char * end;
- json_value * top, * root, * alloc = 0;
- json_state state = { 0 };
- long flags;
- long num_digits = 0, num_e = 0;
- json_int_t num_fraction = 0;
-
- /* Skip UTF-8 BOM
- */
- if (length >= 3 && ((unsigned char) json [0]) == 0xEF
- && ((unsigned char) json [1]) == 0xBB
- && ((unsigned char) json [2]) == 0xBF)
- {
- json += 3;
- length -= 3;
- }
-
- error[0] = '\0';
- end = (json + length);
-
- memcpy (&state.settings, settings, sizeof (json_settings));
-
- if (!state.settings.mem_alloc)
- state.settings.mem_alloc = default_alloc;
-
- if (!state.settings.mem_free)
- state.settings.mem_free = default_free;
-
- memset (&state.uint_max, 0xFF, sizeof (state.uint_max));
- memset (&state.ulong_max, 0xFF, sizeof (state.ulong_max));
-
- state.uint_max -= 8; /* limit of how much can be added before next check */
- state.ulong_max -= 8;
-
- for (state.first_pass = 1; state.first_pass >= 0; -- state.first_pass)
- {
- json_uchar uchar;
- unsigned char uc_b1, uc_b2, uc_b3, uc_b4;
- json_char * string = 0;
- unsigned int string_length = 0;
-
- top = root = 0;
- flags = flag_seek_value;
-
- state.cur_line = 1;
-
- for (state.ptr = json ;; ++ state.ptr)
- {
- json_char b = (state.ptr == end ? 0 : *state.ptr);
-
- if (flags & flag_string)
- {
- if (!b)
- { sprintf (error, "Unexpected EOF in string (at %d:%d)", line_and_col);
- goto e_failed;
- }
-
- if (string_length > state.uint_max)
- goto e_overflow;
-
- if (flags & flag_escaped)
- {
- flags &= ~ flag_escaped;
-
- switch (b)
- {
- case 'b': string_add ('\b'); break;
- case 'f': string_add ('\f'); break;
- case 'n': string_add ('\n'); break;
- case 'r': string_add ('\r'); break;
- case 't': string_add ('\t'); break;
- case 'u':
-
- if (end - state.ptr < 4 ||
- (uc_b1 = hex_value (*++ state.ptr)) == 0xFF ||
- (uc_b2 = hex_value (*++ state.ptr)) == 0xFF ||
- (uc_b3 = hex_value (*++ state.ptr)) == 0xFF ||
- (uc_b4 = hex_value (*++ state.ptr)) == 0xFF)
- {
- sprintf (error, "Invalid character value `%c` (at %d:%d)", b, line_and_col);
- goto e_failed;
- }
-
- uc_b1 = (uc_b1 << 4) | uc_b2;
- uc_b2 = (uc_b3 << 4) | uc_b4;
- uchar = (uc_b1 << 8) | uc_b2;
-
- if ((uchar & 0xF800) == 0xD800) {
- json_uchar uchar2;
-
- if (end - state.ptr < 6 || (*++ state.ptr) != '\\' || (*++ state.ptr) != 'u' ||
- (uc_b1 = hex_value (*++ state.ptr)) == 0xFF ||
- (uc_b2 = hex_value (*++ state.ptr)) == 0xFF ||
- (uc_b3 = hex_value (*++ state.ptr)) == 0xFF ||
- (uc_b4 = hex_value (*++ state.ptr)) == 0xFF)
- {
- sprintf (error, "Invalid character value `%c` (at %d:%d)", b, line_and_col);
- goto e_failed;
- }
-
- uc_b1 = (uc_b1 << 4) | uc_b2;
- uc_b2 = (uc_b3 << 4) | uc_b4;
- uchar2 = (uc_b1 << 8) | uc_b2;
-
- uchar = 0x010000 | ((uchar & 0x3FF) << 10) | (uchar2 & 0x3FF);
- }
-
- if (sizeof (json_char) >= sizeof (json_uchar) || (uchar <= 0x7F))
- {
- string_add ((json_char) uchar);
- break;
- }
-
- if (uchar <= 0x7FF)
- {
- if (state.first_pass)
- string_length += 2;
- else
- { string [string_length ++] = 0xC0 | (uchar >> 6);
- string [string_length ++] = 0x80 | (uchar & 0x3F);
- }
-
- break;
- }
-
- if (uchar <= 0xFFFF) {
- if (state.first_pass)
- string_length += 3;
- else
- { string [string_length ++] = 0xE0 | (uchar >> 12);
- string [string_length ++] = 0x80 | ((uchar >> 6) & 0x3F);
- string [string_length ++] = 0x80 | (uchar & 0x3F);
- }
-
- break;
- }
-
- if (state.first_pass)
- string_length += 4;
- else
- { string [string_length ++] = 0xF0 | (uchar >> 18);
- string [string_length ++] = 0x80 | ((uchar >> 12) & 0x3F);
- string [string_length ++] = 0x80 | ((uchar >> 6) & 0x3F);
- string [string_length ++] = 0x80 | (uchar & 0x3F);
- }
-
- break;
-
- default:
- string_add (b);
- };
-
- continue;
- }
-
- if (b == '\\')
- {
- flags |= flag_escaped;
- continue;
- }
-
- if (b == '"')
- {
- if (!state.first_pass)
- string [string_length] = 0;
-
- flags &= ~ flag_string;
- string = 0;
-
- switch (top->type)
- {
- case json_string:
-
- top->u.string.length = string_length;
- flags |= flag_next;
-
- break;
-
- case json_object:
-
- if (state.first_pass)
- (*(json_char **) &top->u.object.values) += string_length + 1;
- else
- {
- top->u.object.values [top->u.object.length].name
- = (json_char *) top->_reserved.object_mem;
-
- top->u.object.values [top->u.object.length].name_length
- = string_length;
-
- (*(json_char **) &top->_reserved.object_mem) += string_length + 1;
- }
-
- flags |= flag_seek_value | flag_need_colon;
- continue;
-
- default:
- break;
- };
- }
- else
- {
- string_add (b);
- continue;
- }
- }
-
- if (state.settings.settings & json_enable_comments)
- {
- if (flags & (flag_line_comment | flag_block_comment))
- {
- if (flags & flag_line_comment)
- {
- if (b == '\r' || b == '\n' || !b)
- {
- flags &= ~ flag_line_comment;
- -- state.ptr; /* so null can be reproc'd */
- }
-
- continue;
- }
-
- if (flags & flag_block_comment)
- {
- if (!b)
- { sprintf (error, "%d:%d: Unexpected EOF in block comment", line_and_col);
- goto e_failed;
- }
-
- if (b == '*' && state.ptr < (end - 1) && state.ptr [1] == '/')
- {
- flags &= ~ flag_block_comment;
- ++ state.ptr; /* skip closing sequence */
- }
-
- continue;
- }
- }
- else if (b == '/')
- {
- if (! (flags & (flag_seek_value | flag_done)) && top->type != json_object)
- { sprintf (error, "%d:%d: Comment not allowed here", line_and_col);
- goto e_failed;
- }
-
- if (++ state.ptr == end)
- { sprintf (error, "%d:%d: EOF unexpected", line_and_col);
- goto e_failed;
- }
-
- switch (b = *state.ptr)
- {
- case '/':
- flags |= flag_line_comment;
- continue;
-
- case '*':
- flags |= flag_block_comment;
- continue;
-
- default:
- sprintf (error, "%d:%d: Unexpected `%c` in comment opening sequence", line_and_col, b);
- goto e_failed;
- };
- }
- }
-
- if (flags & flag_done)
- {
- if (!b)
- break;
-
- switch (b)
- {
- whitespace:
- continue;
-
- default:
-
- sprintf (error, "%d:%d: Trailing garbage: `%c`",
- state.cur_line, state.cur_col, b);
-
- goto e_failed;
- };
- }
-
- if (flags & flag_seek_value)
- {
- switch (b)
- {
- whitespace:
- continue;
-
- case ']':
-
- if (top && top->type == json_array)
- flags = (flags & ~ (flag_need_comma | flag_seek_value)) | flag_next;
- else
- { sprintf (error, "%d:%d: Unexpected ]", line_and_col);
- goto e_failed;
- }
-
- break;
-
- default:
-
- if (flags & flag_need_comma)
- {
- if (b == ',')
- { flags &= ~ flag_need_comma;
- continue;
- }
- else
- {
- sprintf (error, "%d:%d: Expected , before %c",
- state.cur_line, state.cur_col, b);
-
- goto e_failed;
- }
- }
-
- if (flags & flag_need_colon)
- {
- if (b == ':')
- { flags &= ~ flag_need_colon;
- continue;
- }
- else
- {
- sprintf (error, "%d:%d: Expected : before %c",
- state.cur_line, state.cur_col, b);
-
- goto e_failed;
- }
- }
-
- flags &= ~ flag_seek_value;
-
- switch (b)
- {
- case '{':
-
- if (!new_value (&state, &top, &root, &alloc, json_object))
- goto e_alloc_failure;
-
- continue;
-
- case '[':
-
- if (!new_value (&state, &top, &root, &alloc, json_array))
- goto e_alloc_failure;
-
- flags |= flag_seek_value;
- continue;
-
- case '"':
-
- if (!new_value (&state, &top, &root, &alloc, json_string))
- goto e_alloc_failure;
-
- flags |= flag_string;
-
- string = top->u.string.ptr;
- string_length = 0;
-
- continue;
-
- case 't':
-
- if ((end - state.ptr) < 3 || *(++ state.ptr) != 'r' ||
- *(++ state.ptr) != 'u' || *(++ state.ptr) != 'e')
- {
- goto e_unknown_value;
- }
-
- if (!new_value (&state, &top, &root, &alloc, json_boolean))
- goto e_alloc_failure;
-
- top->u.boolean = 1;
-
- flags |= flag_next;
- break;
-
- case 'f':
-
- if ((end - state.ptr) < 4 || *(++ state.ptr) != 'a' ||
- *(++ state.ptr) != 'l' || *(++ state.ptr) != 's' ||
- *(++ state.ptr) != 'e')
- {
- goto e_unknown_value;
- }
-
- if (!new_value (&state, &top, &root, &alloc, json_boolean))
- goto e_alloc_failure;
-
- flags |= flag_next;
- break;
-
- case 'n':
-
- if ((end - state.ptr) < 3 || *(++ state.ptr) != 'u' ||
- *(++ state.ptr) != 'l' || *(++ state.ptr) != 'l')
- {
- goto e_unknown_value;
- }
-
- if (!new_value (&state, &top, &root, &alloc, json_null))
- goto e_alloc_failure;
-
- flags |= flag_next;
- break;
-
- default:
-
- if (isdigit (b) || b == '-')
- {
- if (!new_value (&state, &top, &root, &alloc, json_integer))
- goto e_alloc_failure;
-
- if (!state.first_pass)
- {
- while (isdigit (b) || b == '+' || b == '-'
- || b == 'e' || b == 'E' || b == '.')
- {
- if ( (++ state.ptr) == end)
- {
- b = 0;
- break;
- }
-
- b = *state.ptr;
- }
-
- flags |= flag_next | flag_reproc;
- break;
- }
-
- flags &= ~ (flag_num_negative | flag_num_e |
- flag_num_e_got_sign | flag_num_e_negative |
- flag_num_zero);
-
- num_digits = 0;
- num_fraction = 0;
- num_e = 0;
-
- if (b != '-')
- {
- flags |= flag_reproc;
- break;
- }
-
- flags |= flag_num_negative;
- continue;
- }
- else
- { sprintf (error, "%d:%d: Unexpected %c when seeking value", line_and_col, b);
- goto e_failed;
- }
- };
- };
- }
- else
- {
- switch (top->type)
- {
- case json_object:
-
- switch (b)
- {
- whitespace:
- continue;
-
- case '"':
-
- if (flags & flag_need_comma)
- { sprintf (error, "%d:%d: Expected , before \"", line_and_col);
- goto e_failed;
- }
-
- flags |= flag_string;
-
- string = (json_char *) top->_reserved.object_mem;
- string_length = 0;
-
- break;
-
- case '}':
-
- flags = (flags & ~ flag_need_comma) | flag_next;
- break;
-
- case ',':
-
- if (flags & flag_need_comma)
- {
- flags &= ~ flag_need_comma;
- break;
- }
-
- default:
- sprintf (error, "%d:%d: Unexpected `%c` in object", line_and_col, b);
- goto e_failed;
- };
-
- break;
-
- case json_integer:
- case json_double:
-
- if (isdigit (b))
- {
- ++ num_digits;
-
- if (top->type == json_integer || flags & flag_num_e)
- {
- if (! (flags & flag_num_e))
- {
- if (flags & flag_num_zero)
- { sprintf (error, "%d:%d: Unexpected `0` before `%c`", line_and_col, b);
- goto e_failed;
- }
-
- if (num_digits == 1 && b == '0')
- flags |= flag_num_zero;
- }
- else
- {
- flags |= flag_num_e_got_sign;
- num_e = (num_e * 10) + (b - '0');
- continue;
- }
-
- top->u.integer = (top->u.integer * 10) + (b - '0');
- continue;
- }
-
- num_fraction = (num_fraction * 10) + (b - '0');
- continue;
- }
-
- if (b == '+' || b == '-')
- {
- if ( (flags & flag_num_e) && !(flags & flag_num_e_got_sign))
- {
- flags |= flag_num_e_got_sign;
-
- if (b == '-')
- flags |= flag_num_e_negative;
-
- continue;
- }
- }
- else if (b == '.' && top->type == json_integer)
- {
- if (!num_digits)
- { sprintf (error, "%d:%d: Expected digit before `.`", line_and_col);
- goto e_failed;
- }
-
- top->type = json_double;
- top->u.dbl = (double) top->u.integer;
-
- num_digits = 0;
- continue;
- }
-
- if (! (flags & flag_num_e))
- {
- if (top->type == json_double)
- {
- if (!num_digits)
- { sprintf (error, "%d:%d: Expected digit after `.`", line_and_col);
- goto e_failed;
- }
-
- top->u.dbl += ((double) num_fraction) / (pow (10.0, (double) num_digits));
- }
-
- if (b == 'e' || b == 'E')
- {
- flags |= flag_num_e;
-
- if (top->type == json_integer)
- {
- top->type = json_double;
- top->u.dbl = (double) top->u.integer;
- }
-
- num_digits = 0;
- flags &= ~ flag_num_zero;
-
- continue;
- }
- }
- else
- {
- if (!num_digits)
- { sprintf (error, "%d:%d: Expected digit after `e`", line_and_col);
- goto e_failed;
- }
-
- top->u.dbl *= pow (10.0, (double)
- (flags & flag_num_e_negative ? - num_e : num_e));
- }
-
- if (flags & flag_num_negative)
- {
- if (top->type == json_integer)
- top->u.integer = - top->u.integer;
- else
- top->u.dbl = - top->u.dbl;
- }
-
- flags |= flag_next | flag_reproc;
- break;
-
- default:
- break;
- };
- }
-
- if (flags & flag_reproc)
- {
- flags &= ~ flag_reproc;
- -- state.ptr;
- }
-
- if (flags & flag_next)
- {
- flags = (flags & ~ flag_next) | flag_need_comma;
-
- if (!top->parent)
- {
- /* root value done */
-
- flags |= flag_done;
- continue;
- }
-
- if (top->parent->type == json_array)
- flags |= flag_seek_value;
-
- if (!state.first_pass)
- {
- json_value * parent = top->parent;
-
- switch (parent->type)
- {
- case json_object:
-
- parent->u.object.values
- [parent->u.object.length].value = top;
-
- break;
-
- case json_array:
-
- parent->u.array.values
- [parent->u.array.length] = top;
-
- break;
-
- default:
- break;
- };
- }
-
- if ( (++ top->parent->u.array.length) > state.uint_max)
- goto e_overflow;
-
- top = top->parent;
-
- continue;
- }
- }
-
- alloc = root;
- }
-
- return root;
-
-e_unknown_value:
-
- sprintf (error, "%d:%d: Unknown value", line_and_col);
- goto e_failed;
-
-e_alloc_failure:
-
- strcpy (error, "Memory allocation failure");
- goto e_failed;
-
-e_overflow:
-
- sprintf (error, "%d:%d: Too long (caught overflow)", line_and_col);
- goto e_failed;
-
-e_failed:
-
- if (error_buf)
- {
- if (*error)
- strcpy (error_buf, error);
- else
- strcpy (error_buf, "Unknown error");
- }
-
- if (state.first_pass)
- alloc = root;
-
- while (alloc)
- {
- top = alloc->_reserved.next_alloc;
- state.settings.mem_free (alloc, state.settings.user_data);
- alloc = top;
- }
-
- if (!state.first_pass)
- json_value_free_ex (&state.settings, root);
-
- return 0;
-}
-
-json_value * json_parse (const json_char * json, size_t length)
-{
- json_settings settings = { 0 };
- return json_parse_ex (&settings, json, length, 0);
-}
-
-void json_value_free_ex (json_settings * settings, json_value * value)
-{
- json_value * cur_value;
-
- if (!value)
- return;
-
- value->parent = 0;
-
- while (value)
- {
- switch (value->type)
- {
- case json_array:
-
- if (!value->u.array.length)
- {
- settings->mem_free (value->u.array.values, settings->user_data);
- break;
- }
-
- value = value->u.array.values [-- value->u.array.length];
- continue;
-
- case json_object:
-
- if (!value->u.object.length)
- {
- settings->mem_free (value->u.object.values, settings->user_data);
- break;
- }
-
- value = value->u.object.values [-- value->u.object.length].value;
- continue;
-
- case json_string:
-
- settings->mem_free (value->u.string.ptr, settings->user_data);
- break;
-
- default:
- break;
- };
-
- cur_value = value;
- value = value->parent;
- settings->mem_free (cur_value, settings->user_data);
- }
-}
-
-void json_value_free (json_value * value)
-{
- json_settings settings = { 0 };
- settings.mem_free = default_free;
- json_value_free_ex (&settings, value);
-}
-
=====================================
modules/misc/webservices/json.h deleted
=====================================
@@ -1,278 +0,0 @@
-/* vim: set et ts=3 sw=3 sts=3 ft=c:
- *
- * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved.
- * https://github.com/udp/json-parser
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef VLC_WEBSERVICES_JSON_H_
-#define VLC_WEBSERVICES_JSON_H_
-
-#ifndef json_char
- #define json_char char
-#endif
-
-#ifndef json_int_t
- #ifndef _MSC_VER
- #include <inttypes.h>
- #define json_int_t int64_t
- #else
- #define json_int_t __int64
- #endif
-#endif
-
-#include <stdlib.h>
-
-#ifdef __cplusplus
-
- #include <string.h>
-
- extern "C"
- {
-
-#endif
-
-typedef struct
-{
- unsigned long max_memory;
- int settings;
-
- /* Custom allocator support (leave null to use malloc/free)
- */
-
- void * (* mem_alloc) (size_t, int zero, void * user_data);
- void (* mem_free) (void *, void * user_data);
-
- void * user_data; /* will be passed to mem_alloc and mem_free */
-
- size_t value_extra; /* how much extra space to allocate for values? */
-
-} json_settings;
-
-#define json_enable_comments 0x01
-
-typedef enum
-{
- json_none,
- json_object,
- json_array,
- json_integer,
- json_double,
- json_string,
- json_boolean,
- json_null
-
-} json_type;
-
-extern const struct _json_value json_value_none;
-
-typedef struct _json_value
-{
- struct _json_value * parent;
-
- json_type type;
-
- union
- {
- int boolean;
- json_int_t integer;
- double dbl;
-
- struct
- {
- unsigned int length;
- json_char * ptr; /* null terminated */
-
- } string;
-
- struct
- {
- unsigned int length;
-
- struct
- {
- json_char * name;
- unsigned int name_length;
-
- struct _json_value * value;
-
- } * values;
-
- #if defined(__cplusplus) && __cplusplus >= 201103L
- decltype(values) begin () const
- { return values;
- }
- decltype(values) end () const
- { return values + length;
- }
- #endif
-
- } object;
-
- struct
- {
- unsigned int length;
- struct _json_value ** values;
-
- #if defined(__cplusplus) && __cplusplus >= 201103L
- decltype(values) begin () const
- { return values;
- }
- decltype(values) end () const
- { return values + length;
- }
- #endif
-
- } array;
-
- } u;
-
- union
- {
- struct _json_value * next_alloc;
- void * object_mem;
-
- } _reserved;
-
- #ifdef JSON_TRACK_SOURCE
-
- /* Location of the value in the source JSON
- */
- unsigned int line, col;
-
- #endif
-
-
- /* Some C++ operator sugar */
-
- #ifdef __cplusplus
-
- public:
-
- inline _json_value ()
- { memset (this, 0, sizeof (_json_value));
- }
-
- inline const struct _json_value &operator [] (int index) const
- {
- if (type != json_array || index < 0
- || ((unsigned int) index) >= u.array.length)
- {
- return json_value_none;
- }
-
- return *u.array.values [index];
- }
-
- inline const struct _json_value &operator [] (const char * index) const
- {
- if (type != json_object)
- return json_value_none;
-
- for (unsigned int i = 0; i < u.object.length; ++ i)
- if (!strcmp (u.object.values [i].name, index))
- return *u.object.values [i].value;
-
- return json_value_none;
- }
-
- inline operator const char * () const
- {
- switch (type)
- {
- case json_string:
- return u.string.ptr;
-
- default:
- return "";
- };
- }
-
- inline operator json_int_t () const
- {
- switch (type)
- {
- case json_integer:
- return u.integer;
-
- case json_double:
- return (json_int_t) u.dbl;
-
- default:
- return 0;
- };
- }
-
- inline operator bool () const
- {
- if (type != json_boolean)
- return false;
-
- return u.boolean != 0;
- }
-
- inline operator double () const
- {
- switch (type)
- {
- case json_integer:
- return (double) u.integer;
-
- case json_double:
- return u.dbl;
-
- default:
- return 0;
- };
- }
-
- #endif
-
-} json_value;
-
-json_value * json_parse (const json_char * json,
- size_t length);
-
-#define json_error_max 128
-json_value * json_parse_ex (json_settings * settings,
- const json_char * json,
- size_t length,
- char * error);
-
-void json_value_free (json_value *);
-
-
-/* Not usually necessary, unless you used a custom mem_alloc and now want to
- * use a custom mem_free.
- */
-void json_value_free_ex (json_settings * settings,
- json_value *);
-
-
-#ifdef __cplusplus
- } /* extern "C" */
-#endif
-
-#endif
=====================================
modules/misc/webservices/json_helper.h
=====================================
@@ -26,59 +26,15 @@
#include <limits.h>
-#include "json.h"
+#include "../../demux/json/json.h"
static inline
-const json_value * json_getbyname(const json_value *object, const char *psz_name)
+char * json_dupstring(const struct json_object *obj, const char *key)
{
- if (object->type != json_object) return NULL;
- for (unsigned int i=0; i < object->u.object.length; i++)
- if (strcmp(object->u.object.values[i].name, psz_name) == 0)
- return object->u.object.values[i].value;
- return NULL;
-}
-
-static inline
-char * jsongetstring(const json_value *node, const char *key)
-{
- node = json_getbyname(node, key);
- if (node && node->type == json_string)
- return node->u.string.ptr;
- return NULL;
-}
-
-static inline
-char * json_dupstring(const json_value *node, const char *key)
-{
- const char *str = jsongetstring(node, key);
+ const char *str = json_get_str(obj, key);
return (str) ? strdup(str) : NULL;
}
-static inline
-json_value * json_parse_document(vlc_object_t *p_obj, const char *psz_buffer, size_t i_buffer)
-{
- json_settings settings;
- char psz_error[128];
- memset (&settings, 0, sizeof (json_settings));
- json_value *root = json_parse_ex(&settings, psz_buffer, i_buffer, psz_error);
- if (root == NULL)
- {
- msg_Warn(p_obj, "Can't parse json data: %s", psz_error);
- goto error;
- }
- if (root->type != json_object)
- {
- msg_Warn(p_obj, "wrong json root node");
- goto error;
- }
-
- return root;
-
-error:
- if (root) json_value_free(root);
- return NULL;
-}
-
static inline
void * json_retrieve_document(vlc_object_t *p_obj, const char *psz_url, size_t *buf_size)
{
@@ -123,4 +79,30 @@ void * json_retrieve_document(vlc_object_t *p_obj, const char *psz_url, size_t *
return p_buffer;
}
+struct json_helper_sys {
+ struct vlc_logger *logger;
+ const char *buffer;
+ size_t size;
+};
+
+void json_parse_error(void *data, const char *msg)
+{
+ struct json_helper_sys *sys = data;
+
+ vlc_error(sys->logger, "%s", msg);
+}
+
+size_t json_read(void *data, void *buf, size_t size)
+{
+ struct json_helper_sys *sys = data;
+
+ /* Read the smallest number of byte between size and the string length */
+ size_t s = size < sys->size ? size : sys->size;
+ memcpy(buf, sys->buffer, s);
+
+ sys->buffer += s;
+ sys->size -= s;
+ return s;
+}
+
#endif
=====================================
modules/misc/webservices/musicbrainz.c
=====================================
@@ -24,18 +24,20 @@
#include <string.h>
#include <limits.h>
+#include <vlc_common.h>
+#include <vlc_messages.h>
+
#include "json_helper.h"
#include "musicbrainz.h"
typedef struct
{
- json_value *root;
+ struct json_object json;
} musicbrainz_lookup_t;
static void musicbrainz_lookup_release(musicbrainz_lookup_t *p)
{
- if(p && p->root)
- json_value_free(p->root);
+ json_free(&p->json);
free(p);
}
@@ -53,86 +55,98 @@ static musicbrainz_lookup_t * musicbrainz_lookup(vlc_object_t *p_obj, const char
return NULL;
musicbrainz_lookup_t *p_lookup = musicbrainz_lookup_new();
+
if(p_lookup)
{
- p_lookup->root = json_parse_document(p_obj, p_buffer, i_buffer);
- if (!p_lookup->root)
- msg_Dbg(p_obj, "No results");
+ struct json_helper_sys sys;
+ sys.logger = p_obj->logger;
+ sys.buffer = p_buffer;
+ sys.size = i_buffer;
+
+ int val = json_parse(&sys, &p_lookup->json);
+ if (val) {
+ msg_Dbg( p_obj, "error: could not parse json!");
+ free(p_buffer);
+ return false;
+ }
}
free(p_buffer);
return p_lookup;
}
-static bool musicbrainz_fill_track(const json_value *tracknode, musicbrainz_track_t *t)
+static bool musicbrainz_fill_track(const struct json_object *trackobj, musicbrainz_track_t *t)
{
- t->psz_title = json_dupstring(tracknode, "title");
+ t->psz_title = json_dupstring(trackobj, "title");
- const json_value *node = json_getbyname(tracknode, "artist-credit");
- if (node && node->type == json_array && node->u.array.length)
- t->psz_artist = json_dupstring(node->u.array.values[0], "name");
+ const struct json_array *array = json_get_array(trackobj, "artist-credit");
+ if (array != NULL && array->size >= 1) {
+ const struct json_value *artist = &array->entries[0];
+ if (artist->type == JSON_OBJECT)
+ t->psz_artist = json_dupstring(&artist->object, "name");
+ }
- node = json_getbyname(tracknode, "position");
- if (node && node->type == json_integer)
- t->i_index = node->u.integer;
+ double pos = json_get_num(trackobj, "position");
+ if (pos != NAN) {
+ t->i_index = pos;
+ }
return true;
}
-static bool musicbrainz_has_cover_in_releasegroup(json_value ** const p_nodes,
+static bool musicbrainz_has_cover_in_releasegroup(const struct json_value *p_nodes,
size_t i_nodes,
const char *psz_group_id)
{
+ /* FIXME, not sure it does what I want */
for(size_t i=0; i<i_nodes; i++)
{
- const json_value *rgnode = json_getbyname(p_nodes[i], "release-group");
- if(rgnode)
- {
- const char *psz_id = jsongetstring(rgnode, "id");
- if(!psz_id || strcmp(psz_id, psz_group_id))
- continue;
-
- const json_value *node = json_getbyname(p_nodes[i], "cover-art-archive");
- if(!node)
- continue;
+ if (p_nodes[i].type != JSON_OBJECT)
+ continue;
+ const struct json_object *obj = &p_nodes[i].object;
- node = json_getbyname(node, "front");
- if(!node || node->type != json_boolean || !node->u.boolean)
- continue;
+ const struct json_object *rgobj = json_get_object(obj, "release-group");
+ if (rgobj == NULL)
+ continue;
+ const char *psz_id = json_get_str(rgobj, "id");
+ if(!psz_id || strcmp(psz_id, psz_group_id))
+ continue;
+
+ const struct json_object *cover = json_get_object(obj, "cover-art-archive");
+ if (cover == NULL)
+ continue;
+ const struct json_value *node = json_get(cover, "front");
+ if (node == NULL)
+ continue;
+ if(node->type != JSON_BOOLEAN || !node->boolean)
+ continue;
- return true;
- }
+ return true;
}
return false;
}
-static char *musicbrainz_fill_artists(const json_value *arraynode)
+static char *musicbrainz_fill_artists(const struct json_array *array)
{
char *psz = NULL;
- if(arraynode->type != json_array || arraynode->u.array.length < 1)
- return psz;
- size_t i_total = 1;
- for(size_t i=0; i<arraynode->u.array.length; i++)
+ for(size_t i = 0; i < array->size; i++)
{
- const json_value *name = json_getbyname(arraynode->u.array.values[i], "name");
- if(name->type != json_string)
+ if (array->entries[i].type != JSON_OBJECT)
continue;
+ const struct json_object *obj = &array->entries[i].object;
- if(psz == NULL)
- {
- psz = strdup(name->u.string.ptr);
- i_total = name->u.string.length + 1;
+ const char *name = json_get_str(obj, "name");
+ if (name == NULL)
+ continue;
+ if(psz == NULL) {
+ psz = strdup(name);
}
- else
- {
- char *p = realloc(psz, i_total + name->u.string.length + 2);
- if(p)
- {
+ else {
+ char *p = NULL;
+ if (asprintf(&p, "%s, %s", psz, name) > 0) {
+ free(psz);
psz = p;
- psz = strcat(psz, ", ");
- psz = strncat(psz, name->u.string.ptr, name->u.string.length);
- i_total += name->u.string.length + 2;
}
}
}
@@ -140,55 +154,54 @@ static char *musicbrainz_fill_artists(const json_value *arraynode)
return psz;
}
-static bool musicbrainz_fill_release(const json_value *releasenode, musicbrainz_release_t *r)
+static bool musicbrainz_fill_release(const struct json_object *release,
+ musicbrainz_release_t *r)
{
- const json_value *media = json_getbyname(releasenode, "media");
- if(!media || media->type != json_array ||
- media->u.array.length == 0)
+ const struct json_array *media_array = json_get_array(release, "media");
+ if (media_array == NULL && media_array->size == 0)
return false;
/* we always use first media */
- media = media->u.array.values[0];
+ const struct json_value *media = &media_array->entries[0];
+ if (media->type != JSON_OBJECT)
+ return false;
- const json_value *tracks = json_getbyname(media, "tracks");
- if(!tracks || tracks->type != json_array ||
- tracks->u.array.length == 0)
+ const struct json_array *tracks = json_get_array(&media->object, "tracks");
+ if (tracks == NULL && tracks->size == 0)
return false;
- r->p_tracks = calloc(tracks->u.array.length, sizeof(*r->p_tracks));
+ r->p_tracks = calloc(tracks->size, sizeof(*r->p_tracks));
if(!r->p_tracks)
return false;
- for(size_t i=0; i<tracks->u.array.length; i++)
+ for(size_t i = 0; i < tracks->size; i++)
{
- if(musicbrainz_fill_track(tracks->u.array.values[i], &r->p_tracks[r->i_tracks]))
+ if (tracks->entries[i].type != JSON_OBJECT)
+ continue;
+ const struct json_object *trackobj = &tracks->entries[i].object;
+ if(musicbrainz_fill_track(trackobj, &r->p_tracks[r->i_tracks]))
r->i_tracks++;
}
- r->psz_title = json_dupstring(releasenode, "title");
- r->psz_id = json_dupstring(releasenode, "id");
+ r->psz_title = json_dupstring(release, "title");
+ r->psz_id = json_dupstring(release, "id");
- const json_value *rgnode = json_getbyname(releasenode, "release-group");
- if(rgnode)
- {
- r->psz_date = json_dupstring(rgnode, "first-release-date");
- r->psz_group_id = json_dupstring(rgnode, "id");
+ const struct json_object *rg = json_get_object(release,
+ "release-group");
+ if (rg != NULL) {
+ r->psz_date = json_dupstring(rg, "first-release-date");
+ r->psz_group_id = json_dupstring(rg, "id");
- const json_value *node = json_getbyname(rgnode, "artist-credit");
- if(node)
- r->psz_artist = musicbrainz_fill_artists(node);
+ const struct json_array *artists = json_get_array(rg, "artist-credit");
+ if (artists != NULL)
+ r->psz_artist = musicbrainz_fill_artists(artists);
}
- else
- {
- const json_value *node = json_getbyname(releasenode, "artist-credit");
- if(node)
- r->psz_artist = musicbrainz_fill_artists(node);
-
- node = json_getbyname(releasenode, "release-events");
- if(node && node->type == json_array && node->u.array.length)
- r->psz_date = json_dupstring(node->u.array.values[0], "date");
+ const struct json_array *events = json_get_array(release,
+ "release-events");
+ if (events != NULL && events->size > 0) {
+ if (events->entries[0].type == JSON_OBJECT)
+ r->psz_date = json_dupstring(&events->entries[0].object, "date");
}
-
return true;
}
@@ -227,25 +240,24 @@ static musicbrainz_recording_t *musicbrainz_lookup_recording_by_apiurl(vlc_objec
return NULL;
}
- const json_value *releases = json_getbyname(lookup->root, "releases");
- if (releases && releases->type == json_array &&
- releases->u.array.length)
+ const struct json_array *releases = json_get_array(&lookup->json, "releases");
+ if (releases != NULL && releases->size)
{
- r->p_releases = calloc(releases->u.array.length, sizeof(*r->p_releases));
+ r->p_releases = calloc(releases->size, sizeof(*r->p_releases));
if(r->p_releases)
{
- for(unsigned i=0; i<releases->u.array.length; i++)
+ for(unsigned i = 0; i < releases->size; i++)
{
- json_value *node = releases->u.array.values[i];
musicbrainz_release_t *p_mbrel = &r->p_releases[r->i_release];
- if (!node || node->type != json_object ||
- !musicbrainz_fill_release(node, p_mbrel))
+ const struct json_value *node = &releases->entries[i];
+ if (node->type != JSON_OBJECT ||
+ !musicbrainz_fill_release(&node->object, p_mbrel))
continue;
/* Try to find cover from other releases from the same group */
if(p_mbrel->psz_group_id && !p_mbrel->psz_coverart_url &&
- musicbrainz_has_cover_in_releasegroup(releases->u.array.values,
- releases->u.array.length,
+ musicbrainz_has_cover_in_releasegroup(releases->entries,
+ releases->size,
p_mbrel->psz_group_id))
{
char *psz_art = coverartarchive_make_releasegroup_arturl(
@@ -349,3 +361,4 @@ coverartarchive_t * coverartarchive_lookup_releasegroup(musicbrainz_config_t *cf
return c;
}
+
=====================================
modules/stream_out/Makefile.am
=====================================
@@ -140,10 +140,10 @@ libstream_out_chromecast_plugin_la_SOURCES = stream_out/chromecast/cast.cpp stre
stream_out/renderer_common.hpp \
stream_out/renderer_common.cpp \
stream_out/chromecast/chromecast_common.h stream_out/chromecast/chromecast_ctrl.cpp \
- misc/webservices/json.h misc/webservices/json.c stream_out/chromecast/chromecast_communication.cpp
+ stream_out/chromecast/chromecast_communication.cpp
nodist_libstream_out_chromecast_plugin_la_SOURCES = stream_out/chromecast/@PROTOBUF_VERSION@/cast_channel.pb.cc
libstream_out_chromecast_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -Istream_out/chromecast/@PROTOBUF_VERSION@ $(CHROMECAST_CFLAGS)
-libstream_out_chromecast_plugin_la_LIBADD = $(CHROMECAST_LIBS) $(SOCKET_LIBS)
+libstream_out_chromecast_plugin_la_LIBADD = $(CHROMECAST_LIBS) $(SOCKET_LIBS) libvlc_json.la
CLEANFILES += $(nodist_libstream_out_chromecast_plugin_la_SOURCES)
if ENABLE_SOUT
=====================================
modules/stream_out/chromecast/chromecast.h
=====================================
@@ -221,10 +221,10 @@ private:
void mainLoop();
void processAuthMessage( const castchannel::CastMessage& msg );
- void processHeartBeatMessage( const castchannel::CastMessage& msg );
- bool processReceiverMessage( const castchannel::CastMessage& msg );
- void processMediaMessage( const castchannel::CastMessage& msg );
- void processConnectionMessage( const castchannel::CastMessage& msg );
+ void processHeartBeatMessage( const castchannel::CastMessage& msg , const struct json_object *entry );
+ bool processReceiverMessage( const castchannel::CastMessage& msg , const struct json_object *entry );
+ void processMediaMessage( const castchannel::CastMessage& msg , const struct json_object *entry );
+ void processConnectionMessage( const castchannel::CastMessage& msg , const struct json_object *entry );
private:
static void* ChromecastThread(void* p_data);
=====================================
modules/stream_out/chromecast/chromecast_ctrl.cpp
=====================================
@@ -41,12 +41,47 @@
#include <vlc_rand.h>
#include <vlc_threads.h>
-#include "../../misc/webservices/json.h"
+extern "C" {
+#include "../../demux/json/json.h"
+}
/* deadline regarding pings sent from receiver */
#define PING_WAIT_TIME 6000
#define PING_WAIT_RETRIES 1
+extern "C" {
+
+struct chromecast_ctrl_json {
+ struct vlc_logger *logger;
+ const char *string;
+};
+
+void json_parse_error(void *data, const char *msg)
+{
+ vlc_assert(data != NULL);
+
+ struct chromecast_ctrl_json *sys = static_cast<struct chromecast_ctrl_json *>(data);
+ vlc_error(sys->logger, "%s", msg);
+}
+
+size_t json_read(void *data, void *buf, size_t size)
+{
+ vlc_assert(data != NULL);
+ vlc_assert(buf != NULL);
+
+ struct chromecast_ctrl_json *sys = static_cast<struct chromecast_ctrl_json *>(data);
+ size_t len = strlen(sys->string);
+
+ /* Read the smallest number of byte between size and the string length */
+ size_t s = size < len ? size : len;
+ memcpy(buf, sys->string, s);
+
+ sys->string += s;
+ return s;
+}
+
+}
+
static int httpd_file_fill_cb( httpd_file_sys_t *data, httpd_file_t *http_file,
uint8_t *psz_request, uint8_t **pp_data, size_t *pi_data );
@@ -538,6 +573,7 @@ void intf_sys_t::sendInputEvent(enum cc_input_event event, union cc_input_arg ar
* @param msg the CastMessage to process
* @return 0 if the message has been successfully processed else -1
*/
+
bool intf_sys_t::processMessage(const castchannel::CastMessage &msg)
{
const std::string & namespace_ = msg.namespace_();
@@ -546,21 +582,40 @@ bool intf_sys_t::processMessage(const castchannel::CastMessage &msg)
msg_Dbg( m_module, "processMessage: %s->%s %s", namespace_.c_str(), msg.destination_id().c_str(), msg.payload_utf8().c_str());
#endif
- bool ret = true;
- if (namespace_ == NAMESPACE_DEVICEAUTH)
+ if (namespace_ == NAMESPACE_DEVICEAUTH) {
processAuthMessage( msg );
- else if (namespace_ == NAMESPACE_HEARTBEAT)
- processHeartBeatMessage( msg );
+ return true;
+ }
+ if ((namespace_ != NAMESPACE_HEARTBEAT)
+ && (namespace_ != NAMESPACE_RECEIVER)
+ && (namespace_ != NAMESPACE_MEDIA)
+ && (namespace_ != NAMESPACE_CONNECTION)) {
+ msg_Err( m_module, "Unknown namespace: %s", namespace_.c_str());
+ return false;
+ }
+
+ struct chromecast_ctrl_json sys;
+ sys.logger = m_module->logger;
+ sys.string = msg.payload_utf8().c_str();
+
+ struct json_object json;
+ int val = json_parse(&sys, &json);
+ if (val) {
+ msg_Dbg(m_module, "error: could not parse json!");
+ return false;
+ }
+
+ bool ret = true;
+ if (namespace_ == NAMESPACE_HEARTBEAT)
+ processHeartBeatMessage(msg , &json);
else if (namespace_ == NAMESPACE_RECEIVER)
- ret = processReceiverMessage( msg );
+ ret = processReceiverMessage(msg , &json);
else if (namespace_ == NAMESPACE_MEDIA)
- processMediaMessage( msg );
+ processMediaMessage(msg , &json);
else if (namespace_ == NAMESPACE_CONNECTION)
- processConnectionMessage( msg );
- else
- {
- msg_Err( m_module, "Unknown namespace: %s", msg.namespace_().c_str());
- }
+ processConnectionMessage(msg , &json);
+
+ json_free(&json);
return ret;
}
@@ -667,11 +722,11 @@ void intf_sys_t::processAuthMessage( const castchannel::CastMessage& msg )
}
}
-void intf_sys_t::processHeartBeatMessage( const castchannel::CastMessage& msg )
+void intf_sys_t::processHeartBeatMessage( const castchannel::CastMessage& msg , const struct json_object *entry )
{
- json_value *p_data = json_parse(msg.payload_utf8().c_str(), msg.payload_utf8().length());
- std::string type((*p_data)["type"]);
-
+ VLC_UNUSED(msg);
+ const char *tmp = json_get_str(entry, "type");
+ std::string type = tmp ? tmp : "";
if (type == "PING")
{
msg_Dbg( m_module, "PING received from the Chromecast");
@@ -686,33 +741,38 @@ void intf_sys_t::processHeartBeatMessage( const castchannel::CastMessage& msg )
{
msg_Warn( m_module, "Heartbeat command not supported: %s", type.c_str());
}
-
- json_value_free(p_data);
}
-bool intf_sys_t::processReceiverMessage( const castchannel::CastMessage& msg )
+bool intf_sys_t::processReceiverMessage( const castchannel::CastMessage& msg , const struct json_object *entry )
{
- json_value *p_data = json_parse(msg.payload_utf8().c_str(), msg.payload_utf8().length());
- std::string type((*p_data)["type"]);
+ const char *tmp = json_get_str(entry, "type");
+ std::string type = tmp ? tmp : "";
bool ret = true;
if (type == "RECEIVER_STATUS")
{
- json_value applications = (*p_data)["status"]["applications"];
- const json_value *p_app = NULL;
-
- for (unsigned i = 0; i < applications.u.array.length; ++i)
- {
- if ( strcmp( applications[i]["appId"], APP_ID ) == 0 )
+ const json_value *status = json_get(entry, "status");
+ if (!status) {
+ msg_Err(m_module, "receiver message: bad payload");
+ return false;
+ }
+ const json_value *applications = json_get(&status->object, "applications");
+ const json_object *p_app = NULL;
+ if (applications) {
+ for (unsigned i = 0; i < applications->array.size; ++i)
{
- if ( (const char*)applications[i]["transportId"] != NULL)
+ const char *appId = json_get_str(&applications->array.entries[i].object, "appId");
+ const char *transportId = json_get_str(&applications->array.entries[i].object, "transportId");
+ if ( strcmp( appId, APP_ID ) == 0 )
{
- p_app = &applications[i];
- break;
+ if ( transportId != NULL )
+ {
+ p_app = &applications->array.entries[i].object;
+ break;
+ }
}
}
}
-
vlc::threads::mutex_locker lock( m_lock );
switch ( m_state )
@@ -723,7 +783,8 @@ bool intf_sys_t::processReceiverMessage( const castchannel::CastMessage& msg )
if ( p_app != NULL )
{
msg_Dbg( m_module, "Media receiver application was already running" );
- m_appTransportId = (const char*)(*p_app)["transportId"];
+ tmp = json_get_str(p_app, "transportId");
+ m_appTransportId = tmp ? tmp: "";
m_communication->msgConnect( m_appTransportId );
setState( Ready );
}
@@ -737,7 +798,8 @@ bool intf_sys_t::processReceiverMessage( const castchannel::CastMessage& msg )
if ( p_app != NULL )
{
msg_Dbg( m_module, "Media receiver application has been started." );
- m_appTransportId = (const char*)(*p_app)["transportId"];
+ tmp = json_get_str(p_app, "transportId");
+ m_appTransportId = tmp ? tmp: "";
m_communication->msgConnect( m_appTransportId );
setState( Ready );
}
@@ -775,9 +837,9 @@ bool intf_sys_t::processReceiverMessage( const castchannel::CastMessage& msg )
}
else if (type == "LAUNCH_ERROR")
{
- json_value reason = (*p_data)["reason"];
+ const char *reason = json_get_str(entry, "reason");
msg_Err( m_module, "Failed to start the MediaPlayer: %s",
- (const char *)reason);
+ reason);
vlc::threads::mutex_locker lock( m_lock );
m_appTransportId = "";
m_mediaSessionId = 0;
@@ -790,41 +852,44 @@ bool intf_sys_t::processReceiverMessage( const castchannel::CastMessage& msg )
msg.payload_utf8().c_str());
}
- json_value_free(p_data);
return ret;
}
-void intf_sys_t::processMediaMessage( const castchannel::CastMessage& msg )
+void intf_sys_t::processMediaMessage( const castchannel::CastMessage& msg , const struct json_object *entry )
{
- json_value *p_data = json_parse(msg.payload_utf8().c_str(), msg.payload_utf8().length());
- std::string type((*p_data)["type"]);
- int64_t requestId = (json_int_t) (*p_data)["requestId"];
+ const char *tmp = json_get_str(entry, "type");
+ std::string type = tmp ? tmp: "";
+ int64_t requestId = (int64_t) json_get_num(entry,"requestId");
vlc::threads::mutex_locker lock( m_lock );
if ((m_last_request_id != 0 && requestId != m_last_request_id))
- {
- json_value_free(p_data);
return;
- }
m_last_request_id = 0;
if (type == "MEDIA_STATUS")
{
- json_value status = (*p_data)["status"];
-
- int64_t sessionId = (json_int_t) status[0]["mediaSessionId"];
- std::string newPlayerState = (const char*)status[0]["playerState"];
- std::string idleReason = (const char*)status[0]["idleReason"];
+ const json_value *status = json_get(entry, "status");
+ if (!status) {
+ return;
+ }
+ if (status->array.size < 1) {
+ msg_Warn( m_module, "Bad payload, got a media status message with less than one session Id");
+ return;
+ }
+ int64_t sessionId = (int64_t) json_get_num(&status->array.entries[0].object,"mediaSessionId");
+ tmp = json_get_str(&status->array.entries[0].object,"playerState");
+ std::string newPlayerState = tmp ? tmp: "";
+ tmp = json_get_str(&status->array.entries[0].object,"idleReason");
+ std::string idleReason = tmp ? tmp: "";
msg_Dbg( m_module, "Player state: %s sessionId: %" PRId64,
- status[0]["playerState"].operator const char *(),
- sessionId );
+ json_get_str(&status->array.entries[0].object,"playerState"),
+ sessionId );
if (sessionId != 0 && m_mediaSessionId != 0 && m_mediaSessionId != sessionId)
{
msg_Warn( m_module, "Ignoring message for a different media session");
- json_value_free(p_data);
return;
}
@@ -881,12 +946,15 @@ void intf_sys_t::processMediaMessage( const castchannel::CastMessage& msg )
setState( Stopping );
}
else if (newPlayerState == "PLAYING")
- {
- vlc_tick_t currentTime = timeCCToVLC((double) status[0]["currentTime"]);
- m_cc_time = currentTime;
- m_cc_time_date = vlc_tick_now();
-
- setState( Playing );
+ {
+ if (status->array.size >= 1) {
+ vlc_tick_t currentTime = timeCCToVLC(json_get_num(&status->array.entries[0].object,
+ "currentTime"));
+ m_cc_time = currentTime;
+ m_cc_time_date = vlc_tick_now();
+
+ setState( Playing );
+ }
}
else if (newPlayerState == "BUFFERING")
{
@@ -930,7 +998,7 @@ void intf_sys_t::processMediaMessage( const castchannel::CastMessage& msg )
}
else if (type == "INVALID_REQUEST")
{
- msg_Dbg( m_module, "We sent an invalid request reason:%s", (const char*)(*p_data)["reason"] );
+ msg_Dbg( m_module, "We sent an invalid request reason:%s", json_get_str(entry, "reason"));
}
else
{
@@ -938,14 +1006,16 @@ void intf_sys_t::processMediaMessage( const castchannel::CastMessage& msg )
msg.payload_utf8().c_str());
}
- json_value_free(p_data);
}
-void intf_sys_t::processConnectionMessage( const castchannel::CastMessage& msg )
+void intf_sys_t::processConnectionMessage( const castchannel::CastMessage& msg , const struct json_object *entry )
{
- json_value *p_data = json_parse(msg.payload_utf8().c_str(), msg.payload_utf8().length());
- std::string type((*p_data)["type"]);
- json_value_free(p_data);
+ const struct json_value *value = json_get(entry, msg.payload_utf8().c_str());
+ if (!value) {
+ msg_Err(m_module, "connection message: bad payload");
+ return;
+ }
+ std::string type = json_get_str(&value->object, "type");
if ( type == "CLOSE" )
{
=====================================
modules/stream_out/chromecast/meson.build
=====================================
@@ -56,11 +56,11 @@ vlc_modules += {
'chromecast_common.h',
'chromecast_ctrl.cpp',
'chromecast_communication.cpp',
- '../../misc/webservices/json.c',
- '../../misc/webservices/json.h',
+ '../../demux/json/json.c',
'../renderer_common.cpp',
'../renderer_common.hpp') +
chromecast_proto,
'dependencies' : [socket_libs, protobuf_dep],
+ 'link_with' : vlc_json_lib,
'enabled': get_option('stream_outputs') and protobuf_dep.found() and protoc.found(),
}
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/02b197170de07c75a70fa5b414d213ba57b46f56...5cb606cf94cc650938f3b4b06c13c464a2ce9231
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/02b197170de07c75a70fa5b414d213ba57b46f56...5cb606cf94cc650938f3b4b06c13c464a2ce9231
You're receiving this email because of your account on code.videolan.org.
VideoLAN code repository instance
More information about the vlc-commits
mailing list