[vlc-devel] [PATCH 3/4] chromaprint: add fingerprinter module
Francois Cartegnie
fcvlcdev at free.fr
Mon Feb 11 14:52:35 CET 2013
---
modules/misc/Modules.am | 7 +
modules/misc/fingerprinter.c | 403 +++++++++++++++++++
modules/misc/webservices/acoustid.c | 229 +++++++++++
modules/misc/webservices/acoustid.h | 59 +++
modules/misc/webservices/json.c | 739 +++++++++++++++++++++++++++++++++++
modules/misc/webservices/json.h | 34 ++
modules/misc/webservices/use_json.h | 189 +++++++++
7 files changed, 1660 insertions(+), 0 deletions(-)
create mode 100644 modules/misc/fingerprinter.c
create mode 100644 modules/misc/webservices/acoustid.c
create mode 100644 modules/misc/webservices/acoustid.h
create mode 100644 modules/misc/webservices/json.c
create mode 100644 modules/misc/webservices/json.h
create mode 100644 modules/misc/webservices/use_json.h
diff --git a/modules/misc/Modules.am b/modules/misc/Modules.am
index f835485..5560ebf 100644
--- a/modules/misc/Modules.am
+++ b/modules/misc/Modules.am
@@ -1,5 +1,11 @@
SOURCES_vod_rtsp = rtsp.c
SOURCES_audioscrobbler = audioscrobbler.c
+SOURCES_fingerprinter = fingerprinter.c \
+ fingerprinter.h \
+ webservices/acoustid.c \
+ webservices/json_fixup.h \
+ webservices/json.c \
+ webservices/json.h
SOURCES_sqlite = sqlite.c
SOURCES_xml = xml/libxml.c
@@ -59,6 +65,7 @@ libstats_plugin_la_LIBADD = $(AM_LIBADD)
libvlc_LTLIBRARIES += \
libaudioscrobbler_plugin.la \
+ libfingerprinter_plugin.la \
liblogger_plugin.la \
libstats_plugin.la
diff --git a/modules/misc/fingerprinter.c b/modules/misc/fingerprinter.c
new file mode 100644
index 0000000..76ec919
--- /dev/null
+++ b/modules/misc/fingerprinter.c
@@ -0,0 +1,403 @@
+/*****************************************************************************
+ * fingerprinter.c: Audio fingerprinter module
+ *****************************************************************************
+ * Copyright (C) 2012 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_stream.h>
+#include <vlc_modules.h>
+#include <vlc_meta.h>
+#include <vlc_url.h>
+
+#include <vlc/vlc.h>
+#include <vlc_input.h>
+#include <vlc_fingerprinter.h>
+#include "webservices/acoustid.h"
+#include "../stream_out/chromaprint_data.h"
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+
+struct fingerprinter_sys_t
+{
+ struct
+ {
+ vlc_array_t *queue;
+ vlc_mutex_t lock;
+ } incoming, processing, results;
+
+ vlc_cond_t incoming_queue_filled;
+
+ struct
+ {
+ vlc_mutex_t lock;
+ vlc_cond_t wait;
+ int i_input_state;
+ } condwait;
+
+ /* tracked in sys for cancelability */
+ input_item_t *p_item;
+ input_thread_t *p_input;
+ chromaprint_fingerprint_t chroma_fingerprint;
+ char *psz_uri;
+
+ /* clobberable by cleanups */
+ int i_cancel_state;
+ int i;
+};
+
+static int Open (vlc_object_t *);
+static void Close (vlc_object_t *);
+static void Run (fingerprinter_thread_t *);
+
+/*****************************************************************************
+ * Module descriptor
+ ****************************************************************************/
+vlc_module_begin ()
+ set_category(CAT_ADVANCED)
+ set_shortname(N_("acoustid"))
+ set_description(N_("Track fingerprinter (based on Acoustid)"))
+ set_capability("fingerprinter", 1)
+ set_callbacks(Open, Close)
+vlc_module_end ()
+
+/*****************************************************************************
+ * Requests lifecycle
+ *****************************************************************************/
+
+static void EnqueueRequest( fingerprinter_thread_t *f, fingerprint_request_t *r )
+{
+ fingerprinter_sys_t *p_sys = f->p_sys;
+ vlc_mutex_lock( &p_sys->incoming.lock );
+ vlc_array_append( p_sys->incoming.queue, r );
+ vlc_mutex_unlock( &p_sys->incoming.lock );
+ vlc_cond_signal( &p_sys->incoming_queue_filled );
+}
+
+static void QueueIncomingRequests( fingerprinter_sys_t *p_sys )
+{
+ vlc_mutex_lock( &p_sys->incoming.lock );
+ int i = vlc_array_count( p_sys->incoming.queue );
+ if ( i == 0 ) goto end;
+ vlc_mutex_lock( &p_sys->processing.lock );
+ while( i )
+ vlc_array_append( p_sys->processing.queue,
+ vlc_array_item_at_index( p_sys->incoming.queue, --i ) );
+ vlc_array_clear( p_sys->incoming.queue );
+ vlc_mutex_unlock( &p_sys->processing.lock );
+end:
+ vlc_mutex_unlock(&p_sys->incoming.lock);
+}
+
+static fingerprint_request_t * GetResult( fingerprinter_thread_t *f )
+{
+ fingerprint_request_t *r = NULL;
+ fingerprinter_sys_t *p_sys = f->p_sys;
+ vlc_mutex_lock( &p_sys->results.lock );
+ if ( vlc_array_count( p_sys->results.queue ) )
+ {
+ r = vlc_array_item_at_index( p_sys->results.queue, 0 );
+ vlc_array_remove( p_sys->results.queue, 0 );
+ }
+ vlc_mutex_unlock( &p_sys->results.lock );
+ return r;
+}
+
+static void ApplyResult( fingerprint_request_t *p_r, int i_resultid )
+{
+ if ( i_resultid >= vlc_array_count( & p_r->results.metas_array ) ) return;
+
+ vlc_meta_t *p_meta = (vlc_meta_t *)
+ vlc_array_item_at_index( & p_r->results.metas_array, i_resultid );
+ input_item_t *p_item = p_r->p_item;
+ vlc_mutex_lock( &p_item->lock );
+ vlc_meta_Merge( p_item->p_meta, p_meta );
+ vlc_mutex_unlock( &p_item->lock );
+}
+
+static void cancelDoFingerprint( void *p_arg )
+{
+ fingerprinter_sys_t *p_sys = ( fingerprinter_sys_t * ) p_arg;
+ if ( p_sys->p_input )
+ {
+ input_Stop( p_sys->p_input, true );
+ input_Close( p_sys->p_input );
+ /* cleanup temporary result */
+ if ( p_sys->chroma_fingerprint.psz_fingerprint )
+ free( p_sys->chroma_fingerprint.psz_fingerprint );
+ } else {
+ if ( p_sys->p_item )
+ input_item_Release( p_sys->p_item );
+ }
+}
+
+static int inputStateCallback( vlc_object_t *obj, const char *var,
+ vlc_value_t old, vlc_value_t cur, void *p_data )
+{
+ VLC_UNUSED(obj);VLC_UNUSED(var);VLC_UNUSED(old);
+ fingerprinter_sys_t *p_sys = (fingerprinter_sys_t *) p_data;
+ if ( cur.i_int != INPUT_EVENT_STATE ) return VLC_SUCCESS;
+ p_sys->condwait.i_input_state = var_GetInteger( p_sys->p_input, "state" );
+ vlc_cond_signal( & p_sys->condwait.wait );
+ return VLC_SUCCESS;
+}
+
+static void DoFingerprint( vlc_object_t *p_this, fingerprinter_sys_t *p_sys, acoustid_fingerprint_t *fp )
+{
+ p_sys->p_input = NULL;
+ p_sys->p_item = NULL;
+ p_sys->chroma_fingerprint.psz_fingerprint = NULL;
+ vlc_cleanup_push( cancelDoFingerprint, p_sys );
+
+ p_sys->p_item = input_item_New( NULL, NULL );
+ if ( ! p_sys->p_item ) goto end;
+
+ input_item_AddOption( p_sys->p_item, "sout=#transcode{acodec=s16l}:chromaprint", VLC_INPUT_OPTION_TRUSTED );
+ input_item_AddOption( p_sys->p_item, "vout=dummy", VLC_INPUT_OPTION_TRUSTED );
+ input_item_AddOption( p_sys->p_item, "aout=dummy", VLC_INPUT_OPTION_TRUSTED );
+ if ( fp->i_duration )
+ input_item_AddOption( p_sys->p_item, "stop-time=91", VLC_INPUT_OPTION_TRUSTED );
+ input_item_SetURI( p_sys->p_item, p_sys->psz_uri ) ;
+
+ p_sys->p_input = input_Create( p_this, p_sys->p_item, "fingerprinter", NULL );
+ if ( p_sys->p_input )
+ {
+ p_sys->chroma_fingerprint.i_duration = fp->i_duration;
+ var_Create( p_sys->p_input, "fingerprint-data", VLC_VAR_ADDRESS );
+ var_SetAddress( p_sys->p_input, "fingerprint-data", & p_sys->chroma_fingerprint );
+
+ input_Start( p_sys->p_input );
+
+ /* Wait for input to start && end */
+ p_sys->condwait.i_input_state = var_GetInteger( p_sys->p_input, "state" );
+
+ if ( likely( var_AddCallback( p_sys->p_input, "intf-event",
+ inputStateCallback, p_sys ) == VLC_SUCCESS ) )
+ {
+ while( p_sys->condwait.i_input_state <= PAUSE_S )
+ {
+ vlc_mutex_lock( &p_sys->condwait.lock );
+ mutex_cleanup_push( &p_sys->condwait.lock );
+ vlc_cond_wait( &p_sys->condwait.wait, &p_sys->condwait.lock );
+ vlc_cleanup_run();
+ }
+ var_DelCallback( p_sys->p_input, "intf-event", inputStateCallback, p_sys );
+ }
+ input_Stop( p_sys->p_input, true );
+ input_Close( p_sys->p_input );
+ input_item_Release( p_sys->p_item );
+
+ if ( p_sys->chroma_fingerprint.psz_fingerprint )
+ {
+ fp->psz_fingerprint = strdup( p_sys->chroma_fingerprint.psz_fingerprint );
+ if ( ! fp->i_duration ) /* had not given hint */
+ fp->i_duration = p_sys->chroma_fingerprint.i_duration;
+ FREENULL( p_sys->chroma_fingerprint.psz_fingerprint );
+ }
+ }
+end:
+ vlc_cleanup_pop( );
+}
+
+/*****************************************************************************
+ * Open:
+ *****************************************************************************/
+static int Open(vlc_object_t *p_this)
+{
+ fingerprinter_thread_t *p_fingerprinter = (fingerprinter_thread_t*) p_this;
+ fingerprinter_sys_t *p_sys = calloc(1, sizeof(fingerprinter_sys_t));
+
+ if ( !p_sys )
+ return VLC_ENOMEM;
+
+ p_fingerprinter->p_sys = p_sys;
+
+ p_sys->incoming.queue = vlc_array_new();
+ vlc_mutex_init( &p_sys->incoming.lock );
+ vlc_cond_init( &p_sys->incoming_queue_filled );
+
+ p_sys->processing.queue = vlc_array_new();
+ vlc_mutex_init( &p_sys->processing.lock );
+
+ p_sys->results.queue = vlc_array_new();
+ vlc_mutex_init( &p_sys->results.lock );
+
+ vlc_mutex_init( &p_sys->condwait.lock );
+ vlc_cond_init( &p_sys->condwait.wait );
+
+ p_sys->psz_uri = NULL;
+
+ p_fingerprinter->pf_run = Run;
+ p_fingerprinter->pf_enqueue = EnqueueRequest;
+ p_fingerprinter->pf_getresults = GetResult;
+ p_fingerprinter->pf_apply = ApplyResult;
+
+ var_Create( p_fingerprinter, "results-available", VLC_VAR_BOOL );
+ if( p_fingerprinter->pf_run
+ && vlc_clone( &p_fingerprinter->thread,
+ (void *(*) (void *)) p_fingerprinter->pf_run,
+ p_fingerprinter, VLC_THREAD_PRIORITY_LOW ) )
+ {
+ msg_Err( p_fingerprinter, "cannot spawn fingerprinter thread" );
+ goto error;
+ }
+
+ return VLC_SUCCESS;
+
+error:
+ free( p_sys );
+ return VLC_EGENERIC;
+}
+
+/*****************************************************************************
+ * Close:
+ *****************************************************************************/
+static void Close(vlc_object_t *p_this)
+{
+ fingerprinter_thread_t *p_fingerprinter = (fingerprinter_thread_t*) p_this;
+ fingerprinter_sys_t *p_sys = p_fingerprinter->p_sys;
+
+ vlc_cancel( p_fingerprinter->thread );
+ vlc_join( p_fingerprinter->thread, NULL );
+
+ vlc_mutex_destroy( &p_sys->condwait.lock );
+ vlc_cond_destroy( &p_sys->condwait.wait );
+
+ for ( int i = 0; i < vlc_array_count( p_sys->incoming.queue ); i++ )
+ delete_fingerprint_request( vlc_array_item_at_index( p_sys->incoming.queue, i ) );
+ vlc_array_destroy( p_sys->incoming.queue );
+ vlc_mutex_destroy( &p_sys->incoming.lock );
+ vlc_cond_destroy( &p_sys->incoming_queue_filled );
+
+ for ( int i = 0; i < vlc_array_count( p_sys->processing.queue ); i++ )
+ delete_fingerprint_request( vlc_array_item_at_index( p_sys->processing.queue, i ) );
+ vlc_array_destroy( p_sys->processing.queue );
+ vlc_mutex_destroy( &p_sys->processing.lock );
+
+ for ( int i = 0; i < vlc_array_count( p_sys->results.queue ); i++ )
+ delete_fingerprint_request( vlc_array_item_at_index( p_sys->results.queue, i ) );
+ vlc_array_destroy( p_sys->results.queue );
+ vlc_mutex_destroy( &p_sys->results.lock );
+
+ free( p_sys );
+}
+
+static void fill_metas_with_results( fingerprint_request_t *p_r, acoustid_fingerprint_t *p_f )
+{
+ for( unsigned int i=0 ; i < p_f->results.count; i++ )
+ {
+ acoustid_result_t *p_result = & p_f->results.p_results[ i ];
+ for ( unsigned int j=0 ; j < p_result->recordings.count; j++ )
+ {
+ musicbrainz_recording_t *p_record = & p_result->recordings.p_recordings[ j ];
+ vlc_meta_t *p_meta = vlc_meta_New();
+ if ( p_meta )
+ {
+ vlc_meta_Set( p_meta, vlc_meta_Title, p_record->psz_title );
+ vlc_meta_Set( p_meta, vlc_meta_Artist, p_record->psz_artist );
+ vlc_meta_AddExtra( p_meta, "musicbrainz-id", p_record->sz_musicbrainz_id );
+ vlc_array_append( & p_r->results.metas_array, p_meta );
+ }
+ }
+ }
+}
+
+/*****************************************************************************
+ * Run :
+ *****************************************************************************/
+static void cancelRun( void * p_arg )
+{
+ fingerprinter_sys_t *p_sys = ( fingerprinter_sys_t * ) p_arg;
+ if ( vlc_array_count( p_sys->processing.queue ) )
+ vlc_array_clear( p_sys->processing.queue );
+ if ( p_sys->psz_uri )
+ free( p_sys->psz_uri );
+}
+
+static void clearPrint( void * p_arg )
+{
+ acoustid_fingerprint_t *acoustid_print = ( acoustid_fingerprint_t * ) p_arg;
+ for( unsigned int j=0 ; j < acoustid_print->results.count; j++ )
+ free_acoustid_result_t( &acoustid_print->results.p_results[j] );
+ if ( acoustid_print->results.count )
+ free( acoustid_print->results.p_results );
+ if ( acoustid_print->psz_fingerprint )
+ free( acoustid_print->psz_fingerprint );
+}
+
+static void Run( fingerprinter_thread_t *p_fingerprinter )
+{
+ fingerprinter_sys_t *p_sys = p_fingerprinter->p_sys;
+
+ /* main loop */
+ for (;;)
+ {
+ vlc_mutex_lock( &p_sys->processing.lock );
+ mutex_cleanup_push( &p_sys->processing.lock );
+ vlc_cond_timedwait( &p_sys->incoming_queue_filled, &p_sys->processing.lock, mdate() + 1000000 );
+ vlc_cleanup_run();
+
+ QueueIncomingRequests( p_sys );
+
+ vlc_mutex_lock( &p_sys->processing.lock ); // L0
+ mutex_cleanup_push( &p_sys->processing.lock );
+ vlc_cleanup_push( cancelRun, p_sys ); // C1
+//**
+ for ( p_sys->i = 0 ; p_sys->i < vlc_array_count( p_sys->processing.queue ); p_sys->i++ )
+ {
+ fingerprint_request_t *p_data = vlc_array_item_at_index( p_sys->processing.queue, p_sys->i );
+ acoustid_fingerprint_t acoustid_print;
+ memset( &acoustid_print , 0, sizeof(acoustid_fingerprint_t) );
+ vlc_cleanup_push( clearPrint, &acoustid_print ); // C2
+ p_sys->psz_uri = input_item_GetURI( p_data->p_item );
+ if ( p_sys->psz_uri )
+ {
+ /* overwrite with hint, as in this case, fingerprint's session will be truncated */
+ if ( p_data->i_duration ) acoustid_print.i_duration = p_data->i_duration;
+
+ DoFingerprint( VLC_OBJECT(p_fingerprinter), p_sys, &acoustid_print );
+
+ DoAcoustIdWebRequest( VLC_OBJECT(p_fingerprinter), &acoustid_print );
+ fill_metas_with_results( p_data, &acoustid_print );
+ FREENULL( p_sys->psz_uri );
+ }
+ vlc_cleanup_run( ); // C2
+
+ /* copy results */
+ vlc_mutex_lock( &p_sys->results.lock );
+ vlc_array_append( p_sys->results.queue, p_data );
+ vlc_mutex_unlock( &p_sys->results.lock );
+
+ vlc_testcancel();
+ }
+
+ if ( vlc_array_count( p_sys->processing.queue ) )
+ {
+ var_TriggerCallback( p_fingerprinter, "results-available" );
+ vlc_array_clear( p_sys->processing.queue );
+ }
+ vlc_cleanup_pop( ); // C1
+//**
+ vlc_cleanup_run(); // L0
+ }
+}
diff --git a/modules/misc/webservices/acoustid.c b/modules/misc/webservices/acoustid.c
new file mode 100644
index 0000000..4729a95
--- /dev/null
+++ b/modules/misc/webservices/acoustid.c
@@ -0,0 +1,229 @@
+/*****************************************************************************
+ * acoustid.c: AcoustId webservice parser
+ *****************************************************************************
+ * Copyright (C) 2012 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_stream.h>
+#include <limits.h>
+#include <vlc_memory.h>
+
+#include <vlc/vlc.h>
+#include "acoustid.h"
+#include "use_json.h"
+
+/*****************************************************************************
+ * Requests lifecycle
+ *****************************************************************************/
+void free_acoustid_result_t( acoustid_result_t * r )
+{
+ free( r->psz_id );
+ for ( unsigned int i=0; i<r->recordings.count; i++ )
+ {
+ free( r->recordings.p_recordings[ i ].psz_artist );
+ free( r->recordings.p_recordings[ i ].psz_title );
+ }
+ free( r->recordings.p_recordings );
+}
+
+static json_value * jsongetbyname( json_value *object, const char *psz_name )
+{
+ 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 void parse_artists( json_value *node, musicbrainz_recording_t *record )
+{
+ /* take only main */
+ if ( !node || node->type != json_array || node->u.array.length < 1 ) return;
+ json_value *artistnode = node->u.array.values[ 0 ];
+ json_value *value = jsongetbyname( artistnode, "name" );
+ if ( value && value->type == json_string )
+ record->psz_artist = strdup( value->u.string.ptr );
+}
+
+static void parse_recordings( vlc_object_t *p_obj, json_value *node, acoustid_result_t *p_result )
+{
+ if ( !node || node->type != json_array ) return;
+ p_result->recordings.p_recordings = calloc( node->u.array.length, sizeof(musicbrainz_recording_t) );
+ if ( ! p_result->recordings.p_recordings ) return;
+ p_result->recordings.count = node->u.array.length;
+
+ for( unsigned int i=0; i<node->u.array.length; i++ )
+ {
+ musicbrainz_recording_t *record = & p_result->recordings.p_recordings[ i ];
+ json_value *recordnode = node->u.array.values[ i ];
+ if ( !recordnode || recordnode->type != json_object ) break;
+ json_value *value = jsongetbyname( recordnode, "title" );
+ if ( value && value->type == json_string )
+ record->psz_title = strdup( value->u.string.ptr );
+ value = jsongetbyname( recordnode, "id" );
+ if ( value && value->type == json_string )
+ strncpy( record->sz_musicbrainz_id, value->u.string.ptr, MB_ID_SIZE );
+ parse_artists( jsongetbyname( recordnode, "artists" ), record );
+ msg_Dbg( p_obj, "recording %d title %s %36s %s", i, record->psz_title, record->sz_musicbrainz_id, record->psz_artist );
+ }
+}
+
+static bool ParseJson( vlc_object_t *p_obj, char *psz_buffer, acoustid_results_t *p_results )
+{
+ json_settings settings;
+ char psz_error[128];
+ memset (&settings, 0, sizeof (json_settings));
+ json_value *root = json_parse_ex( &settings, psz_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;
+ }
+ json_value *node = jsongetbyname( root, "status" );
+ if ( !node || node->type != json_string )
+ {
+ msg_Warn( p_obj, "status node not found or invalid" );
+ goto error;
+ }
+ if ( strcmp( node->u.string.ptr, "ok" ) != 0 )
+ {
+ msg_Warn( p_obj, "Bad request status" );
+ goto error;
+ }
+ node = jsongetbyname( root, "results" );
+ if ( !node || node->type != json_array )
+ {
+ msg_Warn( p_obj, "Bad results array or no results" );
+ goto error;
+ }
+ 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++ )
+ {
+ json_value *resultnode = node->u.array.values[i];
+ if ( resultnode && resultnode->type == json_object )
+ {
+ acoustid_result_t *p_result = & p_results->p_results[i];
+ json_value *value = jsongetbyname( resultnode, "score" );
+ if ( value && value->type == json_double )
+ p_result->d_score = value->u.dbl;
+ value = jsongetbyname( resultnode, "id" );
+ if ( value && value->type == json_string )
+ p_result->psz_id = strdup( value->u.string.ptr );
+ parse_recordings( p_obj, jsongetbyname( resultnode, "recordings" ), p_result );
+ }
+ }
+ json_value_free( root );
+ return true;
+
+error:
+ if ( root ) json_value_free( root );
+ return false;
+}
+
+struct webrequest_t
+{
+ stream_t *p_stream;
+ char *psz_url;
+ char *p_buffer;
+};
+
+static void cancelDoAcoustIdWebRequest( void *p_arg )
+{
+ struct webrequest_t *p_request = (struct webrequest_t *) p_arg;
+ if ( p_request->p_stream )
+ stream_Delete( p_request->p_stream );
+ if ( p_request->psz_url )
+ free( p_request->psz_url );
+ if ( p_request->p_buffer )
+ free( p_request->p_buffer );
+}
+
+int DoAcoustIdWebRequest( vlc_object_t *p_obj, acoustid_fingerprint_t *p_data )
+{
+ int i_ret;
+ int i_status;
+ struct webrequest_t request = { NULL, NULL, NULL };
+
+ if ( !p_data->psz_fingerprint ) return VLC_SUCCESS;
+
+ i_ret = asprintf( & request.psz_url,
+ "http://fingerprint.videolan.org/acoustid.php?meta=recordings+tracks+usermeta+releases&duration=%d&fingerprint=%s",
+ p_data->i_duration, p_data->psz_fingerprint );
+ if ( i_ret < 1 ) return VLC_EGENERIC;
+
+ vlc_cleanup_push( cancelDoAcoustIdWebRequest, &request );
+
+ msg_Dbg( p_obj, "Querying AcoustID from %s", request.psz_url );
+ request.p_stream = stream_UrlNew( p_obj, request.psz_url );
+ if ( !request.p_stream )
+ {
+ i_status = VLC_EGENERIC;
+ goto cleanup;
+ }
+
+ /* read answer */
+ i_ret = 0;
+ for( ;; )
+ {
+ int i_read = 65536;
+
+ if( i_ret >= INT_MAX - i_read )
+ break;
+
+ request.p_buffer = realloc_or_free( request.p_buffer, 1 + i_ret + i_read );
+ if( !request.p_buffer )
+ {
+ i_status = VLC_ENOMEM;
+ goto cleanup;
+ }
+
+ i_read = stream_Read( request.p_stream, &request.p_buffer[i_ret], i_read );
+ if( i_read <= 0 )
+ break;
+
+ i_ret += i_read;
+ }
+ stream_Delete( request.p_stream );
+ request.p_stream = NULL;
+ request.p_buffer[ i_ret ] = 0;
+
+ int i_canc = vlc_savecancel();
+ if ( ParseJson( p_obj, request.p_buffer, & p_data->results ) )
+ {
+ msg_Dbg( p_obj, "results count == %d", p_data->results.count );
+ } else {
+ msg_Dbg( p_obj, "No results" );
+ }
+ vlc_restorecancel( i_canc );
+ i_status = VLC_SUCCESS;
+
+cleanup:
+ vlc_cleanup_run( );
+ return i_status;
+}
diff --git a/modules/misc/webservices/acoustid.h b/modules/misc/webservices/acoustid.h
new file mode 100644
index 0000000..313ad09
--- /dev/null
+++ b/modules/misc/webservices/acoustid.h
@@ -0,0 +1,59 @@
+/*****************************************************************************
+ * acoustid.h: AcoustId webservice parser
+ *****************************************************************************
+ * Copyright (C) 2012 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#define MB_ID_SIZE 36
+
+struct musicbrainz_recording_t
+{
+ char *psz_artist;
+ char *psz_title;
+ char sz_musicbrainz_id[MB_ID_SIZE];
+};
+typedef struct musicbrainz_recording_t musicbrainz_recording_t;
+
+struct acoustid_result_t
+{
+ double d_score;
+ char *psz_id;
+ struct
+ {
+ unsigned int count;
+ musicbrainz_recording_t *p_recordings;
+ } recordings;
+};
+typedef struct acoustid_result_t acoustid_result_t;
+
+struct acoustid_results_t
+{
+ acoustid_result_t * p_results;
+ unsigned int count;
+};
+typedef struct acoustid_results_t acoustid_results_t;
+
+struct acoustid_fingerprint_t
+{
+ char *psz_fingerprint;
+ unsigned int i_duration;
+ acoustid_results_t results;
+};
+typedef struct acoustid_fingerprint_t acoustid_fingerprint_t;
+
+int DoAcoustIdWebRequest( vlc_object_t *p_obj, acoustid_fingerprint_t *p_data );
+void free_acoustid_result_t( acoustid_result_t * r );
diff --git a/modules/misc/webservices/json.c b/modules/misc/webservices/json.c
new file mode 100644
index 0000000..00cf43f
--- /dev/null
+++ b/modules/misc/webservices/json.c
@@ -0,0 +1,739 @@
+/* vim: set et ts=3 sw=3 ft=c:
+ *
+ * Copyright (C) 2012 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
+
+#ifdef __cplusplus
+ const struct _json_value json_value_none; /* zero-d by ctor */
+#else
+ const struct _json_value json_value_none = { 0 };
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+typedef unsigned short json_uchar;
+
+static unsigned char hex_value (json_char c)
+{
+ if (c >= 'A' && c <= 'F')
+ return (c - 'A') + 10;
+
+ if (c >= 'a' && c <= 'f')
+ return (c - 'a') + 10;
+
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ return 0xFF;
+}
+
+typedef struct
+{
+ json_settings settings;
+ int first_pass;
+
+ unsigned long used_memory;
+
+ unsigned int uint_max;
+ unsigned long ulong_max;
+
+} json_state;
+
+static void * json_alloc (json_state * state, unsigned long size, int zero)
+{
+ void * mem;
+
+ 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;
+ }
+
+ if (! (mem = zero ? calloc (size, 1) : malloc (size)))
+ return 0;
+
+ return mem;
+}
+
+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;
+ }
+
+ 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;
+
+ 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;
+ }
+
+ break;
+
+ default:
+ break;
+ };
+
+ value->u.array.length = 0;
+
+ return 1;
+ }
+
+ value = (json_value *) json_alloc (state, sizeof (json_value), 1);
+
+ if (!value)
+ return 0;
+
+ if (!*root)
+ *root = value;
+
+ value->type = type;
+ value->parent = *top;
+
+ if (*alloc)
+ (*alloc)->_reserved.next_alloc = value;
+
+ *alloc = *top = value;
+
+ return 1;
+}
+
+#define e_off \
+ ((int) (i - cur_line_begin))
+
+#define whitespace \
+ case '\n': ++ cur_line; cur_line_begin = i; \
+ case ' ': case '\t': case '\r'
+
+#define string_add(b) \
+ do { if (!state.first_pass) string [string_length] = b; ++ string_length; } while (0);
+
+const static int
+ flag_next = 1, flag_reproc = 2, flag_need_comma = 4, flag_seek_value = 8, flag_exponent = 16,
+ flag_got_exponent_sign = 32, flag_escaped = 64, flag_string = 128, flag_need_colon = 256,
+ flag_done = 512;
+
+json_value * json_parse_ex (json_settings * settings, const json_char * json, char * error_buf)
+{
+ json_char error [128];
+ unsigned int cur_line;
+ const json_char * cur_line_begin, * i;
+ json_value * top, * root, * alloc = 0;
+ json_state state;
+ int flags;
+
+ error[0] = '\0';
+
+ memset (&state, 0, sizeof (json_state));
+ memcpy (&state.settings, settings, sizeof (json_settings));
+
+ 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;
+ unsigned int string_length;
+
+ top = root = 0;
+ flags = flag_seek_value;
+
+ cur_line = 1;
+ cur_line_begin = json;
+
+ for (i = json ;; ++ i)
+ {
+ json_char b = *i;
+
+ if (flags & flag_done)
+ {
+ if (!b)
+ break;
+
+ switch (b)
+ {
+ whitespace:
+ continue;
+
+ default:
+ sprintf (error, "%d:%d: Trailing garbage: `%c`", cur_line, e_off, b);
+ goto e_failed;
+ };
+ }
+
+ if (flags & flag_string)
+ {
+ if (!b)
+ { sprintf (error, "Unexpected EOF in string (at %d:%d)", cur_line, e_off);
+ 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 ((uc_b1 = hex_value (*++ i)) == 0xFF || (uc_b2 = hex_value (*++ i)) == 0xFF
+ || (uc_b3 = hex_value (*++ i)) == 0xFF || (uc_b4 = hex_value (*++ i)) == 0xFF)
+ {
+ sprintf (error, "Invalid character value `%c` (at %d:%d)", b, cur_line, e_off);
+ goto e_failed;
+ }
+
+ uc_b1 = uc_b1 * 16 + uc_b2;
+ uc_b2 = uc_b3 * 16 + uc_b4;
+
+ uchar = ((json_char) uc_b1) * 256 + uc_b2;
+
+ if (sizeof (json_char) >= sizeof (json_uchar) || (uc_b1 == 0 && uc_b2 <= 0x7F))
+ {
+ string_add ((json_char) uchar);
+ break;
+ }
+
+ if (uchar <= 0x7FF)
+ {
+ if (state.first_pass)
+ string_length += 2;
+ else
+ { string [string_length ++] = 0xC0 | ((uc_b2 & 0xC0) >> 6) | ((uc_b1 & 0x3) << 3);
+ string [string_length ++] = 0x80 | (uc_b2 & 0x3F);
+ }
+
+ break;
+ }
+
+ if (state.first_pass)
+ string_length += 3;
+ else
+ { string [string_length ++] = 0xE0 | ((uc_b1 & 0xF0) >> 4);
+ string [string_length ++] = 0x80 | ((uc_b1 & 0xF) << 2) | ((uc_b2 & 0xC0) >> 6);
+ string [string_length ++] = 0x80 | (uc_b2 & 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;
+
+ (*(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 (flags & flag_seek_value)
+ {
+ switch (b)
+ {
+ whitespace:
+ continue;
+
+ case ']':
+
+ if (top->type == json_array)
+ flags = (flags & ~ (flag_need_comma | flag_seek_value)) | flag_next;
+ else if (!state.settings.settings & json_relaxed_commas)
+ { sprintf (error, "%d:%d: Unexpected ]", cur_line, e_off);
+ 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", cur_line, e_off, b);
+ goto e_failed;
+ }
+ }
+
+ if (flags & flag_need_colon)
+ {
+ if (b == ':')
+ { flags &= ~ flag_need_colon;
+ continue;
+ }
+ else
+ { sprintf (error, "%d:%d: Expected : before %c", cur_line, e_off, 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 (*(++ i) != 'r' || *(++ i) != 'u' || *(++ i) != '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 (*(++ i) != 'a' || *(++ i) != 'l' || *(++ i) != 's' || *(++ i) != '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 (*(++ i) != 'u' || *(++ i) != 'l' || *(++ i) != '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;
+
+ flags &= ~ (flag_exponent | flag_got_exponent_sign);
+
+ if (state.first_pass)
+ continue;
+
+ if (top->type == json_double)
+ top->u.dbl = strtod (i, (json_char **) &i);
+ else
+ top->u.integer = strtol (i, (json_char **) &i, 10);
+
+ flags |= flag_next | flag_reproc;
+ }
+ else
+ { sprintf (error, "%d:%d: Unexpected %c when seeking value", cur_line, e_off, b);
+ goto e_failed;
+ }
+ };
+ };
+ }
+ else
+ {
+ switch (top->type)
+ {
+ case json_object:
+
+ switch (b)
+ {
+ whitespace:
+ continue;
+
+ case '"':
+
+ if (flags & flag_need_comma && (!state.settings.settings & json_relaxed_commas))
+ {
+ sprintf (error, "%d:%d: Expected , before \"", cur_line, e_off);
+ 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", cur_line, e_off, b);
+ goto e_failed;
+ };
+
+ break;
+
+ case json_integer:
+ case json_double:
+
+ if (isdigit (b))
+ continue;
+
+ if (b == 'e' || b == 'E')
+ {
+ if (!(flags & flag_exponent))
+ {
+ flags |= flag_exponent;
+ top->type = json_double;
+
+ continue;
+ }
+ }
+ else if (b == '+' || b == '-')
+ {
+ if (flags & flag_exponent && !(flags & flag_got_exponent_sign))
+ {
+ flags |= flag_got_exponent_sign;
+ continue;
+ }
+ }
+ else if (b == '.' && top->type == json_integer)
+ {
+ top->type = json_double;
+ continue;
+ }
+
+ flags |= flag_next | flag_reproc;
+ break;
+
+ default:
+ break;
+ };
+ }
+
+ if (flags & flag_reproc)
+ {
+ flags &= ~ flag_reproc;
+ -- i;
+ }
+
+ 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", cur_line, e_off);
+ goto e_failed;
+
+e_alloc_failure:
+
+ strcpy (error, "Memory allocation failure");
+ goto e_failed;
+
+e_overflow:
+
+ sprintf (error, "%d:%d: Too long (caught overflow)", cur_line, e_off);
+ 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;
+ free (alloc);
+ alloc = top;
+ }
+
+ if (!state.first_pass)
+ json_value_free (root);
+
+ return 0;
+}
+
+json_value * json_parse (const json_char * json)
+{
+ json_settings settings;
+ memset (&settings, 0, sizeof (json_settings));
+
+ return json_parse_ex (&settings, json, 0);
+}
+
+void json_value_free (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)
+ {
+ free (value->u.array.values);
+ break;
+ }
+
+ value = value->u.array.values [-- value->u.array.length];
+ continue;
+
+ case json_object:
+
+ if (!value->u.object.length)
+ {
+ free (value->u.object.values);
+ break;
+ }
+
+ value = value->u.object.values [-- value->u.object.length].value;
+ continue;
+
+ case json_string:
+
+ free (value->u.string.ptr);
+ break;
+
+ default:
+ break;
+ };
+
+ cur_value = value;
+ value = value->parent;
+ free (cur_value);
+ }
+}
diff --git a/modules/misc/webservices/json.h b/modules/misc/webservices/json.h
new file mode 100644
index 0000000..d69bbda
--- /dev/null
+++ b/modules/misc/webservices/json.h
@@ -0,0 +1,34 @@
+/*****************************************************************************
+ * json.h: json-parser fixups
+ *****************************************************************************
+ * Copyright (C) 2012 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef _JSON_H
+#ifndef _JSONFIXUPS_H
+#define _JSONFIXUPS_H
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <vlc_common.h>
+#include <vlc_charset.h>
+/* json.c depends on the locale */
+#define strtod(foo,bar) us_strtod(foo,bar)
+#include "use_json.h"
+
+#endif
+#endif
diff --git a/modules/misc/webservices/use_json.h b/modules/misc/webservices/use_json.h
new file mode 100644
index 0000000..2fb8c01
--- /dev/null
+++ b/modules/misc/webservices/use_json.h
@@ -0,0 +1,189 @@
+/* vim: set et ts=3 sw=3 ft=c:
+ *
+ * Copyright (C) 2012 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 _JSON_H
+#define _JSON_H
+
+#ifndef json_char
+ #define json_char char
+#endif
+
+#ifdef __cplusplus
+
+ #include <string.h>
+
+ extern "C"
+ {
+
+#endif
+
+typedef struct
+{
+ unsigned long max_memory;
+ int settings;
+
+} json_settings;
+
+#define json_relaxed_commas 1
+
+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;
+ long integer;
+ double dbl;
+
+ struct
+ {
+ unsigned int length;
+ json_char * ptr; /* null terminated */
+
+ } string;
+
+ struct
+ {
+ unsigned int length;
+
+ struct
+ {
+ json_char * name;
+ struct _json_value * value;
+
+ } * values;
+
+ } object;
+
+ struct
+ {
+ unsigned int length;
+ struct _json_value ** values;
+
+ } array;
+
+ } u;
+
+ union
+ {
+ struct _json_value * next_alloc;
+ void * object_mem;
+
+ } _reserved;
+
+
+ /* 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 long () const
+ { return u.integer;
+ }
+
+ inline operator bool () const
+ { return u.boolean != 0;
+ }
+
+ #endif
+
+} json_value;
+
+json_value * json_parse
+ (const json_char * json);
+
+json_value * json_parse_ex
+ (json_settings * settings, const json_char * json, char * error);
+
+void json_value_free (json_value *);
+
+
+#ifdef __cplusplus
+ } /* extern "C" */
+#endif
+
+#endif
--
1.7.9
More information about the vlc-devel
mailing list