Commit 98a6a934 authored by Rafaël Carré's avatar Rafaël Carré

taglib: support for id3v2 embedded album art

the only demuxer supported is mpga, other demuxers should be updated if they demux files that can have id3v2 tags
fix some bugs in flac & ogg demuxers
parent f607a368
......@@ -281,6 +281,7 @@ typedef struct access_sys_t access_sys_t;
typedef struct stream_t stream_t;
typedef struct stream_sys_t stream_sys_t;
typedef struct demux_t demux_t;
typedef struct demux_meta_t demux_meta_t;
typedef struct demux_sys_t demux_sys_t;
typedef struct es_out_t es_out_t;
typedef struct es_out_id_t es_out_id_t;
......
......@@ -72,6 +72,16 @@ struct demux_t
demux_sys_t *p_sys;
};
/* demux_meta_t is returned by "meta reader" module to the demuxer */
struct demux_meta_t
{
vlc_meta_t *p_meta; /**< meta data */
int i_attachments; /**< number of attachments */
input_attachment_t **attachments; /**< array of attachments */
};
enum demux_query_e
{
/* I. Common queries to access_demux and demux */
......
......@@ -259,13 +259,25 @@ int E_(OpenDemux)( vlc_object_t *p_this )
input_thread_t *p_input = (input_thread_t *)vlc_object_find( p_demux, VLC_OBJECT_INPUT, FIND_PARENT );
if( p_input )
{
p_demux->p_private = malloc( sizeof( demux_meta_t ) );
if( !p_demux->p_private )
{
vlc_object_release( p_input );
return VLC_ENOMEM;
}
module_t *p_meta = module_Need( p_demux, "meta reader", NULL, 0 );
if( p_meta )
{
vlc_meta_Merge( input_GetItem(p_input)->p_meta, (vlc_meta_t*)(p_demux->p_private ) );
demux_meta_t *p_demux_meta = (demux_meta_t *)p_demux->p_private;
vlc_meta_Merge( input_GetItem(p_input)->p_meta,
p_demux_meta->p_meta );
vlc_meta_Delete( p_demux_meta->p_meta );
module_Unneed( p_demux, p_meta );
TAB_CLEAN( p_demux_meta->i_attachments,
p_demux_meta->attachments );
}
vlc_object_release( p_input );
free( p_demux->p_private );
}
return VLC_SUCCESS;
......
......@@ -78,8 +78,8 @@ struct demux_sys_t
seekpoint_t **seekpoint;
/* */
int i_attachment;
input_attachment_t **attachment;
int i_attachments;
input_attachment_t **attachments;
int i_cover_idx;
int i_cover_score;
};
......@@ -123,7 +123,7 @@ static int Open( vlc_object_t * p_this )
p_sys->i_pts_start = 0;
p_sys->p_es = NULL;
TAB_INIT( p_sys->i_seekpoint, p_sys->seekpoint );
TAB_INIT( p_sys->i_attachment, p_sys->attachment );
TAB_INIT( p_sys->i_attachments, p_sys->attachments);
p_sys->i_cover_idx = 0;
p_sys->i_cover_score = 0;
......@@ -155,9 +155,13 @@ static int Open( vlc_object_t * p_this )
}
/* Parse possible id3 header */
p_demux->p_private = malloc( sizeof( demux_meta_t ) );
if( !p_demux->p_private )
return VLC_ENOMEM;
if( ( p_id3 = module_Need( p_demux, "meta reader", NULL, 0 ) ) )
{
vlc_meta_t *p_meta = (vlc_meta_t *)p_demux->p_private;
demux_meta_t *p_demux_meta = (demux_meta_t *)p_demux->p_private;
vlc_meta_t *p_meta = p_demux_meta->p_meta;
if( !p_sys->p_meta )
{
......@@ -170,15 +174,23 @@ static int Open( vlc_object_t * p_this )
}
p_demux->p_private = NULL;
module_Unneed( p_demux, p_id3 );
int i;
for( i = 0; i < p_demux_meta->i_attachments; i++ )
TAB_APPEND_CAST( (input_attachment_t**),
p_sys->i_attachments, p_sys->attachments,
p_demux_meta->attachments[p_demux_meta->i_attachments] );
TAB_CLEAN( p_demux_meta->i_attachments, p_demux_meta->attachments );
}
free( p_demux->p_private );
if( p_sys->i_cover_idx < p_sys->i_attachment )
if( p_sys->i_cover_idx < p_sys->i_attachments )
{
char psz_url[128];
if( !p_sys->p_meta )
p_sys->p_meta = vlc_meta_New();
snprintf( psz_url, sizeof(psz_url), "attachment://%s",
p_sys->attachment[p_sys->i_cover_idx]->psz_name );
p_sys->attachments[p_sys->i_cover_idx]->psz_name );
vlc_meta_Set( p_sys->p_meta, vlc_meta_ArtworkURL, psz_url );
}
vlc_audio_replay_gain_MergeFromMeta( &p_sys->replay_gain, p_sys->p_meta );
......@@ -193,6 +205,9 @@ static void Close( vlc_object_t * p_this )
demux_t *p_demux = (demux_t*)p_this;
demux_sys_t *p_sys = p_demux->p_sys;
TAB_CLEAN( p_sys->i_seekpoint, p_sys->seekpoint );
TAB_CLEAN( p_sys->i_attachments, p_sys->attachments);
/* Unneed module */
module_Unneed( p_sys->p_packetizer, p_sys->p_packetizer->p_module );
......@@ -394,13 +409,13 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
int *pi_int = (int*)va_arg( args, int * );
int i;
if( p_sys->i_attachment <= 0 )
if( p_sys->i_attachments <= 0 )
return VLC_EGENERIC;
*pi_int = p_sys->i_attachment;;
*ppp_attach = malloc( sizeof(input_attachment_t**) * p_sys->i_attachment );
for( i = 0; i < p_sys->i_attachment; i++ )
*(ppp_attach)[i] = vlc_input_attachment_Duplicate( p_sys->attachment[i] );
*pi_int = p_sys->i_attachments;;
*ppp_attach = malloc( sizeof(input_attachment_t**) * p_sys->i_attachments );
for( i = 0; i < p_sys->i_attachments; i++ )
(*ppp_attach)[i] = vlc_input_attachment_Duplicate( p_sys->attachments[i] );
return VLC_SUCCESS;
}
......@@ -473,8 +488,6 @@ static int ReadMeta( demux_t *p_demux, uint8_t **pp_streaminfo, int *pi_streami
s->i_byte_offset = 0;
TAB_APPEND( p_sys->i_seekpoint, p_sys->seekpoint, s );
b_last = (*pp_streaminfo)[4]&0x80;
while( !b_last )
{
......@@ -699,7 +712,7 @@ static void ParsePicture( demux_t *p_demux, const uint8_t *p_data, int i_data )
msg_Dbg( p_demux, "FLAC: Picture type=%d mime=%s description='%s' file length=%d",
i_type, psz_mime, psz_description, i_len );
snprintf( psz_name, sizeof(psz_name), "picture%d", p_sys->i_attachment );
snprintf( psz_name, sizeof(psz_name), "picture%d", p_sys->i_attachments );
if( !strcasecmp( psz_mime, "image/jpeg" ) )
strcat( psz_name, ".jpg" );
else if( !strcasecmp( psz_mime, "image/png" ) )
......@@ -707,12 +720,12 @@ static void ParsePicture( demux_t *p_demux, const uint8_t *p_data, int i_data )
p_attachment = vlc_input_attachment_New( psz_name, psz_mime, psz_description,
p_data, i_data );
TAB_APPEND( p_sys->i_attachment, p_sys->attachment, p_attachment );
TAB_APPEND( p_sys->i_attachments, p_sys->attachments, p_attachment );
if( i_type >= 0 && i_type < sizeof(pi_cover_score)/sizeof(pi_cover_score[0]) &&
p_sys->i_cover_score < pi_cover_score[i_type] )
{
p_sys->i_cover_idx = p_sys->i_attachment-1;
p_sys->i_cover_idx = p_sys->i_attachments-1;
p_sys->i_cover_score = pi_cover_score[i_type];
}
error:
......
......@@ -189,12 +189,17 @@ static int Open( vlc_object_t * p_this )
}
/* Parse possible id3 header */
p_demux->p_private = malloc( sizeof( demux_meta_t ) );
if( !p_demux->p_private )
return VLC_ENOMEM;
if( ( p_id3 = module_Need( p_demux, "meta reader", NULL, 0 ) ) )
{
p_sys->p_meta = (vlc_meta_t *)p_demux->p_private;
p_demux->p_private = NULL;
demux_meta_t *p_demux_meta = (demux_meta_t *)p_demux->p_private;
p_sys->p_meta = p_demux_meta->p_meta;
module_Unneed( p_demux, p_id3 );
TAB_CLEAN( p_demux_meta->i_attachments, p_demux_meta->attachments );
}
free( p_demux->p_private );
if( !p_sys->p_meta )
p_sys->p_meta = vlc_meta_New();
......
......@@ -115,12 +115,18 @@ static int Open( vlc_object_t * p_this )
LOAD_PACKETIZER_OR_FAIL( p_sys->p_packetizer, "mp4 audio" );
/* Parse possible id3 header */
p_demux->p_private = malloc( sizeof( demux_meta_t ) );
if( !p_demux->p_private )
return VLC_ENOMEM;
if( ( p_id3 = module_Need( p_demux, "meta reader", NULL, 0 ) ) )
{
p_sys->meta = (vlc_meta_t *)p_demux->p_private;
demux_meta_t *p_demux_meta = (demux_meta_t *)p_demux->p_private;
p_sys->meta = p_demux_meta->p_meta;
p_demux->p_private = NULL;
module_Unneed( p_demux, p_id3 );
TAB_CLEAN( p_demux_meta->i_attachments, p_demux_meta->attachments );
}
free( p_demux->p_private );
return VLC_SUCCESS;
}
......
......@@ -74,6 +74,9 @@ struct demux_sys_t
int i_xing_bitrate_avg;
int i_xing_frame_samples;
block_t *p_block_in, *p_block_out;
int i_attachments;
input_attachment_t **attachments;
};
static int HeaderCheck( uint32_t h )
......@@ -252,12 +255,19 @@ static int Open( vlc_object_t * p_this )
p_sys->p_block_out = p_block_out;
/* Parse possible id3 header */
p_demux->p_private = malloc( sizeof( demux_meta_t ) );
if( !p_demux->p_private )
return VLC_ENOMEM;
if( ( p_id3 = module_Need( p_demux, "meta reader", NULL, 0 ) ) )
{
p_sys->meta = (vlc_meta_t *)p_demux->p_private;
demux_meta_t *p_demux_meta = (demux_meta_t *)p_demux->p_private;
p_sys->meta = p_demux_meta->p_meta;
p_demux->p_private = NULL;
module_Unneed( p_demux, p_id3 );
p_sys->i_attachments = p_demux_meta->i_attachments;
p_sys->attachments = p_demux_meta->attachments;
}
free( p_demux->p_private );
/* */
p_sys->p_packetizer->fmt_out.b_packetized = VLC_TRUE;
......@@ -337,6 +347,8 @@ static void Close( vlc_object_t * p_this )
if( p_sys->meta ) vlc_meta_Delete( p_sys->meta );
if( p_sys->p_block_out ) block_Release( p_sys->p_block_out );
TAB_CLEAN( p_sys->i_attachments, p_sys->attachments);
free( p_sys );
}
......@@ -350,6 +362,9 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
vlc_meta_t *p_meta;
int i_ret;
input_attachment_t ***ppp_attach;
int *pi_int, i;
switch( i_query )
{
case DEMUX_GET_META:
......@@ -357,6 +372,20 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
vlc_meta_Merge( p_meta, p_sys->meta );
return VLC_SUCCESS;
case DEMUX_GET_ATTACHMENTS:
ppp_attach =
(input_attachment_t***)va_arg( args, input_attachment_t*** );
pi_int = (int*)va_arg( args, int * );
if( p_sys->i_attachments <= 0 )
return VLC_EGENERIC;
*pi_int = p_sys->i_attachments;
*ppp_attach = malloc( sizeof(input_attachment_t**) * p_sys->i_attachments );
for( i = 0; i < p_sys->i_attachments; i++ )
(*ppp_attach)[i] = vlc_input_attachment_Duplicate( p_sys->attachments[i] );
return VLC_SUCCESS;
case DEMUX_GET_TIME:
pi64 = (int64_t*)va_arg( args, int64_t * );
*pi64 = p_sys->i_pts + p_sys->i_time_offset;
......
......@@ -207,17 +207,25 @@ static int Open( vlc_object_t * p_this )
p_input = (input_thread_t *)vlc_object_find( p_demux, VLC_OBJECT_INPUT, FIND_PARENT );
if( p_input )
{
p_demux->p_private = malloc( sizeof( demux_meta_t ) );
if( !p_demux->p_private )
{
vlc_object_release( p_input );
return VLC_ENOMEM;
}
module_t *p_meta = module_Need( p_demux, "meta reader", NULL, 0 );
if( p_meta )
{
vlc_meta_Merge( input_GetItem(p_input)->p_meta, (vlc_meta_t*)(p_demux->p_private ) );
demux_meta_t *p_demux_meta = (demux_meta_t *)p_demux->p_private;
vlc_meta_Merge( input_GetItem(p_input)->p_meta,
p_demux_meta->p_meta );
vlc_meta_Delete( p_demux_meta->p_meta );
module_Unneed( p_demux, p_meta );
TAB_CLEAN( p_demux_meta->i_attachments, p_demux_meta->attachments );
}
vlc_object_release( p_input );
return VLC_SUCCESS;
free( p_demux->p_private );
}
if( p_input )
vlc_object_release( p_input );
/* Initialize the Ogg physical bitstream parser */
ogg_sync_init( &p_sys->oy );
......
......@@ -105,7 +105,7 @@ static int Open( vlc_object_t * p_this )
p_demux->pf_demux = Demux;
p_demux->pf_control = Control;
p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
/* Read the metadata */
es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'T', 'T', 'A', '1' ) );
fmt.audio.i_channels = GetWLE( &p_header[6] );
......@@ -138,15 +138,21 @@ static int Open( vlc_object_t * p_this )
p_sys->p_es = es_out_Add( p_demux->out, &fmt );
free( p_seektable );
p_sys->i_start = stream_Tell( p_demux->s );
#if 0
/* Parse possible id3 header */
p_demux->p_private = malloc( sizeof( demux_meta_t ) );
if( !p_demux->p_private )
return VLC_ENOMEM;
if( ( p_id3 = module_Need( p_demux, "meta reader", NULL, 0 ) ) )
{
p_sys->p_meta = (vlc_meta_t *)p_demux->p_private;
demux_meta_t *p_demux_meta = (demux_meta_t *)p_demux->p_private;
p_sys->p_meta = p_demux_meta->p_meta;
p_demux->p_private = NULL;
module_Unneed( p_demux, p_id3 );
TAB_CLEAN( p_demux_meta->i_attachments, p_demux_meta->attachments );
}
free( p_demux->p_private );
if( !p_sys->p_meta )
p_sys->p_meta = vlc_meta_New();
......
......@@ -61,15 +61,18 @@ static void ParseID3Tag( demux_t *p_demux, uint8_t *p_data, int i_size )
{
struct id3_tag *p_id3_tag;
struct id3_frame *p_frame;
vlc_meta_t *p_meta = (vlc_meta_t *)p_demux->p_private;
demux_meta_t *p_demux_meta = p_demux->p_private;
vlc_meta_t *p_meta;
int i;
TAB_INIT( p_demux_meta->i_attachments, p_demux_meta->attachments );
p_demux_meta->p_meta = NULL;
p_id3_tag = id3_tag_parse( p_data, i_size );
if( !p_id3_tag )
return;
if( !p_meta )
p_demux->p_private = p_meta = vlc_meta_New();
p_demux_meta->p_meta = p_meta = vlc_meta_New();
#define ID_IS( a ) (!strcmp( p_frame->id, a ))
#define DESCR_IS( a) strstr( (char*)p_frame->description, a )
......
......@@ -35,6 +35,7 @@
#include <tbytevector.h>
#include <mpegfile.h>
#include <flacfile.h>
#include <attachedpictureframe.h>
#if 0
#include <oggflacfile.h>
#endif
......@@ -63,27 +64,114 @@ vlc_module_begin();
set_callbacks( WriteMeta, NULL );
vlc_module_end();
static bool checkID3Image( const TagLib::ID3v2::Tag *p_tag )
{
TagLib::ID3v2::FrameList l = p_tag->frameListMap()[ "APIC" ];
return !l.isEmpty();
}
/* Try detecting embedded art */
static void DetectImage( TagLib::FileRef f, vlc_meta_t *p_meta )
static void DetectImage( TagLib::FileRef f, demux_t *p_demux )
{
demux_meta_t *p_demux_meta = (demux_meta_t *)p_demux->p_private;
vlc_meta_t *p_meta = p_demux_meta->p_meta;
TagLib::ID3v2::Tag *p_tag;
int i_score = -1;
/* Preferred type of image
* The 21 types are defined in id3v2 standard:
* http://www.id3.org/id3v2.4.0-frames */
static const int pi_cover_score[] = {
0, /* Other */
5, /* 32x32 PNG image that should be used as the file icon */
4, /* File icon of a different size or format. */
20, /* Front cover image of the album. */
19, /* Back cover image of the album. */
13, /* Inside leaflet page of the album. */
18, /* Image from the album itself. */
17, /* Picture of the lead artist or soloist. */
16, /* Picture of the artist or performer. */
14, /* Picture of the conductor. */
15, /* Picture of the band or orchestra. */
9, /* Picture of the composer. */
8, /* Picture of the lyricist or text writer. */
7, /* Picture of the recording location or studio. */
10, /* Picture of the artists during recording. */
11, /* Picture of the artists during performance. */
6, /* Picture from a movie or video related to the track. */
1, /* Picture of a large, coloured fish. */
12, /* Illustration related to the track. */
3, /* Logo of the band or performer. */
2 /* Logo of the publisher (record company). */
};
if( TagLib::MPEG::File *mpeg =
dynamic_cast<TagLib::MPEG::File *>(f.file() ) )
{
if( mpeg->ID3v2Tag() && checkID3Image( mpeg->ID3v2Tag() ) )
vlc_meta_SetArtURL( p_meta, "APIC" );
p_tag = mpeg->ID3v2Tag();
if( !p_tag )
return;
TagLib::ID3v2::FrameList list = p_tag->frameListMap()[ "APIC" ];
if( list.isEmpty() )
return;
input_thread_t *p_input = (input_thread_t *)
vlc_object_find( p_demux,VLC_OBJECT_INPUT, FIND_PARENT );
if( !p_input )
return;
input_item_t *p_item = input_GetItem( p_input );
TagLib::ID3v2::AttachedPictureFrame *p_apic;
TAB_INIT( p_demux_meta->i_attachments, p_demux_meta->attachments );
for( TagLib::ID3v2::FrameList::Iterator iter = list.begin();
iter != list.end(); iter++ )
{
p_apic = dynamic_cast<TagLib::ID3v2::AttachedPictureFrame*>(*iter);
input_attachment_t *p_attachment;
const char *psz_name, *psz_mime, *psz_description;
TagLib::ByteVector p_data_taglib; const char *p_data; int i_data;
psz_mime = p_apic->mimeType().toCString(true);
psz_description = p_apic->description().toCString(true);
psz_name = psz_description;
p_data_taglib = p_apic->picture();
p_data = p_data_taglib.data();
i_data = p_data_taglib.size();
msg_Dbg( p_demux, "Found embedded art: %s (%s) is %i bytes",
psz_name, psz_mime, i_data );
p_attachment = vlc_input_attachment_New( psz_name, psz_mime,
psz_description, p_data, i_data );
TAB_APPEND_CAST( (input_attachment_t**),
p_demux_meta->i_attachments, p_demux_meta->attachments,
p_attachment );
if( pi_cover_score[p_apic->type()] > i_score )
{
i_score = pi_cover_score[p_apic->type()];
char *psz_url;
if( asprintf( &psz_url, "attachment://%s",
p_attachment->psz_name ) == -1 )
{
vlc_object_release( p_input );
return;
}
vlc_meta_SetArtURL( p_meta, psz_url );
free( psz_url );
}
}
vlc_object_release( p_input );
}
#if 0
//flac embedded images are extracted in the flac demuxer
else if( TagLib::FLAC::File *flac =
dynamic_cast<TagLib::FLAC::File *>(f.file() ) )
{
if( flac->ID3v2Tag() && checkID3Image( flac->ID3v2Tag() ) )
p_tag = flac->ID3v2Tag();
if( p_tag )
return;
TagLib::ID3v2::FrameList l = p_tag->frameListMap()[ "APIC" ];
if( l.isEmpty() )
return;
vlc_meta_SetArtURL( p_meta, "APIC" );
}
#endif
#if 0
/* This needs special additions to taglib */
* else if( TagLib::MP4::File *mp4 =
......@@ -99,10 +187,12 @@ static void DetectImage( TagLib::FileRef f, vlc_meta_t *p_meta )
static int ReadMeta( vlc_object_t *p_this )
{
demux_t *p_demux = (demux_t *)p_this;
demux_t *p_demux = (demux_t *)p_this;
demux_meta_t *p_demux_meta = (demux_meta_t*)p_demux->p_private;
vlc_meta_t *p_meta = p_demux_meta->p_meta;
if( strncmp( p_demux->psz_access, "file", 4 ) )
return VLC_EGENERIC;
TAB_INIT( p_demux_meta->i_attachments, p_demux_meta->attachments );
p_demux_meta->p_meta = NULL;
TagLib::FileRef f( p_demux->psz_path );
if( f.isNull() )
......@@ -111,9 +201,7 @@ static int ReadMeta( vlc_object_t *p_this )
if ( !f.tag() || f.tag()->isEmpty() )
return VLC_EGENERIC;
if( !p_demux->p_private )
p_demux->p_private = (void*)vlc_meta_New();
vlc_meta_t *p_meta = (vlc_meta_t *)(p_demux->p_private );
p_demux_meta->p_meta = p_meta = vlc_meta_New();
TagLib::Tag *p_tag = f.tag();
if( TagLib::MPEG::File *p_mpeg =
......@@ -203,7 +291,7 @@ vlc_meta_Set##bar( p_meta, p_t->toString().toCString(true))
(mtime_t) i_ogg_v_length * 1000000 );
vlc_object_release( p_input );
}
}
#if 0 /* at this moment, taglib is unable to detect ogg/flac files
* becauses type detection is based on file extension:
......@@ -260,7 +348,7 @@ vlc_meta_Set##bar( p_meta, p_t->toString().toCString(true))
#undef SET
#undef SETINT
DetectImage( f, p_meta );
DetectImage( f, p_demux );
return VLC_SUCCESS;
}
......@@ -270,7 +358,7 @@ static int WriteMeta( vlc_object_t *p_this )
playlist_t *p_playlist = (playlist_t *)p_this;
meta_export_t *p_export = (meta_export_t *)p_playlist->p_private;
input_item_t *p_item = p_export->p_item;
if( p_item == NULL )
{
msg_Err( p_this, "Can't save meta data of an empty input" );
......@@ -348,7 +436,7 @@ static int WriteMeta( vlc_object_t *p_this )
WRITE( Copyright, "TCOP" );
WRITE( EncodedBy, "TENC" );
WRITE( Language, "TLAN" );
#undef WRITE
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment