flac.c 18.6 KB
Newer Older
1
/*****************************************************************************
gbazin's avatar
   
gbazin committed
2
 * flac.c : FLAC demux module for vlc
3
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2001-2008 VLC authors and VideoLAN
5
 * $Id$
6
 *
gbazin's avatar
   
gbazin committed
7
 * Authors: Gildas Bazin <gbazin@netcourrier.com>
Laurent Aimar's avatar
Laurent Aimar committed
8
 *          Laurent Aimar <fenrir@via.ecp.fr>
9
 *
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
10
11
12
 * 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
13
 * (at your option) any later version.
14
 *
15
16
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
17
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
19
 *
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
20
21
22
 * 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.
23
24
25
26
27
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
28
29
30
31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

32
#include <vlc_common.h>
33
#include <vlc_plugin.h>
zorglub's avatar
zorglub committed
34
#include <vlc_demux.h>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
35
36
37
38
39
#include <vlc_meta.h>                 /* vlc_meta_* */
#include <vlc_input.h>                /* vlc_input_attachment, vlc_seekpoint */
#include <vlc_codec.h>                /* decoder_t */
#include <vlc_charset.h>              /* EnsureUTF8 */

Laurent Aimar's avatar
Laurent Aimar committed
40
#include <assert.h>
41
#include "xiph_metadata.h"            /* vorbis comments */
42
43

/*****************************************************************************
44
 * Module descriptor
45
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
46
47
static int  Open  ( vlc_object_t * );
static void Close ( vlc_object_t * );
48

49
50
51
52
53
54
55
56
vlc_module_begin ()
    set_description( N_("FLAC demuxer") )
    set_capability( "demux", 155 )
    set_category( CAT_INPUT )
    set_subcategory( SUBCAT_INPUT_DEMUX )
    set_callbacks( Open, Close )
    add_shortcut( "flac" )
vlc_module_end ()
57
58
59
60
61
62

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static int Demux  ( demux_t * );
static int Control( demux_t *, int, va_list );
63

Laurent Aimar's avatar
Laurent Aimar committed
64
65
static int  ReadMeta( demux_t *, uint8_t **pp_streaminfo, int *pi_streaminfo );

gbazin's avatar
   
gbazin committed
66
67
struct demux_sys_t
{
68
    bool  b_start;
gbazin's avatar
   
gbazin committed
69
70
71
72
    es_out_id_t *p_es;

    /* Packetizer */
    decoder_t *p_packetizer;
73

74
    vlc_meta_t *p_meta;
Laurent Aimar's avatar
Laurent Aimar committed
75
76
77
78
79
80
81
82
83

    int64_t i_pts;

    int64_t i_length; /* Length from stream info */
    int64_t i_data_pos;

    /* */
    int         i_seekpoint;
    seekpoint_t **seekpoint;
Laurent Aimar's avatar
Laurent Aimar committed
84
85

    /* */
86
87
    int                i_attachments;
    input_attachment_t **attachments;
Laurent Aimar's avatar
Laurent Aimar committed
88
89
    int                i_cover_idx;
    int                i_cover_score;
gbazin's avatar
   
gbazin committed
90
91
};

92
#define STREAMINFO_SIZE 34
93
#define FLAC_PACKET_SIZE 16384
94
95

/*****************************************************************************
gbazin's avatar
   
gbazin committed
96
 * Open: initializes ES structures
97
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
98
static int Open( vlc_object_t * p_this )
99
{
100
101
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys;
102
    const uint8_t *p_peek;
Laurent Aimar's avatar
Laurent Aimar committed
103
104
    uint8_t     *p_streaminfo;
    int         i_streaminfo;
105
    es_format_t fmt;
106

107
    /* Have a peep at the show. */
108
    if( stream_Peek( p_demux->s, &p_peek, 4 ) < 4 ) return VLC_EGENERIC;
109

gbazin's avatar
   
gbazin committed
110
    if( p_peek[0]!='f' || p_peek[1]!='L' || p_peek[2]!='a' || p_peek[3]!='C' )
111
    {
112
113
        if( !p_demux->b_force ) return VLC_EGENERIC;

114
115
116
        /* User forced */
        msg_Err( p_demux, "this doesn't look like a flac stream, "
                 "continuing anyway" );
117
118
    }

119
120
121
122
    p_sys = malloc( sizeof( demux_sys_t ) );
    if( unlikely(p_sys == NULL) )
        return VLC_ENOMEM;

123
124
    p_demux->pf_demux   = Demux;
    p_demux->pf_control = Control;
125
    p_demux->p_sys      = p_sys;
126
    p_sys->b_start = true;
127
    p_sys->p_meta = NULL;
Laurent Aimar's avatar
Laurent Aimar committed
128
129
130
131
    p_sys->i_length = 0;
    p_sys->i_pts = 0;
    p_sys->p_es = NULL;
    TAB_INIT( p_sys->i_seekpoint, p_sys->seekpoint );
132
    TAB_INIT( p_sys->i_attachments, p_sys->attachments);
Laurent Aimar's avatar
Laurent Aimar committed
133
134
    p_sys->i_cover_idx = 0;
    p_sys->i_cover_score = 0;
gbazin's avatar
   
gbazin committed
135
136

    /* We need to read and store the STREAMINFO metadata */
Laurent Aimar's avatar
Laurent Aimar committed
137
    if( ReadMeta( p_demux, &p_streaminfo, &i_streaminfo ) )
138
    {
Laurent Aimar's avatar
Laurent Aimar committed
139
        free( p_sys );
140
        return VLC_EGENERIC;
141
    }
gbazin's avatar
   
gbazin committed
142

zorglub's avatar
zorglub committed
143
    /* Load the FLAC packetizer */
144
    /* Store STREAMINFO for the decoder and packetizer */
145
    es_format_Init( &fmt, AUDIO_ES, VLC_CODEC_FLAC );
146
147
    fmt.i_extra = i_streaminfo;
    fmt.p_extra = p_streaminfo;
gbazin's avatar
   
gbazin committed
148

149
150
    p_sys->p_packetizer = demux_PacketizerNew( p_demux, &fmt, "flac" );
    if( !p_sys->p_packetizer )
gbazin's avatar
   
gbazin committed
151
    {
152
        free( p_sys );
153
        return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
154
    }
155

156
    if( p_sys->i_cover_idx < p_sys->i_attachments )
Laurent Aimar's avatar
Laurent Aimar committed
157
158
159
160
161
    {
        char psz_url[128];
        if( !p_sys->p_meta )
            p_sys->p_meta = vlc_meta_New();
        snprintf( psz_url, sizeof(psz_url), "attachment://%s",
162
                  p_sys->attachments[p_sys->i_cover_idx]->psz_name );
163
        vlc_meta_Set( p_sys->p_meta, vlc_meta_ArtworkURL, psz_url );
Laurent Aimar's avatar
Laurent Aimar committed
164
    }
165
    return VLC_SUCCESS;
gbazin's avatar
   
gbazin committed
166
167
168
169
170
171
172
}

/*****************************************************************************
 * Close: frees unused data
 *****************************************************************************/
static void Close( vlc_object_t * p_this )
{
173
174
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys = p_demux->p_sys;
gbazin's avatar
   
gbazin committed
175

176
177
    for( int i = 0; i < p_sys->i_seekpoint; i++ )
        vlc_seekpoint_Delete(p_sys->seekpoint[i]);
178
    TAB_CLEAN( p_sys->i_seekpoint, p_sys->seekpoint );
179

180
    for( int i = 0; i < p_sys->i_attachments; i++ )
Rafaël Carré's avatar
Rafaël Carré committed
181
        vlc_input_attachment_Delete( p_sys->attachments[i] );
182
183
    TAB_CLEAN( p_sys->i_attachments, p_sys->attachments);

gbazin's avatar
   
gbazin committed
184
    /* Delete the decoder */
185
186
    demux_PacketizerDestroy( p_sys->p_packetizer );

Laurent Aimar's avatar
Laurent Aimar committed
187
188
    if( p_sys->p_meta )
        vlc_meta_Delete( p_sys->p_meta );
gbazin's avatar
   
gbazin committed
189
    free( p_sys );
190
191
192
193
194
195
196
}

/*****************************************************************************
 * Demux: reads and demuxes data packets
 *****************************************************************************
 * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
 *****************************************************************************/
