Coverage Report

Created: 2023-06-07 07:26

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