Coverage Report

Created: 2026-06-09 09:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/modules/demux/asf/asf.c
Line
Count
Source
1
/*****************************************************************************
2
 * asf.c : ASF demux module
3
 *****************************************************************************
4
 * Copyright © 2002-2004, 2006-2008, 2010 VLC authors and VideoLAN
5
 *
6
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
7
 *
8
 * This program is free software; you can redistribute it and/or modify it
9
 * under the terms of the GNU Lesser General Public License as published by
10
 * the Free Software Foundation; either version 2.1 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with this program; if not, write to the Free Software Foundation,
20
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21
 *****************************************************************************/
22
23
/*****************************************************************************
24
 * Preamble
25
 *****************************************************************************/
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_dialog.h>
36
37
#include <vlc_meta.h>                  /* vlc_meta_Set*, vlc_meta_New */
38
#include <vlc_input.h>                 /* seekpoint_t, vlc_seekpoint_New */
39
#include <vlc_access.h>                /* GET_PRIVATE_ID_STATE */
40
#include <vlc_codecs.h>                /* VLC_BITMAPINFOHEADER, WAVEFORMATEX */
41
#include <vlc_vout.h>
42
43
#include <limits.h>
44
#include <stdckdint.h>
45
#include <stdlib.h>
46
47
#include "asfpacket.h"
48
#include "libasf.h"
49
#include "assert.h"
50
51
/* TODO
52
 *  - add support for the newly added object: language, bitrate,
53
 *                                            extended stream properties.
54
 */
55
56
/*****************************************************************************
57
 * Module descriptor
58
 *****************************************************************************/
59
static int  Open  ( vlc_object_t * );
60
static void Close ( vlc_object_t * );
61
62
168
vlc_module_begin ()
63
84
    set_subcategory( SUBCAT_INPUT_DEMUX )
64
84
    set_description( N_("ASF/WMV demuxer") )
65
84
    set_capability( "demux", 200 )
66
168
    set_callbacks( Open, Close )
67
84
    add_shortcut( "asf", "wmv" )
68
84
    add_file_extension("asf")
69
84
    add_file_extension("wma")
70
84
    add_file_extension("wmv")
71
84
vlc_module_end ()
72
73
74
/*****************************************************************************
75
 * Local prototypes
76
 *****************************************************************************/
77
static int Demux  ( demux_t * );
78
static int Control( demux_t *, int i_query, va_list args );
79
80
24.8k
#define MAX_ASF_TRACKS (ASF_MAX_STREAMNUMBER + 1)
81
#define ASF_PREROLL_FROM_CURRENT -1
82
83
/* callbacks for packet parser */
84
static void Packet_UpdateTime( asf_packet_sys_t *p_packetsys, uint8_t i_stream_number,
85
                               vlc_tick_t i_time );
86
static void Packet_SetSendTime( asf_packet_sys_t *p_packetsys, vlc_tick_t i_time);
87
static bool Block_Dequeue( demux_t *p_demux, vlc_tick_t i_nexttime );
88
static asf_track_info_t * Packet_GetTrackInfo( asf_packet_sys_t *p_packetsys,
89
                                               uint8_t i_stream_number );
90
static bool Packet_DoSkip( asf_packet_sys_t *p_packetsys, uint8_t i_stream_number, bool b_packet_keyframe );
91
static void Packet_Enqueue( asf_packet_sys_t *p_packetsys, uint8_t i_stream_number, block_t **pp_frame );
92
static void Packet_SetAR( asf_packet_sys_t *p_packetsys, uint8_t i_stream_number,
93
                          uint8_t i_ratio_x, uint8_t i_ratio_y );
94
95
typedef struct
96
{
97
    int i_cat;
98
99
    es_out_id_t     *p_es;
100
    es_format_t     *p_fmt; /* format backup for video changes */
101
    bool             b_selected;
102
103
    vlc_tick_t       i_time; /* track time*/
104
105
    asf_track_info_t info;
106
107
    struct
108
    {
109
        block_t     *p_first;
110
        block_t    **pp_last;
111
    } queue;
112
113
} asf_track_t;
114
115
typedef struct
116
{
117
    vlc_tick_t          i_time;     /* s */
118
    vlc_tick_t          i_sendtime;
119
    vlc_tick_t          i_length;   /* length of file */
120
    uint64_t            i_bitrate;  /* global file bitrate */
121
    bool                b_eos;      /* end of current stream */
122
    bool                b_eof;      /* end of current media */
123
124
    asf_object_root_t            *p_root;
125
    asf_object_file_properties_t *p_fp;
126
127
    unsigned int        i_track;
128
    asf_track_t         *track[MAX_ASF_TRACKS]; /* track number is stored on 7 bits */
129
130
    uint64_t            i_data_begin;
131
    uint64_t            i_data_end;
132
133
    bool                b_index;
134
    bool                b_canfastseek;
135
    bool                b_pcr_sent;
136
    uint8_t             i_seek_track;
137
    uint8_t             i_access_selected_track[ES_CATEGORY_COUNT]; /* mms, depends on access algorithm */
138
    unsigned int        i_wait_keyframe;
139
140
    vlc_tick_t          i_preroll_start;
141
142
    asf_packet_sys_t    packet_sys;
143
144
    vlc_meta_t          *meta;
145
146
    /* timecode seekpoints built from WM TC index */
147
    seekpoint_t        **pp_seekpoints;
148
    int                  i_seekpoints;
149
} demux_sys_t;
150
151
static int      DemuxInit( demux_t * );
152
static void     DemuxEnd( demux_t * );
153
154
static void     FlushQueue( asf_track_t * );
155
static void     FlushQueues( demux_t *p_demux );
156
157
/*****************************************************************************
158
 * Open: check file and initializes ASF structures
159
 *****************************************************************************/
160
static int Open( vlc_object_t * p_this )
161
939
{
162
939
    demux_t     *p_demux = (demux_t *)p_this;
163
939
    demux_sys_t *p_sys;
164
939
    vlc_guid_t      guid;
165
939
    const uint8_t     *p_peek;
166
167
    /* A little test to see if it could be a asf stream */
168
939
    if( vlc_stream_Peek( p_demux->s, &p_peek, 16 ) < 16 ) return VLC_EGENERIC;
169
170
939
    ASF_GetGUID( &guid, p_peek );
171
939
    if( !guidcmp( &guid, &asf_object_header_guid ) ) return VLC_EGENERIC;
172
173
    /* Set p_demux fields */
174
39
    p_demux->pf_demux = Demux;
175
39
    p_demux->pf_control = Control;
176
39
    p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) );
177
178
    /* Load the headers */
179
39
    if( DemuxInit( p_demux ) )
180
16
    {
181
16
        free( p_sys );
182
16
        return VLC_EGENERIC;
183
16
    }
184
185
23
    p_sys->packet_sys.priv = p_demux;
186
23
    p_sys->packet_sys.s = p_demux->s;
187
23
    p_sys->packet_sys.logger = p_demux->obj.logger;
188
23
    p_sys->packet_sys.b_deduplicate = false;
189
23
    p_sys->packet_sys.b_can_hold_multiple_packets = false;
190
23
    p_sys->packet_sys.pf_doskip = Packet_DoSkip;
191
23
    p_sys->packet_sys.pf_send = Packet_Enqueue;
192
23
    p_sys->packet_sys.pf_gettrackinfo = Packet_GetTrackInfo;
193
23
    p_sys->packet_sys.pf_updatetime = Packet_UpdateTime;
194
23
    p_sys->packet_sys.pf_updatesendtime = Packet_SetSendTime;
195
23
    p_sys->packet_sys.pf_setaspectratio = Packet_SetAR;
196
197
23
    return VLC_SUCCESS;
198
39
}
199
200
/*****************************************************************************
201
 * Demux: read packet and send them to decoders
202
 *****************************************************************************/
203
2.28k
#define CHUNK VLC_TICK_FROM_MS(100)
204
static int Demux( demux_t *p_demux )
205
54
{
206
54
    demux_sys_t *p_sys = p_demux->p_sys;
207
208
324
    for( int i=0; i<ES_CATEGORY_COUNT; i++ )
209
270
    {
210
270
        if ( p_sys->i_access_selected_track[i] > 0 )
211
0
        {
212
0
            es_out_Control( p_demux->out, ES_OUT_SET_ES_STATE,
213
0
                            p_sys->track[p_sys->i_access_selected_track[i]]->p_es, true );
214
0
            p_sys->i_access_selected_track[i] = 0;
215
0
        }
216
270
    }
217
218
    /* Get selected tracks, especially for computing PCR */
219
6.96k
    for( int i=0; i<MAX_ASF_TRACKS; i++ )
220
6.91k
    {
221
6.91k
        asf_track_t *tk = p_sys->track[i];
222
6.91k
        if ( !tk ) continue;
223
85
        if ( tk->p_es )
224
67
            es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE, tk->p_es, & tk->b_selected );
225
18
        else
226
18
            tk->b_selected = false;
227
85
    }
228
229
2.15k
    while( !p_sys->b_eos && ( p_sys->i_sendtime - p_sys->i_time - CHUNK < 0 ||
230
99
                            ( p_sys->i_sendtime - p_sys->i_time - CHUNK ) <
231
99
                                                     p_sys->p_fp->i_preroll ) )
