Coverage Report

Created: 2025-08-25 06:17

/src/vlc/modules/demux/flac.c
Line
Count
Source (jump to first uncovered line)
1
/*****************************************************************************
2
 * flac.c : FLAC demux module for vlc
3
 *****************************************************************************
4
 * Copyright (C) 2001-2008 VLC authors and VideoLAN
5
 *
6
 * Authors: Gildas Bazin <gbazin@netcourrier.com>
7
 *          Laurent Aimar <fenrir@via.ecp.fr>
8
 *
9
 * This program is free software; you can redistribute it and/or modify it
10
 * under the terms of the GNU Lesser General Public License as published by
11
 * the Free Software Foundation; either version 2.1 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Lesser General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public License
20
 * along with this program; if not, write to the Free Software Foundation,
21
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22
 *****************************************************************************/
23
24
/*****************************************************************************
25
 * Preamble
26
 *****************************************************************************/
27
#ifdef HAVE_CONFIG_H
28
# include "config.h"
29
#endif
30
31
#include <vlc_common.h>
32
#include <vlc_arrays.h>
33
#include <vlc_plugin.h>
34
#include <vlc_demux.h>
35
#include <vlc_meta.h>                 /* vlc_meta_* */
36
#include <vlc_input.h>                /* vlc_input_attachment, vlc_seekpoint */
37
#include <vlc_codec.h>                /* decoder_t */
38
#include <vlc_charset.h>              /* EnsureUTF8 */
39
#include <vlc_replay_gain.h>
40
41
#include <assert.h>
42
#include <limits.h>
43
#include "xiph_metadata.h"            /* vorbis comments */
44
#include "../packetizer/flac.h"
45
46
/*****************************************************************************
47
 * Module descriptor
48
 *****************************************************************************/
49
static int  Open  ( vlc_object_t * );
50
static void Close ( vlc_object_t * );
51
52
4
vlc_module_begin ()
53
2
    set_description( N_("FLAC demuxer") )
54
2
    set_capability( "demux", 155 )
55
2
    set_subcategory( SUBCAT_INPUT_DEMUX )
56
2
    set_callbacks( Open, Close )
57
2
    add_shortcut( "flac" )
58
2
    add_file_extension("flac")
59
2
vlc_module_end ()
60
61
/*****************************************************************************
62
 * Local prototypes
63
 *****************************************************************************/
64
static int Demux  ( demux_t * );
65
static int Control( demux_t *, int, va_list );
66
67
static int  ParseHeaders( demux_t *, es_format_t * );
68
69
typedef struct
70
{
71
    vlc_tick_t  i_time_offset;
72
    uint64_t i_byte_offset;
73
} flac_seekpoint_t;
74
75
typedef struct
76
{
77
    bool  b_start;
78
    int   i_next_block_flags;
79
    es_out_id_t *p_es;
80
    block_t *p_current_block;
81
82
    /* Packetizer */
83
    decoder_t *p_packetizer;
84
85
    vlc_meta_t *p_meta;
86
87
    vlc_tick_t i_pts;
88
    struct flac_stream_info stream_info;
89
    bool b_stream_info;
90
91
    vlc_tick_t i_length; /* Length from stream info */
92
    uint64_t i_data_pos;
93
94
    /* */
95
    int         i_seekpoint;
96
    flac_seekpoint_t **seekpoint;
97
98
    /* title/chapters seekpoints */
99
    int           i_title_seekpoints;
100
    seekpoint_t **pp_title_seekpoints;
101
102
    /* */
103
    int                i_attachments;
104
    input_attachment_t **attachments;
105
    int                i_cover_idx;
106
    int                i_cover_score;
107
} demux_sys_t;
108
109
0
#define FLAC_PACKET_SIZE 16384
110
0
#define FLAC_MAX_PREROLL      VLC_TICK_FROM_SEC(4)
111
0
#define FLAC_MAX_SLOW_PREROLL VLC_TICK_FROM_SEC(45)
112
113
/*****************************************************************************
114
 * Open: initializes ES structures
115
 *****************************************************************************/
116
static int Open( vlc_object_t * p_this )
117
0
{
118
0
    demux_t     *p_demux = (demux_t*)p_this;
119
0
    demux_sys_t *p_sys;
120
0
    const uint8_t *p_peek;
121
0
    es_format_t fmt;
122
123
    /* Have a peep at the show. */
124
0
    if( vlc_stream_Peek( p_demux->s, &p_peek, 4 ) < 4 ) return VLC_EGENERIC;
125
126
0
    if( p_peek[0]!='f' || p_peek[1]!='L' || p_peek[2]!='a' || p_peek[3]!='C' )
127
0
    {
128
0
        if( !p_demux->obj.force
129
0
         && !demux_IsContentType( p_demux, "audio/flac" ) )
130
0
            return VLC_EGENERIC;
131
132
        /* User forced */
133
0
        msg_Err( p_demux, "this doesn't look like a flac stream, "
134
0
                 "continuing anyway" );
135
0
    }
136
137
0
    p_sys = malloc( sizeof( demux_sys_t ) );
138
0
    if( unlikely(p_sys == NULL) )
139
0
        return VLC_ENOMEM;
140
141
0
    p_demux->pf_demux   = Demux;
142
0
    p_demux->pf_control = Control;
143
0
    p_demux->p_sys      = p_sys;
144
0
    p_sys->b_start = true;
145
0
    p_sys->i_next_block_flags = 0;
146
0
    p_sys->p_packetizer = NULL;
147
0
    p_sys->p_meta = NULL;
148
0
    p_sys->i_length = 0;
149
0
    p_sys->i_pts = VLC_TICK_INVALID;
150
0
    p_sys->b_stream_info = false;
151
0
    p_sys->p_es = NULL;
152
0
    p_sys->p_current_block = NULL;
153
0
    TAB_INIT( p_sys->i_seekpoint, p_sys->seekpoint );
154
0
    TAB_INIT( p_sys->i_attachments, p_sys->attachments);
155
0
    TAB_INIT( p_sys->i_title_seekpoints, p_sys->pp_title_seekpoints );
156
0
    p_sys->i_cover_idx = 0;
157
0
    p_sys->i_cover_score = 0;
158
159
0
    es_format_Init( &fmt, AUDIO_ES, VLC_CODEC_FLAC );
160
0
    fmt.i_id = 0;
161
162
    /* We need to read and store the STREAMINFO metadata into fmt extra */
163
0
    if( ParseHeaders( p_demux, &fmt ) )
164
0
        goto error;
165
166
    /* Load the FLAC packetizer */
167
0
    p_sys->p_packetizer = demux_PacketizerNew( VLC_OBJECT(p_demux), &fmt, "flac" );
168
0
    if( !p_sys->p_packetizer )
169
0
        goto error;
170
171
0
    if( p_sys->i_cover_idx < p_sys->i_attachments )
172
0
    {
173
0
        char psz_url[128];
174
0
        if( !p_sys->p_meta )
175
0
            p_sys->p_meta = vlc_meta_New();
176
0
        snprintf( psz_url, sizeof(psz_url), "attachment://%s",
177
0
                  p_sys->attachments[p_sys->i_cover_idx]->psz_name );
178
0
        vlc_meta_Set( p_sys->p_meta, vlc_meta_ArtworkURL, psz_url );
179
0
    }
180
181
0
    p_sys->p_es = es_out_Add( p_demux->out, &fmt );
182
0
    if( !p_sys->p_es )
183
0
        goto error;
184
185
0
    return VLC_SUCCESS;
186
0
error:
187
0
    Close( p_this );
188
0
    return VLC_EGENERIC;
189
0
}
190
191
/*****************************************************************************
192
 * Close: frees unused data
193
 *****************************************************************************/
194
static void Close( vlc_object_t * p_this )
195
0
{
196
0
    demux_t     *p_demux = (demux_t*)p_this;
197
0
    demux_sys_t *p_sys = p_demux->p_sys;
198
199
0
    if( p_sys->p_current_block )
200
0
        block_Release( p_sys->p_current_block );
201
202
0
    for( int i = 0; i < p_sys->i_seekpoint; i++ )
203
0
        free(p_sys->seekpoint[i]);
204
0
    TAB_CLEAN( p_sys->i_seekpoint, p_sys->seekpoint );
205
206
0
    for( int i = 0; i < p_sys->i_attachments; i++ )
207
0
        vlc_input_attachment_Release( p_sys->attachments[i] );
208
0
    TAB_CLEAN( p_sys->i_attachments, p_sys->attachments);
209
210
0
    for( int i = 0; i < p_sys->i_title_seekpoints; i++ )
211
0
        vlc_seekpoint_Delete( p_sys->pp_title_seekpoints[i] );
212
0
    TAB_CLEAN( p_sys->i_title_seekpoints, p_sys->pp_title_seekpoints );
213
214
    /* Delete the decoder */
215
0
    if( p_sys->p_packetizer )
216
0
        demux_PacketizerDestroy( p_sys->p_packetizer );
217
218
0
    if( p_sys->p_meta )
219
0
        vlc_meta_Delete( p_sys->p_meta );
220
0
    free( p_sys );
221
0
}
222
223
static block_t *GetPacketizedBlock( decoder_t *p_packetizer,
224
                                    const struct flac_stream_info *streaminfo,
225
                                    block_t **pp_current_block )
226
0
{
227
0
    block_t *p_block = p_packetizer->pf_packetize( p_packetizer, pp_current_block );
228
0
    if( p_block )
229
0
    {
230
0
        if( p_block->i_buffer >= FLAC_HEADER_SIZE_MIN && p_block->i_buffer < INT_MAX )
231
0
        {
232
0
            struct flac_header_info headerinfo = { .i_pts = VLC_TICK_INVALID };
233
0
            int i_ret = FLAC_ParseSyncInfo( p_block->p_buffer, p_block->i_buffer,
234
0
                                            streaminfo, NULL, &headerinfo );
235
0
            assert( i_ret != 0 ); /* Same as packetizer */
236
            /* Use Frame PTS, not the interpolated one */
237
0
            p_block->i_dts = p_block->i_pts = headerinfo.i_pts;
238
0
        }
239
0
    }
240
0
    return p_block;
241
0
}
242
243
static void FlushPacketizer( decoder_t *p_packetizer )
244
0
{
245
0
    if( p_packetizer->pf_flush )
246
0
        p_packetizer->pf_flush( p_packetizer );
247
0
    else
248
0
    {
249
0
        block_t *p_block_out;
250
0
        while( (p_block_out = p_packetizer->pf_packetize( p_packetizer, NULL )) )
251
0
            block_Release( p_block_out );
252
0
    }
253
0
}
254
255
static void Reset( demux_sys_t *p_sys )
256
0
{
257
0
    p_sys->i_pts = VLC_TICK_INVALID;
258
259
0
    FlushPacketizer( p_sys->p_packetizer );
260
0
    if( p_sys->p_current_block )
261
0
    {
262
0
        block_Release( p_sys->p_current_block );
263
0
        p_sys->p_current_block = NULL;
264
0
    }
265
0
}
266
267
static int RefineSeek( demux_t *p_demux, vlc_tick_t i_time, double i_bytemicrorate,
268
                       uint64_t i_lowpos, uint64_t i_highpos )
269
0
{
270
0
    demux_sys_t *p_sys = p_demux->p_sys;
271
0
    bool b_found = false;
272
0
    block_t *p_block_out;
273
0
    block_t *p_block_in;
274
275
0
    unsigned i_frame_size = FLAC_FRAME_SIZE_MIN;
276
277
0
    bool b_canfastseek = false;
278
0
    vlc_stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &b_canfastseek );
279
280
0
    uint64_t i_start_pos = vlc_stream_Tell( p_demux->s );
281
282
0
    while( !b_found )
283
0
    {
284
0
        FlushPacketizer( p_sys->p_packetizer );
285
286
0
        p_block_out = NULL;
287
0
        p_block_in = NULL;
288
289
0
        while( !p_block_out )
290
0
        {
291
0
            if( !p_block_in )
292
0
            {
293
0
                if( !(p_block_in = vlc_stream_Block( p_demux->s, i_frame_size )) )
294
0
                    break;
295
0
            }
296
297
0
            p_block_out = GetPacketizedBlock( p_sys->p_packetizer,
298
0
                                              p_sys->b_stream_info ? &p_sys->stream_info : NULL,
299
0
                                             &p_block_in );
300
0
        }
301
302
0
        if( !p_block_out )
303
0
        {
304
0
            if( p_block_in )
305
0
                block_Release( p_block_in );
306
0
            break;
307
0
        }
308
309
0
        if( p_block_out->i_buffer > i_frame_size )
310
0
            i_frame_size = p_block_out->i_buffer;
311
312
        /* If we are further than wanted block */
313
0
        if( p_block_out->i_dts >= i_time )
314
0
        {
315
0
            vlc_tick_t i_diff = p_block_out->i_dts - i_time;
316
            /* Not in acceptable approximation range */
317
0
            if( i_diff > VLC_TICK_FROM_MS(100) && i_diff / i_bytemicrorate > i_frame_size )
318
0
            {
319
0
                i_highpos = i_start_pos;
320
0
                i_start_pos -= ( i_diff / i_bytemicrorate );
321
0
                i_start_pos = __MAX(i_start_pos, i_lowpos + i_frame_size);
322
0
            }
323
0
            else b_found = true;
324
0
        }
325
0
        else
326
0
        {
327
0
            vlc_tick_t i_diff = i_time - p_block_out->i_dts;
328
            /* Not in acceptable NEXT_TIME demux range */
329
0
            if( i_diff >= ((b_canfastseek) ? FLAC_MAX_PREROLL : FLAC_MAX_SLOW_PREROLL) &&
330
0
                i_diff / i_bytemicrorate > i_frame_size )
331
0
            {
332
0
                i_lowpos = i_start_pos;
333
0
                i_start_pos += ( i_diff / i_bytemicrorate );
334
0
                i_start_pos = __MIN(i_start_pos, i_highpos - i_frame_size);
335
0
            }
336
0
            else b_found = true;
337
0
        }
338
339
0
        if( p_block_out )
340
0
            block_Release( p_block_out );
341
0
        if( p_block_in )
342
0
            block_Release( p_block_in );
343
344
0
        if( !b_found )
345
0
        {
346
0
            if( i_highpos < i_lowpos || i_highpos - i_lowpos <= i_frame_size )
347
0
                break;
348
349
0
            if( VLC_SUCCESS != vlc_stream_Seek( p_demux->s, i_start_pos ) )
350
0
                break;
351
0
        }
352
0
    }
353
354
0
    return b_found ? VLC_SUCCESS : VLC_EGENERIC;
355
0
}
356
357
/*****************************************************************************
358
 * Demux: reads and demuxes data packets
359
 *****************************************************************************
360
 * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
361
 *****************************************************************************/
362
static int Demux( demux_t *p_demux )
363
0
{
364
0
    demux_sys_t *p_sys = p_demux->p_sys;
365
0
    block_t *p_block_out;
366
367
0
    if( p_sys->p_current_block == NULL )
368
0
        p_sys->p_current_block = vlc_stream_Block( p_demux->s, FLAC_PACKET_SIZE );
369
370
0
    bool b_eof = (p_sys->p_current_block == NULL);
371
372
0
    if ( !b_eof )
373
0
    {
374
0
        p_sys->p_current_block->i_flags = p_sys->i_next_block_flags;
375
0
        p_sys->i_next_block_flags = 0;
376
0
        p_sys->p_current_block->i_pts =
377
0
        p_sys->p_current_block->i_dts = p_sys->b_start ? VLC_TICK_0 : VLC_TICK_INVALID;
378
0
    }
379
380
0
    while( (p_block_out = GetPacketizedBlock( p_sys->p_packetizer,
381
0
                            p_sys->b_stream_info ? &p_sys->stream_info : NULL,
382
0
                            p_sys->p_current_block ? &p_sys->p_current_block : NULL ) ) )
383
0
    {
384
        /* Only clear on output when packet is accepted as sync #17111 */
385
0
        p_sys->b_start = false;
386
0
        while( p_block_out )
387
0
        {
388
0
            block_t *p_next = p_block_out->p_next;
389
390
0
            p_block_out->p_next = NULL;
391
392
            /* set PCR */
393
0
            if( unlikely(p_sys->i_pts == VLC_TICK_INVALID) )
394
0
                es_out_SetPCR( p_demux->out, __MAX(p_block_out->i_dts - 1, VLC_TICK_0) );
395
396
0
            if(p_block_out->i_dts != VLC_TICK_INVALID)
397
0
                p_sys->i_pts = p_block_out->i_dts;
398
399
0
            es_out_Send( p_demux->out, p_sys->p_es, p_block_out );
400
401
0
            es_out_SetPCR( p_demux->out, p_sys->i_pts );
402
403
0
            p_block_out = p_next;
404
0
        }
405
0
        break;
406
0
    }
407
408
0
    return b_eof ? VLC_DEMUXER_EOF : VLC_DEMUXER_SUCCESS;
409
0
}
410
411
/*****************************************************************************
412
 * Control:
413
 *****************************************************************************/
414
static vlc_tick_t ControlGetLength( demux_t *p_demux )
415
0
{
416
0
    demux_sys_t *p_sys = p_demux->p_sys;
417
0
    const uint64_t i_size = stream_Size(p_demux->s) - p_sys->i_data_pos;
418
0
    vlc_tick_t i_length = p_sys->i_length;
419
0
    int i;
420
421
    /* Try to fix length using seekpoint and current size for truncated file */
422
0
    for( i = p_sys->i_seekpoint-1; i >= 0; i-- )
423
0
    {
424
0
        flac_seekpoint_t *s = p_sys->seekpoint[i];
425
0
        if( s->i_byte_offset <= i_size )
426
0
        {
427
0
            if( i+1 < p_sys->i_seekpoint )
428
0
            {
429
                /* Broken file */
430
0
                flac_seekpoint_t *n = p_sys->seekpoint[i+1];
431
0
                assert( n->i_byte_offset != s->i_byte_offset); /* Should be ensured by ParseSeekTable */
432
0
                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);
433
0
            }
434
0
            break;
435
0
        }
436
0
    }
437
0
    return i_length;
438
0
}
439
440
static vlc_tick_t ControlGetTime( demux_t *p_demux )
441
0
{
442
0
    demux_sys_t *p_sys = p_demux->p_sys;
443
0
    return p_sys->i_pts;
444
0
}
445
446
static int ControlSetTime( demux_t *p_demux, vlc_tick_t i_time )
447
0
{
448
0
    demux_sys_t *p_sys = p_demux->p_sys;
449
0
    bool b_seekable;
450
0
    int i;
451
452
    /* */
453
0
    vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_seekable );
454
0
    if( !b_seekable )
455
0
        return VLC_EGENERIC;
456
457
0
    const vlc_tick_t i_length = ControlGetLength( p_demux );
458
0
    if( i_length <= 0 )
459
0
        return VLC_EGENERIC;
460
461
0
    const uint64_t i_stream_size = stream_Size( p_demux->s );
462
0
    if( i_stream_size <= p_sys->i_data_pos )
463
0
        return VLC_EGENERIC;
464
465
0
    const double i_bytemicrorate = (double) i_length / (i_stream_size - p_sys->i_data_pos);
466
0
    if( i_bytemicrorate == 0 )
467
0
        return VLC_EGENERIC;
468
469
0
    uint64_t i_lower = p_sys->i_data_pos;
470
0
    uint64_t i_upper = i_stream_size;
471
0
    uint64_t i_start_pos;
472
473
0
    assert( p_sys->i_seekpoint > 0 );   /* ReadMeta ensure at least (0,0) */
474
0
    if( p_sys->i_seekpoint > 1 )
475
0
    {
476
        /* lookup base offset */
477
0
        for( i = p_sys->i_seekpoint-1; i >= 0; i-- )
478
0
        {
479
0
            if( p_sys->seekpoint[i]->i_time_offset <= i_time )
480
0
                break;
481
0
        }
482
483
0
        i_lower = p_sys->seekpoint[0]->i_byte_offset + p_sys->i_data_pos;
484
0
        if( i+1 < p_sys->i_seekpoint )
485
0
            i_upper = p_sys->seekpoint[i+1]->i_byte_offset + p_sys->i_data_pos;
486
487
0
        i_start_pos = i_lower;
488
0
    }
489
0
    else
490
0
    {
491
0
        i_start_pos = i_time / i_bytemicrorate;
492
0
    }
493
494
0
    if( VLC_SUCCESS != vlc_stream_Seek( p_demux->s, i_start_pos ) )
495
0
        return VLC_EGENERIC;
496
497
0
    int i_ret = RefineSeek( p_demux, i_time, i_bytemicrorate, i_lower, i_upper );
498
0
    if( i_ret == VLC_SUCCESS )
499
0
    {
500
0
        p_sys->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
501
0
        Reset( p_sys );
502
0
        es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, i_time );
503
0
    }
504
505
0
    return i_ret;