197
static int Demux( demux_t *p_demux )
198
{
199
200
    demux_sys_t *p_sys = p_demux->p_sys;
    block_t     *p_block_in, *p_block_out;
201

202
    bool b_eof = !( p_block_in = stream_Block( p_demux->s, FLAC_PACKET_SIZE ) );
203

204
205
206
207
208
    if ( p_block_in )
    {
        p_block_in->i_pts = p_block_in->i_dts = p_sys->b_start ? VLC_TS_0 : VLC_TS_INVALID;
        p_sys->b_start = false;
    }
209

gbazin's avatar
   
gbazin committed
210
    while( (p_block_out = p_sys->p_packetizer->pf_packetize(
211
                p_sys->p_packetizer, (p_block_in) ? &p_block_in : NULL )) )
212
    {
gbazin's avatar
   
gbazin committed
213
214
215
        while( p_block_out )
        {
            block_t *p_next = p_block_out->p_next;
216

Laurent Aimar's avatar
Laurent Aimar committed
217
218
219
220
            p_block_out->p_next = NULL;

            if( p_sys->p_es == NULL )
            {
221
                p_sys->p_packetizer->fmt_out.b_packetized = true;
Laurent Aimar's avatar
Laurent Aimar committed
222
223
224
                p_sys->p_es = es_out_Add( p_demux->out, &p_sys->p_packetizer->fmt_out);
            }

225
            p_sys->i_pts = p_block_out->i_dts;
226

227
            /* set PCR */
228
            es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_block_out->i_dts );
gbazin's avatar
   
gbazin committed
229

230
            es_out_Send( p_demux->out, p_sys->p_es, p_block_out );
231

gbazin's avatar
   
gbazin committed
232
233
234
            p_block_out = p_next;
        }
    }
235
    return !b_eof;
236
}
237
238
239
240

/*****************************************************************************
 * Control:
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
static int64_t ControlGetLength( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    const int64_t i_size = stream_Size(p_demux->s) - p_sys->i_data_pos;
    int64_t i_length = p_sys->i_length;
    int i;

    /* Try to fix length using seekpoint and current size for truncated file */
    for( i = p_sys->i_seekpoint-1; i >= 0; i-- )
    {
        seekpoint_t *s = p_sys->seekpoint[i];
        if( s->i_byte_offset <= i_size )
        {
            if( i+1 < p_sys->i_seekpoint )
            {
                /* Broken file */
                seekpoint_t *n = p_sys->seekpoint[i+1];
                assert( n->i_byte_offset != s->i_byte_offset); /* Should be ensured by ParseSeekTable */
                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);
            }
            break;
        }
    }
    return i_length;
}
266

Laurent Aimar's avatar
Laurent Aimar committed
267
268
269
static int64_t ControlGetTime( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;
270
    return p_sys->i_pts;
Laurent Aimar's avatar
Laurent Aimar committed
271
}
272

Laurent Aimar's avatar
Laurent Aimar committed
273
274
275
276
static int ControlSetTime( demux_t *p_demux, int64_t i_time )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    int64_t i_delta_time;
277
    bool b_seekable;
Laurent Aimar's avatar
Laurent Aimar committed
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
    int i;

    /* */
    stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_seekable );
    if( !b_seekable )
        return VLC_EGENERIC;

    /* */
    assert( p_sys->i_seekpoint > 0 );   /* ReadMeta ensure at least (0,0) */
    for( i = p_sys->i_seekpoint-1; i >= 0; i-- )
    {
        if( p_sys->seekpoint[i]->i_time_offset <= i_time )
            break;
    }
    i_delta_time = i_time - p_sys->seekpoint[i]->i_time_offset;

    /* XXX We do exact seek if it's not too far away(45s) */
295
    if( i_delta_time < CLOCK_FREQ * 45 )
Laurent Aimar's avatar
Laurent Aimar committed
296
297
298
    {
        if( stream_Seek( p_demux->s, p_sys->seekpoint[i]->i_byte_offset+p_sys->i_data_pos ) )
            return VLC_EGENERIC;
299

300
        es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, i_time );
Laurent Aimar's avatar
Laurent Aimar committed
301
302
303
    }
    else
    {
304
305
306
        int64_t i_delta_offset;
        int64_t i_next_time;
        int64_t i_next_offset;
307
        uint32_t i_time_align = 1;
308
309
310
311
312
313
314
315
316
317
318
319
320

        if( i+1 < p_sys->i_seekpoint )
        {
            i_next_time   = p_sys->seekpoint[i+1]->i_time_offset;
            i_next_offset = p_sys->seekpoint[i+1]->i_byte_offset;
        }
        else
        {
            i_next_time   = p_sys->i_length;
            i_next_offset = stream_Size(p_demux->s)-p_sys->i_data_pos;
        }

        i_delta_offset = 0;
321
322

        if ( INT64_MAX / i_delta_time < (i_next_offset - p_sys->seekpoint[i]->i_byte_offset) )
323
            i_time_align = CLOCK_FREQ;
324

325
        if( i_next_time-p_sys->seekpoint[i]->i_time_offset > 0 )
326
327
            i_delta_offset = (i_next_offset - p_sys->seekpoint[i]->i_byte_offset) * (i_delta_time / i_time_align) /
                             ((i_next_time-p_sys->seekpoint[i]->i_time_offset) / i_time_align);
328

Laurent Aimar's avatar
Laurent Aimar committed
329
330
331
332
333
334
        if( stream_Seek( p_demux->s, p_sys->seekpoint[i]->i_byte_offset+p_sys->i_data_pos + i_delta_offset ) )
            return VLC_EGENERIC;
    }
    return VLC_SUCCESS;
}

335
336
static int Control( demux_t *p_demux, int i_query, va_list args )
{
Laurent Aimar's avatar
Laurent Aimar committed
337
338
339
    demux_sys_t *p_sys = p_demux->p_sys;

    if( i_query == DEMUX_GET_META )
340
    {
341
        vlc_meta_t *p_meta = (vlc_meta_t *)va_arg( args, vlc_meta_t* );
342
        if( p_demux->p_sys->p_meta )
343
            vlc_meta_Merge( p_meta, p_demux->p_sys->p_meta );
344
345
        return VLC_SUCCESS;
    }
346
347
    else if( i_query == DEMUX_HAS_UNSUPPORTED_META )
    {
348
349
        bool *pb_bool = (bool*)va_arg( args, bool* );
        *pb_bool = true;
350
351
        return VLC_SUCCESS;
    }
Laurent Aimar's avatar
Laurent Aimar committed
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
    else if( i_query == DEMUX_GET_LENGTH )
    {
        int64_t *pi64 = (int64_t*)va_arg( args, int64_t * );
        *pi64 = ControlGetLength( p_demux );
        return VLC_SUCCESS;
    }
    else if( i_query == DEMUX_SET_TIME )
    {
        int64_t i_time = (int64_t)va_arg( args, int64_t );
        return ControlSetTime( p_demux, i_time );
    }
    else if( i_query == DEMUX_SET_POSITION )
    {
        const double f = (double)va_arg( args, double );
        int64_t i_time = f * ControlGetLength( p_demux );
        return ControlSetTime( p_demux, i_time );
    }
    else if( i_query == DEMUX_GET_TIME )
    {
        int64_t *pi64 = (int64_t*)va_arg( args, int64_t * );
        *pi64 = ControlGetTime( p_demux );
        return VLC_SUCCESS;
    }
    else if( i_query == DEMUX_GET_POSITION )
    {
        double *pf = (double*)va_arg( args, double * );
        const int64_t i_length = ControlGetLength(p_demux);
        if( i_length > 0 )
380
381
382
383
        {
            double current = ControlGetTime(p_demux);
            *pf = current / (double)i_length;
        }
Laurent Aimar's avatar
Laurent Aimar committed
384
385
386
387
        else
            *pf= 0.0;
        return VLC_SUCCESS;
    }
Laurent Aimar's avatar
Laurent Aimar committed
388
389
390
391
392
393
    else if( i_query == DEMUX_GET_ATTACHMENTS )
    {
        input_attachment_t ***ppp_attach =
            (input_attachment_t***)va_arg( args, input_attachment_t*** );
        int *pi_int = (int*)va_arg( args, int * );

394
        if( p_sys->i_attachments <= 0 )
Laurent Aimar's avatar
Laurent Aimar committed
395
396
            return VLC_EGENERIC;

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
397
        *pi_int = p_sys->i_attachments;
ivoire's avatar
ivoire committed
398
        *ppp_attach = xmalloc( sizeof(input_attachment_t*) * p_sys->i_attachments );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
399
        for( int i = 0; i < p_sys->i_attachments; i++ )
400
            (*ppp_attach)[i] = vlc_input_attachment_Duplicate( p_sys->attachments[i] );
Laurent Aimar's avatar
Laurent Aimar committed
401
402
        return VLC_SUCCESS;
    }
Laurent Aimar's avatar
Laurent Aimar committed
403

404
    return demux_vaControlHelper( p_demux->s, p_sys->i_data_pos, -1,
Laurent Aimar's avatar
Laurent Aimar committed
405
406
407
408
409
410
411
412
                                   8*0, 1, i_query, args );
}

