]> git.sesse.net Git - vlc/blob - modules/demux/flac.c
Added replay gain support for:
[vlc] / modules / demux / flac.c
1 /*****************************************************************************
2  * flac.c : FLAC demux module for vlc
3  *****************************************************************************
4  * Copyright (C) 2001-2003 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
8  *          Laurent Aimar <fenrir@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <vlc/vlc.h>
29 #include <vlc_demux.h>
30 #include <vlc_meta.h>
31 #include <vlc_input.h>
32 #include <vlc_codec.h>
33 #include <assert.h>
34
35 /*****************************************************************************
36  * Module descriptor
37  *****************************************************************************/
38 static int  Open  ( vlc_object_t * );
39 static void Close ( vlc_object_t * );
40
41 vlc_module_begin();
42     set_description( _("FLAC demuxer") );
43     set_capability( "demux2", 155 );
44     set_category( CAT_INPUT );
45     set_subcategory( SUBCAT_INPUT_DEMUX );
46     set_callbacks( Open, Close );
47     add_shortcut( "flac" );
48 vlc_module_end();
49
50 /*****************************************************************************
51  * Local prototypes
52  *****************************************************************************/
53 static int Demux  ( demux_t * );
54 static int Control( demux_t *, int, va_list );
55
56 static int  ReadMeta( demux_t *, uint8_t **pp_streaminfo, int *pi_streaminfo );
57
58 struct demux_sys_t
59 {
60     vlc_bool_t  b_start;
61     es_out_id_t *p_es;
62
63     /* Packetizer */
64     decoder_t *p_packetizer;
65     vlc_meta_t *p_meta;
66     audio_replay_gain_t replay_gain;
67
68     int64_t i_time_offset;
69     int64_t i_pts;
70     int64_t i_pts_start;
71
72     int64_t i_length; /* Length from stream info */
73     int64_t i_data_pos;
74
75     /* */
76     int         i_seekpoint;
77     seekpoint_t **seekpoint;
78
79     /* */
80     int                i_attachment;
81     input_attachment_t **attachment;
82     int                i_cover_idx;
83     int                i_cover_score;
84 };
85
86 #define STREAMINFO_SIZE 38
87 #define FLAC_PACKET_SIZE 16384
88
89 /*****************************************************************************
90  * Open: initializes ES structures
91  *****************************************************************************/
92 static int Open( vlc_object_t * p_this )
93 {
94     demux_t     *p_demux = (demux_t*)p_this;
95     module_t    *p_id3;
96     demux_sys_t *p_sys;
97     byte_t      *p_peek;
98     uint8_t     *p_streaminfo;
99     int         i_streaminfo;
100
101     /* Have a peep at the show. */
102     if( stream_Peek( p_demux->s, &p_peek, 4 ) < 4 ) return VLC_EGENERIC;
103
104     if( p_peek[0]!='f' || p_peek[1]!='L' || p_peek[2]!='a' || p_peek[3]!='C' )
105     {
106         if( !p_demux->b_force ) return VLC_EGENERIC;
107
108         /* User forced */
109         msg_Err( p_demux, "this doesn't look like a flac stream, "
110                  "continuing anyway" );
111     }
112
113     p_demux->pf_demux   = Demux;
114     p_demux->pf_control = Control;
115     p_demux->p_sys      = p_sys = malloc( sizeof( demux_sys_t ) );
116     p_sys->b_start = VLC_TRUE;
117     p_sys->p_meta = NULL;
118     memset( &p_sys->replay_gain, 0, sizeof(p_sys->replay_gain) );
119     p_sys->i_length = 0;
120     p_sys->i_time_offset = 0;
121     p_sys->i_pts = 0;
122     p_sys->i_pts_start = 0;
123     p_sys->p_es = NULL;
124     TAB_INIT( p_sys->i_seekpoint, p_sys->seekpoint );
125     TAB_INIT( p_sys->i_attachment, p_sys->attachment );
126     p_sys->i_cover_idx = 0;
127     p_sys->i_cover_score = 0;
128
129     /* We need to read and store the STREAMINFO metadata */
130     if( ReadMeta( p_demux, &p_streaminfo, &i_streaminfo ) )
131     {
132         free( p_sys );
133         return VLC_EGENERIC;
134     }
135
136     /* Load the FLAC packetizer */
137     INIT_APACKETIZER( p_sys->p_packetizer, 'f', 'l', 'a', 'c' );
138
139     /* Store STREAMINFO for the decoder and packetizer */
140     p_streaminfo[4] |= 0x80; /* Fake this as the last metadata block */
141     p_sys->p_packetizer->fmt_in.i_extra = i_streaminfo;
142     p_sys->p_packetizer->fmt_in.p_extra = p_streaminfo;
143
144     p_sys->p_packetizer->p_module =
145         module_Need( p_sys->p_packetizer, "packetizer", NULL, 0 );
146     if( !p_sys->p_packetizer->p_module )
147     {
148         if( p_sys->p_packetizer->fmt_in.p_extra )
149             free( p_sys->p_packetizer->fmt_in.p_extra );
150         vlc_object_destroy( p_sys->p_packetizer );
151
152         msg_Err( p_demux, "cannot find flac packetizer" );
153         return VLC_EGENERIC;
154     }
155
156     /* Parse possible id3 header */
157     if( ( p_id3 = module_Need( p_demux, "meta reader", NULL, 0 ) ) )
158     {
159         vlc_meta_t *p_meta = (vlc_meta_t *)p_demux->p_private;
160
161         if( !p_sys->p_meta )
162         {
163             p_sys->p_meta = p_meta;
164         }
165         else if( p_meta )
166         {
167             vlc_meta_Merge( p_sys->p_meta, p_meta );
168             vlc_meta_Delete( p_meta );
169         }
170         p_demux->p_private = NULL;
171         module_Unneed( p_demux, p_id3 );
172     }
173
174     if( p_sys->i_cover_idx < p_sys->i_attachment )
175     {
176         char psz_url[128];
177         if( !p_sys->p_meta )
178             p_sys->p_meta = vlc_meta_New();
179         snprintf( psz_url, sizeof(psz_url), "attachment://%s",
180                   p_sys->attachment[p_sys->i_cover_idx]->psz_name );
181         vlc_meta_SetArtURL( p_sys->p_meta, psz_url );
182     }
183     vlc_audio_replay_gain_MergeFromMeta( &p_sys->replay_gain, p_sys->p_meta );
184     return VLC_SUCCESS;
185 }
186
187 /*****************************************************************************
188  * Close: frees unused data
189  *****************************************************************************/
190 static void Close( vlc_object_t * p_this )
191 {
192     demux_t     *p_demux = (demux_t*)p_this;
193     demux_sys_t *p_sys = p_demux->p_sys;
194
195     /* Unneed module */
196     module_Unneed( p_sys->p_packetizer, p_sys->p_packetizer->p_module );
197
198     if( p_sys->p_packetizer->fmt_in.p_extra )
199         free( p_sys->p_packetizer->fmt_in.p_extra );
200
201     /* Delete the decoder */
202     vlc_object_destroy( p_sys->p_packetizer );
203     if( p_sys->p_meta )
204         vlc_meta_Delete( p_sys->p_meta );
205     free( p_sys );
206 }
207
208 /*****************************************************************************
209  * Demux: reads and demuxes data packets
210  *****************************************************************************
211  * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
212  *****************************************************************************/
213 static int Demux( demux_t *p_demux )
214 {
215     demux_sys_t *p_sys = p_demux->p_sys;
216     block_t     *p_block_in, *p_block_out;
217
218     if( !( p_block_in = stream_Block( p_demux->s, FLAC_PACKET_SIZE ) ) )
219         return 0;
220
221     p_block_in->i_pts = p_block_in->i_dts = p_sys->b_start ? 1 : 0;
222     p_sys->b_start = VLC_FALSE;
223
224     while( (p_block_out = p_sys->p_packetizer->pf_packetize(
225                 p_sys->p_packetizer, &p_block_in )) )
226     {
227         while( p_block_out )
228         {
229             block_t *p_next = p_block_out->p_next;
230
231             p_block_out->p_next = NULL;
232
233             if( p_sys->p_es == NULL )
234             {
235                 p_sys->p_packetizer->fmt_out.b_packetized = VLC_TRUE;
236                 p_sys->p_packetizer->fmt_out.audio_replay_gain = p_sys->replay_gain;
237                 p_sys->p_es = es_out_Add( p_demux->out, &p_sys->p_packetizer->fmt_out);
238             }
239
240             /* set PCR */
241             if( p_block_out->i_dts >= p_sys->i_pts_start )
242                 es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_block_out->i_dts );
243             else
244                 es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
245
246             p_sys->i_pts = p_block_out->i_dts;
247             es_out_Send( p_demux->out, p_sys->p_es, p_block_out );
248
249             p_block_out = p_next;
250         }
251     }
252     return 1;
253 }
254
255 /*****************************************************************************
256  * Control:
257  *****************************************************************************/
258 static int64_t ControlGetLength( demux_t *p_demux )
259 {
260     demux_sys_t *p_sys = p_demux->p_sys;
261     const int64_t i_size = stream_Size(p_demux->s) - p_sys->i_data_pos;
262     int64_t i_length = p_sys->i_length;
263     int i;
264
265     /* Try to fix length using seekpoint and current size for truncated file */
266     for( i = p_sys->i_seekpoint-1; i >= 0; i-- )
267     {
268         seekpoint_t *s = p_sys->seekpoint[i];
269         if( s->i_byte_offset <= i_size )
270         {
271             if( i+1 < p_sys->i_seekpoint )
272             {
273                 /* Broken file */
274                 seekpoint_t *n = p_sys->seekpoint[i+1];
275                 assert( n->i_byte_offset != s->i_byte_offset); /* Should be ensured by ParseSeekTable */
276                 i_length = s->i_time_offset + (n->i_time_offset-s->i_time_offset) * (i_size-s->i_byte_offset) / (n->i_byte_offset-s->i_byte_offset);
277             }
278             break;
279         }
280     }
281     return i_length;
282 }
283 static int64_t ControlGetTime( demux_t *p_demux )
284 {
285     demux_sys_t *p_sys = p_demux->p_sys;
286     return __MAX(p_sys->i_pts, p_sys->i_pts_start) + p_sys->i_time_offset;
287 }
288 static int ControlSetTime( demux_t *p_demux, int64_t i_time )
289 {
290     demux_sys_t *p_sys = p_demux->p_sys;
291     int64_t i_next_time;
292     int64_t i_next_offset;
293     int64_t i_delta_time;
294     int64_t i_delta_offset;
295     vlc_bool_t b_seekable;
296     int i;
297
298     /* */
299     stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_seekable );
300     if( !b_seekable )
301         return VLC_EGENERIC;
302
303     /* */
304     assert( p_sys->i_seekpoint > 0 );   /* ReadMeta ensure at least (0,0) */
305     for( i = p_sys->i_seekpoint-1; i >= 0; i-- )
306     {
307         if( p_sys->seekpoint[i]->i_time_offset <= i_time )
308             break;
309     }
310     if( i+1 < p_sys->i_seekpoint )
311     {
312         i_next_time   = p_sys->seekpoint[i+1]->i_time_offset;
313         i_next_offset = p_sys->seekpoint[i+1]->i_byte_offset;
314     }
315     else
316     {
317         i_next_time   = p_sys->i_length;
318         i_next_offset = stream_Size(p_demux->s)-p_sys->i_data_pos;
319     }
320     i_delta_time = i_time - p_sys->seekpoint[i]->i_time_offset;
321     i_delta_offset = (i_next_offset - p_sys->seekpoint[i]->i_byte_offset) * i_delta_time / 
322                             (p_sys->seekpoint[i+1]->i_time_offset-p_sys->seekpoint[i]->i_time_offset);
323
324     /* XXX We do exact seek if it's not too far away(45s) */
325     if( i_delta_time < 45*I64C(1000000) )
326     {
327         if( stream_Seek( p_demux->s, p_sys->seekpoint[i]->i_byte_offset+p_sys->i_data_pos ) )
328             return VLC_EGENERIC;
329         p_sys->i_time_offset = p_sys->seekpoint[i]->i_time_offset - p_sys->i_pts;
330         p_sys->i_pts_start = p_sys->i_pts+i_delta_time;
331         es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, p_sys->p_es, p_sys->i_pts_start );
332     }
333     else
334     {
335         if( stream_Seek( p_demux->s, p_sys->seekpoint[i]->i_byte_offset+p_sys->i_data_pos + i_delta_offset ) )
336             return VLC_EGENERIC;
337         p_sys->i_pts_start = p_sys->i_pts;
338         p_sys->i_time_offset = (p_sys->seekpoint[i]->i_time_offset+i_delta_time) - p_sys->i_pts;
339     }
340     return VLC_SUCCESS;
341 }
342
343 static int Control( demux_t *p_demux, int i_query, va_list args )
344 {
345     demux_sys_t *p_sys = p_demux->p_sys;
346
347     if( i_query == DEMUX_GET_META )
348     {
349         vlc_meta_t *p_meta = (vlc_meta_t *)va_arg( args, vlc_meta_t* );
350         if( p_demux->p_sys->p_meta )
351             vlc_meta_Merge( p_meta, p_demux->p_sys->p_meta );
352         return VLC_SUCCESS;
353     }
354     else if( i_query == DEMUX_GET_LENGTH )
355     {
356         int64_t *pi64 = (int64_t*)va_arg( args, int64_t * );
357         *pi64 = ControlGetLength( p_demux );
358         return VLC_SUCCESS;
359     }
360     else if( i_query == DEMUX_SET_TIME )
361     {
362         int64_t i_time = (int64_t)va_arg( args, int64_t );
363         return ControlSetTime( p_demux, i_time );
364     }
365     else if( i_query == DEMUX_SET_POSITION )
366     {
367         const double f = (double)va_arg( args, double );
368         int64_t i_time = f * ControlGetLength( p_demux );
369         return ControlSetTime( p_demux, i_time );
370     }
371     else if( i_query == DEMUX_GET_TIME )
372     {
373         int64_t *pi64 = (int64_t*)va_arg( args, int64_t * );
374         *pi64 = ControlGetTime( p_demux );
375         return VLC_SUCCESS;
376     }
377     else if( i_query == DEMUX_GET_POSITION )
378     {
379         double *pf = (double*)va_arg( args, double * );
380         const int64_t i_length = ControlGetLength(p_demux);
381         if( i_length > 0 )
382             *pf = (double)ControlGetTime(p_demux) / (double)i_length;
383         else
384             *pf= 0.0;
385         return VLC_SUCCESS;
386     }
387     else if( i_query == DEMUX_GET_ATTACHMENTS )
388     {
389         input_attachment_t ***ppp_attach =
390             (input_attachment_t***)va_arg( args, input_attachment_t*** );
391         int *pi_int = (int*)va_arg( args, int * );
392         int i;
393
394         if( p_sys->i_attachment <= 0 )
395             return VLC_EGENERIC;
396
397         *pi_int = p_sys->i_attachment;;
398         *ppp_attach = malloc( sizeof(input_attachment_t**) * p_sys->i_attachment );
399         for( i = 0; i < p_sys->i_attachment; i++ )
400             *(ppp_attach)[i] = vlc_input_attachment_Duplicate( p_sys->attachment[i] );
401         return VLC_SUCCESS;
402     }
403
404     return demux2_vaControlHelper( p_demux->s, p_sys->i_data_pos, -1,
405                                    8*0, 1, i_query, args );
406 }
407
408 enum
409 {
410     META_STREAMINFO = 0,
411     META_SEEKTABLE = 3,
412     META_COMMENT = 4,
413     META_PICTURE = 6,
414 };
415
416 static inline int Get24bBE( uint8_t *p )
417 {
418     return (p[0] << 16)|(p[1] << 8)|(p[2]);
419 }
420
421 static void ParseStreamInfo( demux_t *p_demux, int *pi_rate, int64_t *pi_count, uint8_t *p_data, int i_data );
422 static void ParseSeekTable( demux_t *p_demux, uint8_t *p_data, int i_data, int i_sample_rate );
423 static void ParseComment( demux_t *, uint8_t *p_data, int i_data );
424 static void ParsePicture( demux_t *, uint8_t *p_data, int i_data );
425
426 static int  ReadMeta( demux_t *p_demux, uint8_t **pp_streaminfo, int *pi_streaminfo )
427 {
428     demux_sys_t *p_sys = p_demux->p_sys;
429     int     i_peek;
430     uint8_t *p_peek;
431     vlc_bool_t b_last;
432     int i_sample_rate;
433     int64_t i_sample_count;
434     seekpoint_t *s;
435
436     /* Read STREAMINFO */
437     i_peek = stream_Peek( p_demux->s, &p_peek, 8 );
438     if( (p_peek[4] & 0x7F) != META_STREAMINFO )
439     {
440         msg_Err( p_demux, "this isn't a STREAMINFO metadata block" );
441         return VLC_EGENERIC;
442     }
443     if( Get24bBE(&p_peek[5]) != (STREAMINFO_SIZE - 4) )
444     {
445         msg_Err( p_demux, "invalid size for a STREAMINFO metadata block" );
446         return VLC_EGENERIC;
447     }
448
449     *pi_streaminfo = 4 + STREAMINFO_SIZE;
450     *pp_streaminfo = malloc( 4 + STREAMINFO_SIZE );
451     if( *pp_streaminfo == NULL )
452         return VLC_EGENERIC;
453
454     if( stream_Read( p_demux->s, *pp_streaminfo, 4+STREAMINFO_SIZE ) != 4+STREAMINFO_SIZE )
455     {
456         msg_Err( p_demux, "failed to read STREAMINFO metadata block" );
457         free( *pp_streaminfo );
458         return VLC_EGENERIC;
459     }
460
461     /* */
462     ParseStreamInfo( p_demux, &i_sample_rate, &i_sample_count,  *pp_streaminfo, *pi_streaminfo );
463     if( i_sample_rate > 0 )
464         p_sys->i_length = i_sample_count * I64C(1000000)/i_sample_rate;
465
466     /* Be sure we have seekpoint 0 */
467     s = vlc_seekpoint_New();
468     s->i_time_offset = 0;
469     s->i_byte_offset = 0;
470     TAB_APPEND( p_sys->i_seekpoint, p_sys->seekpoint, s );
471
472
473
474     b_last = (*pp_streaminfo)[4]&0x80;
475     while( !b_last )
476     {
477         int i_len;
478         int i_type;
479
480         i_peek = stream_Peek( p_demux->s, &p_peek, 4 );
481         if( i_peek < 4 )
482             break;
483         b_last = p_peek[0]&0x80;
484         i_type = p_peek[0]&0x7f;
485         i_len  = Get24bBE( &p_peek[1] );
486
487         if( i_type == META_SEEKTABLE )
488         {
489             i_peek = stream_Peek( p_demux->s, &p_peek, 4+i_len );
490             if( i_peek == 4+i_len )
491                 ParseSeekTable( p_demux, p_peek, i_peek, i_sample_rate );
492         }
493         else if( i_type == META_COMMENT )
494         {
495             i_peek = stream_Peek( p_demux->s, &p_peek, 4+i_len );
496             if( i_peek == 4+i_len )
497                 ParseComment( p_demux, p_peek, i_peek );
498         }
499         else if( i_type == META_PICTURE )
500         {
501             i_peek = stream_Peek( p_demux->s, &p_peek, 4+i_len );
502             if( i_peek == 4+i_len )
503                 ParsePicture( p_demux, p_peek, i_peek );
504         }
505
506         if( stream_Read( p_demux->s, NULL, 4+i_len ) < 4+i_len )
507             break;
508     }
509
510     /* */
511     p_sys->i_data_pos = stream_Tell( p_demux->s );
512
513     return VLC_SUCCESS;
514 }
515 static void ParseStreamInfo( demux_t *p_demux, int *pi_rate, int64_t *pi_count, uint8_t *p_data, int i_data )
516 {
517     const int i_skip = 4+4;
518
519     *pi_rate = GetDWBE(&p_data[i_skip+4+6]) >> 12;
520     *pi_count = GetQWBE(&p_data[i_skip+4+6]) &  ((I64C(1)<<36)-1);
521 }
522 static void ParseSeekTable( demux_t *p_demux, uint8_t *p_data, int i_data, int i_sample_rate )
523 {
524     demux_sys_t *p_sys = p_demux->p_sys;
525     seekpoint_t *s;
526     int i;
527
528     if( i_sample_rate <= 0 )
529         return;
530
531     /* */
532     for( i = 0; i < (i_data-4)/18; i++ )
533     {
534         const int64_t i_sample = GetQWBE( &p_data[4+18*i+0] );
535         int j;
536
537         if( i_sample < 0 || i_sample >= INT64_MAX )
538             continue;
539
540         s = vlc_seekpoint_New();
541         s->i_time_offset = i_sample * I64C(1000000)/i_sample_rate;
542         s->i_byte_offset = GetQWBE( &p_data[4+18*i+8] );
543
544         /* Check for duplicate entry */
545         for( j = 0; j < p_sys->i_seekpoint; j++ )
546         {
547             if( p_sys->seekpoint[j]->i_time_offset == s->i_time_offset ||
548                 p_sys->seekpoint[j]->i_byte_offset == s->i_byte_offset )
549             {
550                 vlc_seekpoint_Delete( s );
551                 s = NULL;
552                 break;
553             }
554         }
555         if( s )
556         {
557             TAB_APPEND( p_sys->i_seekpoint, p_sys->seekpoint, s );
558         }
559     }
560     /* TODO sort it by size and remove wrong seek entry (time not increasing) */
561 }
562 static inline void astrcat( char **ppsz_dst, const char *psz_src )
563 {
564     char *psz_old = *ppsz_dst;
565
566     if( !psz_src || !psz_src[0] )
567         return;
568
569     if( psz_old )
570     {
571         asprintf( ppsz_dst, "%s,%s", psz_old, psz_src );
572         free( psz_old );
573     }
574     else
575     {
576         *ppsz_dst = strdup( psz_src );
577     }
578 }
579
580 #define RM(x) do { i_data -= (x); p_data += (x); } while(0)
581 static void ParseComment( demux_t *p_demux, uint8_t *p_data, int i_data )
582 {
583     demux_sys_t *p_sys = p_demux->p_sys;
584     int n;
585     int i_comment;
586
587     if( i_data < 8 )
588         return;
589
590     RM(4);
591
592     n = GetDWLE(p_data); RM(4);
593     if( n < 0 || n > i_data )
594         return;
595 #if 0
596     if( n > 0 )
597     {
598         /* TODO report vendor string ? */
599         char *psz_vendor = psz_vendor = strndup( p_data, n );
600         msg_Dbg( p_demux, "FLAC: COMMENT vendor length=%d vendor=%s\n", n, psz_vendor );
601         free( psz_vendor );
602     }
603 #endif
604     RM(n);
605
606     if( i_data < 4 )
607         return;
608
609     i_comment = GetDWLE(p_data); RM(4);
610     if( i_comment <= 0 )
611         return;
612
613     p_sys->p_meta = vlc_meta_New();
614
615     for( ; i_comment > 0; i_comment-- )
616     {
617         char *psz;
618         if( i_data < 4 )
619             break;
620         n = GetDWLE(p_data); RM(4);
621         if( n > i_data )
622             break;
623         if( n <= 0 )
624             continue;
625
626         psz = strndup( p_data, n );
627         RM(n);
628
629         EnsureUTF8( psz );
630
631 #define IF_EXTRACT(txt,var) if( !strncasecmp(psz, txt, strlen(txt)) ) { astrcat( &p_sys->p_meta->var, &psz[strlen(txt)] ); }
632         IF_EXTRACT("TITLE=", psz_title )
633         else IF_EXTRACT("ALBUM=", psz_album )
634         else IF_EXTRACT("TRACKNUMBER=", psz_tracknum )
635         else IF_EXTRACT("ARTIST=", psz_artist )
636         else IF_EXTRACT("COPYRIGHT=", psz_copyright )
637         else IF_EXTRACT("DESCRIPTION=", psz_description )
638         else IF_EXTRACT("GENRE=", psz_genre )
639         else IF_EXTRACT("DATE=", psz_date )
640         else if( strchr( psz, '=' ) )
641         {
642             /* generic (PERFORMER/LICENSE/ORGANIZATION/LOCATION/CONTACT/ISRC,
643              * undocumented tags and replay gain ) */
644             audio_replay_gain_t *r = &p_sys->replay_gain;
645             char *p = strchr( psz, '=' );
646             *p++ = '\0';
647             vlc_meta_AddExtra( p_sys->p_meta, psz, p );
648         }
649 #undef IF_EXTRACT
650         free( psz );
651     }
652 #undef RM
653 }
654
655 static void ParsePicture( demux_t *p_demux, uint8_t *p_data, int i_data )
656 {
657     static const int pi_cover_score[] = {
658         0,      /* other */
659         2, 1,   /* icons */
660         10,     /* front cover */
661         9,      /* back cover */
662         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
663         6,      /* movie/video screen capture */
664         0,
665         7,      /* Illustration */
666         8,      /* Band/Artist logotype */
667         0,      /* Publisher/Studio */
668     };
669     demux_sys_t *p_sys = p_demux->p_sys;
670     int i_type;
671     int i_len;
672     char *psz_mime = NULL;
673     char *psz_description = NULL;
674     input_attachment_t *p_attachment;
675     char psz_name[128];
676
677     if( i_data < 4 + 3*4 )
678         return;
679 #define RM(x) do { i_data -= (x); p_data += (x); } while(0)
680     RM(4);
681
682     i_type = GetDWBE( p_data ); RM(4);
683     i_len = GetDWBE( p_data ); RM(4);
684     if( i_data < i_len + 4 )
685         goto error;
686     psz_mime = strndup( p_data, i_len ); RM(i_len);
687     i_len = GetDWBE( p_data ); RM(4);
688     if( i_data < i_len + 4*4 + 4)
689         goto error;
690     psz_description = strndup( p_data, i_len ); RM(i_len);
691     EnsureUTF8( psz_description );
692     RM(4*4);
693     i_len = GetDWBE( p_data ); RM(4);
694     if( i_len > i_data )
695         goto error;
696
697     msg_Dbg( p_demux, "FLAC: Picture type=%d mime=%s description='%s' file length=%d",
698              i_type, psz_mime, psz_description, i_len );
699
700     snprintf( psz_name, sizeof(psz_name), "picture%d", p_sys->i_attachment );
701     if( !strcasecmp( psz_mime, "image/jpeg" ) )
702         strcat( psz_name, ".jpg" );
703     else if( !strcasecmp( psz_mime, "image/png" ) )
704         strcat( psz_name, ".png" );
705
706     p_attachment = vlc_input_attachment_New( psz_name, psz_mime, psz_description,
707                                              p_data, i_data );
708     TAB_APPEND( p_sys->i_attachment, p_sys->attachment, p_attachment );
709
710     if( i_type >= 0 && i_type < sizeof(pi_cover_score)/sizeof(pi_cover_score[0]) &&
711         p_sys->i_cover_score < pi_cover_score[i_type] )
712     {
713         p_sys->i_cover_idx = p_sys->i_attachment-1;
714         p_sys->i_cover_score = pi_cover_score[i_type];
715     }
716 error:
717     if( psz_mime )
718         free( psz_mime );
719     if( psz_description )
720         free( psz_description );
721 }
722 #undef RM
723