506
0
}
507
508
static int Control( demux_t *p_demux, int i_query, va_list args )
509
0
{
510
0
    demux_sys_t *p_sys = p_demux->p_sys;
511
512
0
    if( i_query == DEMUX_GET_META )
513
0
    {
514
0
        vlc_meta_t *p_meta = va_arg( args, vlc_meta_t * );
515
0
        if( p_sys->p_meta )
516
0
            vlc_meta_Merge( p_meta, p_sys->p_meta );
517
0
        return VLC_SUCCESS;
518
0
    }
519
0
    else if( i_query == DEMUX_HAS_UNSUPPORTED_META )
520
0
    {
521
0
        bool *pb_bool = va_arg( args, bool* );
522
0
        *pb_bool = true;
523
0
        return VLC_SUCCESS;
524
0
    }
525
0
    else if( i_query == DEMUX_GET_LENGTH )
526
0
    {
527
0
        *va_arg( args, vlc_tick_t * ) = ControlGetLength( p_demux );
528
0
        return VLC_SUCCESS;
529
0
    }
530
0
    else if( i_query == DEMUX_SET_TIME )
531
0
    {
532
0
        return ControlSetTime( p_demux, va_arg( args, vlc_tick_t ) );
533
0
    }
534
0
    else if( i_query == DEMUX_SET_POSITION )
535
0
    {
536
0
        const double f = va_arg( args, double );
537
0
        vlc_tick_t i_length = ControlGetLength( p_demux );
538
0
        int i_ret;
539
0
        if( i_length > 0 )
540
0
        {
541
0
            i_ret = ControlSetTime( p_demux, i_length * f );
542
0
            if( i_ret == VLC_SUCCESS )
543
0
                return i_ret;
544
0
        }
545
        /* just byte pos seek */
546
0
        i_ret = vlc_stream_Seek( p_demux->s, (int64_t) (f * stream_Size( p_demux->s )) );
547
0
        if( i_ret == VLC_SUCCESS )
548
0
        {
549
0
            p_sys->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
550
0
            Reset( p_sys );
551
0
        }
552
0
        return i_ret;
553
0
    }
554
0
    else if( i_query == DEMUX_GET_TIME )
555
0
    {
556
0
        *va_arg( args, vlc_tick_t * ) = ControlGetTime( p_demux );
557
0
        return VLC_SUCCESS;
558
0
    }
559
0
    else if( i_query == DEMUX_GET_POSITION )
560
0
    {
561
0
        const vlc_tick_t i_length = ControlGetLength(p_demux);
562
0
        if( i_length > 0 )
563
0
        {
564
0
            vlc_tick_t current = ControlGetTime(p_demux);
565
0
            if( current <= i_length )
566
0
            {
567
0
                *(va_arg( args, double * )) = (double)current / (double)i_length;
568
0
                return VLC_SUCCESS;
569
0
            }
570
0
        }
571
        /* Else fallback on byte position */
572
0
    }
573
0
    else if( i_query == DEMUX_GET_ATTACHMENTS )
574
0
    {
575
0
        input_attachment_t ***ppp_attach =
576
0
            va_arg( args, input_attachment_t *** );
577
0
        int *pi_int = va_arg( args, int * );
578
579
0
        if( p_sys->i_attachments <= 0 )
580
0
            return VLC_EGENERIC;
581
582
0
        *ppp_attach = vlc_alloc( p_sys->i_attachments, sizeof(input_attachment_t*) );
583
0
        if( !*ppp_attach )
584
0
            return VLC_EGENERIC;
585
0
        *pi_int = p_sys->i_attachments;
586
0
        for( int i = 0; i < p_sys->i_attachments; i++ )
587
0
            (*ppp_attach)[i] = vlc_input_attachment_Hold( p_sys->attachments[i] );
588
0
        return VLC_SUCCESS;
589
0
    }
590
0
    else if( i_query == DEMUX_GET_TITLE_INFO )
591
0
    {
592
0
        input_title_t ***ppp_title = va_arg( args, input_title_t *** );
593
0
        int *pi_int = va_arg( args, int * );
594
0
        int *pi_title_offset = va_arg( args, int * );
595
0
        int *pi_seekpoint_offset = va_arg( args, int * );
596
597
0
        if( !p_sys->i_title_seekpoints )
598
0
            return VLC_EGENERIC;
599
600
0
        *pi_int = 1;
601
0
        *ppp_title = malloc( sizeof(input_title_t*) );
602
0
        if(!*ppp_title)
603
0
            return VLC_EGENERIC;
604
605
0
        input_title_t *p_title = (*ppp_title)[0] = vlc_input_title_New();
606
0
        if(!p_title)
607
0
        {
608
0
            free(*ppp_title);
609
0
            return VLC_EGENERIC;
610
0
        }
611
612
0
        p_title->seekpoint = vlc_alloc( p_sys->i_title_seekpoints, sizeof(seekpoint_t*) );
613
0
        if(!p_title->seekpoint)
614
0
        {
615
0
            vlc_input_title_Delete(p_title);
616
0
            free(*ppp_title);
617
0
            return VLC_EGENERIC;
618
0
        }
619
620
0
        p_title->i_seekpoint = p_sys->i_title_seekpoints;
621
0
        for( int i = 0; i < p_title->i_seekpoint; i++ )
622
0
            p_title->seekpoint[i] = vlc_seekpoint_Duplicate( p_sys->pp_title_seekpoints[i] );
623
624
0
        *pi_title_offset = 0;
625
0
        *pi_seekpoint_offset = 0;
626
627
0
        return VLC_SUCCESS;
628
0
    }
629
0
    else if( i_query == DEMUX_SET_TITLE )
630
0
    {
631
0
        const int i_title = va_arg( args, int );
632
0
        if( i_title != 0 )
633
0
            return VLC_EGENERIC;
634
0
        return VLC_SUCCESS;
635
0
    }
636
0
    else if( i_query == DEMUX_SET_SEEKPOINT )
637
0
    {
638
0
        const int i_seekpoint = va_arg( args, int );
639
0
        if( !p_sys->i_title_seekpoints || i_seekpoint >= p_sys->i_title_seekpoints )
640
0
            return VLC_EGENERIC;
641
0
        return ControlSetTime( p_demux, p_sys->pp_title_seekpoints[i_seekpoint]->i_time_offset );
642
0
    }
643
644
0
    return demux_vaControlHelper( p_demux->s, p_sys->i_data_pos, -1,
645
0
                                   8*0, 1, i_query, args );
646
0
}
647
648
enum
649
{
650
    META_STREAMINFO = 0,
651
    META_SEEKTABLE = 3,
652
    META_COMMENT = 4,
653
    META_PICTURE = 6,
654
};
655
656
static inline int Get24bBE( const uint8_t *p )
657
0
{
658
0
    return (p[0] << 16)|(p[1] << 8)|(p[2]);
659
0
}
660
661
static void ParseSeekTable( demux_t *p_demux, const uint8_t *p_data, size_t i_data,
662
                            unsigned i_sample_rate );