232
2.10k
    {
233
        /* Read and demux a packet */
234
2.10k
        if( DemuxASFPacket( &p_sys->packet_sys,
235
2.10k
                             p_sys->p_fp->i_min_data_packet_size,
236
2.10k
                             p_sys->p_fp->i_max_data_packet_size,
237
2.10k
                             p_sys->i_data_begin, p_sys->i_data_end ) <= 0 )
238
23
        {
239
23
            p_sys->b_eos = true;
240
            /* Check if we have concatenated files */
241
23
            const uint8_t *p_peek;
242
23
            if( vlc_stream_Peek( p_demux->s, &p_peek, 16 ) == 16 )
243
23
            {
244
23
                vlc_guid_t guid;
245
246
23
                ASF_GetGUID( &guid, p_peek );
247
23
                p_sys->b_eof = !guidcmp( &guid, &asf_object_header_guid );
248
23
                if( !p_sys->b_eof )
249
23
                    msg_Warn( p_demux, "found a new ASF header" );
250
23
            }
251
0
            else
252
0
            {
253
0
                p_sys->b_eof = true;
254
0
                for ( int i=0; i<MAX_ASF_TRACKS; i++ )
255
0
                {
256
0
                    asf_track_t *tk = p_sys->track[i];
257
0
                    if ( tk && tk->info.p_frame )
258
0
                        Packet_Enqueue( &p_sys->packet_sys, i, &tk->info.p_frame );
259
0
                }
260
0
            }
261
23
        }
262
263
2.10k
        if ( p_sys->i_time == VLC_TICK_INVALID )
264
1.92k
            p_sys->i_time = p_sys->i_sendtime;
265
2.10k
    }
266
267
54
    if( p_sys->b_eos || ( p_sys->i_sendtime - p_sys->i_time - CHUNK >= 0 &&
268
0
                        ( p_sys->i_sendtime - p_sys->i_time - CHUNK ) >=
269
0
                                                     p_sys->p_fp->i_preroll ) )
270
54
    {
271
54
        bool b_data = Block_Dequeue( p_demux, p_sys->i_time + CHUNK );
272
273
54
        if( p_sys->i_time != VLC_TICK_INVALID )
274
35
        {
275
35
            p_sys->i_time += CHUNK;
276
35
            p_sys->b_pcr_sent = true;
277
35
            es_out_SetPCR( p_demux->out, p_sys->i_time );
278
#ifdef ASF_DEBUG
279
            msg_Dbg( p_demux, "Demux Loop Setting PCR to %"PRId64, p_sys->i_time );
280
#endif
281
35
        }
282
283
54
        if ( !b_data && p_sys->b_eos )
284
23
        {
285
23
            if( p_sys->i_time != VLC_TICK_INVALID )
286
4
                es_out_SetPCR( p_demux->out, p_sys->i_time );
287
288
            /* We end this stream */
289
23
            if( !p_sys->b_eof )
290
0
            {
291
0
                DemuxEnd( p_demux );
292
293
                /* And we prepare to read the next one */
294
0
                if( DemuxInit( p_demux ) )
295
0
                {
296
0
                    msg_Err( p_demux, "failed to load the new header" );
297
0
                    return VLC_DEMUXER_EOF;
298
0
                }
299
0
                es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
300
0
            }
301
23
            else
302
23
                return VLC_DEMUXER_EOF;
303
23
        }
304
54
    }
305
306
31
    return 1;
307
54
}
308
309
/*****************************************************************************
310
 * Close: frees unused data
311
 *****************************************************************************/
312
static void Close( vlc_object_t * p_this )
313
23
{
314
23
    demux_t     *p_demux = (demux_t *)p_this;
315
316
23
    DemuxEnd( p_demux );
317
318
23
    free( p_demux->p_sys );
319
23
}
320
321
/*****************************************************************************
322
 * WaitKeyframe: computes the number of frames to wait for a keyframe
323
 *****************************************************************************/
324
static void WaitKeyframe( demux_t *p_demux )
325
0
{
326
0
    demux_sys_t *p_sys = p_demux->p_sys;
327
0
    if ( ! p_sys->i_seek_track )
328
0
    {
329
0
        for ( int i=0; i<MAX_ASF_TRACKS; i++ )
330
0
        {
331
0
            asf_track_t *tk = p_sys->track[i];
332
0
            if ( tk && tk->info.p_sp && tk->i_cat == VIDEO_ES && tk->b_selected )
333
0
            {
334
0
                p_sys->i_seek_track = tk->info.p_sp->i_stream_number;
335
0
                break;
336
0
            }
337
0
        }
338
0
    }
339
340
0
    if ( p_sys->i_seek_track )
341
0
    {
342
        /* Skip forward at least 1 min */
343
0
        asf_track_t *tk = p_sys->track[p_sys->i_seek_track];
344
0
        if ( tk->info.p_esp && tk->info.p_esp->i_average_time_per_frame )
345
0
        {
346
            /* 1 min if fastseek, otherwise 5 sec */
347
            /* That's a guess for bandwidth */
348
0
            msftime_t i_maxwaittime = MSFTIME_FROM_SEC( p_sys->b_canfastseek ? 60 : 5);
349
0
            uint64_t frames = i_maxwaittime / tk->info.p_esp->i_average_time_per_frame;
350
0
            p_sys->i_wait_keyframe = __MIN( frames, UINT_MAX );
351
0
        }
352
0
        else
353
0
        {
354
0
            p_sys->i_wait_keyframe = ( p_sys->b_canfastseek ) ? 25 * 30 : 25 * 5;
355
0
        }
356
0
    }
357
0
    else
358
0
    {
359
0
        p_sys->i_wait_keyframe = 0;
360
0
    }
361
362
0
}
363
364
/*****************************************************************************
365
 * SeekIndex: goto to i_date or i_percent
366
 *****************************************************************************/
367
static int SeekPercent( demux_t *p_demux, int i_query, va_list args )
368
0
{
369
0
    demux_sys_t *p_sys = p_demux->p_sys;
370
371
0
    WaitKeyframe( p_demux );
372
373
0
    msg_Dbg( p_demux, "seek with percent: waiting %i frames", p_sys->i_wait_keyframe );
374
0
    return demux_vaControlHelper( p_demux->s, __MIN( INT64_MAX, p_sys->i_data_begin ),
375
0
                                   __MIN( INT64_MAX, p_sys->i_data_end ),
376
0
                                   __MIN( INT64_MAX, p_sys->i_bitrate ),
377
0
                                   __MIN( INT16_MAX, p_sys->p_fp->i_min_data_packet_size ),
378
0
                                   i_query, args );
379
0
}
380
381
static int SeekIndex( demux_t *p_demux, vlc_tick_t i_date, float f_pos )
382
0
{
383
0
    demux_sys_t *p_sys = p_demux->p_sys;
384
0
    asf_object_index_t *p_index;
385
386
0
    msg_Dbg( p_demux, "seek with index: %i seconds, position %f",
387
0
             i_date >= 0 ? (int)SEC_FROM_VLC_TICK(i_date) : -1, f_pos );
388
389
0
    if( i_date < 0 )
390
0
        i_date = p_sys->i_length * f_pos;
391
392
0
    p_sys->i_preroll_start = i_date - p_sys->p_fp->i_preroll;
393
0
    if ( p_sys->i_preroll_start < 0 ) p_sys->i_preroll_start = 0;
394
395
0
    p_index = ASF_FindObject( p_sys->p_root, &asf_object_simple_index_guid, 0 );
396
397
0
    uint64_t i_entry = MSFTIME_FROM_VLC_TICK(p_sys->i_preroll_start) / p_index->i_index_entry_time_interval;
398
0
    if( i_entry >= p_index->i_index_entry_count )
399
0
    {
400
0
        msg_Warn( p_demux, "Incomplete index" );
401
0
        return VLC_EGENERIC;
402
0
    }
403
404
0
    WaitKeyframe( p_demux );
405
406
0
    uint64_t i_offset = (uint64_t)p_index->index_entry[i_entry].i_packet_number *
407
0
                        p_sys->p_fp->i_min_data_packet_size;
408
409
0
    if ( vlc_stream_Seek( p_demux->s, i_offset + p_sys->i_data_begin ) == VLC_SUCCESS )
410
0
    {
411
0
        es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, VLC_TICK_0 + i_date );
412
0
        return VLC_SUCCESS;
413
0
    }
414
0
    else return VLC_EGENERIC;
415
0
}
416
417
static void SeekPrepare( demux_t *p_demux )
418
0
{
419
0
    demux_sys_t *p_sys = p_demux->p_sys;
420
421
0
    p_sys->b_eof = false;
422
0
    p_sys->b_eos = false;
423
0
    p_sys->b_pcr_sent = false;
424
0
    p_sys->i_time = VLC_TICK_INVALID;
425
0
    p_sys->i_sendtime = VLC_TICK_INVALID;
426
0
    p_sys->i_preroll_start = ASFPACKET_PREROLL_FROM_CURRENT;
427
428
0
    for( int i = 0; i < MAX_ASF_TRACKS ; i++ )
429
0
    {
430
0
        asf_track_t *tk = p_sys->track[i];
431
0
        if( tk )
432
0
        {
433
0
            FlushQueue( tk );
434
0
            tk->i_time = VLC_TICK_INVALID;
435
0
        }
436
0
    }
437
438
0
    es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
439
0
}
440
441
/*****************************************************************************
442
 * Control:
443
 *****************************************************************************/
444
static int Control( demux_t *p_demux, int i_query, va_list args )
445
0
{
446
0
    demux_sys_t *p_sys = p_demux->p_sys;
447
0
    vlc_meta_t  *p_meta;
448
0
    vlc_tick_t  i64;
449
0
    int         i;
450
0
    double      f, *pf;
451
452
0
    switch( i_query )
453
0
    {
454
0
    case DEMUX_GET_LENGTH:
455
0
        *va_arg( args, vlc_tick_t * ) = p_sys->i_length;
456
0
        return VLC_SUCCESS;
457
458
0
    case DEMUX_GET_TIME:
459
0
        if( p_sys->i_time == VLC_TICK_INVALID ) return VLC_EGENERIC;
460
0
        *va_arg( args, vlc_tick_t * ) = p_sys->i_time;
461
0
        return VLC_SUCCESS;
462
463
0
    case DEMUX_SET_TIME:
464
0
        if ( !p_sys->p_fp ||
465
0
             ! ( p_sys->p_fp->i_flags & ASF_FILE_PROPERTIES_SEEKABLE ) )
466
0
            return VLC_EGENERIC;
467
468
0
        SeekPrepare( p_demux );
469
470
0
        if( p_sys->b_index && p_sys->i_length != 0 )
471
0
        {
472
0
            va_list acpy;
473
0
            va_copy( acpy, args );
474
0
            i64 = va_arg( acpy, vlc_tick_t );
475
0
            va_end( acpy );
476
477
0
            if( !SeekIndex( p_demux, i64, -1 ) )
478
0
                return VLC_SUCCESS;
479
0
        }
480
0
        return SeekPercent( p_demux, i_query, args );
481
482
0
    case DEMUX_SET_ES:
483
0
    {
484
0
        i = va_arg( args, int );
485
0
        int i_ret;
486
0
        if ( i >= 0 )
487
0
        {
488
0
            msg_Dbg( p_demux, "Requesting access to enable stream %d", i );
489
0
            i_ret = vlc_stream_Control( p_demux->s,
490
0
                                        STREAM_SET_PRIVATE_ID_STATE, i, true );
491
0
        }
492
0
        else
493
0
        {  /* i contains -1 * es_category */
494
0
            msg_Dbg( p_demux, "Requesting access to disable stream %d", i );
495
0
            i_ret = vlc_stream_Control( p_demux->s,
496
0
                                        STREAM_SET_PRIVATE_ID_STATE, i,
497
0
                                        false );
498
0
        }
499
500
0
        if ( i_ret == VLC_SUCCESS )
501
0
        {
502
0
            asf_track_t *tk;
503
0
            if( i >= 0 )
504
0
            {
505
0
                tk = p_sys->track[i];
506
0
            }
507
0
            else
508
0
            {
509
0
                for( int j = 0; j < MAX_ASF_TRACKS ; j++ )
510
0
                {
511
0
                    tk = p_sys->track[j];
512
0
                    if( !tk || !tk->p_fmt || tk->i_cat != -1 * i )
513
0
                        continue;
514
0
                    FlushQueue( tk );
515
0
                    tk->i_time = VLC_TICK_INVALID;
516
0
                }
517
0
            }
518
519
0
            p_sys->i_seek_track = 0;
520
0
            if ( ( tk && tk->i_cat == VIDEO_ES ) || i == -1 * VIDEO_ES )
521
0
                WaitKeyframe( p_demux );
522
0
        }
523
0
        return i_ret;
524
0
    }
525
526
0
    case DEMUX_SET_ES_LIST:
527
0
        return VLC_EGENERIC; /* TODO */
528
529
0
    case DEMUX_GET_POSITION:
530
0
        if( p_sys->i_time == VLC_TICK_INVALID ) return VLC_EGENERIC;
531
0
        if( p_sys->i_length != 0 )
532
0
        {
533
0
            pf = va_arg( args, double * );
534
0
            *pf = p_sys->i_time / (double)p_sys->i_length;
535
0
            return VLC_SUCCESS;
536
0
        }
537
0
        return demux_vaControlHelper( p_demux->s,
538
0
                                       __MIN( INT64_MAX, p_sys->i_data_begin ),
539
0
                                       __MIN( INT64_MAX, p_sys->i_data_end ),
540
0
                                       __MIN( INT64_MAX, p_sys->i_bitrate ),
541
0
                                       __MIN( INT16_MAX, p_sys->p_fp->i_min_data_packet_size ),
542
0
                                       i_query, args );
543
544
0
    case DEMUX_SET_POSITION:
545
0
        if ( !p_sys->p_fp ||
546
0
             ( !( p_sys->p_fp->i_flags & ASF_FILE_PROPERTIES_SEEKABLE ) && !p_sys->b_index ) )
547
0
            return VLC_EGENERIC;
548
549
0
        SeekPrepare( p_demux );
550
551
0
        if( p_sys->b_index && p_sys->i_length != 0 )
552
0
        {
553
0
            va_list acpy;
554
0
            va_copy( acpy, args );
555
0
            f = va_arg( acpy, double );
556
0
            va_end( acpy );
557
558
0
            if( !SeekIndex( p_demux, -1, f ) )
559
0
                return VLC_SUCCESS;
560
0
        }
561
0
        return SeekPercent( p_demux, i_query, args );
562
563
0
    case DEMUX_GET_META:
564
0
        p_meta = va_arg( args, vlc_meta_t * );
565
0
        vlc_meta_Merge( p_meta, p_sys->meta );
566
0
        return VLC_SUCCESS;
567
568
0
    case DEMUX_GET_TITLE_INFO:
569
0
    {
570
0
        input_title_t ***ppp_title = va_arg( args, input_title_t *** );
571
0
        int *pi_int             = va_arg( args, int * );
572
0
        int *pi_title_offset    = va_arg( args, int * );
573
0
        int *pi_seekpoint_offset = va_arg( args, int * );
574
575
0
        if( p_sys->i_seekpoints == 0 )
576
0
            return VLC_EGENERIC;
577
578
0
        *pi_int = 1;
579
0
        *ppp_title = malloc( sizeof(input_title_t *) );
580
0
        if( !*ppp_title )
581
0
            return VLC_ENOMEM;
582
583
0
        input_title_t *p_title = (*ppp_title)[0] = vlc_input_title_New();
584
0
        if( !p_title )
585
0
        {
586
0
            free( *ppp_title );
587
0
            return VLC_ENOMEM;
588
0
        }
589
590
0
        for( int i = 0; i < p_sys->i_seekpoints; i++ )
591
0
        {
592
0
            seekpoint_t *p_sp = vlc_seekpoint_Duplicate( p_sys->pp_seekpoints[i] );
593
0
            if( unlikely(!p_sp) )
594
0
                break;
595
0
            TAB_APPEND( p_title->i_seekpoint, p_title->seekpoint, p_sp );
596
0
        }
597
598
0
        *pi_title_offset    = 0;
599
0
        *pi_seekpoint_offset = 0;
600
0
        return VLC_SUCCESS;
601
0
    }
602
603
0
    case DEMUX_SET_TITLE:
604
        /* only one title */
605
0
        if( va_arg( args, int ) != 0 )
606
0
            return VLC_EGENERIC;
607
0
        return VLC_SUCCESS;
608
609
0
    case DEMUX_SET_SEEKPOINT:
610
0
    {
611
0
        const int i_seekpoint = va_arg( args, int );
612
0
        if( i_seekpoint < 0 || i_seekpoint >= p_sys->i_seekpoints )
613
0
            return VLC_EGENERIC;
614
615
0
        const seekpoint_t *p_sp = p_sys->pp_seekpoints[i_seekpoint];
616
0
        if( p_sp->i_time_offset == VLC_TICK_INVALID )
617
0
            return VLC_EGENERIC;
618
619
0
        SeekPrepare( p_demux );
620
621
0
        if( p_sys->b_index && p_sys->i_length != 0 )
622
0
        {
623
0
            if( !SeekIndex( p_demux, p_sp->i_time_offset, -1 ) )
624
0
                return VLC_SUCCESS;
625
0
        }
626
        /* fallback: proportional seek by time offset */
627
0
        if( p_sys->i_length > 0 )
628
0
        {
629
0
            double f_pos = (double)p_sp->i_time_offset / p_sys->i_length;
630
0
            WaitKeyframe( p_demux );
631
0
            return vlc_stream_Seek( p_demux->s,
632
0
                p_sys->i_data_begin + (uint64_t)( f_pos *
633
0
                    (double)( p_sys->i_data_end - p_sys->i_data_begin ) ) );
634
0
        }
635
0
        return VLC_EGENERIC;
636
0
    }
637
638
0
    case DEMUX_CAN_SEEK:
639
0
        if ( !p_sys->p_fp ||
640
0
             ( !( p_sys->p_fp->i_flags & ASF_FILE_PROPERTIES_SEEKABLE ) && !p_sys->b_index ) )
641
0
        {
642
0
            bool *pb_bool = va_arg( args, bool * );
643
0
            *pb_bool = false;
644
0
            return VLC_SUCCESS;
645
0
        }
646
        /* fall through */
647
0
    default:
648
0
        return demux_vaControlHelper( p_demux->s,
649
0
                                      __MIN( INT64_MAX, p_sys->i_data_begin ),
650
0
                                      __MIN( INT64_MAX, p_sys->i_data_end),
651
0
                                      __MIN( INT64_MAX, p_sys->i_bitrate ),
652
0
                    ( p_sys->p_fp ) ? __MIN( INT_MAX, p_sys->p_fp->i_min_data_packet_size ) : 1,
653
0
                    i_query, args );
654
0
    }
655
0
}
656
657
/*****************************************************************************
658
 *
659
 *****************************************************************************/
660
static void Packet_SetAR( asf_packet_sys_t *p_packetsys, uint8_t i_stream_number,
661
                          uint8_t i_ratio_x, uint8_t i_ratio_y )
662
0
{
663
0
    demux_t *p_demux = p_packetsys->priv;
664
0
    demux_sys_t *p_sys = p_demux->p_sys;
665
0
    asf_track_t *tk = p_sys->track[i_stream_number];
666
667
0
    if ( !tk->p_fmt || (tk->p_fmt->video.i_sar_num == i_ratio_x && tk->p_fmt->video.i_sar_den == i_ratio_y ) )
668
0
        return;
669
670
0
    tk->p_fmt->video.i_sar_num = i_ratio_x;
671
0
    tk->p_fmt->video.i_sar_den = i_ratio_y;
672
0
    if( tk->p_es )
673
0
        es_out_Control( p_demux->out, ES_OUT_SET_ES_FMT, tk->p_es, tk->p_fmt );
674
0
}
675
676
static void Packet_SetSendTime( asf_packet_sys_t *p_packetsys, vlc_tick_t i_time )
677
145
{
678
145
    demux_t *p_demux = p_packetsys->priv;
679
145
    demux_sys_t *p_sys = p_demux->p_sys;
680
681
145
    p_sys->i_sendtime = VLC_TICK_0 + i_time;
682
145
}
683
684
static void Packet_UpdateTime( asf_packet_sys_t *p_packetsys, uint8_t i_stream_number,
685
                               vlc_tick_t i_time )
686
0
{
687
0
    demux_t *p_demux = p_packetsys->priv;
688
0
    demux_sys_t *p_sys = p_demux->p_sys;
689
0
    asf_track_t *tk = p_sys->track[i_stream_number];
690
691
0
    if ( tk )
692
0
        tk->i_time = VLC_TICK_0 + i_time;
693
0
}
694
695
static asf_track_info_t * Packet_GetTrackInfo( asf_packet_sys_t *p_packetsys,
696
                                               uint8_t i_stream_number )
697
2.99k
{
698
2.99k
    demux_t *p_demux = p_packetsys->priv;
699
2.99k
    demux_sys_t *p_sys = p_demux->p_sys;
700
2.99k
    asf_track_t *tk = p_sys->track[i_stream_number];
701
702
2.99k
    if (!tk)
703
2.70k
        return NULL;
704
291
    else
705
291
        return & tk->info;
706
2.99k
}
707
708
static bool Packet_DoSkip( asf_packet_sys_t *p_packetsys, uint8_t i_stream_number, bool b_packet_keyframe )
709
283
{
710
283
    demux_t *p_demux = p_packetsys->priv;
711
283
    demux_sys_t *p_sys = p_demux->p_sys;
712
283
    const asf_track_t *tk = p_sys->track[i_stream_number];
713
714
283
    if( tk == NULL )
715
0
    {
716
0
        msg_Warn( p_demux, "undeclared stream[Id 0x%x]", i_stream_number );
717
0
        return true;
718
0
    }
719
720
283
    if( p_sys->i_wait_keyframe )
721
0
    {
722
0
        if ( i_stream_number == p_sys->i_seek_track )
723
0
        {
724
0
            if ( !b_packet_keyframe )
725
0
            {
726
0
                p_sys->i_wait_keyframe--;
727
0
                return true;
728
0
            }
729
0
            else
730
0
                p_sys->i_wait_keyframe = 0;
731
0
        }
732
0
        else
733
0
            return true;
734
0
    }
735
736
283
    if( !tk->p_es )
737
138
        return true;
738
739
145
    return false;
740
283
}
741
742
static void Packet_Enqueue( asf_packet_sys_t *p_packetsys, uint8_t i_stream_number, block_t **pp_frame )
743
85
{
744
85
    demux_t *p_demux = p_packetsys->priv;
745
85
    demux_sys_t *p_sys = p_demux->p_sys;
746
85
    asf_track_t *tk = p_sys->track[i_stream_number];
747
85
    if ( !tk )
748
0
        return;
749
750
85
    block_t *p_gather = block_ChainGather( *pp_frame );
751
85
    if( p_gather )
752
85
    {
753
85
        block_ChainLastAppend( & tk->queue.pp_last, p_gather );
754
#ifdef ASF_DEBUG
755
        msg_Dbg( p_demux, "    enqueue packet dts %"PRId64" pts %"PRId64" pcr %"PRId64, p_gather->i_dts, p_gather->i_pts, p_sys->i_time );
756
#endif
757
85
    }
758
759
85
    *pp_frame = NULL;
760
85
}
761
762
static bool Block_Dequeue( demux_t *p_demux, vlc_tick_t i_nexttime )
763
54
{
764
54
    demux_sys_t *p_sys = p_demux->p_sys;
765
54
    bool b_tracks_have_data = false;
766
6.96k
    for( int i = 0; i < MAX_ASF_TRACKS; i++ )
767
6.91k
    {
768
6.91k
        asf_track_t *tk = p_sys->track[i];
769
6.91k
        if (!tk)
770
6.82k
            continue;
771
85
        b_tracks_have_data |= (tk->queue.p_first != NULL);
772
170
        while( tk->queue.p_first && tk->queue.p_first->i_dts <= i_nexttime )
773
85
        {
774
85
            block_t *p_block = tk->queue.p_first;
775
85
            tk->queue.p_first = p_block->p_next;
776
85
            if( tk->queue.p_first == NULL )
777
3
                tk->queue.pp_last = &tk->queue.p_first;
778
82
            else
779
82
                p_block->p_next = NULL;
780
781
85
            if( !p_sys->b_pcr_sent && p_sys->i_time != VLC_TICK_INVALID )
782
2
            {
783
2
                p_sys->b_pcr_sent = true;
784
2
                es_out_SetPCR( p_demux->out, p_sys->i_time );
785
#ifdef ASF_DEBUG
786
                msg_Dbg( p_demux, "    dequeue setting PCR to %"PRId64, p_sys->i_time );
787
#endif
788
2
            }
789
790
#ifdef ASF_DEBUG
791
            msg_Dbg( p_demux, "    sending packet dts %"PRId64" pts %"PRId64" pcr %"PRId64, p_block->i_dts, p_block->i_pts, p_sys->i_time );
792
#endif
793
85
            es_out_Send( p_demux->out, tk->p_es, p_block );
794
85
        }
795
85
    }
796
54
    return b_tracks_have_data;
797
54
}
798
799
/*****************************************************************************
800
 *
801
 *****************************************************************************/
802
typedef struct asf_es_priorities_t
803
{
804
    uint16_t *pi_stream_numbers;
805
    uint16_t i_count;
806
} asf_es_priorities_t;
807
808
/* Fills up our exclusion list */
809
static void ASF_fillup_es_priorities_ex( demux_sys_t *p_sys, void *p_hdr,
810
                                         asf_es_priorities_t *p_prios )
811
21
{
812
    /* Find stream exclusions */
813
21
    asf_object_advanced_mutual_exclusion_t *p_mutex =
814
21
            ASF_FindObject( p_hdr, &asf_object_advanced_mutual_exclusion, 0 );
815
21
    if (! p_mutex ) return;
816
817
0
    p_prios->pi_stream_numbers = vlc_alloc( p_sys->i_track, sizeof(*p_prios->pi_stream_numbers) );
818
0
    if ( !p_prios->pi_stream_numbers ) return;
819
820
0
    if ( p_mutex->i_stream_number_count )
821
0
    {
822
        /* Just set highest prio on highest in the group */
823
0
        for ( uint16_t i = 1; i < p_mutex->i_stream_number_count; i++ )
824
0
        {
825
0
            if ( p_prios->i_count > p_sys->i_track || i > p_sys->i_track ) break;
826
0
            p_prios->pi_stream_numbers[ p_prios->i_count++ ] = p_mutex->pi_stream_number[ i ];
827
0
        }
828
0
    }
829
0
}
830
831
/* Fills up our bitrate exclusion list */
832
static void ASF_fillup_es_bitrate_priorities_ex( demux_sys_t *p_sys, void *p_hdr,
833
                                                 asf_es_priorities_t *p_prios )