enum
{
    META_STREAMINFO = 0,
    META_SEEKTABLE = 3,
    META_COMMENT = 4,
Laurent Aimar's avatar
Laurent Aimar committed
413
    META_PICTURE = 6,
Laurent Aimar's avatar
Laurent Aimar committed
414
415
};

416
static inline int Get24bBE( const uint8_t *p )
Laurent Aimar's avatar
Laurent Aimar committed
417
418
419
420
{
    return (p[0] << 16)|(p[1] << 8)|(p[2]);
}

421
static void ParseStreamInfo( int *pi_rate, int64_t *pi_count, uint8_t *p_data );
422
423
424
425
static void ParseSeekTable( demux_t *p_demux, const uint8_t *p_data, int i_data,
                            int i_sample_rate );
static void ParseComment( demux_t *, const uint8_t *p_data, int i_data );
static void ParsePicture( demux_t *, const uint8_t *p_data, int i_data );
Laurent Aimar's avatar
Laurent Aimar committed
426
427
428
429
430

static int  ReadMeta( demux_t *p_demux, uint8_t **pp_streaminfo, int *pi_streaminfo )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    int     i_peek;
431
    const uint8_t *p_peek;
432
    bool b_last;
433
    int i_sample_rate = 0;
Laurent Aimar's avatar
Laurent Aimar committed
434
435
    int64_t i_sample_count;

436
    *pp_streaminfo = NULL;
Laurent Aimar's avatar
Laurent Aimar committed
437
438

    /* Be sure we have seekpoint 0 */
439
    seekpoint_t *s = vlc_seekpoint_New();
Laurent Aimar's avatar
Laurent Aimar committed
440
441
442
443
    s->i_time_offset = 0;
    s->i_byte_offset = 0;
    TAB_APPEND( p_sys->i_seekpoint, p_sys->seekpoint, s );

444
445
446
447
    uint8_t header[4];
    if( stream_Read( p_demux->s, header, 4) < 4)
        return VLC_EGENERIC;

448
    if (memcmp(header, "fLaC", 4))
449
450
451
        return VLC_EGENERIC;

    b_last = 0;
Laurent Aimar's avatar
Laurent Aimar committed
452
453
454
455
456
457
458
459
460
461
462
463
    while( !b_last )
    {
        int i_len;
        int i_type;

        i_peek = stream_Peek( p_demux->s, &p_peek, 4 );
        if( i_peek < 4 )
            break;
        b_last = p_peek[0]&0x80;
        i_type = p_peek[0]&0x7f;
        i_len  = Get24bBE( &p_peek[1] );

464
465
        if( i_type == META_STREAMINFO && !*pp_streaminfo )
        {
466
            if( i_len != STREAMINFO_SIZE ) {
467
468
469
470
                msg_Err( p_demux, "invalid size %d for a STREAMINFO metadata block", i_len );
                return VLC_EGENERIC;
            }

471
472
            *pi_streaminfo = STREAMINFO_SIZE;
            *pp_streaminfo = malloc( STREAMINFO_SIZE);
473
474
475
            if( *pp_streaminfo == NULL )
                return VLC_EGENERIC;

476
            if( stream_Read( p_demux->s, NULL, 4) < 4)
ivoire's avatar
ivoire committed
477
478
            {
                free( *pp_streaminfo );
479
                return VLC_EGENERIC;
ivoire's avatar
ivoire committed
480
            }
481
            if( stream_Read( p_demux->s, *pp_streaminfo, STREAMINFO_SIZE ) != STREAMINFO_SIZE )
482
483
484
485
486
487
488
489
490
            {
                msg_Err( p_demux, "failed to read STREAMINFO metadata block" );
                free( *pp_streaminfo );
                return VLC_EGENERIC;
            }

            /* */
            ParseStreamInfo( &i_sample_rate, &i_sample_count, *pp_streaminfo );
            if( i_sample_rate > 0 )
491
                p_sys->i_length = i_sample_count * CLOCK_FREQ /i_sample_rate;
492
            continue;
493
494
        }
        else if( i_type == META_SEEKTABLE )
Laurent Aimar's avatar
Laurent Aimar committed
495
496
497
498
499
500
501
502
503
504
505
        {
            i_peek = stream_Peek( p_demux->s, &p_peek, 4+i_len );
            if( i_peek == 4+i_len )
                ParseSeekTable( p_demux, p_peek, i_peek, i_sample_rate );
        }
        else if( i_type == META_COMMENT )
        {
            i_peek = stream_Peek( p_demux->s, &p_peek, 4+i_len );
            if( i_peek == 4+i_len )
                ParseComment( p_demux, p_peek, i_peek );
        }
Laurent Aimar's avatar
Laurent Aimar committed
506
507
508
509
510
511
        else if( i_type == META_PICTURE )
        {
            i_peek = stream_Peek( p_demux->s, &p_peek, 4+i_len );
            if( i_peek == 4+i_len )
                ParsePicture( p_demux, p_peek, i_peek );
        }
Laurent Aimar's avatar
Laurent Aimar committed
512
513
514
515
516
517
518
519

        if( stream_Read( p_demux->s, NULL, 4+i_len ) < 4+i_len )
            break;
    }

    /* */
    p_sys->i_data_pos = stream_Tell( p_demux->s );