663
static void ParseComment( demux_t *, const uint8_t *p_data, size_t i_data );
664
static void ParsePicture( demux_t *, const uint8_t *p_data, size_t i_data );
665
666
static int  ParseHeaders( demux_t *p_demux, es_format_t *p_fmt )
667
0
{
668
0
    demux_sys_t *p_sys = p_demux->p_sys;
669
0
    ssize_t i_peek;
670
0
    const uint8_t *p_peek;
671
0
    bool b_last;
672
673
    /* Be sure we have seekpoint 0 */
674
0
    flac_seekpoint_t *s = xmalloc( sizeof (*s) );
675
0
    s->i_time_offset = 0;
676
0
    s->i_byte_offset = 0;
677
0
    TAB_APPEND( p_sys->i_seekpoint, p_sys->seekpoint, s );
678
679
0
    uint8_t header[4];
680
0
    if( vlc_stream_Read( p_demux->s, header, 4) < 4)
681
0
        return VLC_EGENERIC;
682
683
0
    if (memcmp(header, "fLaC", 4))
684
0
        return VLC_EGENERIC;
685
686
0
    b_last = 0;
687
0
    while( !b_last )
688
0
    {
689
0
        int i_len;
690
0
        int i_type;
691
692
0
        i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 4 );
693
0
        if( i_peek < 4 )
694
0
            break;
695
0
        b_last = p_peek[0]&0x80;
696
0
        i_type = p_peek[0]&0x7f;
697
0
        i_len  = Get24bBE( &p_peek[1] );
698
699
0
        if( i_type == META_STREAMINFO && p_fmt->p_extra == NULL )
700
0
        {
701
0
            if( i_len != FLAC_STREAMINFO_SIZE ) {
702
0
                msg_Err( p_demux, "invalid size %d for a STREAMINFO metadata block", i_len );
703
0
                return VLC_EGENERIC;
704
0
            }
705
706
0
            p_fmt->p_extra = malloc( FLAC_STREAMINFO_SIZE );
707
0
            if( p_fmt->p_extra == NULL )
708
0
                return VLC_EGENERIC;
709
710
0
            if( vlc_stream_Read( p_demux->s, NULL, 4 ) != 4 )
711
0
            {
712
0
                FREENULL( p_fmt->p_extra );
713
0
                return VLC_EGENERIC;
714
0
            }
715
0
            if( vlc_stream_Read( p_demux->s, p_fmt->p_extra,
716
0
                                 FLAC_STREAMINFO_SIZE ) != FLAC_STREAMINFO_SIZE )
717
0
            {
718
0
                msg_Err( p_demux, "failed to read STREAMINFO metadata block" );
719
0
                FREENULL( p_fmt->p_extra );
720
0
                return VLC_EGENERIC;
721
0
            }
722
0
            p_fmt->i_extra = FLAC_STREAMINFO_SIZE;
723
724
            /* */
725
0
            p_sys->b_stream_info = true;
726
0
            FLAC_ParseStreamInfo( (uint8_t *) p_fmt->p_extra, &p_sys->stream_info );
727
728
0
            p_fmt->audio.i_rate = p_sys->stream_info.sample_rate;
729
0
            p_fmt->audio.i_channels = p_sys->stream_info.channels;
730
0
            p_fmt->audio.i_bitspersample = p_sys->stream_info.bits_per_sample;
731
0
            if( p_sys->stream_info.sample_rate > 0 )
732
0
                p_sys->i_length = vlc_tick_from_samples(p_sys->stream_info.total_samples,
733
0
                                  p_sys->stream_info.sample_rate);
734
735
0
            continue;
736
0
        }
737
0
        else if( i_type == META_SEEKTABLE )
738
0
        {
739
0
            i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 4+i_len );
740
0
            if( i_peek == 4+i_len )
741
0
                ParseSeekTable( p_demux, p_peek, i_peek, p_fmt->audio.i_rate );
742
0
        }
