[vlc-commits] [Git][videolan/vlc][3.0.x] 7 commits: access: cdrom: return TOC struct instead of only sectors
Hugo Beauzée-Luyssen (@chouquette)
gitlab at videolan.org
Fri Aug 27 12:02:26 UTC 2021
Hugo Beauzée-Luyssen pushed to branch 3.0.x at VideoLAN / VLC
Commits:
3941c7b8 by Francois Cartegnie at 2021-08-27T11:10:13+00:00
access: cdrom: return TOC struct instead of only sectors
(cherry picked from commit 890db4f7e7b04dc1c28bc3ba5eeebf76b102b9d0)
- - - - -
7e656c3b by Francois Cartegnie at 2021-08-27T11:10:13+00:00
access: cdda: fix audio/data mixed mode
best way to kill your speakers
(cherry picked from commit 182133eab2c6cf3c9ae98dd6383a774d07b5762d)
- - - - -
c48778b2 by Francois Cartegnie at 2021-08-27T11:10:13+00:00
acoustid: rename mb structure
(cherry picked from commit 6065ecc4217102d73af6d2266d25b27811647dd8)
- - - - -
ebd88159 by Francois Cartegnie at 2021-08-27T11:10:13+00:00
acoustid: refactor
(cherry picked from commit cdb7b9d8b21815d6dbe06aed25d9bf3621634e9d)
- - - - -
f0598afd by Francois Cartegnie at 2021-08-27T11:10:13+00:00
acoustid: parametrize server options
(cherry picked from commit 06e1aafd0d7d39db236723fa394205d956404070)
- - - - -
f2ef3fee by Francois Cartegnie at 2021-08-27T11:10:13+00:00
misc: webservices: add musicbrainz
(cherry picked from commit e8f5c617dfcf6dc571701eaa8c5d41b5a23d57a5)
- - - - -
4c49c64e by Francois Cartegnie at 2021-08-27T11:10:13+00:00
access: cdda: add support for musicbrainz (fix #21796)
deprecates CDDB which has too low entropy for lookups
(cherry picked from commit 7fde19d9d23a7a075f1e532ef3144dab86d4306b)
- - - - -
13 changed files:
- modules/access/Makefile.am
- modules/access/cdda.c
- modules/access/vcd/cdrom.c
- modules/access/vcd/cdrom.h
- modules/access/vcd/cdrom_internals.h
- modules/access/vcd/vcd.c
- modules/misc/Makefile.am
- modules/misc/fingerprinter.c
- modules/misc/webservices/acoustid.c
- modules/misc/webservices/acoustid.h
- + modules/misc/webservices/json_helper.h
- + modules/misc/webservices/musicbrainz.c
- + modules/misc/webservices/musicbrainz.h
Changes:
=====================================
modules/access/Makefile.am
=====================================
@@ -205,8 +205,9 @@ 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
+libcdda_plugin_la_SOURCES = access/cdda.c access/vcd/cdrom.c access/vcd/cdrom.h access/vcd/cdrom_internals.h \
+ misc/webservices/json.c misc/webservices/json.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_LDFLAGS = $(AM_LDFLAGS) -rpath '$(accessdir)'
@@ -214,6 +215,10 @@ if HAVE_DARWIN
libcdda_plugin_la_LIBADD += -liconv
libcdda_plugin_la_LDFLAGS += -Wl,-framework,IOKit,-framework,CoreFoundation
endif
+if HAVE_GCRYPT
+libcdda_plugin_la_CFLAGS += $(GCRYPT_CFLAGS)
+libcdda_plugin_la_LIBADD += $(GCRYPT_LIBS)
+endif
EXTRA_LTLIBRARIES += libcdda_plugin.la
access_LTLIBRARIES += $(LTLIBcdda)
=====================================
modules/access/cdda.c
=====================================
@@ -40,6 +40,7 @@
#include <math.h>
#include <stdlib.h>
#include <string.h>
+#include <ctype.h>
#include <vlc_common.h>
#include <vlc_demux.h>
@@ -49,9 +50,16 @@
#include <vlc_meta.h>
#include <vlc_charset.h> /* ToLocaleDup */
#include <vlc_url.h>
+#include <vlc_strings.h>
+#include <vlc_memstream.h>
+#ifdef HAVE_GCRYPT
+# include <gcrypt.h>
+# include <vlc_gcrypt.h>
+#endif
#include "disc_helper.h"
#include "vcd/cdrom.h" /* For CDDA_DATA_SIZE */
+#include "../misc/webservices/musicbrainz.h"
#ifdef HAVE_LIBCDDB
@@ -219,6 +227,67 @@ static int DemuxControl(demux_t *demux, int query, va_list args)
return VLC_SUCCESS;
}
+/*
+ * Check for Mixed Audio/CDROM data
+ *
+ * ioctl will return first track # and last track # from last TOC
+ * (this could start > 1.. but where's the case ?)
+ *
+ * vcdev_toc stores i_tracks as the number of STORED sectors info,
+ * this is last - first track, plus one (the LEADOUT one)
+ *
+ * tracks can be DATA in mixed data/audio mode (CDROM spec, not CDDA)
+ * and this is control flag (Subcode Channel Q)
+ *
+ * first: 1 last 13
+ * track: 1 lba: 0 ( 0) 00:02:00 adr: 1 control: 0 mode: -1
+ * track: 2 lba: 16462 ( 65848) 03:41:37 adr: 1 control: 0 mode: -1
+ * ...
+ * track: 12 lba: 212405 ( 849620) 47:14:05 adr: 1 control: 0 mode: -1
+ * track: 13 lba: 250948 ( 1003792) 55:47:73 adr: 1 control: 4 mode: -1
+ * track:lout lba: 266286 ( 1065144) 59:12:36 adr: 1 control: 4 mode: -1
+*/
+static int TOC_GetAudioRange(vcddev_toc_t *p_toc,
+ int *pi_first, int *pi_last)
+{
+ if( p_toc->i_tracks < 1 )
+ return 0;
+
+ int i_first = p_toc->i_first_track;
+ int i_last = p_toc->i_last_track;
+ for(int i=i_first; i<p_toc->i_tracks; i++)
+ {
+ if((p_toc->p_sectors[i - 1].i_control & CD_ROM_DATA_FLAG) == 0)
+ break;
+ i_first++;
+ }
+ for(int i=i_last; i > 0; i--)
+ {
+ if((p_toc->p_sectors[i - 1].i_control & CD_ROM_DATA_FLAG) == 0)
+ break;
+ i_last--;
+ }
+
+ /* FIX copy protection TOC
+ * https://github.com/metabrainz/libdiscid/blob/e46249415eb6d657ecc63667b03d670a4347712f/src/toc.c#L101 */
+ do
+ {
+ vcddev_sector_t *p_last = &p_toc->p_sectors[i_last - p_toc->i_first_track];
+ vcddev_sector_t *p_lout = &p_toc->p_sectors[p_toc->i_tracks];
+ if(p_lout->i_lba > p_last->i_lba || i_last <= i_first)
+ break;
+ /* last audio is invalid, bigger than lead out */
+ i_last = i_last - 1;
+ p_toc->i_last_track = i_last;
+ p_last->i_lba -= CD_ROM_XA_INTERVAL;
+ p_toc->i_tracks--; /* change lead out */
+ } while( i_last > i_first );
+
+ *pi_first = i_first;
+ *pi_last = i_last;
+ return (i_last >= i_first) ? i_last - i_first + 1 : 0;
+}
+
static int DemuxOpen(vlc_object_t *obj)
{
demux_t *demux = (demux_t *)obj;
@@ -244,19 +313,29 @@ static int DemuxOpen(vlc_object_t *obj)
/* Track number in input item */
if (sys->start == (unsigned)-1 || sys->length == (unsigned)-1)
{
- int *sectors = NULL; /* Track sectors */
- unsigned titles = ioctl_GetTracksMap(obj, dev, §ors);
+ vcddev_toc_t *p_toc = ioctl_GetTOC(obj, dev, true);
+ if(p_toc == NULL)
+ goto error;
+
+ int i_cdda_tracks, i_cdda_first, i_cdda_last;
+ i_cdda_tracks = TOC_GetAudioRange(p_toc, &i_cdda_first, &i_cdda_last);
- if (track > titles)
+ if (track == 0 || track > (unsigned) i_cdda_tracks)
{
- msg_Err(obj, "invalid track number: %u/%u", track, titles);
- free(sectors);
+ msg_Err(obj, "invalid track number: %u/%i", track, i_cdda_tracks);
+ vcddev_toc_Free(p_toc);
goto error;
}
- sys->start = sectors[track - 1];
- sys->length = sectors[track] - sys->start;
- free(sectors);
+ track--;
+ int i_first_sector = p_toc->p_sectors[track].i_lba;
+ int i_last_sector = p_toc->p_sectors[track + 1].i_lba;
+ if(i_cdda_first + track == (unsigned) i_cdda_last && p_toc->i_last_track > i_cdda_last)
+ i_last_sector -= CD_ROM_XA_INTERVAL;
+
+ sys->start = i_first_sector;
+ sys->length = i_last_sector - i_first_sector;
+ vcddev_toc_Free(p_toc);
}
es_format_t fmt;
@@ -293,24 +372,166 @@ static void DemuxClose(vlc_object_t *obj)
struct access_sys_t
{
vcddev_t *vcddev; /* vcd device descriptor */
- int *p_sectors; /* Track sectors */
- int titles;
+ vcddev_toc_t *p_toc; /* Tracks TOC */
+ int i_cdda_tracks; /* # of audio tracks in TOC */
+ int i_cdda_first; /* First .. */
+ int i_cdda_last; /* Last .. */
int cdtextc;
vlc_meta_t **cdtextv;
#ifdef HAVE_LIBCDDB
cddb_disc_t *cddb;
#endif
+ musicbrainz_recording_t *mbrecord;
};
-#ifdef HAVE_LIBCDDB
-static cddb_disc_t *GetCDDBInfo( vlc_object_t *obj, int i_titles, int *p_sectors )
+static inline int LBAPregap( int i_sector )
+{
+ /* LBA to sector pregap fixup
+ see libdiscid/src/toc.c #L80 */
+ return i_sector + 150;
+}
+
+#ifdef HAVE_GCRYPT
+static char * BuildMusicbrainzDiscID( const vcddev_toc_t *p_toc,
+ int i_total, int i_first, int i_last )
{
- if( !var_InheritBool( obj, "metadata-network-access" ) )
+ gcry_md_hd_t hd;
+ gcry_error_t err = gcry_md_open( &hd, GCRY_MD_SHA1, 0 );
+ if(err)
+ return NULL;
+
+ if(gcry_md_enable( hd, GCRY_MD_SHA1 ))
{
- msg_Dbg( obj, "album art policy set to manual: not fetching" );
+ gcry_md_close( hd );
return NULL;
}
+ char buffer[16];
+
+ sprintf( buffer, "%02X", i_first );
+ gcry_md_write( hd, buffer, 2 );
+ sprintf( buffer, "%02X", i_last );
+ gcry_md_write( hd, buffer, 2 );
+ /* LEAD OUT sector info */
+
+ /* LEAD OUT sector info
+ * https://github.com/metabrainz/libdiscid/blob/e46249415eb6d657ecc63667b03d670a4347712f/src/toc.c#L90 */
+ int i_last_track_end;
+ if (p_toc->i_tracks > i_total)
+ i_last_track_end = LBAPregap(p_toc->p_sectors[i_total].i_lba) - CD_ROM_XA_INTERVAL;
+ else
+ i_last_track_end = LBAPregap(p_toc->p_sectors[p_toc->i_tracks].i_lba);
+
+ sprintf( buffer, "%08X", i_last_track_end );
+ gcry_md_write( hd, buffer, 8 );
+
+ for( int i = 0; i<i_total; i++ ) /* skip LEAD OUT */
+ {
+ sprintf( buffer, "%08X", LBAPregap(p_toc->p_sectors[i].i_lba) );
+ gcry_md_write( hd, buffer, 8 );
+ }
+
+ for( int i = i_total; i<100; i++ )
+ {
+ if( i == p_toc->i_tracks )
+ continue;
+ gcry_md_write( hd, "00000000", 8 );
+ }
+
+ gcry_md_final( hd );
+
+ size_t i_len = gcry_md_get_algo_dlen( GCRY_MD_SHA1 );
+ unsigned char *output = gcry_md_read( hd, GCRY_MD_SHA1 );
+ char *out = vlc_b64_encode_binary( output, i_len );
+ if( out )
+ {
+ i_len = strlen( out );
+ for( size_t i=0; i<i_len; i++ )
+ {
+ if( !isalpha(out[i]) )
+ {
+ if( out[i] == '+' )
+ out[i] = '.';
+ else if( out[i] == '/' )
+ out[i] = '_';
+ else if( out[i] == '=' )
+ out[i] = '-';
+ }
+ }
+ }
+
+ gcry_md_close( hd );
+
+ return out;
+}
+#else
+# define BuildMusicbrainzDiscID(a, b, c, d) (NULL)
+#endif
+
+static musicbrainz_recording_t * GetMusicbrainzInfo( vlc_object_t *obj,
+ const vcddev_toc_t *p_toc,
+ int i_total, int i_first, int i_last )
+{
+ musicbrainz_recording_t *recording = NULL;
+
+ char *psz_mbserver = var_InheritString( obj, "musicbrainz-server" );
+ if( !psz_mbserver || !*psz_mbserver )
+ return NULL;
+
+ musicbrainz_config_t cfg = { .obj = obj,
+ .psz_mb_server = psz_mbserver,
+ .psz_coverart_server = NULL };
+
+ /* Build DISC ID based on SHA1 */
+ char *psz_disc_id = BuildMusicbrainzDiscID( p_toc,
+ i_total, i_first, i_last );
+ if( psz_disc_id )
+ {
+ recording = musicbrainz_lookup_recording_by_discid( &cfg, psz_disc_id );
+ }
+ else /* Fuzzy lookup using TOC */
+ {
+ struct vlc_memstream ms;
+ if( vlc_memstream_open(&ms) )
+ {
+ free( psz_mbserver );
+ return NULL;
+ }
+
+ vlc_memstream_printf(&ms, "toc=%u+%u", i_first, i_last );
+ /* LEAD OUT sector info
+ * https://github.com/metabrainz/libdiscid/blob/e46249415eb6d657ecc63667b03d670a4347712f/src/toc.c#L90 */
+ int i_last_track_end;
+ if (p_toc->i_tracks > i_total)
+ i_last_track_end = LBAPregap(p_toc->p_sectors[i_total].i_lba) - CD_ROM_XA_INTERVAL;
+ else
+ i_last_track_end = LBAPregap(p_toc->p_sectors[p_toc->i_tracks].i_lba);
+ vlc_memstream_printf(&ms, "+%u", i_last_track_end );
+ for( int i = 0; i<i_total; i++ ) /* skipped LEAD OUT, audio only */
+ vlc_memstream_printf(&ms, "+%u", LBAPregap(p_toc->p_sectors[i].i_lba) );
+ if( vlc_memstream_flush(&ms) )
+ {
+ if( vlc_memstream_close(&ms) )
+ free( ms.ptr );
+ free( psz_mbserver );
+ return NULL;
+ }
+
+ recording = musicbrainz_lookup_recording_by_toc( &cfg, ms.ptr );
+
+ if( vlc_memstream_close(&ms) == 0 )
+ free( ms.ptr );
+ }
+
+ free( psz_mbserver );
+ return recording;
+}
+
+#ifdef HAVE_LIBCDDB
+static cddb_disc_t *GetCDDBInfo( vlc_object_t *obj, const vcddev_toc_t *p_toc )
+{
+ msg_Dbg( obj, "retrieving metadata with CDDB" );
+
/* */
cddb_conn_t *p_cddb = cddb_new();
if( !p_cddb )
@@ -359,17 +580,17 @@ static cddb_disc_t *GetCDDBInfo( vlc_object_t *obj, int i_titles, int *p_sectors
}
int64_t i_length = 2000000; /* PreGap */
- for( int i = 0; i < i_titles; i++ )
+ for( int i = 0; i < p_toc->i_tracks; i++ )
{
cddb_track_t *t = cddb_track_new();
- cddb_track_set_frame_offset( t, p_sectors[i] + 150 ); /* Pregap offset */
+ cddb_track_set_frame_offset( t, p_toc->p_sectors[i].i_lba + 150 ); /* Pregap offset */
cddb_disc_add_track( p_disc, t );
- const int64_t i_size = ( p_sectors[i+1] - p_sectors[i] ) *
+ const int64_t i_size = ( p_toc->p_sectors[i+1].i_lba - p_toc->p_sectors[i].i_lba ) *
(int64_t)CDDA_DATA_SIZE;
i_length += INT64_C(1000000) * i_size / 44100 / 4 ;
- msg_Dbg( obj, "Track %i offset: %i", i, p_sectors[i] + 150 );
+ msg_Dbg( obj, "Track %i offset: %i", i, p_toc->p_sectors[i].i_lba + 150 );
}
msg_Dbg( obj, "Total length: %i", (int)(i_length/1000000) );
@@ -397,6 +618,8 @@ static cddb_disc_t *GetCDDBInfo( vlc_object_t *obj, int i_titles, int *p_sectors
cddb_read( p_cddb, p_disc );
+ msg_Dbg( obj, "disc ID: 0x%08x", cddb_disc_get_discid(p_disc) );
+
cddb_destroy( p_cddb);
return p_disc;
@@ -404,6 +627,7 @@ error:
if( p_disc )
cddb_disc_destroy( p_disc );
cddb_destroy( p_cddb );
+ msg_Dbg( obj, "CDDB failure" );
return NULL;
}
#endif /* HAVE_LIBCDDB */
@@ -448,7 +672,7 @@ static void AccessGetMeta(stream_t *access, vlc_meta_t *meta)
str = cddb_disc_get_artist(sys->cddb);
if (NONEMPTY(str))
{
- for (int i = 0; i < sys->titles; i++)
+ for (int i = 0; i < sys->p_toc->i_tracks; i++)
{
cddb_track_t *t = cddb_disc_get_track(sys->cddb, i);
if (t == NULL)
@@ -475,21 +699,33 @@ static void AccessGetMeta(stream_t *access, vlc_meta_t *meta)
static int ReadDir(stream_t *access, input_item_node_t *node)
{
access_sys_t *sys = access->p_sys;
+ const vcddev_toc_t *p_toc = sys->p_toc;
/* Build title table */
- for (int i = 0; i < sys->titles; i++)
+ const int i_start_track_offset = sys->i_cdda_first - sys->p_toc->i_first_track;
+ for (int i = 0; i < sys->i_cdda_tracks; i++)
{
- msg_Dbg(access, "track[%d] start=%d", i, sys->p_sectors[i]);
+ if(i < i_start_track_offset)
+ continue;
+
+ msg_Dbg(access, "track[%d] start=%d", i, p_toc->p_sectors[i].i_lba);
/* Initial/default name */
char *name;
- if (unlikely(asprintf(&name, _("Audio CD - Track %02i"), i + 1) == -1))
+ if (unlikely(asprintf(&name, _("Audio CD - Track %02i"),
+ i - i_start_track_offset + 1 ) == -1))
name = NULL;
/* Create playlist items */
+ int i_first_sector = p_toc->p_sectors[i].i_lba;
+ int i_last_sector = p_toc->p_sectors[i + 1].i_lba;
+ if(sys->i_cdda_first + i == sys->i_cdda_last &&
+ p_toc->i_last_track > sys->i_cdda_last)
+ i_last_sector -= CD_ROM_XA_INTERVAL;
+
const mtime_t duration =
- (mtime_t)(sys->p_sectors[i + 1] - sys->p_sectors[i])
+ (mtime_t)(i_last_sector - i_first_sector)
* CDDA_DATA_SIZE * CLOCK_FREQ / 44100 / 2 / 2;
input_item_t *item = input_item_NewDisc(access->psz_url,
@@ -508,14 +744,13 @@ static int ReadDir(stream_t *access, input_item_node_t *node)
}
if (likely(asprintf(&opt, "cdda-first-sector=%i",
- sys->p_sectors[i]) != -1))
+ p_toc->p_sectors[i].i_lba) != -1))
{
input_item_AddOption(item, opt, VLC_INPUT_OPTION_TRUSTED);
free(opt);
}
- if (likely(asprintf(&opt, "cdda-last-sector=%i",
- sys->p_sectors[i + 1]) != -1))
+ if (likely(asprintf(&opt, "cdda-last-sector=%i", i_last_sector) != -1))
{
input_item_AddOption(item, opt, VLC_INPUT_OPTION_TRUSTED);
free(opt);
@@ -562,6 +797,37 @@ static int ReadDir(stream_t *access, input_item_node_t *node)
ON_EMPTY(description, vlc_meta_Get(m, vlc_meta_Description));
}
+ if(sys->mbrecord && sys->mbrecord->i_release)
+ {
+ musicbrainz_release_t *r = sys->mbrecord->p_releases;
+ for(size_t j=0; j<r->i_tracks; j++)
+ {
+ if(r->p_tracks[j].i_index == (unsigned)i + 1)
+ {
+ if (r->p_tracks[j].psz_title)
+ {
+ ON_EMPTY(title, r->p_tracks[j].psz_title);
+ ON_EMPTY(artist, r->p_tracks[j].psz_artist);
+ }
+ break;
+ }
+ }
+ ON_EMPTY(album, r->psz_title);
+ if(NONEMPTY(r->psz_artist))
+ {
+ artist = r->psz_artist;
+ input_item_SetAlbumArtist(item, r->psz_artist);
+ }
+ if(year == 0 && r->psz_date)
+ {
+ unsigned i_year;
+ if(sscanf(r->psz_date, "%4d", &i_year) == 1)
+ year = i_year;
+ }
+ if(NONEMPTY(r->psz_coverart_url))
+ input_item_SetArtworkURL(item, r->psz_coverart_url);
+ }
+
if (NONEMPTY(title))
{
input_item_SetName(item, title);
@@ -589,8 +855,10 @@ static int ReadDir(stream_t *access, input_item_node_t *node)
}
char num[4];
- snprintf(num, sizeof (num), "%d", i + 1);
- input_item_SetTrackNum(item, num);
+ if(snprintf(num, sizeof (num), "%u", i + 1) < 4)
+ input_item_SetTrackNum(item, num);
+ snprintf(num, sizeof (num), "%u", p_toc->i_tracks);
+ input_item_SetTrackTotal(item, num);
input_item_node_AppendItem(node, item);
input_item_Release(item);
@@ -634,31 +902,21 @@ static int AccessOpen(vlc_object_t *obj)
}
sys->vcddev = dev;
- sys->p_sectors = NULL;
-
- sys->titles = ioctl_GetTracksMap(obj, dev, &sys->p_sectors);
- if (sys->titles < 0)
+ sys->p_toc = ioctl_GetTOC(obj, dev, true);
+ if (sys->p_toc == NULL)
{
msg_Err(obj, "cannot count tracks");
goto error;
}
- if (sys->titles == 0)
+ sys->i_cdda_tracks = TOC_GetAudioRange(sys->p_toc, &sys->i_cdda_first, &sys->i_cdda_last);
+ if (sys->i_cdda_tracks == 0)
{
msg_Err(obj, "no audio tracks found");
+ vcddev_toc_Free(sys->p_toc);
goto error;
}
-#ifdef HAVE_LIBCDDB
- msg_Dbg(obj, "retrieving metadata with CDDB");
-
- sys->cddb = GetCDDBInfo(obj, sys->titles, sys->p_sectors);
- if (sys->cddb != NULL)
- msg_Dbg(obj, "disc ID: 0x%08x", cddb_disc_get_discid(sys->cddb));
- else
- msg_Dbg(obj, "CDDB failure");
-#endif
-
if (ioctl_GetCdText(obj, dev, &sys->cdtextv, &sys->cdtextc))
{
msg_Dbg(obj, "CD-TEXT information missing");
@@ -666,6 +924,20 @@ static int AccessOpen(vlc_object_t *obj)
sys->cdtextc = 0;
}
+ sys->mbrecord = NULL;
+ sys->cddb = NULL;
+
+ if(var_InheritBool(obj, "metadata-network-access"))
+ {
+ sys->mbrecord = GetMusicbrainzInfo(obj, sys->p_toc, sys->i_cdda_tracks,
+ sys->i_cdda_first, sys->i_cdda_last );
+#ifdef HAVE_LIBCDDB
+ if(!sys->mbrecord)
+ sys->cddb = GetCDDBInfo(obj, sys->p_toc);
+#endif
+ }
+ else msg_Dbg(obj, "album art policy set to manual: not fetching");
+
access->p_sys = sys;
access->pf_read = NULL;
access->pf_block = NULL;
@@ -675,7 +947,6 @@ static int AccessOpen(vlc_object_t *obj)
return VLC_SUCCESS;
error:
- free(sys->p_sectors);
ioctl_Close(obj, dev);
return VLC_EGENERIC;
}
@@ -698,8 +969,11 @@ static void AccessClose(vlc_object_t *obj)
cddb_disc_destroy(sys->cddb);
#endif
- free(sys->p_sectors);
ioctl_Close(obj, sys->vcddev);
+
+ if(sys->mbrecord)
+ musicbrainz_recording_release(sys->mbrecord);
+ vcddev_toc_Free(sys->p_toc);
}
/*****************************************************************************
@@ -742,6 +1016,9 @@ vlc_module_begin ()
add_integer( "cdda-last-sector", -1, NULL, NULL, true )
change_volatile ()
+ add_string( "musicbrainz-server", MUSICBRAINZ_DEFAULT_SERVER,
+ N_( "Musicbrainz Server" ),
+ N_( "Address of the musicbrainz server to use." ), true )
#ifdef HAVE_LIBCDDB
add_string( "cddb-server", "freedb.videolan.org", N_( "CDDB Server" ),
N_( "Address of the CDDB server to use." ), true )
=====================================
modules/access/vcd/cdrom.c
=====================================
@@ -140,8 +140,8 @@ exit_free:
# error FIXME
#endif
-#include "cdrom_internals.h"
#include "cdrom.h"
+#include "cdrom_internals.h"
/*****************************************************************************
* ioctl_Open: Opens a VCD device or file and returns an opaque handle
@@ -165,6 +165,7 @@ vcddev_t *ioctl_Open( vlc_object_t *p_this, const char *psz_dev )
return NULL;
p_vcddev->i_vcdimage_handle = -1;
p_vcddev->psz_dev = NULL;
+ memset( &p_vcddev->toc, 0, sizeof(p_vcddev->toc) );
b_is_file = 1;
/*
@@ -257,14 +258,15 @@ void ioctl_Close( vlc_object_t * p_this, vcddev_t *p_vcddev )
}
/*****************************************************************************
- * ioctl_GetTracksMap: Read the Table of Content, fill in the pp_sectors map
- * if pp_sectors is not null and return the number of
- * tracks available.
+ * ioctl_GetTOC: Read the Table of Content, fill in the p_sectors map
+ * if b_fill_sector_info is true.
*****************************************************************************/
-int ioctl_GetTracksMap( vlc_object_t *p_this, const vcddev_t *p_vcddev,
- int **pp_sectors )
+vcddev_toc_t * ioctl_GetTOC( vlc_object_t *p_this, const vcddev_t *p_vcddev,
+ bool b_fill_sectorinfo )
{
- int i_tracks = 0;
+ vcddev_toc_t *p_toc = calloc(1, sizeof(*p_toc));
+ if(!p_toc)
+ return NULL;
if( p_vcddev->i_vcdimage_handle != -1 )
{
@@ -272,18 +274,22 @@ int ioctl_GetTracksMap( vlc_object_t *p_this, const vcddev_t *p_vcddev,
* vcd image mode
*/
- i_tracks = p_vcddev->i_tracks;
+ *p_toc = p_vcddev->toc;
+ p_toc->p_sectors = NULL;
- if( pp_sectors )
+ if( b_fill_sectorinfo )
{
- *pp_sectors = calloc( i_tracks + 1, sizeof(**pp_sectors) );
- if( *pp_sectors == NULL )
- return 0;
- memcpy( *pp_sectors, p_vcddev->p_sectors,
- (i_tracks + 1) * sizeof(**pp_sectors) );
+ p_toc->p_sectors = calloc( p_toc->i_tracks + 1, sizeof(*p_toc->p_sectors) );
+ if( p_toc->p_sectors == NULL )
+ {
+ free( p_toc );
+ return NULL;
+ }
+ memcpy( p_toc->p_sectors, p_vcddev->toc.p_sectors,
+ (p_toc->i_tracks + 1) * sizeof(*p_toc->p_sectors) );
}
- return i_tracks;
+ return p_toc;
}
else
{
@@ -300,28 +306,33 @@ int ioctl_GetTracksMap( vlc_object_t *p_this, const vcddev_t *p_vcddev,
if( ( pTOC = darwin_getTOC( p_this, p_vcddev ) ) == NULL )
{
msg_Err( p_this, "failed to get the TOC" );
- return 0;
+ vcddev_toc_Free( p_toc );
+ return NULL;
}
i_descriptors = CDTOCGetDescriptorCount( pTOC );
- i_tracks = darwin_getNumberOfTracks( pTOC, i_descriptors );
+ p_toc->i_tracks = darwin_getNumberOfTracks( pTOC, i_descriptors,
+ &p_toc->i_first_track,
+ &p_toc->i_last_track );
- if( pp_sectors )
+ if( b_fill_sectorinfo )
{
int i, i_leadout = -1;
CDTOCDescriptor *pTrackDescriptors;
u_char track;
- *pp_sectors = calloc( i_tracks + 1, sizeof(**pp_sectors) );
- if( *pp_sectors == NULL )
+ p_toc->p_sectors = calloc( p_toc->i_tracks + 1,
+ sizeof(*p_toc->p_sectors) );
+ if( p_toc->p_sectors == NULL )
{
+ vcddev_toc_Free( p_toc );
darwin_freeTOC( pTOC );
- return 0;
+ return NULL;
}
pTrackDescriptors = pTOC->descriptors;
- for( i_tracks = 0, i = 0; i < i_descriptors; i++ )
+ for( p_toc->i_tracks = 0, i = 0; i < i_descriptors; i++ )
{
track = pTrackDescriptors[i].point;
@@ -331,20 +342,21 @@ int ioctl_GetTracksMap( vlc_object_t *p_this, const vcddev_t *p_vcddev,
if( track > CD_MAX_TRACK_NO || track < CD_MIN_TRACK_NO )
continue;
- (*pp_sectors)[i_tracks++] =
+ p_toc->p_sectors[p_toc->i_tracks].i_control = pTrackDescriptors[i].control;
+ p_toc->p_sectors[p_toc->i_tracks++].i_lba =
CDConvertMSFToLBA( pTrackDescriptors[i].p );
}
if( i_leadout == -1 )
{
msg_Err( p_this, "leadout not found" );
- free( *pp_sectors );
+ vcddev_toc_Free( p_toc );
darwin_freeTOC( pTOC );
- return 0;
+ return NULL;
}
/* set leadout sector */
- (*pp_sectors)[i_tracks] =
+ p_toc->p_sectors[p_toc->i_tracks].i_lba =
CDConvertMSFToLBA( pTrackDescriptors[i_leadout].p );
}
@@ -359,24 +371,31 @@ int ioctl_GetTracksMap( vlc_object_t *p_this, const vcddev_t *p_vcddev,
&dwBytesReturned, NULL ) == 0 )
{
msg_Err( p_this, "could not read TOCHDR" );
- return 0;
+ vcddev_toc_Free( p_toc );
+ return NULL;
}
- i_tracks = cdrom_toc.LastTrack - cdrom_toc.FirstTrack + 1;
+ p_toc->i_tracks = cdrom_toc.LastTrack - cdrom_toc.FirstTrack + 1;
+ p_toc->i_first_track = cdrom_toc.FirstTrack;
+ p_toc->i_last_track = cdrom_toc.LastTrack;
- if( pp_sectors )
+ if( b_fill_sectorinfo )
{
- *pp_sectors = calloc( i_tracks + 1, sizeof(**pp_sectors) );
- if( *pp_sectors == NULL )
- return 0;
+ p_toc->p_sectors = calloc( p_toc->i_tracks + 1, sizeof(p_toc->p_sectors) );
+ if( p_toc->p_sectors == NULL )
+ {
+ vcddev_toc_Free( p_toc );
+ return NULL;
+ }
- for( int i = 0 ; i <= i_tracks ; i++ )
+ for( int i = 0 ; i <= p_toc->i_tracks ; i++ )
{
- (*pp_sectors)[ i ] = MSF_TO_LBA2(
+ p_toc->p_sectors[ i ].i_control = cdrom_toc.TrackData[i].Control;
+ p_toc->p_sectors[ i ].i_lba = MSF_TO_LBA2(
cdrom_toc.TrackData[i].Address[1],
cdrom_toc.TrackData[i].Address[2],
cdrom_toc.TrackData[i].Address[3] );
- msg_Dbg( p_this, "p_sectors: %i, %i", i, (*pp_sectors)[i]);
+ msg_Dbg( p_this, "p_sectors: %i, %i", i, p_toc->p_sectors[i].i_lba);
}
}
@@ -398,19 +417,24 @@ int ioctl_GetTracksMap( vlc_object_t *p_this, const vcddev_t *p_vcddev,
return 0;
}
- i_tracks = tochdr.last_track - tochdr.first_track + 1;
+ p_toc->i_tracks = tochdr.last_track - tochdr.first_track + 1;
+ p_toc->i_first_track = tochdr.first_track;
+ p_toc->i_last_track = tochdr.last_track;
- if( pp_sectors )
+ if( b_fill_sectorinfo )
{
cdrom_get_track_t get_track = {{'C', 'D', '0', '1'}, };
cdrom_track_t track;
int i;
- *pp_sectors = calloc( i_tracks + 1, sizeof(**pp_sectors) );
- if( *pp_sectors == NULL )
- return 0;
+ p_toc->p_sectors = calloc( p_toc->i_tracks + 1, sizeof(*p_toc->p_sectors) );
+ if( *p_toc->p_sectors == NULL )
+ {
+ vcddev_toc_Free( p_toc );
+ return NULL;
+ }
- for( i = 0 ; i < i_tracks ; i++ )
+ for( i = 0 ; i < p_toc->i_tracks ; i++ )
{
get_track.track = tochdr.first_track + i;
rc = DosDevIOCtl( p_vcddev->hcd, IOCTL_CDROMAUDIO,
@@ -421,22 +445,23 @@ int ioctl_GetTracksMap( vlc_object_t *p_this, const vcddev_t *p_vcddev,
{
msg_Err( p_this, "could not read %d track",
get_track.track );
- return 0;
+ vcddev_toc_Free( p_toc );
+ return NULL;
}
- (*pp_sectors)[ i ] = MSF_TO_LBA2(
+ p_toc->p_sectors[ i ].i_lba = MSF_TO_LBA2(
track.start.minute,
track.start.second,
track.start.frame );
- msg_Dbg( p_this, "p_sectors: %i, %i", i, (*pp_sectors)[i]);
+ msg_Dbg( p_this, "p_sectors: %i, %i", i, p_toc->p_sectors[i].i_lba);
}
/* for lead-out track */
- (*pp_sectors)[ i ] = MSF_TO_LBA2(
+ p_toc->p_sectors[ i ].i_lba = MSF_TO_LBA2(
tochdr.lead_out.minute,
tochdr.lead_out.second,
tochdr.lead_out.frame );
- msg_Dbg( p_this, "p_sectors: %i, %i", i, (*pp_sectors)[i]);
+ msg_Dbg( p_this, "p_sectors: %i, %i", i, p_toc->p_sectors[i].i_lba);
}
#elif defined( HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H ) \
@@ -448,27 +473,33 @@ int ioctl_GetTracksMap( vlc_object_t *p_this, const vcddev_t *p_vcddev,
== -1 )
{
msg_Err( p_this, "could not read TOCHDR" );
- return 0;
+ vcddev_toc_Free( p_toc );
+ return NULL;
}
- i_tracks = tochdr.ending_track - tochdr.starting_track + 1;
+ p_toc->i_tracks = tochdr.ending_track - tochdr.starting_track + 1;
+ p_toc->i_first_track = tochdr.starting_track;
+ p_toc->i_last_track = tochdr.ending_track;
- if( pp_sectors )
+ if( b_fill_sectorinfo )
{
- *pp_sectors = calloc( i_tracks + 1, sizeof(**pp_sectors) );
- if( *pp_sectors == NULL )
- return 0;
+ p_toc->p_sectors = calloc( p_toc->i_tracks + 1, sizeof(*p_toc->p_sectors) );
+ if( p_toc->p_sectors == NULL )
+ {
+ vcddev_toc_Free( p_toc );
+ return NULL;
+ }
toc_entries.address_format = CD_LBA_FORMAT;
toc_entries.starting_track = 0;
- toc_entries.data_len = ( i_tracks + 1 ) *
+ toc_entries.data_len = ( p_toc->i_tracks + 1 ) *
sizeof( struct cd_toc_entry );
toc_entries.data = (struct cd_toc_entry *)
malloc( toc_entries.data_len );
if( toc_entries.data == NULL )
{
- free( *pp_sectors );
- return 0;
+ vcddev_toc_Free( p_toc );
+ return NULL;
}
/* Read the TOC */
@@ -476,19 +507,19 @@ int ioctl_GetTracksMap( vlc_object_t *p_this, const vcddev_t *p_vcddev,
&toc_entries ) == -1 )
{
msg_Err( p_this, "could not read the TOC" );
- free( *pp_sectors );
free( toc_entries.data );
- return 0;
+ vcddev_toc_Free( p_toc );
+ return NULL;
}
/* Fill the p_sectors structure with the track/sector matches */
- for( int i = 0 ; i <= i_tracks ; i++ )
+ for( int i = 0 ; i <= p_toc->i_tracks ; i++ )
{
#if defined( HAVE_SCSIREQ_IN_SYS_SCSIIO_H )
/* FIXME: is this ok? */
- (*pp_sectors)[ i ] = toc_entries.data[i].addr.lba;
+ p_toc->p_sectors[ i ].i_lba = toc_entries.data[i].addr.lba;
#else
- (*pp_sectors)[ i ] = ntohl( toc_entries.data[i].addr.lba );
+ p_toc->p_sectors[ i ].i_lba = ntohl( toc_entries.data[i].addr.lba );
#endif
}
}
@@ -501,38 +532,46 @@ int ioctl_GetTracksMap( vlc_object_t *p_this, const vcddev_t *p_vcddev,
== -1 )
{
msg_Err( p_this, "could not read TOCHDR" );
- return 0;
+ free( p_toc );
+ return NULL;
}
- i_tracks = tochdr.cdth_trk1 - tochdr.cdth_trk0 + 1;
+ p_toc->i_tracks = tochdr.cdth_trk1 - tochdr.cdth_trk0 + 1;
+ p_toc->i_first_track = tochdr.cdth_trk0;
+ p_toc->i_last_track = tochdr.cdth_trk1;
- if( pp_sectors )
+ if( b_fill_sectorinfo )
{
- *pp_sectors = calloc( i_tracks + 1, sizeof(**pp_sectors) );
- if( *pp_sectors == NULL )
- return 0;
+ p_toc->p_sectors = calloc( p_toc->i_tracks + 1, sizeof(*p_toc->p_sectors) );
+ if( p_toc->p_sectors == NULL )
+ {
+ free( p_toc );
+ return NULL;
+ }
/* Fill the p_sectors structure with the track/sector matches */
- for( int i = 0 ; i <= i_tracks ; i++ )
+ for( int i = 0 ; i <= p_toc->i_tracks ; i++ )
{
tocent.cdte_format = CDROM_LBA;
tocent.cdte_track =
- ( i == i_tracks ) ? CDROM_LEADOUT : tochdr.cdth_trk0 + i;
+ ( i == p_toc->i_tracks ) ? CDROM_LEADOUT : tochdr.cdth_trk0 + i;
if( ioctl( p_vcddev->i_device_handle, CDROMREADTOCENTRY,
&tocent ) == -1 )
{
msg_Err( p_this, "could not read TOCENTRY" );
- free( *pp_sectors );
- return 0;
+ free( p_toc->p_sectors );
+ free( p_toc );
+ return NULL;
}
- (*pp_sectors)[ i ] = tocent.cdte_addr.lba;
+ p_toc->p_sectors[ i ].i_lba = tocent.cdte_addr.lba;
+ p_toc->p_sectors[ i ].i_control = tocent.cdte_ctrl;
}
}
#endif
- return i_tracks;
+ return p_toc;
}
}
@@ -767,7 +806,7 @@ static int OpenVCDImage( vlc_object_t * p_this, const char *psz_dev,
char *psz_vcdfile = NULL;
char *psz_cuefile = NULL;
FILE *cuefile = NULL;
- int *p_sectors = NULL;
+ vcddev_toc_t *p_toc = &p_vcddev->toc;
char line[1024];
bool b_found = false;
@@ -857,9 +896,9 @@ static int OpenVCDImage( vlc_object_t * p_this, const char *psz_dev,
/* Try to parse the i_tracks and p_sectors info so we can just forget
* about the cuefile */
- size_t i_tracks = 0;
+ p_toc->i_tracks = 0;
- while( fgets( line, 1024, cuefile ) && i_tracks < INT_MAX-1 )
+ while( fgets( line, 1024, cuefile ) && p_toc->i_tracks < INT_MAX-1 )
{
/* look for a TRACK line */
char psz_dummy[10];
@@ -875,35 +914,42 @@ static int OpenVCDImage( vlc_object_t * p_this, const char *psz_dev,
&i_min, &i_sec, &i_frame ) != 4) || (i_num != 1) )
continue;
- int *buf = realloc (p_sectors, (i_tracks + 1) * sizeof (*buf));
+ vcddev_sector_t *buf = realloc (p_toc->p_sectors,
+ (p_toc->i_tracks + 1) * sizeof (*buf));
if (buf == NULL)
goto error;
- p_sectors = buf;
- p_sectors[i_tracks] = MSF_TO_LBA(i_min, i_sec, i_frame);
+ p_toc->p_sectors = buf;
+ p_toc->p_sectors[p_toc->i_tracks].i_lba = MSF_TO_LBA(i_min, i_sec, i_frame);
+ p_toc->p_sectors[p_toc->i_tracks].i_control = 0x00;
msg_Dbg( p_this, "vcd track %i begins at sector:%i",
- (int)i_tracks, (int)p_sectors[i_tracks] );
- i_tracks++;
+ p_toc->i_tracks, p_toc->p_sectors[p_toc->i_tracks].i_lba );
+ p_toc->i_tracks++;
break;
}
}
/* fill in the last entry */
- int *buf = realloc (p_sectors, (i_tracks + 1) * sizeof (*buf));
+ vcddev_sector_t *buf = realloc (p_toc->p_sectors,
+ (p_toc->i_tracks + 1) * sizeof (*buf));
if (buf == NULL)
goto error;
- p_sectors = buf;
- p_sectors[i_tracks] = lseek(p_vcddev->i_vcdimage_handle, 0, SEEK_END)
- / VCD_SECTOR_SIZE;
+ p_toc->p_sectors = buf;
+ p_toc->p_sectors[p_toc->i_tracks].i_lba =
+ lseek(p_vcddev->i_vcdimage_handle, 0, SEEK_END) / VCD_SECTOR_SIZE;
+ p_toc->p_sectors[p_toc->i_tracks].i_control = 0x00;
msg_Dbg( p_this, "vcd track %i, begins at sector:%i",
- (int)i_tracks, (int)p_sectors[i_tracks] );
- p_vcddev->i_tracks = ++i_tracks;
- p_vcddev->p_sectors = p_sectors;
- p_sectors = NULL;
+ p_toc->i_tracks, p_toc->p_sectors[p_toc->i_tracks].i_lba );
+ p_toc->i_tracks++;
+ p_toc->i_first_track = 1;
+ p_toc->i_last_track = p_toc->i_tracks;
i_ret = 0;
+ goto end;
error:
+ free( p_toc->p_sectors );
+ memset( p_toc, 0, sizeof(*p_toc) );
+end:
if( cuefile ) fclose( cuefile );
- free( p_sectors );
free( psz_cuefile );
free( psz_vcdfile );
@@ -921,7 +967,7 @@ static void CloseVCDImage( vlc_object_t * p_this, vcddev_t *p_vcddev )
else
return;
- free( p_vcddev->p_sectors );
+ free( p_vcddev->toc.p_sectors );
}
#if defined( __APPLE__ )
@@ -1028,11 +1074,16 @@ static CDTOC *darwin_getTOC( vlc_object_t * p_this, const vcddev_t *p_vcddev )
/****************************************************************************
* darwin_getNumberOfTracks: get number of tracks in TOC
+ * and first and last CDDA ones
****************************************************************************/
-static int darwin_getNumberOfTracks( CDTOC *pTOC, int i_descriptors )
+static int darwin_getNumberOfTracks( CDTOC *pTOC, int i_descriptors,
+ int *pi_first_track,
+ int *pi_last_track )
{
u_char track;
int i, i_tracks = 0;
+ int i_min = CD_MAX_TRACK_NO;
+ int i_max = CD_MIN_TRACK_NO;
CDTOCDescriptor *pTrackDescriptors = NULL;
pTrackDescriptors = (CDTOCDescriptor *)pTOC->descriptors;
@@ -1044,9 +1095,23 @@ static int darwin_getNumberOfTracks( CDTOC *pTOC, int i_descriptors )
if( track > CD_MAX_TRACK_NO || track < CD_MIN_TRACK_NO )
continue;
+ if( pTrackDescriptors[i].adr == 0x01 /* kCDSectorTypeCDDA */ )
+ {
+ i_min = __MIN(i_min, track);
+ i_max = __MAX(i_max, track);
+ }
+
i_tracks++;
}
+ if( i_max < i_min )
+ *pi_first_track = *pi_last_track = 0;
+ else
+ {
+ *pi_first_track = i_min;
+ *pi_last_track = i_max;
+ }
+
return( i_tracks );
}
#endif /* __APPLE__ */
=====================================
modules/access/vcd/cdrom.h
=====================================
@@ -38,6 +38,12 @@ enum {
#define CD_ROM_XA_MODE2_F1_DATA_SIZE 2048
#define CD_ROM_XA_MODE2_F2_DATA_SIZE 2324
+#define CD_ROM_XA_FRAMES 75
+#define CD_ROM_XA_INTERVAL ((60 + 90 + 2) * CD_ROM_XA_FRAMES)
+
+/* Subcode control flag */
+#define CD_ROM_DATA_FLAG 0x04
+
/* size of a CD sector */
#define CD_SECTOR_SIZE CD_ROM_MODE1_DATA_SIZE
@@ -74,6 +80,36 @@ static inline int MSF_TO_LBA2(uint8_t min, uint8_t sec, uint8_t frame)
(uint8_t)((uint8_t)(0xf & (uint8_t)i)+((uint8_t)10*((uint8_t)i >> 4)))
typedef struct vcddev_s vcddev_t;
+typedef struct
+{
+ int i_lba;
+ int i_control;
+} vcddev_sector_t;
+
+typedef struct
+{
+ int i_tracks;
+ vcddev_sector_t *p_sectors;
+ int i_first_track;
+ int i_last_track;
+} vcddev_toc_t;
+
+static inline vcddev_toc_t * vcddev_toc_New( void )
+{
+ return calloc(1, sizeof(vcddev_toc_t));
+}
+
+static inline void vcddev_toc_Reset( vcddev_toc_t *toc )
+{
+ free(toc->p_sectors);
+ memset(toc, 0, sizeof(*toc));
+}
+
+static inline void vcddev_toc_Free( vcddev_toc_t *toc )
+{
+ free(toc->p_sectors);
+ free(toc);
+}
/*****************************************************************************
* structure to store minute/second/frame locations
@@ -111,7 +147,7 @@ typedef struct entries_sect_s
*****************************************************************************/
vcddev_t *ioctl_Open ( vlc_object_t *, const char * );
void ioctl_Close ( vlc_object_t *, vcddev_t * );
-int ioctl_GetTracksMap ( vlc_object_t *, const vcddev_t *, int ** );
+vcddev_toc_t * ioctl_GetTOC ( vlc_object_t *, const vcddev_t *, bool );
int ioctl_ReadSectors ( vlc_object_t *, const vcddev_t *,
int, uint8_t *, int, int );
=====================================
modules/access/vcd/cdrom_internals.h
=====================================
@@ -31,8 +31,7 @@ struct vcddev_s
/* Section used in vcd image mode */
int i_vcdimage_handle; /* vcd image file descriptor */
- int i_tracks; /* number of tracks of the vcd */
- int *p_sectors; /* tracks layout on the vcd */
+ vcddev_toc_t toc; /* tracks layout on the vcd */
/* Section used in vcd device mode */
@@ -181,7 +180,7 @@ static void CloseVCDImage( vlc_object_t *, struct vcddev_s * );
#if defined( __APPLE__ )
static CDTOC *darwin_getTOC( vlc_object_t *, const struct vcddev_s * );
-static int darwin_getNumberOfTracks( CDTOC *, int );
+static int darwin_getNumberOfTracks( CDTOC *, int, int *, int * );
#elif defined( _WIN32 )
static int win32_vcd_open( vlc_object_t *, const char *, struct vcddev_s *);
=====================================
modules/access/vcd/vcd.c
=====================================
@@ -69,7 +69,8 @@ struct access_sys_t
uint64_t offset;
/* Title infos */
- int i_titles;
+ vcddev_toc_t *p_toc;
+
struct
{
uint64_t *seekpoints;
@@ -77,9 +78,7 @@ struct access_sys_t
} titles[99]; /* No more that 99 track in a vcd ? */
int i_current_title;
unsigned i_current_seekpoint;
-
int i_sector; /* Current Sector */
- int *p_sectors; /* Track sectors */
};
static block_t *Block( stream_t *, bool * );
@@ -152,26 +151,28 @@ static int Open( vlc_object_t *p_this )
p_sys->titles[i].seekpoints = NULL;
/* We read the Table Of Content information */
- p_sys->i_titles = ioctl_GetTracksMap( VLC_OBJECT(p_access),
- p_sys->vcddev, &p_sys->p_sectors );
- if( p_sys->i_titles < 0 )
+ p_sys->p_toc = ioctl_GetTOC( VLC_OBJECT(p_access), p_sys->vcddev, true );
+ if( p_sys->p_toc == NULL )
{
msg_Err( p_access, "unable to count tracks" );
goto error;
}
- else if( p_sys->i_titles <= 1 )
+ else if( p_sys->p_toc->i_tracks <= 1 )
{
+ vcddev_toc_Free( p_sys->p_toc );
+ p_sys->p_toc = NULL;
msg_Err( p_access, "no movie tracks found" );
goto error;
}
/* The first title isn't usable */
- p_sys->i_titles--;
+#define USABLE_TITLES(a) (a - 1)
+ //p_sys->i_titles--;
- for( int i = 0; i < p_sys->i_titles; i++ )
+ for( int i = 0; i < USABLE_TITLES(p_sys->p_toc->i_tracks); i++ )
{
- msg_Dbg( p_access, "title[%d] start=%d", i, p_sys->p_sectors[1+i] );
- msg_Dbg( p_access, "title[%d] end=%d", i, p_sys->p_sectors[i+2] );
+ msg_Dbg( p_access, "title[%d] start=%d", i, p_sys->p_toc->p_sectors[1+i].i_lba );
+ msg_Dbg( p_access, "title[%d] end=%d", i, p_sys->p_toc->p_sectors[i+2].i_lba );
}
/* Map entry points into chapters */
@@ -181,12 +182,12 @@ static int Open( vlc_object_t *p_this )
}
/* Starting title/chapter and sector */
- if( i_title >= p_sys->i_titles )
+ if( i_title > USABLE_TITLES(p_sys->p_toc->i_tracks) )
i_title = 0;
if( (unsigned)i_chapter >= p_sys->titles[i_title].count )
i_chapter = 0;
- p_sys->i_sector = p_sys->p_sectors[1+i_title];
+ p_sys->i_sector = p_sys->p_toc->p_sectors[1+i_title].i_lba;
if( i_chapter > 0 )
p_sys->i_sector += p_sys->titles[i_title].seekpoints[i_chapter]
/ VCD_DATA_SIZE;
@@ -199,7 +200,7 @@ static int Open( vlc_object_t *p_this )
p_sys->i_current_title = i_title;
p_sys->i_current_seekpoint = i_chapter;
- p_sys->offset = (uint64_t)(p_sys->i_sector - p_sys->p_sectors[1+i_title]) *
+ p_sys->offset = (uint64_t)(p_sys->i_sector - p_sys->p_toc->p_sectors[1+i_title].i_lba) *
VCD_DATA_SIZE;
return VLC_SUCCESS;
@@ -221,6 +222,8 @@ static void Close( vlc_object_t *p_this )
for( size_t i = 0; i < ARRAY_SIZE(p_sys->titles); i++ )
free( p_sys->titles[i].seekpoints );
+ vcddev_toc_Free( p_sys->p_toc );
+
ioctl_Close( p_this, p_sys->vcddev );
free( p_sys );
}
@@ -248,8 +251,8 @@ static int Control( stream_t *p_access, int i_query, va_list args )
int i = p_sys->i_current_title;
*va_arg( args, uint64_t * ) =
- (p_sys->p_sectors[i + 2] - p_sys->p_sectors[i + 1])
- * (uint64_t)VCD_DATA_SIZE;
+ (p_sys->p_toc->p_sectors[i + 2].i_lba -
+ p_sys->p_toc->p_sectors[i + 1].i_lba) * (uint64_t)VCD_DATA_SIZE;
break;
}
@@ -266,11 +269,12 @@ static int Control( stream_t *p_access, int i_query, va_list args )
case STREAM_GET_TITLE_INFO:
ppp_title = va_arg( args, input_title_t*** );
/* Duplicate title infos */
- *ppp_title = vlc_alloc( p_sys->i_titles, sizeof(input_title_t *) );
+ *ppp_title = vlc_alloc( USABLE_TITLES(p_sys->p_toc->i_tracks),
+ sizeof(input_title_t *) );
if (!*ppp_title)
return VLC_ENOMEM;
- *va_arg( args, int* ) = p_sys->i_titles;
- for( int i = 0; i < p_sys->i_titles; i++ )
+ *va_arg( args, int* ) = USABLE_TITLES(p_sys->p_toc->i_tracks);
+ for( int i = 0; i < USABLE_TITLES(p_sys->p_toc->i_tracks); i++ )
(*ppp_title)[i] = vlc_input_title_New();
break;
@@ -297,7 +301,7 @@ static int Control( stream_t *p_access, int i_query, va_list args )
p_sys->i_current_seekpoint = 0;
/* Next sector to read */
- p_sys->i_sector = p_sys->p_sectors[1+i];
+ p_sys->i_sector = p_sys->p_toc->p_sectors[1+i].i_lba;
}
break;
}
@@ -311,11 +315,11 @@ static int Control( stream_t *p_access, int i_query, va_list args )
{
p_sys->i_current_seekpoint = i;
- p_sys->i_sector = p_sys->p_sectors[1 + i_title] +
+ p_sys->i_sector = p_sys->p_toc->p_sectors[1 + i_title].i_lba +
p_sys->titles[i_title].seekpoints[i] / VCD_DATA_SIZE;
p_sys->offset = (uint64_t)(p_sys->i_sector -
- p_sys->p_sectors[1 + i_title]) * VCD_DATA_SIZE;
+ p_sys->p_toc->p_sectors[1 + i_title].i_lba) * VCD_DATA_SIZE;
}
break;
}
@@ -332,13 +336,14 @@ static int Control( stream_t *p_access, int i_query, va_list args )
static block_t *Block( stream_t *p_access, bool *restrict eof )
{
access_sys_t *p_sys = p_access->p_sys;
+ const vcddev_toc_t *p_toc = p_sys->p_toc;
int i_blocks = VCD_BLOCKS_ONCE;
block_t *p_block;
/* Check end of title */
- while( p_sys->i_sector >= p_sys->p_sectors[p_sys->i_current_title + 2] )
+ while( p_sys->i_sector >= p_toc->p_sectors[p_sys->i_current_title + 2].i_lba )
{
- if( p_sys->i_current_title + 2 >= p_sys->i_titles )
+ if( p_sys->i_current_title + 2 >= USABLE_TITLES(p_toc->i_tracks) )
{
*eof = true;
return NULL;
@@ -351,9 +356,9 @@ static block_t *Block( stream_t *p_access, bool *restrict eof )
/* Don't read after the end of a title */
if( p_sys->i_sector + i_blocks >=
- p_sys->p_sectors[p_sys->i_current_title + 2] )
+ p_toc->p_sectors[p_sys->i_current_title + 2].i_lba )
{
- i_blocks = p_sys->p_sectors[p_sys->i_current_title + 2 ] - p_sys->i_sector;
+ i_blocks = p_toc->p_sectors[p_sys->i_current_title + 2 ].i_lba - p_sys->i_sector;
}
/* Do the actual reading */
@@ -404,12 +409,13 @@ static block_t *Block( stream_t *p_access, bool *restrict eof )
static int Seek( stream_t *p_access, uint64_t i_pos )
{
access_sys_t *p_sys = p_access->p_sys;
+ const vcddev_toc_t *p_toc = p_sys->p_toc;
int i_title = p_sys->i_current_title;
unsigned i_seekpoint;
/* Next sector to read */
p_sys->offset = i_pos;
- p_sys->i_sector = i_pos / VCD_DATA_SIZE + p_sys->p_sectors[i_title + 1];
+ p_sys->i_sector = i_pos / VCD_DATA_SIZE + p_toc->p_sectors[i_title + 1].i_lba;
/* Update current seekpoint */
for( i_seekpoint = 0; i_seekpoint < p_sys->titles[i_title].count; i_seekpoint++ )
@@ -434,6 +440,7 @@ static int Seek( stream_t *p_access, uint64_t i_pos )
static int EntryPoints( stream_t *p_access )
{
access_sys_t *p_sys = p_access->p_sys;
+ const vcddev_toc_t *p_toc = p_sys->p_toc;
uint8_t sector[VCD_DATA_SIZE];
entries_sect_t entries;
@@ -470,7 +477,7 @@ static int EntryPoints( stream_t *p_access )
BCD_TO_BIN( entries.entry[i].msf.second ),
BCD_TO_BIN( entries.entry[i].msf.frame ) ));
if( i_title < 0 ) continue; /* Should not occur */
- if( i_title >= p_sys->i_titles ) continue;
+ if( i_title >= USABLE_TITLES(p_toc->i_tracks) ) continue;
msg_Dbg( p_access, "Entry[%d] title=%d sector=%d",
i, i_title, i_sector );
@@ -479,7 +486,7 @@ static int EntryPoints( stream_t *p_access )
p_sys->titles[i_title].seekpoints,
sizeof( uint64_t ) * (p_sys->titles[i_title].count + 1) );
p_sys->titles[i_title].seekpoints[p_sys->titles[i_title].count++] =
- (i_sector - p_sys->p_sectors[i_title+1]) * VCD_DATA_SIZE;
+ (i_sector - p_toc->p_sectors[i_title+1].i_lba) * VCD_DATA_SIZE;
}
return VLC_SUCCESS;
=====================================
modules/misc/Makefile.am
=====================================
@@ -19,6 +19,7 @@ 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) $(LIBPTHREAD)
=====================================
modules/misc/fingerprinter.c
=====================================
@@ -309,7 +309,7 @@ static void fill_metas_with_results( fingerprint_request_t *p_r, acoustid_finger
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 ];
+ acoustid_mb_result_t *p_record = & p_result->recordings.p_recordings[ j ];
vlc_meta_t *p_meta = vlc_meta_New();
if ( p_meta )
{
@@ -362,11 +362,13 @@ static void *Run( void *opaque )
DoFingerprint( p_fingerprinter, &acoustid_print, psz_uri );
free( psz_uri );
- DoAcoustIdWebRequest( VLC_OBJECT(p_fingerprinter), &acoustid_print );
+ acoustid_config_t cfg = { .p_obj = VLC_OBJECT(p_fingerprinter),
+ .psz_server = NULL, .psz_apikey = NULL };
+ acoustid_lookup_fingerprint( &cfg, &acoustid_print );
fill_metas_with_results( p_data, &acoustid_print );
for( unsigned j = 0; j < acoustid_print.results.count; j++ )
- free_acoustid_result_t( &acoustid_print.results.p_results[j] );
+ acoustid_result_release( &acoustid_print.results.p_results[j] );
if( acoustid_print.results.count )
free( acoustid_print.results.p_results );
free( acoustid_print.psz_fingerprint );
=====================================
modules/misc/webservices/acoustid.c
=====================================
@@ -22,19 +22,13 @@
# include "config.h"
#endif
-#include <vlc_common.h>
-#include <vlc_stream.h>
-#include <limits.h>
-#include <vlc_memory.h>
-
-#include <vlc/vlc.h>
+#include "json_helper.h"
#include "acoustid.h"
-#include "json.h"
/*****************************************************************************
* Requests lifecycle
*****************************************************************************/
-void free_acoustid_result_t( acoustid_result_t * r )
+void acoustid_result_release( acoustid_result_t * r )
{
free( r->psz_id );
for ( unsigned int i=0; i<r->recordings.count; i++ )
@@ -45,69 +39,48 @@ void free_acoustid_result_t( acoustid_result_t * r )
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 )
+static void parse_artists( const json_value *node, acoustid_mb_result_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 );
+ if ( !node || node->type != json_array || node->u.array.length < 1 )
+ return;
+ record->psz_artist = json_dupstring( node->u.array.values[ 0 ], "name" );
}
-static void parse_recordings( vlc_object_t *p_obj, json_value *node, acoustid_result_t *p_result )
+static void parse_recordings( vlc_object_t *p_obj, const 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) );
+ p_result->recordings.p_recordings = calloc( node->u.array.length, sizeof(acoustid_mb_result_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" );
+ 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 )
+ break;
+ record->psz_title = json_dupstring( recordnode, "title" );
+ const json_value *value = json_getbyname( recordnode, "id" );
if ( value && value->type == json_string )
{
size_t i_len = strlen( value->u.string.ptr );
i_len = __MIN( i_len, MB_ID_SIZE );
memcpy( record->s_musicbrainz_id, value->u.string.ptr, i_len );
}
- parse_artists( jsongetbyname( recordnode, "artists" ), record );
- msg_Dbg( p_obj, "recording %d title %s %36s %s", i, record->psz_title, record->s_musicbrainz_id, record->psz_artist );
+ parse_artists( json_getbyname( recordnode, "artists" ), record );
+ msg_Dbg( p_obj, "recording %d title %s %36s %s", i, record->psz_title,
+ record->s_musicbrainz_id, record->psz_artist );
}
}
-static bool ParseJson( vlc_object_t *p_obj, const char *psz_buffer, acoustid_results_t *p_results )
+static bool ParseJson( vlc_object_t *p_obj, const void *p_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" );
+ json_value *root = json_parse_document( p_obj, p_buffer );
+ if( !root )
+ return false;
+
+ const json_value *node = json_getbyname( root, "status" );
if ( !node || node->type != json_string )
{
msg_Warn( p_obj, "status node not found or invalid" );
@@ -118,7 +91,7 @@ static bool ParseJson( vlc_object_t *p_obj, const char *psz_buffer, acoustid_res
msg_Warn( p_obj, "Bad request status" );
goto error;
}
- node = jsongetbyname( root, "results" );
+ node = json_getbyname( root, "results" );
if ( !node || node->type != json_array )
{
msg_Warn( p_obj, "Bad results array or no results" );
@@ -129,17 +102,15 @@ static bool ParseJson( vlc_object_t *p_obj, const char *psz_buffer, acoustid_res
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];
+ const 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" );
+ const json_value *value = json_getbyname( 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 );
+ p_result->psz_id = json_dupstring( resultnode, "id" );
+ parse_recordings( p_obj, json_getbyname( resultnode, "recordings" ), p_result );
}
}
json_value_free( root );
@@ -150,62 +121,47 @@ error:
return false;
}
-int DoAcoustIdWebRequest( vlc_object_t *p_obj, acoustid_fingerprint_t *p_data )
+int acoustid_lookup_fingerprint( const acoustid_config_t *p_cfg, acoustid_fingerprint_t *p_data )
{
- if ( !p_data->psz_fingerprint ) return VLC_SUCCESS;
+ if ( !p_data->psz_fingerprint )
+ return VLC_SUCCESS;
char *psz_url;
- if( unlikely(asprintf( &psz_url, "https://fingerprint.videolan.org/"
- "acoustid.php?meta=recordings+tracks+usermeta+"
- "releases&duration=%d&fingerprint=%s",
- p_data->i_duration, p_data->psz_fingerprint ) < 1 ) )
- return VLC_EGENERIC;
-
- msg_Dbg( p_obj, "Querying AcoustID from %s", psz_url );
- int i_saved_flags = p_obj->obj.flags;
- p_obj->obj.flags |= OBJECT_FLAGS_NOINTERACT;
-
- stream_t *p_stream = vlc_stream_NewURL( p_obj, psz_url );
+ if( p_cfg->psz_server )
+ {
+ if( unlikely(asprintf( &psz_url, "https://%s/v2/lookup"
+ "?client=%s"
+ "&meta=recordings+tracks+usermeta+releases"
+ "&duration=%d"
+ "&fingerprint=%s",
+ p_cfg->psz_server,
+ p_cfg->psz_apikey ? p_cfg->psz_apikey : "",
+ p_data->i_duration,
+ p_data->psz_fingerprint ) < 1 ) )
+ return VLC_EGENERIC;
+ }
+ else /* Use VideoLAN anonymized requests proxy */
+ {
+ if( unlikely(asprintf( &psz_url, "https://" ACOUSTID_ANON_SERVER
+ ACOUSTID_ANON_SERVER_PATH
+ "?meta=recordings+tracks+usermeta+releases"
+ "&duration=%d"
+ "&fingerprint=%s",
+ p_data->i_duration,
+ p_data->psz_fingerprint ) < 1 ) )
+ return VLC_EGENERIC;
+ }
+ msg_Dbg( p_cfg->p_obj, "Querying AcoustID from %s", psz_url );
+ void *p_buffer = json_retrieve_document( p_cfg->p_obj, psz_url );
free( psz_url );
- p_obj->obj.flags = i_saved_flags;
- if ( p_stream == NULL )
+ if( !p_buffer )
return VLC_EGENERIC;
- stream_t *p_chain = vlc_stream_FilterNew( p_stream, "inflate" );
- if( p_chain )
- p_stream = p_chain;
-
- /* read answer */
- char *p_buffer = NULL;
- int i_ret = 0;
- for( ;; )
- {
- int i_read = 65536;
-
- if( i_ret >= INT_MAX - i_read )
- break;
-
- p_buffer = realloc_or_free( p_buffer, 1 + i_ret + i_read );
- if( unlikely(p_buffer == NULL) )
- {
- vlc_stream_Delete( p_stream );
- return VLC_ENOMEM;
- }
-
- i_read = vlc_stream_Read( p_stream, &p_buffer[i_ret], i_read );
- if( i_read <= 0 )
- break;
-
- i_ret += i_read;
- }
- vlc_stream_Delete( p_stream );
- p_buffer[i_ret] = 0;
-
- if ( ParseJson( p_obj, p_buffer, & p_data->results ) )
- msg_Dbg( p_obj, "results count == %d", p_data->results.count );
+ if ( ParseJson( p_cfg->p_obj, p_buffer, & p_data->results ) )
+ msg_Dbg( p_cfg->p_obj, "results count == %d", p_data->results.count );
else
- msg_Dbg( p_obj, "No results" );
+ msg_Dbg( p_cfg->p_obj, "No results" );
free( p_buffer );
return VLC_SUCCESS;
=====================================
modules/misc/webservices/acoustid.h
=====================================
@@ -17,16 +17,17 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
-
+#define ACOUSTID_ANON_SERVER "fingerprint.videolan.org"
+#define ACOUSTID_ANON_SERVER_PATH "/acoustid.php"
#define MB_ID_SIZE 36
-struct musicbrainz_recording_t
+struct acoustid_mb_result_t
{
char *psz_artist;
char *psz_title;
char s_musicbrainz_id[MB_ID_SIZE];
};
-typedef struct musicbrainz_recording_t musicbrainz_recording_t;
+typedef struct acoustid_mb_result_t acoustid_mb_result_t;
struct acoustid_result_t
{
@@ -35,7 +36,7 @@ struct acoustid_result_t
struct
{
unsigned int count;
- musicbrainz_recording_t *p_recordings;
+ acoustid_mb_result_t *p_recordings;
} recordings;
};
typedef struct acoustid_result_t acoustid_result_t;
@@ -55,5 +56,12 @@ struct acoustid_fingerprint_t
};
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 );
+typedef struct
+{
+ vlc_object_t *p_obj;
+ char *psz_server;
+ char *psz_apikey;
+} acoustid_config_t;
+
+int acoustid_lookup_fingerprint( const acoustid_config_t *, acoustid_fingerprint_t * );
+void acoustid_result_release( acoustid_result_t * );
=====================================
modules/misc/webservices/json_helper.h
=====================================
@@ -0,0 +1,127 @@
+/*****************************************************************************
+ * json_helper.h:
+ *****************************************************************************
+ * Copyright (C) 2012-2019 VLC authors, VideoLabs 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_HELPER_H
+#define JSON_HELPER_H
+
+#include <vlc_common.h>
+#include <vlc_stream.h>
+
+#include <limits.h>
+
+#include "json.h"
+
+static inline
+const json_value * json_getbyname(const 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 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);
+ return (str) ? strdup(str) : NULL;
+}
+
+static inline
+json_value * json_parse_document(vlc_object_t *p_obj, const char *psz_buffer)
+{
+ 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;
+ }
+
+ 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)
+{
+ int i_saved_flags = p_obj->obj.flags;
+ p_obj->obj.flags |= OBJECT_FLAGS_NOINTERACT;
+ stream_t *p_stream = vlc_stream_NewURL(p_obj, psz_url);
+
+ p_obj->obj.flags = i_saved_flags;
+ if (p_stream == NULL)
+ return NULL;
+
+ stream_t *p_chain = vlc_stream_FilterNew(p_stream, "inflate");
+ if(p_chain)
+ p_stream = p_chain;
+
+ /* read answer */
+ char *p_buffer = NULL;
+ int i_ret = 0;
+ for(;;)
+ {
+ int i_read = 65536;
+
+ if(i_ret >= INT_MAX - i_read)
+ break;
+
+ char *p_realloc = realloc(p_buffer, 1 + i_ret + i_read);
+ if(unlikely(p_realloc == NULL))
+ {
+ free(p_buffer);
+ vlc_stream_Delete(p_stream);
+ return NULL;
+ }
+ p_buffer = p_realloc;
+
+ i_read = vlc_stream_Read(p_stream, &p_buffer[i_ret], i_read);
+ if(i_read <= 0)
+ break;
+
+ i_ret += i_read;
+ }
+ vlc_stream_Delete(p_stream);
+ p_buffer[i_ret] = 0;
+
+ return p_buffer;
+}
+
+#endif
=====================================
modules/misc/webservices/musicbrainz.c
=====================================
@@ -0,0 +1,349 @@
+/*****************************************************************************
+ * musicbrainz.c : Musicbrainz API lookup
+ *****************************************************************************
+ * Copyright (C) 2019 VideoLabs, 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 <string.h>
+#include <limits.h>
+
+#include "json_helper.h"
+#include "musicbrainz.h"
+
+typedef struct
+{
+ json_value *root;
+} musicbrainz_lookup_t;
+
+static void musicbrainz_lookup_release(musicbrainz_lookup_t *p)
+{
+ if(p && p->root)
+ json_value_free(p->root);
+ free(p);
+}
+
+static musicbrainz_lookup_t * musicbrainz_lookup_new(void)
+{
+ return calloc(1, sizeof(musicbrainz_lookup_t));
+}
+
+static musicbrainz_lookup_t * musicbrainz_lookup(vlc_object_t *p_obj, const char *psz_url)
+{
+ msg_Dbg(p_obj, "Querying MB for %s", psz_url);
+ void *p_buffer = json_retrieve_document(p_obj, psz_url);
+ if(!p_buffer)
+ return NULL;
+
+ musicbrainz_lookup_t *p_lookup = musicbrainz_lookup_new();
+ if(p_lookup)
+ {
+ p_lookup->root = json_parse_document(p_obj, p_buffer);
+ if (!p_lookup->root)
+ msg_Dbg(p_obj, "No results");
+ }
+ free(p_buffer);
+ return p_lookup;
+}
+
+static bool musicbrainz_fill_track(const json_value *tracknode, musicbrainz_track_t *t)
+{
+ t->psz_title = json_dupstring(tracknode, "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");
+
+ node = json_getbyname(tracknode, "position");
+ if (node && node->type == json_integer)
+ t->i_index = node->u.integer;
+
+ return true;
+}
+
+static bool musicbrainz_has_cover_in_releasegroup(json_value ** const p_nodes,
+ size_t i_nodes,
+ const char *psz_group_id)
+{
+ 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;
+
+ node = json_getbyname(node, "front");
+ if(!node || node->type != json_boolean || !node->u.boolean)
+ continue;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static char *musicbrainz_fill_artists(const json_value *arraynode)
+{
+ 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++)
+ {
+ const json_value *name = json_getbyname(arraynode->u.array.values[i], "name");
+ if(name->type != json_string)
+ continue;
+
+ if(psz == NULL)
+ {
+ psz = strdup(name->u.string.ptr);
+ i_total = name->u.string.length + 1;
+ }
+ else
+ {
+ char *p = realloc(psz, i_total + name->u.string.length + 2);
+ if(p)
+ {
+ psz = p;
+ psz = strcat(psz, ", ");
+ psz = strncat(psz, name->u.string.ptr, name->u.string.length);
+ i_total += name->u.string.length + 2;
+ }
+ }
+ }
+
+ return psz;
+}
+
+static bool musicbrainz_fill_release(const json_value *releasenode, musicbrainz_release_t *r)
+{
+ const json_value *media = json_getbyname(releasenode, "media");
+ if(!media || media->type != json_array ||
+ media->u.array.length == 0)
+ return false;
+ /* we always use first media */
+ media = media->u.array.values[0];
+
+ const json_value *tracks = json_getbyname(media, "tracks");
+ if(!tracks || tracks->type != json_array ||
+ tracks->u.array.length == 0)
+ return false;
+
+ r->p_tracks = calloc(tracks->u.array.length, sizeof(*r->p_tracks));
+ if(!r->p_tracks)
+ return false;
+
+ for(size_t i=0; i<tracks->u.array.length; i++)
+ {
+ if(musicbrainz_fill_track(tracks->u.array.values[i], &r->p_tracks[r->i_tracks]))
+ r->i_tracks++;
+ }
+
+ r->psz_title = json_dupstring(releasenode, "title");
+ r->psz_id = json_dupstring(releasenode, "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 json_value *node = json_getbyname(rgnode, "artist-credit");
+ if(node)
+ r->psz_artist = musicbrainz_fill_artists(node);
+ }
+ 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");
+ }
+
+
+ return true;
+}
+
+void musicbrainz_recording_release(musicbrainz_recording_t *mbr)
+{
+ for(size_t i=0; i<mbr->i_release; i++)
+ {
+ free(mbr->p_releases[i].psz_id);
+ free(mbr->p_releases[i].psz_group_id);
+ free(mbr->p_releases[i].psz_artist);
+ free(mbr->p_releases[i].psz_title);
+ free(mbr->p_releases[i].psz_date);
+ free(mbr->p_releases[i].psz_coverart_url);
+ for(size_t j=0; j<mbr->p_releases[i].i_tracks; j++)
+ {
+ free(mbr->p_releases[i].p_tracks[j].psz_title);
+ free(mbr->p_releases[i].p_tracks[j].psz_artist);
+ }
+ free(mbr->p_releases[i].p_tracks);
+ }
+ free(mbr->p_releases);
+ free(mbr);
+}
+
+static musicbrainz_recording_t *musicbrainz_lookup_recording_by_apiurl(vlc_object_t *obj,
+ const char *psz_url)
+{
+ musicbrainz_recording_t *r = calloc(1, sizeof(*r));
+ if(!r)
+ return NULL;
+
+ musicbrainz_lookup_t *lookup = musicbrainz_lookup(obj, psz_url);
+ if(!lookup)
+ {
+ free(r);
+ return NULL;
+ }
+
+ const json_value *releases = json_getbyname(lookup->root, "releases");
+ if (releases && releases->type == json_array &&
+ releases->u.array.length)
+ {
+ r->p_releases = calloc(releases->u.array.length, sizeof(*r->p_releases));
+ if(r->p_releases)
+ {
+ for(unsigned i=0; i<releases->u.array.length; 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))
+ 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,
+ p_mbrel->psz_group_id))
+ {
+ char *psz_art = coverartarchive_make_releasegroup_arturl(
+ COVERARTARCHIVE_DEFAULT_SERVER,
+ p_mbrel->psz_group_id );
+ if(psz_art)
+ p_mbrel->psz_coverart_url = psz_art;
+ }
+
+ r->i_release++;
+ }
+ }
+ }
+
+ musicbrainz_lookup_release(lookup);
+
+ return r;
+}
+
+static char *musicbrainz_build_discid_json_url(const char *psz_server,
+ const char *psz_disc_id,
+ const char *psz_tail)
+{
+ char *psz_url;
+ if(asprintf(&psz_url,
+ "https://%s/ws/2/discid/%s?"
+ "fmt=json"
+ "&inc=artist-credits+recordings+release-groups"
+ "&cdstubs=no"
+ "%s%s",
+ psz_server ? psz_server : MUSICBRAINZ_DEFAULT_SERVER,
+ psz_disc_id,
+ psz_tail ? "&" : "",
+ psz_tail ? psz_tail : "" ) > -1 )
+ {
+ return psz_url;
+ }
+ return NULL;
+}
+
+musicbrainz_recording_t *musicbrainz_lookup_recording_by_toc(musicbrainz_config_t *cfg,
+ const char *psz_toc)
+{
+ char *psz_url = musicbrainz_build_discid_json_url(cfg->psz_mb_server, "-", psz_toc);
+ if(!psz_url)
+ return NULL;
+
+ musicbrainz_recording_t *r = musicbrainz_lookup_recording_by_apiurl(cfg->obj, psz_url);
+ free(psz_url);
+ return r;
+}
+
+musicbrainz_recording_t *musicbrainz_lookup_recording_by_discid(musicbrainz_config_t *cfg,
+ const char *psz_disc_id)
+{
+ char *psz_url = musicbrainz_build_discid_json_url(cfg->psz_mb_server, psz_disc_id, NULL);
+ if(!psz_url)
+ return NULL;
+
+ musicbrainz_recording_t *r = musicbrainz_lookup_recording_by_apiurl(cfg->obj, psz_url);
+ free(psz_url);
+ return r;
+}
+
+char * coverartarchive_make_releasegroup_arturl(const char *psz_server, const char *psz_group_id)
+{
+ char *psz_art;
+ if(-1 < asprintf(&psz_art, "https://%s/release-group/%s/front",
+ psz_server ? psz_server : COVERARTARCHIVE_DEFAULT_SERVER,
+ psz_group_id))
+ return psz_art;
+ return NULL;
+}
+
+void musicbrainz_release_covert_art(coverartarchive_t *c)
+{
+ free(c);
+}
+
+coverartarchive_t * coverartarchive_lookup_releasegroup(musicbrainz_config_t *cfg, const char *psz_id)
+{
+ coverartarchive_t *c = calloc(1, sizeof(*c));
+ if(!c)
+ return NULL;
+
+ char *psz_url;
+ if(0 < asprintf(&psz_url, "https://%s/releasegroup/%s", cfg->psz_coverart_server, psz_id ))
+ {
+ return NULL;
+ }
+
+ musicbrainz_lookup_t *p_lookup = musicbrainz_lookup(cfg->obj, psz_url);
+ free(psz_url);
+
+ if(!p_lookup)
+ {
+ free(c);
+ return NULL;
+ }
+
+ return c;
+}
=====================================
modules/misc/webservices/musicbrainz.h
=====================================
@@ -0,0 +1,73 @@
+/*****************************************************************************
+ * musicbrainz.h : Musicbrainz API lookup
+ *****************************************************************************
+ * Copyright (C) 2019 VideoLabs, 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 VLC_MUSICBRAINZ_H_
+#define VLC_MUSICBRAINZ_H_
+
+#define MUSICBRAINZ_DEFAULT_SERVER "musicbrainz.org"
+#define COVERARTARCHIVE_DEFAULT_SERVER "coverartarchive.org"
+
+typedef struct
+{
+ unsigned i_index;
+ char *psz_title;
+ char *psz_artist;
+} musicbrainz_track_t;
+
+typedef struct
+{
+ char *psz_id;
+ char *psz_group_id;
+ char *psz_title;
+ char *psz_artist;
+ /* https://github.com/metabrainz/mmd-schema/blob/master/schema/musicbrainz_mmd-2.0.rng
+ "def_incomplete-date" [0-9]{4}(-[0-9]{2})?(-[0-9]{2})? */
+ char *psz_date;
+ char *psz_coverart_url;
+ size_t i_tracks;
+ musicbrainz_track_t *p_tracks;
+} musicbrainz_release_t;
+
+typedef struct
+{
+ size_t i_release;
+ musicbrainz_release_t *p_releases;
+} musicbrainz_recording_t;
+
+typedef struct
+{
+ vlc_object_t *obj;
+ char *psz_mb_server;
+ char *psz_coverart_server;
+} musicbrainz_config_t;
+
+void musicbrainz_recording_release(musicbrainz_recording_t *);
+musicbrainz_recording_t * musicbrainz_lookup_recording_by_toc(musicbrainz_config_t *, const char *);
+musicbrainz_recording_t * musicbrainz_lookup_recording_by_discid(musicbrainz_config_t *, const char *);
+
+typedef struct
+{
+ char *psz_url;
+} coverartarchive_t;
+
+void musicbrainz_release_covert_art(coverartarchive_t *c);
+coverartarchive_t * coverartarchive_lookup_releasegroup(musicbrainz_config_t *, const char *);
+char * coverartarchive_make_releasegroup_arturl(const char *, const char *);
+
+#endif
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/e496d3776da5f5b84e09996c31f2089d1299a8a3...4c49c64e813be16b9b98e2e96717fab1f7912ee2
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/e496d3776da5f5b84e09996c31f2089d1299a8a3...4c49c64e813be16b9b98e2e96717fab1f7912ee2
You're receiving this email because of your account on code.videolan.org.
More information about the vlc-commits
mailing list