520
521
522
    if (!*pp_streaminfo)
        return VLC_EGENERIC;

Laurent Aimar's avatar
Laurent Aimar committed
523
524
    return VLC_SUCCESS;
}
525
static void ParseStreamInfo( int *pi_rate, int64_t *pi_count, uint8_t *p_data )
Laurent Aimar's avatar
Laurent Aimar committed
526
{
527
528
    *pi_rate = GetDWBE(&p_data[4+6]) >> 12;
    *pi_count = GetQWBE(&p_data[4+6]) &  ((INT64_C(1)<<36)-1);
Laurent Aimar's avatar
Laurent Aimar committed
529
}
530
531
532

static void ParseSeekTable( demux_t *p_demux, const uint8_t *p_data, int i_data,
                            int i_sample_rate )
Laurent Aimar's avatar
Laurent Aimar committed
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
{
    demux_sys_t *p_sys = p_demux->p_sys;
    seekpoint_t *s;
    int i;

    if( i_sample_rate <= 0 )
        return;

    /* */
    for( i = 0; i < (i_data-4)/18; i++ )
    {
        const int64_t i_sample = GetQWBE( &p_data[4+18*i+0] );
        int j;

        if( i_sample < 0 || i_sample >= INT64_MAX )
            continue;

        s = vlc_seekpoint_New();
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
551
        s->i_time_offset = i_sample * INT64_C(1000000)/i_sample_rate;
Laurent Aimar's avatar
Laurent Aimar committed
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
        s->i_byte_offset = GetQWBE( &p_data[4+18*i+8] );

        /* Check for duplicate entry */
        for( j = 0; j < p_sys->i_seekpoint; j++ )
        {
            if( p_sys->seekpoint[j]->i_time_offset == s->i_time_offset ||
                p_sys->seekpoint[j]->i_byte_offset == s->i_byte_offset )
            {
                vlc_seekpoint_Delete( s );
                s = NULL;
                break;
            }
        }
        if( s )
        {
            TAB_APPEND( p_sys->i_seekpoint, p_sys->seekpoint, s );
        }
    }
    /* TODO sort it by size and remove wrong seek entry (time not increasing) */
}
572
573

static void ParseComment( demux_t *p_demux, const uint8_t *p_data, int i_data )
Laurent Aimar's avatar
Laurent Aimar committed
574
575
576
577
578
579
{
    demux_sys_t *p_sys = p_demux->p_sys;

    if( i_data < 4 )
        return;

580
    vorbis_ParseComment( NULL, &p_sys->p_meta, &p_data[4], i_data - 4,
581
        &p_sys->i_attachments, &p_sys->attachments,
582
        &p_sys->i_cover_score, &p_sys->i_cover_idx, NULL, NULL, NULL, NULL );
583
584
}

585
static void ParsePicture( demux_t *p_demux, const uint8_t *p_data, int i_data )
Laurent Aimar's avatar
Laurent Aimar committed
586
587
588
{
    demux_sys_t *p_sys = p_demux->p_sys;

589
590
    i_data -= 4; p_data += 4;

591
592
    input_attachment_t *p_attachment = ParseFlacPicture( p_data, i_data,
        p_sys->i_attachments, &p_sys->i_cover_score, &p_sys->i_cover_idx );
593
    if( p_attachment == NULL )
Laurent Aimar's avatar
Laurent Aimar committed
594
        return;
595

596
    TAB_APPEND( p_sys->i_attachments, p_sys->attachments, p_attachment );
Laurent Aimar's avatar
Laurent Aimar committed
597
598
}