743
0
        else if( i_type == META_COMMENT )
744
0
        {
745
0
            i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 4+i_len );
746
0
            if( i_peek == 4+i_len )
747
0
                ParseComment( p_demux, p_peek, i_peek );
748
749
0
            vlc_replay_gain_CopyFromMeta( &p_fmt->audio_replay_gain, p_sys->p_meta );
750
0
        }
751
0
        else if( i_type == META_PICTURE )
752
0
        {
753
0
            i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 4+i_len );
754
0
            if( i_peek == 4+i_len )
755
0
                ParsePicture( p_demux, p_peek, i_peek );
756
0
        }
757
758
0
        if( vlc_stream_Read( p_demux->s, NULL, 4+i_len ) != (4+i_len) )
759
0
            break;
760
0
    }
761
762
    /* */
763
0
    p_sys->i_data_pos = vlc_stream_Tell( p_demux->s );
764
765
0
    if ( p_fmt->p_extra == NULL )
766
0
        return VLC_EGENERIC;
767
768
0
    return VLC_SUCCESS;
769
0
}
770
771
static void ParseSeekTable( demux_t *p_demux, const uint8_t *p_data, size_t i_data,
772
                            unsigned i_sample_rate )
773
0
{
774
0
    demux_sys_t *p_sys = p_demux->p_sys;
775
0
    flac_seekpoint_t *s;
776
0
    size_t i;
777
778
0
    if( i_sample_rate == 0 )
779
0
        return;
780
781
    /* */
782
0
    for( i = 0; i < (i_data-4)/18; i++ )
783
0
    {
784
0
        const int64_t i_sample = GetQWBE( &p_data[4+18*i+0] );
785
0
        int j;
786
787
0
        if( i_sample < 0 || i_sample >= INT64_MAX ||
788
0
            GetQWBE( &p_data[4+18*i+8] ) < FLAC_STREAMINFO_SIZE )
789
0
            break;
790
791
0
        s = xmalloc( sizeof (*s) );
792
0
        s->i_time_offset = vlc_tick_from_samples(i_sample, i_sample_rate);
793
0
        s->i_byte_offset = GetQWBE( &p_data[4+18*i+8] );
794
795
        /* Check for duplicate entry */
796
0
        for( j = 0; j < p_sys->i_seekpoint; j++ )
797
0
        {
798
0
            if( p_sys->seekpoint[j]->i_time_offset == s->i_time_offset ||
799
0
                p_sys->seekpoint[j]->i_byte_offset == s->i_byte_offset )
800
0
            {
801
0
                free( s );
802
0
                s = NULL;
803
0
                break;
804
0
            }
805
0
        }
806
0
        if( s )
807
0
        {
808
0
            TAB_APPEND( p_sys->i_seekpoint, p_sys->seekpoint, s );
809
0
        }
810
0
    }
811
    /* TODO sort it by size and remove wrong seek entry (time not increasing) */
812
0
}
813
814
static void ParseComment( demux_t *p_demux, const uint8_t *p_data, size_t i_data )
815
0
{
816
0
    demux_sys_t *p_sys = p_demux->p_sys;
817
818
0
    if( i_data < 4 )
819
0
        return;
820
821
0
    vorbis_ParseComment( NULL, &p_sys->p_meta, &p_data[4], i_data - 4,
822
0
        &p_sys->i_attachments, &p_sys->attachments,
823
0
        &p_sys->i_cover_score, &p_sys->i_cover_idx,
824
0
        &p_sys->i_title_seekpoints, &p_sys->pp_title_seekpoints );
825
0
}
826
827
static void ParsePicture( demux_t *p_demux, const uint8_t *p_data, size_t i_data )
828
0
{
829
0
    demux_sys_t *p_sys = p_demux->p_sys;
830
831
0
    i_data -= 4; p_data += 4;
832
833
0
    input_attachment_t *p_attachment = ParseFlacPicture( p_data, i_data,
834
0
        p_sys->i_attachments, &p_sys->i_cover_score, &p_sys->i_cover_idx );
835
0
    if( p_attachment == NULL )
836
0
        return;
837
838
0
    TAB_APPEND( p_sys->i_attachments, p_sys->attachments, p_attachment );
839
0
}