834
21
{
835
    /* Find bitrate exclusions */
836
21
    asf_object_bitrate_mutual_exclusion_t *p_bitrate_mutex =
837
21
            ASF_FindObject( p_hdr, &asf_object_bitrate_mutual_exclusion_guid, 0 );
838
21
    if (! p_bitrate_mutex ) return;
839
840
0
    p_prios->pi_stream_numbers = vlc_alloc( p_sys->i_track, sizeof(*p_prios->pi_stream_numbers) );
841
0
    if ( !p_prios->pi_stream_numbers ) return;
842
843
0
    if ( p_bitrate_mutex->i_stream_number_count )
844
0
    {
845
        /* Just remove < highest */
846
0
        for ( uint16_t i = 1; i < p_bitrate_mutex->i_stream_number_count; i++ )
847
0
        {
848
0
            if ( p_prios->i_count > p_sys->i_track || i > p_sys->i_track ) break;
849
0
            p_prios->pi_stream_numbers[ p_prios->i_count++ ] = p_bitrate_mutex->pi_stream_numbers[ i ];
850
0
        }
851
0
    }
852
853
0
}
854
855
22
#define GET_CHECKED( target, getter, maxtarget, temp ) \
856
22
{\
857
22
    temp i_temp = getter;\
858
22
    if ( i_temp > maxtarget ) {\
859
0
        msg_Warn( p_demux, "rejecting stream %u : " #target " overflow", i_stream );\
860
0
        es_format_Clean( &fmt );\
861
0
        goto error;\
862
22
    } else {\
863
22
        target = i_temp;\
864
22
    }\
865
22
}
866
867
static int DemuxInit( demux_t *p_demux )
868
39
{
869
39
    demux_sys_t *p_sys = p_demux->p_sys;
870
39
    asf_es_priorities_t fmt_priorities_ex = { NULL, 0 };
871
39
    asf_es_priorities_t fmt_priorities_bitrate_ex = { NULL, 0 };
872
873
    /* init context */
874
39
    p_sys->i_time   = VLC_TICK_INVALID;
875
39
    p_sys->i_sendtime    = VLC_TICK_INVALID;
876
39
    p_sys->i_length = 0;
877
39
    p_sys->b_eos = false;
878
39
    p_sys->b_eof = false;
879
39
    p_sys->i_bitrate = 0;
880
39
    p_sys->p_root   = NULL;
881
39
    p_sys->p_fp     = NULL;
882
39
    p_sys->b_index  = 0;
883
39
    p_sys->i_track  = 0;
884
39
    p_sys->i_seek_track = 0;
885
39
    p_sys->b_pcr_sent = false;
886
39
    p_sys->i_wait_keyframe = 0;
887
5.03k
    for( int i = 0; i < MAX_ASF_TRACKS; i++ )
888
4.99k
    {
889
4.99k
        p_sys->track[i] = NULL;
890
4.99k
    }
891
39
    p_sys->i_data_begin = 0;
892
39
    p_sys->i_data_end   = 0;
893
39
    p_sys->i_preroll_start = 0;
894
39
    p_sys->meta         = NULL;
895
39
    p_sys->pp_seekpoints = NULL;
896
39
    p_sys->i_seekpoints  = 0;
897
898
    /* Now load all object ( except raw data ) */
899
39
    vlc_stream_Control( p_demux->s, STREAM_CAN_FASTSEEK,
900
39
                        &p_sys->b_canfastseek );
901
39
    if( !(p_sys->p_root = ASF_ReadObjectRoot(p_demux->s, p_sys->b_canfastseek)) )
902
16
    {
903
16
        msg_Warn( p_demux, "ASF plugin discarded (not a valid file)" );
904
16
        return VLC_EGENERIC;
905
16
    }
906
23
    p_sys->p_fp = p_sys->p_root->p_fp;
907
908
23
    if( p_sys->p_fp->i_min_data_packet_size != p_sys->p_fp->i_max_data_packet_size )
909
0
    {
910
0
        msg_Warn( p_demux, "ASF plugin discarded (invalid file_properties object)" );
911
0
        goto error;
912
0
    }
913
914
23
    if ( ASF_FindObject( p_sys->p_root->p_hdr,
915
23
                         &asf_object_content_encryption_guid, 0 ) != NULL
916
23
         || ASF_FindObject( p_sys->p_root->p_hdr,
917
23
                            &asf_object_extended_content_encryption_guid, 0 ) != NULL
918
23
         || ASF_FindObject( p_sys->p_root->p_hdr,
919
23
                         &asf_object_advanced_content_encryption_guid, 0 ) != NULL )
920
0
    {
921
0
        vlc_dialog_display_error( p_demux, _("Could not demux ASF stream"), "%s",
922
0
            ("DRM protected streams are not supported.") );
923
0
        goto error;
924
0
    }
925
926
23
    p_sys->i_track = ASF_CountObject( p_sys->p_root->p_hdr,
927
23
                                      &asf_object_stream_properties_guid );
928
23
    if( p_sys->i_track == 0 )
929
0
    {
930
0
        msg_Warn( p_demux, "ASF plugin discarded (cannot find any stream!)" );
931
0
        goto error;
932
0
    }
933
23
    msg_Dbg( p_demux, "found %u streams", p_sys->i_track );
934
935
    /* check if index is available */
936
23
    asf_object_index_t *p_index = ASF_FindObject( p_sys->p_root,
937
23
                                                  &asf_object_simple_index_guid, 0 );
938
23
    const bool b_index = p_index && p_index->i_index_entry_count;
939
940
    /* Find the extended header if any */
941
23
    asf_object_t *p_hdr_ext = ASF_FindObject( p_sys->p_root->p_hdr,
942
23
                                              &asf_object_header_extension_guid, 0 );
943
944
23
    asf_object_language_list_t *p_languages = NULL;
945
946
23
    if( p_hdr_ext )
947
21
    {
948
21
        p_languages = ASF_FindObject( p_hdr_ext, &asf_object_language_list, 0 );
949
950
21
        ASF_fillup_es_priorities_ex( p_sys, p_hdr_ext, &fmt_priorities_ex );
951
21
        ASF_fillup_es_bitrate_priorities_ex( p_sys, p_hdr_ext, &fmt_priorities_bitrate_ex );
952
21
    }
953
954
23
    const bool b_mms = !strncasecmp( p_demux->psz_url, "mms:", 4 );
955
23
    bool b_dvrms = false;
956
957
23
    if( b_mms )
958
0
    {
959
0
        es_out_Control( p_demux->out, ES_OUT_SET_ES_CAT_POLICY,
960
0
                        VIDEO_ES, ES_OUT_ES_POLICY_EXCLUSIVE );
961
0
    }
962
963
47
    for( unsigned i_stream = 0; i_stream < p_sys->i_track; i_stream++ )
964
24
    {
965
24
        asf_track_t    *tk;
966
24
        asf_object_stream_properties_t *p_sp;
967
24
        asf_object_extended_stream_properties_t *p_esp;
968
24
        bool b_access_selected;
969
970
24
        p_sp = ASF_FindObject( p_sys->p_root->p_hdr,
971
24
                               &asf_object_stream_properties_guid,
972
24
                               i_stream );
973
24
        p_esp = NULL;
974
975
        /* Ignore duplicated streams numbers */
976
24
        if (p_sys->track[p_sp->i_stream_number])
977
0
            continue;
978
979
24
        tk = p_sys->track[p_sp->i_stream_number] = malloc( sizeof( asf_track_t ) );
980
24
        if (!tk)
981
0
            goto error;
982
24
        memset( tk, 0, sizeof( asf_track_t ) );
983
984
24
        ASFPacketTrackInit( &tk->info );
985
24
        tk->i_time = VLC_TICK_INVALID;
986
24
        tk->info.p_sp = p_sp;
987
24
        tk->p_es = NULL;
988
24
        tk->queue.p_first = NULL;
989
24
        tk->queue.pp_last = &tk->queue.p_first;
990
24
        tk->info.i_pkt = 0;
991
24
        tk->info.i_pktcount = 0;
992
993
24
        if ( !b_mms )
994
24
        {
995
            /* Check (not mms) if this track is selected (ie will receive data) */
996
24
            if( !vlc_stream_Control( p_demux->s, STREAM_GET_PRIVATE_ID_STATE,
997
24
                                     (int) p_sp->i_stream_number,
998
24
                                     &b_access_selected ) &&
999
0
                !b_access_selected )
1000
0
            {
1001
0
                tk->i_cat = UNKNOWN_ES;
1002
0
                msg_Dbg( p_demux, "ignoring not selected stream(ID:%u) (by access)",
1003
0
                         p_sp->i_stream_number );
1004
0
                continue;
1005
0
            }
1006
24
        }
1007
1008
        /* Find the associated extended_stream_properties if any */
1009
24
        if( p_hdr_ext )
1010
22
        {
1011
22
            int i_ext_stream = ASF_CountObject( p_hdr_ext,
1012
22
                                                &asf_object_extended_stream_properties_guid );
1013
23
            for( int i = 0; i < i_ext_stream; i++ )
1014
5
            {
1015
5
                asf_object_t *p_tmp =
1016
5
                    ASF_FindObject( p_hdr_ext,
1017
5
                                    &asf_object_extended_stream_properties_guid, i );
1018
5
                if( p_tmp->ext_stream.i_stream_number == p_sp->i_stream_number )
1019
4
                {
1020
4
                    p_esp = &p_tmp->ext_stream;
1021
4
                    tk->info.p_esp = p_esp;
1022
4
                    break;
1023
4
                }
1024
5
            }
1025
22
        }
1026
1027
        /* Check for DVR-MS */
1028
24
        if( p_esp )
1029
4
            for( uint16_t i=0; i<p_esp->i_payload_extension_system_count && !b_dvrms; i++ )
1030
0
                b_dvrms = guidcmp( &p_esp->p_ext[i].i_extension_id, &asf_dvr_sampleextension_timing_rep_data_guid );
1031
1032
24
        es_format_t fmt;
1033
1034
24
        if( guidcmp( &p_sp->i_stream_type, &asf_object_stream_type_audio ) &&
1035
3
            p_sp->i_type_specific_data_length >= sizeof( WAVEFORMATEX ) - 2 )
1036
3
        {
1037
3
            uint8_t *p_data = p_sp->p_type_specific_data;
1038
3
            int i_format;
1039
1040
3
            es_format_Init( &fmt, AUDIO_ES, 0 );
1041
3
            i_format = GetWLE( &p_data[0] );
1042
3
            wf_tag_to_fourcc( i_format, &fmt.i_codec, NULL );
1043
1044
3
            GET_CHECKED( fmt.audio.i_channels,      GetWLE( &p_data[2] ),
1045
3
                                                        255, uint16_t );
1046
3
            GET_CHECKED( fmt.audio.i_rate,          GetDWLE( &p_data[4] ),
1047
3
                                                        UINT_MAX, uint32_t );
1048
3
            GET_CHECKED( fmt.i_bitrate,             GetDWLE( &p_data[8] ) * 8,
1049
3
                                                        UINT_MAX, uint32_t );
1050
3
            fmt.audio.i_blockalign      = GetWLE(  &p_data[12] );
1051
3
            fmt.audio.i_bitspersample   = GetWLE(  &p_data[14] );
1052
1053
3
            if( p_sp->i_type_specific_data_length > sizeof( WAVEFORMATEX ) &&
1054
3
                i_format != WAVE_FORMAT_MPEGLAYER3 &&
1055
3
                i_format != WAVE_FORMAT_MPEG )
1056
3
            {
1057
3
                GET_CHECKED( fmt.i_extra, __MIN( GetWLE( &p_data[16] ),
1058
3
                                     p_sp->i_type_specific_data_length -
1059
3
                                     sizeof( WAVEFORMATEX ) ),
1060
3
                             INT_MAX, uint32_t );
1061
3
                fmt.p_extra = malloc( fmt.i_extra );
1062
3
                memcpy( fmt.p_extra, &p_data[sizeof( WAVEFORMATEX )],
1063
3
                        fmt.i_extra );
1064
3
            }
1065
3
            msg_Dbg( p_demux, "added new audio stream (codec:%4.4s(0x%x),ID:%d)",
1066
3
                (char*)&fmt.i_codec, GetWLE( p_data ), p_sp->i_stream_number );
1067
3
        }
1068
21
        else if( guidcmp( &p_sp->i_stream_type,
1069
21
                              &asf_object_stream_type_video ) &&
1070
3
                 p_sp->i_type_specific_data_length >= 11 +
1071
3
                 sizeof( VLC_BITMAPINFOHEADER ) )
1072
3
        {
1073
3
            uint8_t      *p_data = &p_sp->p_type_specific_data[11];
1074
1075
3
            es_format_Init( &fmt, VIDEO_ES,
1076
3
                            VLC_FOURCC( p_data[16], p_data[17],
1077
3
                                        p_data[18], p_data[19] ) );
1078
1079
3
            GET_CHECKED( fmt.video.i_width,      GetDWLE( p_data + 4 ),
1080
3
                                                     UINT_MAX, uint32_t );
1081
3
            GET_CHECKED( fmt.video.i_height,     GetDWLE( p_data + 8 ),
1082
3
                                                     UINT_MAX, uint32_t );
1083
3
            fmt.video.i_visible_width = fmt.video.i_width;
1084
3
            fmt.video.i_visible_height = fmt.video.i_height;
1085
1086
3
            if( p_esp && p_esp->i_average_time_per_frame > 0 )
1087
1
            {
1088
1
                fmt.video.i_frame_rate = 10000000;
1089
1
                GET_CHECKED( fmt.video.i_frame_rate_base,
1090
1
                             p_esp->i_average_time_per_frame,
1091
1
                             UINT_MAX, uint64_t );
1092
1
            }
1093
1094
3
            if( fmt.i_codec == VLC_FOURCC( 'D','V','R',' ') )
1095
0
            {
1096
                /* DVR-MS special ASF */
1097
0
                fmt.i_codec = VLC_CODEC_MPGV;
1098
0
            }
1099
1100
3
            if( p_sp->i_type_specific_data_length > 11 +
1101
3
                sizeof( VLC_BITMAPINFOHEADER ) )
1102
3
            {
1103
3
                GET_CHECKED( fmt.i_extra, __MIN( GetDWLE( p_data ),
1104
3
                                     p_sp->i_type_specific_data_length - 11 -
1105
3
                                     sizeof( VLC_BITMAPINFOHEADER ) ),
1106
3
                             UINT_MAX, uint32_t );
1107
3
                fmt.p_extra = malloc( fmt.i_extra );
1108
3
                memcpy( fmt.p_extra, &p_data[sizeof( VLC_BITMAPINFOHEADER )],
1109
3
                        fmt.i_extra );
1110
3
            }
1111
1112
            /* Look for an aspect ratio */
1113
3
            if( p_sys->p_root->p_metadata )
1114
3
            {
1115
3
                asf_object_metadata_t *p_meta = p_sys->p_root->p_metadata;
1116
3
                unsigned int i_aspect_x = 0, i_aspect_y = 0;
1117
3
                uint32_t i;
1118
15
                for( i = 0; i < p_meta->i_record_entries_count; i++ )
1119
12
                {
1120
12
                    if( !p_meta->record[i].psz_name )
1121
0
                        continue;
1122
12
                    if( !strcmp( p_meta->record[i].psz_name, "AspectRatioX" ) )
1123
0
                    {
1124
0
                        if( (!i_aspect_x && !p_meta->record[i].i_stream) ||
1125
0
                            p_meta->record[i].i_stream ==
1126
0
                            p_sp->i_stream_number )
1127
0
                            GET_CHECKED( i_aspect_x, p_meta->record[i].i_val,
1128
0
                                         UINT_MAX, uint64_t );
1129
0
                    }
1130
12
                    if( !strcmp( p_meta->record[i].psz_name, "AspectRatioY" ) )
1131
0
                    {
1132
0
                        if( (!i_aspect_y && !p_meta->record[i].i_stream) ||
1133
0
                            p_meta->record[i].i_stream ==
1134
0
                            p_sp->i_stream_number )
1135
0
                            GET_CHECKED( i_aspect_y, p_meta->record[i].i_val,
1136
0
                                         UINT_MAX, uint64_t );
1137
0
                    }
1138
12
                }
1139
1140
3
                if( i_aspect_x && i_aspect_y )
1141
0
                {
1142
0
                    fmt.video.i_sar_num = i_aspect_x;
1143
0
                    fmt.video.i_sar_den = i_aspect_y;
1144
0
                }
1145
3
            }
1146
1147
            /* If there is a video track then use the index for seeking */
1148
3
            p_sys->b_index = b_index;
1149
1150
3
            msg_Dbg( p_demux, "added new video stream(codec:%4.4s,ID:%d)",
1151
3
                     (char*)&fmt.i_codec, p_sp->i_stream_number );
1152
3
        }
1153
18
        else if( guidcmp( &p_sp->i_stream_type, &asf_object_stream_type_binary ) &&
1154
0
            p_sp->i_type_specific_data_length >= 64 )
1155
0
        {
1156
0
            vlc_guid_t i_major_media_type;
1157
0
            ASF_GetGUID( &i_major_media_type, p_sp->p_type_specific_data );
1158
0
            msg_Dbg( p_demux, "stream(ID:%d) major type " GUID_FMT, p_sp->i_stream_number,
1159
0
                     GUID_PRINT(i_major_media_type) );
1160
1161
0
            vlc_guid_t i_media_subtype;
1162
0
            ASF_GetGUID( &i_media_subtype, &p_sp->p_type_specific_data[16] );
1163
0
            msg_Dbg( p_demux, "stream(ID:%d) subtype " GUID_FMT, p_sp->i_stream_number,
1164
0
                     GUID_PRINT(i_media_subtype) );
1165
1166
            //uint32_t i_fixed_size_samples = GetDWBE( &p_sp->p_type_specific_data[32] );
1167
            //uint32_t i_temporal_compression = GetDWBE( &p_sp->p_type_specific_data[36] );
1168
            //uint32_t i_sample_size = GetDWBE( &p_sp->p_type_specific_data[40] );
1169
1170
0
            vlc_guid_t i_format_type;
1171
0
            ASF_GetGUID( &i_format_type, &p_sp->p_type_specific_data[44] );
1172
0
            msg_Dbg( p_demux, "stream(ID:%d) format type " GUID_FMT, p_sp->i_stream_number,
1173
0
                     GUID_PRINT(i_format_type) );
1174
1175
            //uint32_t i_format_data_size = GetDWBE( &p_sp->p_type_specific_data[60] );
1176
0
            uint8_t *p_data = p_sp->p_type_specific_data + 64;
1177
0
            unsigned int i_data = p_sp->i_type_specific_data_length - 64;
1178
1179
0
            msg_Dbg( p_demux, "Ext stream header detected. datasize = %d", p_sp->i_type_specific_data_length );
1180
0
            if( guidcmp( &i_major_media_type, &asf_object_extended_stream_type_audio ) &&
1181
0
                i_data >= sizeof( WAVEFORMATEX ) - 2)
1182
0
            {
1183
0
                uint16_t i_format;
1184
0
                es_format_Init( &fmt, AUDIO_ES, 0 );
1185
1186
0
                i_format = GetWLE( &p_data[0] );
1187
0
                if( i_format == 0 )
1188
0
                    fmt.i_codec = VLC_CODEC_A52;
1189
0
                else
1190
0
                    wf_tag_to_fourcc( i_format, &fmt.i_codec, NULL );
1191
1192
0
                GET_CHECKED( fmt.audio.i_channels,      GetWLE( &p_data[2] ),
1193
0
                                                            255, uint16_t );
1194
0
                GET_CHECKED( fmt.audio.i_rate,          GetDWLE( &p_data[4] ),
1195
0
                                                            UINT_MAX, uint32_t );
1196
0
                GET_CHECKED( fmt.i_bitrate,             GetDWLE( &p_data[8] ) * 8,
1197
0
                                                            UINT_MAX, uint32_t );
1198
0
                fmt.audio.i_blockalign      = GetWLE(  &p_data[12] );
1199
0
                fmt.audio.i_bitspersample   = GetWLE(  &p_data[14] );
1200
1201
0
                if( p_sp->i_type_specific_data_length > sizeof( WAVEFORMATEX ) &&
1202
0
                    i_format != WAVE_FORMAT_MPEGLAYER3 &&
1203
0
                    i_format != WAVE_FORMAT_MPEG && i_data >= 19 )
1204
0
                {
1205
0
                    GET_CHECKED( fmt.i_extra, __MIN( GetWLE( &p_data[16] ),
1206
0
                                         p_sp->i_type_specific_data_length -
1207
0
                                         sizeof( WAVEFORMATEX ) - 64),
1208
0
                                 INT_MAX, uint32_t );
1209
0
                    fmt.p_extra = malloc( fmt.i_extra );
1210
0
                    if ( fmt.p_extra )
1211
0
                        memcpy( fmt.p_extra, &p_data[sizeof( WAVEFORMATEX )], fmt.i_extra );
1212
0
                    else
1213
0
                        fmt.i_extra = 0;
1214
0
                }
1215
1216
0
                msg_Dbg( p_demux, "added new audio stream (codec:%4.4s(0x%x),ID:%d)",
1217
0
                    (char*)&fmt.i_codec, i_format, p_sp->i_stream_number );
1218
0
            }
1219
0
            else
1220
0
            {
1221
0
                es_format_Init( &fmt, UNKNOWN_ES, 0 );
1222
0
            }
1223
0
        }
1224
18
        else
1225
18
        {
1226
18
            es_format_Init( &fmt, UNKNOWN_ES, 0 );
1227
18
        }
1228
1229
24
        if( b_dvrms )
1230
0
        {
1231
0
            fmt.i_original_fourcc = VLC_FOURCC( 'D','V','R',' ');
1232
0
            fmt.b_packetized = false;
1233
0
        }
1234
1235
24
        if( fmt.i_codec == VLC_CODEC_MP4A )
1236
0
            fmt.b_packetized = false;
1237
1238
24
        tk->i_cat = tk->info.i_cat = fmt.i_cat;
1239
24
        if( fmt.i_cat != UNKNOWN_ES )
1240
6
        {
1241
6
            if( p_esp && p_languages &&
1242
2
                p_esp->i_language_index < p_languages->i_language &&
1243
2
                p_languages->ppsz_language[p_esp->i_language_index] )
1244
2
            {
1245
2
                fmt.psz_language = strdup( p_languages->ppsz_language[p_esp->i_language_index] );
1246
2
                char *p;
1247
2
                if( fmt.psz_language && (p = strchr( fmt.psz_language, '-' )) )
1248
2
                    *p = '\0';
1249
2
            }
1250
1251
            /* Set our priority so we won't get multiple videos */
1252
6
            int i_priority = ES_PRIORITY_SELECTABLE_MIN;
1253
6
            for( uint16_t i = 0; i < fmt_priorities_ex.i_count; i++ )
1254
0
            {
1255
0
                if ( fmt_priorities_ex.pi_stream_numbers[i] == p_sp->i_stream_number )
1256
0
                {
1257
0
                    i_priority = ES_PRIORITY_NOT_DEFAULTABLE;
1258
0
                    break;
1259
0
                }
1260
0
            }
1261
6
            for( uint16_t i = 0; i < fmt_priorities_bitrate_ex.i_count; i++ )
1262
0
            {
1263
0
                if ( fmt_priorities_bitrate_ex.pi_stream_numbers[i] == p_sp->i_stream_number )
1264
0
                {
1265
0
                    i_priority = ES_PRIORITY_NOT_DEFAULTABLE;
1266
0
                    break;
1267
0
                }
1268
0
            }
1269
6
            fmt.i_priority = i_priority;
1270
1271
6
            if ( i_stream <= INT_MAX )
1272
6
                fmt.i_id = i_stream;
1273
0
            else
1274
6
                msg_Warn( p_demux, "Can't set fmt.i_id to match stream id %u", i_stream );
1275
1276
6
            if ( fmt.i_cat == VIDEO_ES )
1277
3
            {
1278
                /* Backup our video format */
1279
3
                tk->p_fmt = malloc( sizeof( es_format_t ) );
1280
3
                if ( tk->p_fmt )
1281
3
                    es_format_Copy( tk->p_fmt, &fmt );
1282
3
            }
1283
1284
6
            fmt.i_id = tk->info.p_sp->i_stream_number;
1285
1286
6
            tk->p_es = es_out_Add( p_demux->out, &fmt );
1287
1288
6
            if( !vlc_stream_Control( p_demux->s, STREAM_GET_PRIVATE_ID_STATE,
1289
6
                                     (int) p_sp->i_stream_number,
1290
6
                                     &b_access_selected ) &&
1291
0
                b_access_selected )
1292
0
            {
1293
0
                p_sys->i_access_selected_track[fmt.i_cat] = p_sp->i_stream_number;
1294
0
            }
1295
1296
6
        }
1297
18
        else
1298
18
        {
1299
18
            msg_Dbg( p_demux, "ignoring unknown stream(ID:%d)",
1300
18
                     p_sp->i_stream_number );
1301
18
        }
1302
1303
24
        es_format_Clean( &fmt );
1304
24
    }
1305
1306
23
    p_sys->i_data_begin = p_sys->p_root->p_data->i_object_pos + 50;
1307
23
    if( p_sys->p_root->p_data->i_object_size > 50 ) /* see libasf ASF_OBJECT_DATA <= 50 handling */
1308
22
    { /* local file */
1309
22
        p_sys->i_data_end = p_sys->p_root->p_data->i_object_pos +
1310
22
                                    p_sys->p_root->p_data->i_object_size;
1311
22
        p_sys->i_data_end = __MIN( (uint64_t)stream_Size( p_demux->s ), p_sys->i_data_end );
1312
22
    }
1313
1
    else
1314
1
    { /* live/broacast */
1315
1
        p_sys->i_data_end = 0;
1316
1
    }
1317
1318
    /* go to first packet */
1319
23
    if( vlc_stream_Seek( p_demux->s, p_sys->i_data_begin ) != VLC_SUCCESS )
1320
0
        goto error;
1321
1322
    /* try to calculate movie time */
1323
23
    if( p_sys->p_fp->i_data_packets_count > 0 )
1324
23
    {
1325
23
        uint64_t i_count;
1326
23
        uint64_t i_size = stream_Size( p_demux->s );
1327
1328
23
        if( p_sys->i_data_end > 0 && i_size > p_sys->i_data_end )
1329
0
        {
1330
0
            i_size = p_sys->i_data_end;
1331
0
        }
1332
1333
        /* real number of packets */
1334
23
        i_count = ( i_size - p_sys->i_data_begin ) /
1335
23
                  p_sys->p_fp->i_min_data_packet_size;
1336
1337
        /* calculate the time duration in micro-s */
1338
23
        if ( ckd_mul(&p_sys->i_length, VLC_TICK_FROM_MSFTIME(p_sys->p_fp->i_play_duration), i_count) )
1339
0
            p_sys->i_length = 0;
1340
23
        p_sys->i_length = p_sys->i_length /
1341
23
                   (vlc_tick_t)p_sys->p_fp->i_data_packets_count;
1342
23
        if( p_sys->i_length <= p_sys->p_fp->i_preroll )
1343
18
            p_sys->i_length = 0;
1344
5
        else
1345
5
        {
1346
5
            p_sys->i_length  -= p_sys->p_fp->i_preroll;
1347
5
            p_sys->i_bitrate = 8 * i_size * CLOCK_FREQ / p_sys->i_length;
1348
5
        }
1349
23
    }
1350
1351
    /* Create meta information */
1352
23
    p_sys->meta = vlc_meta_New();
1353
1354
23
    asf_object_content_description_t *p_cd;
1355
23
    if( ( p_cd = ASF_FindObject( p_sys->p_root->p_hdr,
1356
23
                                 &asf_object_content_description_guid, 0 ) ) )
1357
2
    {
1358
2
        if( p_cd->psz_title && *p_cd->psz_title )
1359
1
        {
1360
1
            vlc_meta_SetTitle( p_sys->meta, p_cd->psz_title );
1361
1
        }
1362
2
        if( p_cd->psz_artist && *p_cd->psz_artist )
1363
0
        {
1364
0
             vlc_meta_SetArtist( p_sys->meta, p_cd->psz_artist );
1365
0
        }
1366
2
        if( p_cd->psz_copyright && *p_cd->psz_copyright )
1367
1
        {
1368
1
            vlc_meta_SetCopyright( p_sys->meta, p_cd->psz_copyright );
1369
1
        }
1370
2
        if( p_cd->psz_description && *p_cd->psz_description )
1371
1
        {
1372
1
            vlc_meta_SetDescription( p_sys->meta, p_cd->psz_description );
1373
1
        }
1374
2
        if( p_cd->psz_rating && *p_cd->psz_rating )
1375
1
        {
1376
1
            vlc_meta_SetRating( p_sys->meta, p_cd->psz_rating );
1377
1
        }
1378
2
    }
1379
23
    asf_object_extended_content_description_t *p_ecd;
1380
23
    if( ( p_ecd = ASF_FindObject( p_sys->p_root->p_hdr,
1381
23
                                 &asf_object_extended_content_description, 0 ) ) )
1382
22
    {
1383
1.37k
        for( int i = 0; i < p_ecd->i_count; i++ )
1384
1.35k
        {
1385
1386
1.35k
#define set_meta( name, vlc_type ) \
1387
10.8k
            if( p_ecd->ppsz_name[i] && !strncmp( p_ecd->ppsz_name[i], name, strlen(name) ) ) \
1388
10.8k
                vlc_meta_Set( p_sys->meta, vlc_type, p_ecd->ppsz_value[i] );
1389
1390
1.35k
            set_meta( "WM/AlbumTitle",   vlc_meta_Album )
1391
1.35k
            else set_meta( "WM/TrackNumber",  vlc_meta_TrackNumber )
1392
1.35k
            else set_meta( "WM/Year",         vlc_meta_Date )
1393
1.35k
            else set_meta( "WM/Genre",        vlc_meta_Genre )
1394
1.35k
            else set_meta( "WM/Genre",        vlc_meta_Genre )
1395
1.35k
            else set_meta( "WM/AlbumArtist",  vlc_meta_AlbumArtist )
1396
1.35k
            else set_meta( "WM/Publisher",    vlc_meta_Publisher )
1397
1.35k
            else set_meta( "WM/PartOfSet",    vlc_meta_DiscNumber )
1398
1.35k
            else if( p_ecd->ppsz_value[i] != NULL && p_ecd->ppsz_name[i] &&
1399
617
                    *p_ecd->ppsz_value[i] != '\0' && /* no empty value */
1400
350
                    *p_ecd->ppsz_value[i] != '{'  && /* no guid value */
1401
350
                    *p_ecd->ppsz_name[i] != '{' )    /* no guid name */
1402
350
                    vlc_meta_SetExtra( p_sys->meta, p_ecd->ppsz_name[i], p_ecd->ppsz_value[i] );
1403
            /* TODO map WM/Composer, WM/Provider, WM/PartOfSet, PeakValue, AverageLevel  */
1404
1.35k
#undef set_meta
1405
1.35k
        }
1406
22
    }
1407
1408
    /// \todo Fix Child meta for ASF tracks
1409
#if 0
1410
    for( i_stream = 0, i = 0; i < MAX_ASF_TRACKS; i++ )
1411
    {
1412
        asf_object_codec_list_t *p_cl = ASF_FindObject( p_sys->p_root->p_hdr,
1413
                                                        &asf_object_codec_list_guid, 0 );
1414
1415
        if( p_sys->track[i] )
1416
        {
1417
            vlc_meta_t *tk = vlc_meta_New();
1418
            TAB_APPEND( p_sys->meta->i_track, p_sys->meta->track, tk );
1419
1420
            if( p_cl && i_stream < p_cl->i_codec_entries_count )
1421
            {
1422
                if( p_cl->codec[i_stream].psz_name &&
1423
                    *p_cl->codec[i_stream].psz_name )
1424
                {
1425
                    vlc_meta_Add( tk, VLC_META_CODEC_NAME,
1426
                                  p_cl->codec[i_stream].psz_name );
1427
                }
1428
                if( p_cl->codec[i_stream].psz_description &&
1429
                    *p_cl->codec[i_stream].psz_description )
1430
                {
1431
                    vlc_meta_Add( tk, VLC_META_CODEC_DESCRIPTION,
1432
                                  p_cl->codec[i_stream].psz_description );
1433
                }
1434
            }
1435
            i_stream++;
1436
        }
1437
    }
1438
#endif
1439
23
    free( fmt_priorities_ex.pi_stream_numbers );
1440
23
    free( fmt_priorities_bitrate_ex.pi_stream_numbers );
1441
1442
23
    p_sys->packet_sys.pi_preroll = &p_sys->p_fp->i_preroll;
1443
23
    p_sys->packet_sys.pi_preroll_start = &p_sys->i_preroll_start;
1444
23
    p_sys->packet_sys.b_can_hold_multiple_packets = false;
1445
1446
    /* Build seekpoints from the WM TC index.
1447
     * Each entry's i_timecode is a presentation time in milliseconds.
1448
     * The first two entries are header/metadata entries and are skipped. */
1449
23
    if( p_sys->p_root && p_sys->p_root->p_timecode_index )
1450
0
    {
1451
0
        const asf_object_timecode_index_t *p_tc =
1452
0
                p_sys->p_root->p_timecode_index;
1453
1454
0
        msg_Dbg( p_demux, "timecode index: %u entries", p_tc->i_index_entry_count );
1455
1456
0
        for( uint32_t i = 2; i < p_tc->i_index_entry_count; i++ )
1457
0
        {
1458
0
            const uint32_t i_tc_ms = p_tc->timecode_entry[i].i_timecode;
1459
1460
0
            seekpoint_t *p_sp = vlc_seekpoint_New();
1461
0
            if( unlikely(!p_sp) )
1462
0
                break;
1463
1464
0
            p_sp->i_time_offset = VLC_TICK_FROM_MS( i_tc_ms );
1465
1466
0
            if( asprintf( &p_sp->psz_name, "%u:%02u:%02u",
1467
0
                          i_tc_ms / 3600000,
1468
0
                          (i_tc_ms % 3600000) / 60000,
1469
0
                          (i_tc_ms % 60000) / 1000 ) < 0 )
1470
0
            {
1471
0
                vlc_seekpoint_Delete( p_sp );
1472
0
                break;
1473
0
            }
1474
1475
0
            TAB_APPEND( p_sys->i_seekpoints, p_sys->pp_seekpoints, p_sp );
1476
0
        }
1477
0
    }
1478
1479
23
    return VLC_SUCCESS;
1480
1481
0
error:
1482
0
    free( fmt_priorities_ex.pi_stream_numbers );
1483
0
    free( fmt_priorities_bitrate_ex.pi_stream_numbers );
1484
0
    DemuxEnd( p_demux );
1485
0
    return VLC_EGENERIC;
1486
23
}
1487
1488
/*****************************************************************************
1489
 * FlushQueues: flushes tail packets and send queues
1490
 *****************************************************************************/
1491
static void FlushQueue( asf_track_t *tk )
1492
24
{
1493
24
    ASFPacketTrackReset( &tk->info );
1494
24
    if( tk->queue.p_first )
1495
0
    {
1496
0
        block_ChainRelease( tk->queue.p_first );
1497
0
        tk->queue.p_first = NULL;
1498
0
        tk->queue.pp_last = &tk->queue.p_first;
1499
0
    }
1500
24
}
1501
1502
static void FlushQueues( demux_t *p_demux )
1503
23
{
1504
23
    demux_sys_t *p_sys = p_demux->p_sys;
1505
2.96k
    for ( unsigned int i = 0; i < MAX_ASF_TRACKS; i++ )
1506
2.94k
    {
1507
2.94k
        asf_track_t *tk = p_sys->track[i];
1508
2.94k
        if( !tk )
1509
2.92k
            continue;
1510
24
        FlushQueue( tk );
1511
24
    }
1512
23
}
1513
1514
/*****************************************************************************
1515
 *
1516
 *****************************************************************************/
1517
static void DemuxEnd( demux_t *p_demux )
1518
23
{
1519
23
    demux_sys_t *p_sys = p_demux->p_sys;
1520
1521
23
    if( p_sys->p_root )
1522
23
    {
1523
23
        ASF_FreeObjectRoot( p_demux->s, p_sys->p_root );
1524
23
        p_sys->p_root = NULL;
1525
23
        p_sys->p_fp = NULL;
1526
23
    }
1527
23
    if( p_sys->meta )
1528
23
    {
1529
23
        vlc_meta_Delete( p_sys->meta );
1530
23
        p_sys->meta = NULL;
1531
23
    }
1532
1533
23
    for( int i = 0; i < p_sys->i_seekpoints; i++ )
1534
0
        vlc_seekpoint_Delete( p_sys->pp_seekpoints[i] );
1535
23
    TAB_CLEAN( p_sys->i_seekpoints, p_sys->pp_seekpoints );
1536
1537
23
    FlushQueues( p_demux );
1538
1539
2.96k
    for( int i = 0; i < MAX_ASF_TRACKS; i++ )
1540
2.94k
    {
1541
2.94k
        asf_track_t *tk = p_sys->track[i];
1542
1543
2.94k
        if( tk )
1544
24
        {
1545
24
            if( tk->p_es )
1546
6
            {
1547
6
                es_out_Del( p_demux->out, tk->p_es );
1548
6
            }
1549
24
            if ( tk->p_fmt )
1550
3
            {
1551
3
                es_format_Clean( tk->p_fmt );
1552
3
                free( tk->p_fmt );
1553
3
            }
1554
24
            free( tk );
1555
24
        }
1556
2.94k
        p_sys->track[i] = 0;
1557
2.94k
    }
1558
23
}