asf.c 37 KB
Newer Older
1
/*****************************************************************************
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
2
 * asf.c : ASF demux module
3
 *****************************************************************************
4
 * Copyright (C) 2002-2003 the VideoLAN team
Laurent Aimar's avatar
Laurent Aimar committed
5
 * $Id$
6
 *
7
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8
 *
9 10 11 12
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
13
 *
14 15 16 17 18 19 20
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 23 24 25 26 27
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

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

32
#include <vlc_common.h>
33
#include <vlc_plugin.h>
Clément Stenac's avatar
Clément Stenac committed
34
#include <vlc_demux.h>
35
#include <vlc_dialog.h>
36

Clément Stenac's avatar
Clément Stenac committed
37 38 39
#include <vlc_meta.h>
#include <vlc_access.h>                /* GET_PRIVATE_ID_STATE */
#include <vlc_codecs.h>                /* BITMAPINFOHEADER, WAVEFORMATEX */
40 41
#include "libasf.h"

42 43 44 45 46
/* TODO
 *  - add support for the newly added object: language, bitrate,
 *                                            extended stream properties.
 */

47 48 49
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
50 51 52
static int  Open  ( vlc_object_t * );
static void Close ( vlc_object_t * );

53 54 55 56 57 58 59 60
vlc_module_begin ()
    set_category( CAT_INPUT )
    set_subcategory( SUBCAT_INPUT_DEMUX )
    set_description( N_("ASF v1.0 demuxer") )
    set_capability( "demux", 200 )
    set_callbacks( Open, Close )
    add_shortcut( "asf" )
vlc_module_end ()
61

Laurent Aimar's avatar
Laurent Aimar committed
62

63
/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
64
 * Local prototypes
65
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
66 67
static int Demux  ( demux_t * );
static int Control( demux_t *, int i_query, va_list args );
Laurent Aimar's avatar
Laurent Aimar committed
68

Laurent Aimar's avatar
Laurent Aimar committed
69
typedef struct
70 71
{
    int i_cat;
72 73 74

    es_out_id_t     *p_es;

75 76 77 78
    asf_object_stream_properties_t *p_sp;

    mtime_t i_time;

Laurent Aimar's avatar
Laurent Aimar committed
79
    block_t         *p_frame; /* use to gather complete frame */
80

Laurent Aimar's avatar
Laurent Aimar committed
81
} asf_track_t;
82 83 84

struct demux_sys_t
{
85
    mtime_t             i_time;     /* s */
86
    mtime_t             i_length;   /* length of file file */
Laurent Aimar's avatar
Laurent Aimar committed
87
    int64_t             i_bitrate;  /* global file bitrate */
88 89 90 91

    asf_object_root_t            *p_root;
    asf_object_file_properties_t *p_fp;

Laurent Aimar's avatar
Laurent Aimar committed
92
    unsigned int        i_track;
93
    asf_track_t         *track[128]; /* track number is stored on 7 bits */
94 95 96

    int64_t             i_data_begin;
    int64_t             i_data_end;
97

98
    bool          b_index;
99
    vlc_meta_t          *meta;
100 101
};

Laurent Aimar's avatar
Laurent Aimar committed
102
static mtime_t  GetMoviePTS( demux_sys_t * );
103 104
static int      DemuxInit( demux_t * );
static void     DemuxEnd( demux_t * );
Laurent Aimar's avatar
Laurent Aimar committed
105
static int      DemuxPacket( demux_t * );
Laurent Aimar's avatar
Laurent Aimar committed
106 107 108 109 110

/*****************************************************************************
 * Open: check file and initializes ASF structures
 *****************************************************************************/
static int Open( vlc_object_t * p_this )
111
{
Laurent Aimar's avatar
Laurent Aimar committed
112 113 114
    demux_t     *p_demux = (demux_t *)p_this;
    demux_sys_t *p_sys;
    guid_t      guid;
115
    const uint8_t     *p_peek;
116

117 118 119
    /* A little test to see if it could be a asf stream */
    if( stream_Peek( p_demux->s, &p_peek, 16 ) < 16 ) return VLC_EGENERIC;

Laurent Aimar's avatar
Laurent Aimar committed
120
    ASF_GetGUID( &guid, p_peek );
121
    if( !ASF_CmpGUID( &guid, &asf_object_header_guid ) ) return VLC_EGENERIC;
122

Laurent Aimar's avatar
Laurent Aimar committed
123 124 125 126
    /* Set p_demux fields */
    p_demux->pf_demux = Demux;
    p_demux->pf_control = Control;
    p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
127
    memset( p_sys, 0, sizeof( demux_sys_t ) );
Laurent Aimar's avatar
Laurent Aimar committed
128

129 130
    /* Load the headers */
    if( DemuxInit( p_demux ) )
131
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
132
        free( p_sys );
133
        return VLC_EGENERIC;
134
    }
Laurent Aimar's avatar
Laurent Aimar committed
135
    return VLC_SUCCESS;
136 137
}

138

Laurent Aimar's avatar
Laurent Aimar committed
139 140 141
/*****************************************************************************
 * Demux: read packet and send them to decoders
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
142
static int Demux( demux_t *p_demux )
Laurent Aimar's avatar
Laurent Aimar committed
143
{
Laurent Aimar's avatar
Laurent Aimar committed
144
    demux_sys_t *p_sys = p_demux->p_sys;
Laurent Aimar's avatar
Laurent Aimar committed
145 146 147

    for( ;; )
    {
148
        const uint8_t *p_peek;
Laurent Aimar's avatar
Laurent Aimar committed
149
        mtime_t i_length;
150
        mtime_t i_time_begin = GetMoviePTS( p_sys );
Laurent Aimar's avatar
Laurent Aimar committed
151 152
        int i_result;

153
        if( !vlc_object_alive (p_demux) )
154
            break;
155 156
#if 0
        /* FIXME: returns EOF too early for some mms streams */
157 158 159
        if( p_sys->i_data_end >= 0 &&
                stream_Tell( p_demux->s ) >= p_sys->i_data_end )
            return 0; /* EOF */
160
#endif
Laurent Aimar's avatar
Laurent Aimar committed
161

162 163 164 165 166 167 168 169
        /* Check if we have concatenated files */
        if( stream_Peek( p_demux->s, &p_peek, 16 ) == 16 )
        {
            guid_t guid;

            ASF_GetGUID( &guid, p_peek );
            if( ASF_CmpGUID( &guid, &asf_object_header_guid ) )
            {
170
                msg_Warn( p_demux, "found a new ASF header" );
171 172 173 174 175 176 177
                /* We end this stream */
                DemuxEnd( p_demux );

                /* And we prepare to read the next one */
                if( DemuxInit( p_demux ) )
                {
                    msg_Err( p_demux, "failed to load the new header" );
178
                    dialog_Fatal( p_demux, _("Could not demux ASF stream"), "%s",
179
                                    _("VLC failed to load the ASF header.") );
180 181
                    return 0;
                }
Laurent Aimar's avatar
Laurent Aimar committed
182
                es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
183 184 185 186 187
                continue;
            }
        }

        /* Read and demux a packet */
Laurent Aimar's avatar
Laurent Aimar committed
188
        if( ( i_result = DemuxPacket( p_demux ) ) <= 0 )
Laurent Aimar's avatar
Laurent Aimar committed
189 190 191 192 193
        {
            return i_result;
        }
        if( i_time_begin == -1 )
        {
194
            i_time_begin = GetMoviePTS( p_sys );
Laurent Aimar's avatar
Laurent Aimar committed
195 196 197
        }
        else
        {
198
            i_length = GetMoviePTS( p_sys ) - i_time_begin;
199
            if( i_length < 0 || i_length >= 40 * 1000 ) break;
Laurent Aimar's avatar
Laurent Aimar committed
200 201 202
        }
    }

203
    /* Set the PCR */
204 205
    p_sys->i_time = GetMoviePTS( p_sys );
    if( p_sys->i_time >= 0 )
Laurent Aimar's avatar
Laurent Aimar committed
206
    {
Laurent Aimar's avatar
Laurent Aimar committed
207
        es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_time+1 );
Laurent Aimar's avatar
Laurent Aimar committed
208 209
    }

Laurent Aimar's avatar
Laurent Aimar committed
210
    return 1;
Laurent Aimar's avatar
Laurent Aimar committed
211 212 213 214 215 216 217
}

/*****************************************************************************
 * Close: frees unused data
 *****************************************************************************/
static void Close( vlc_object_t * p_this )
{
Laurent Aimar's avatar
Laurent Aimar committed
218
    demux_t     *p_demux = (demux_t *)p_this;
Laurent Aimar's avatar
Laurent Aimar committed
219

220
    DemuxEnd( p_demux );
Laurent Aimar's avatar
Laurent Aimar committed
221

222
    free( p_demux->p_sys );
Laurent Aimar's avatar
Laurent Aimar committed
223 224
}

225 226 227
/*****************************************************************************
 * SeekIndex: goto to i_date or i_percent
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
228 229 230 231 232 233 234 235
static int SeekPercent( demux_t *p_demux, int i_query, va_list args )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    return demux_vaControlHelper( p_demux->s, p_sys->i_data_begin,
                                   p_sys->i_data_end, p_sys->i_bitrate,
                                   p_sys->p_fp->i_min_data_packet_size,
                                   i_query, args );
}
236

237 238 239 240 241 242
static int SeekIndex( demux_t *p_demux, mtime_t i_date, float f_pos )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    asf_object_index_t *p_index;

    msg_Dbg( p_demux, "seek with index: %i seconds, position %f",
Laurent Aimar's avatar
Laurent Aimar committed
243
             i_date >= 0 ? (int)(i_date/1000000) : -1, f_pos );
244

245 246
    if( i_date < 0 )
        i_date = p_sys->i_length * f_pos;
247

248
    p_index = ASF_FindObject( p_sys->p_root, &asf_object_index_guid, 0 );
249

250 251 252 253 254 255
    uint64_t i_entry = i_date * 10 / p_index->i_index_entry_time_interval;
    if( i_entry >= p_index->i_index_entry_count )
    {
        msg_Warn( p_demux, "Incomplete index" );
        return VLC_EGENERIC;
    }
256

257 258 259
    uint64_t i_offset = (uint64_t)p_index->index_entry[i_entry].i_packet_number *
                        p_sys->p_fp->i_min_data_packet_size;
    return stream_Seek( p_demux->s, p_sys->i_data_begin + i_offset );
260 261
}

262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
static void SeekPrepare( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;

    p_sys->i_time = -1;
    for( int i = 0; i < 128 ; i++ )
    {
        asf_track_t *tk = p_sys->track[i];
        if( !tk )
            continue;

        tk->i_time = 1;
        if( tk->p_frame )
            block_ChainRelease( tk->p_frame );
        tk->p_frame = NULL;
    }
}

280 281 282
/*****************************************************************************
 * Control:
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
283
static int Control( demux_t *p_demux, int i_query, va_list args )
284
{
Laurent Aimar's avatar
Laurent Aimar committed
285
    demux_sys_t *p_sys = p_demux->p_sys;
286 287 288
    vlc_meta_t  *p_meta;
    int64_t     i64, *pi64;
    double      f, *pf;
289 290 291

    switch( i_query )
    {
292 293 294 295 296 297 298 299 300 301 302 303
    case DEMUX_GET_LENGTH:
        pi64 = (int64_t*)va_arg( args, int64_t * );
        *pi64 = p_sys->i_length;
        return VLC_SUCCESS;

    case DEMUX_GET_TIME:
        pi64 = (int64_t*)va_arg( args, int64_t * );
        if( p_sys->i_time < 0 ) return VLC_EGENERIC;
        *pi64 = p_sys->i_time;
        return VLC_SUCCESS;

    case DEMUX_SET_TIME:
304
        SeekPrepare( p_demux );
305 306 307

        if( p_sys->b_index && p_sys->i_length > 0 )
        {
308 309 310 311 312 313 314
            va_list acpy;
            va_copy( acpy, args );
            i64 = (int64_t)va_arg( acpy, int64_t );
            va_end( acpy );

            if( !SeekIndex( p_demux, i64, -1 ) )
                return VLC_SUCCESS;
315
        }
316
        return SeekPercent( p_demux, i_query, args );
317

318 319 320 321 322 323
    case DEMUX_GET_POSITION:
        if( p_sys->i_time < 0 ) return VLC_EGENERIC;
        if( p_sys->i_length > 0 )
        {
            pf = (double*)va_arg( args, double * );
            *pf = p_sys->i_time / (double)p_sys->i_length;
324
            return VLC_SUCCESS;
325
        }
326
        return demux_vaControlHelper( p_demux->s, p_sys->i_data_begin,
327 328 329
                                       p_sys->i_data_end, p_sys->i_bitrate,
                                       p_sys->p_fp->i_min_data_packet_size,
                                       i_query, args );
330

331
    case DEMUX_SET_POSITION:
332
        SeekPrepare( p_demux );
Laurent Aimar's avatar
Laurent Aimar committed
333

334 335
        if( p_sys->b_index && p_sys->i_length > 0 )
        {
336 337 338 339 340 341 342
            va_list acpy;
            va_copy( acpy, args );
            f = (double)va_arg( acpy, double );
            va_end( acpy );

            if( !SeekIndex( p_demux, -1, f ) )
                return VLC_SUCCESS;
343
        }
344
        return SeekPercent( p_demux, i_query, args );
345 346 347 348 349 350 351

    case DEMUX_GET_META:
        p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t* );
        vlc_meta_Merge( p_meta, p_sys->meta );
        return VLC_SUCCESS;

    default:
352
        return demux_vaControlHelper( p_demux->s, p_sys->i_data_begin,
353 354 355
                                       p_sys->i_data_end, p_sys->i_bitrate,
                                       p_sys->p_fp->i_min_data_packet_size,
                                       i_query, args );
356 357
    }
}
Laurent Aimar's avatar
Laurent Aimar committed
358 359 360 361

/*****************************************************************************
 *
 *****************************************************************************/
362
static mtime_t GetMoviePTS( demux_sys_t *p_sys )
363
{
364
    mtime_t i_time = -1;
Laurent Aimar's avatar
Laurent Aimar committed
365
    int     i;
366

Laurent Aimar's avatar
Laurent Aimar committed
367
    for( i = 0; i < 128 ; i++ )
368
    {
Laurent Aimar's avatar
Laurent Aimar committed
369
        asf_track_t *tk = p_sys->track[i];
Laurent Aimar's avatar
Laurent Aimar committed
370

Laurent Aimar's avatar
Laurent Aimar committed
371
        if( tk && tk->p_es && tk->i_time > 0)
372
        {
373 374
            if( i_time < 0 ) i_time = tk->i_time;
            else i_time = __MIN( i_time, tk->i_time );
375 376 377
        }
    }

Laurent Aimar's avatar
Laurent Aimar committed
378
    return i_time;
379 380
}

381
#define GETVALUE2b( bits, var, def ) \
382
    switch( (bits)&0x03 ) \
383 384 385 386 387 388 389 390
    { \
        case 1: var = p_peek[i_skip]; i_skip++; break; \
        case 2: var = GetWLE( p_peek + i_skip );  i_skip+= 2; break; \
        case 3: var = GetDWLE( p_peek + i_skip ); i_skip+= 4; break; \
        case 0: \
        default: var = def; break;\
    }

Laurent Aimar's avatar
Laurent Aimar committed
391
static int DemuxPacket( demux_t *p_demux )
392
{
Laurent Aimar's avatar
Laurent Aimar committed
393 394
    demux_sys_t *p_sys = p_demux->p_sys;
    int         i_data_packet_min = p_sys->p_fp->i_min_data_packet_size;
395
    const uint8_t *p_peek;
Laurent Aimar's avatar
Laurent Aimar committed
396
    int         i_skip;
397

Laurent Aimar's avatar
Laurent Aimar committed
398 399 400
    int         i_packet_size_left;
    int         i_packet_flags;
    int         i_packet_property;
401

Laurent Aimar's avatar
Laurent Aimar committed
402 403 404 405
    int         b_packet_multiple_payload;
    int         i_packet_length;
    int         i_packet_sequence;
    int         i_packet_padding_length;
406 407 408 409 410 411 412 413

    uint32_t    i_packet_send_time;
    uint16_t    i_packet_duration;
    int         i_payload;
    int         i_payload_count;
    int         i_payload_length_type;


Laurent Aimar's avatar
Laurent Aimar committed
414
    if( stream_Peek( p_demux->s, &p_peek,i_data_packet_min)<i_data_packet_min )
415
    {
Laurent Aimar's avatar
Laurent Aimar committed
416 417
        msg_Warn( p_demux, "cannot peek while getting new packet, EOF ?" );
        return 0;
418 419
    }
    i_skip = 0;
420

421 422 423
    /* *** parse error correction if present *** */
    if( p_peek[0]&0x80 )
    {
424 425 426
        unsigned int i_error_correction_length_type;
        unsigned int i_error_correction_data_length;
        unsigned int i_opaque_data_present;
427 428 429 430 431 432 433 434 435

        i_error_correction_data_length = p_peek[0] & 0x0f;  // 4bits
        i_opaque_data_present = ( p_peek[0] >> 4 )& 0x01;    // 1bit
        i_error_correction_length_type = ( p_peek[0] >> 5 ) & 0x03; // 2bits
        i_skip += 1; // skip error correction flags

        if( i_error_correction_length_type != 0x00 ||
            i_opaque_data_present != 0 ||
            i_error_correction_data_length != 0x02 )
436
        {
437
            goto loop_error_recovery;
438 439
        }

440 441 442 443
        i_skip += i_error_correction_data_length;
    }
    else
    {
Laurent Aimar's avatar
Laurent Aimar committed
444
        msg_Warn( p_demux, "p_peek[0]&0x80 != 0x80" );
445 446
    }

447 448 449 450 451 452
    /* sanity check */
    if( i_skip + 2 >= i_data_packet_min )
    {
        goto loop_error_recovery;
    }

453 454
    i_packet_flags = p_peek[i_skip]; i_skip++;
    i_packet_property = p_peek[i_skip]; i_skip++;
455

456
    b_packet_multiple_payload = i_packet_flags&0x01;
457

458 459 460 461
    /* read some value */
    GETVALUE2b( i_packet_flags >> 5, i_packet_length, i_data_packet_min );
    GETVALUE2b( i_packet_flags >> 1, i_packet_sequence, 0 );
    GETVALUE2b( i_packet_flags >> 3, i_packet_padding_length, 0 );
462

463 464 465 466 467 468
    if( i_packet_padding_length > i_packet_length )
    {
        msg_Warn( p_demux, "Too large padding: %d", i_packet_padding_length );
        goto loop_error_recovery;
    }

469 470
    i_packet_send_time = GetDWLE( p_peek + i_skip ); i_skip += 4;
    i_packet_duration  = GetWLE( p_peek + i_skip ); i_skip += 2;
471

472 473
    /* FIXME I have to do that for some file, I don't known why */
    i_packet_size_left = i_data_packet_min /*i_packet_length*/ ;
474 475 476 477 478 479 480 481 482 483 484 485

    if( b_packet_multiple_payload )
    {
        i_payload_count = p_peek[i_skip] & 0x3f;
        i_payload_length_type = ( p_peek[i_skip] >> 6 )&0x03;
        i_skip++;
    }
    else
    {
        i_payload_count = 1;
        i_payload_length_type = 0x02; // unused
    }
486

487 488
    for( i_payload = 0; i_payload < i_payload_count ; i_payload++ )
    {
Laurent Aimar's avatar
Laurent Aimar committed
489
        asf_track_t   *tk;
490

491
        int i_packet_keyframe;
492 493 494 495 496 497 498 499 500 501 502 503 504
        int i_stream_number;
        int i_media_object_number;
        int i_media_object_offset;
        int i_replicated_data_length;
        int i_payload_data_length;
        int i_payload_data_pos;
        int i_sub_payload_data_length;
        int i_tmp;

        mtime_t i_pts;
        mtime_t i_pts_delta;

        if( i_skip >= i_packet_size_left )
505
        {
506 507
            /* prevent some segfault with invalid file */
            break;
508
        }
509

510 511
        i_packet_keyframe = p_peek[i_skip] >> 7;
        i_stream_number = p_peek[i_skip++] & 0x7f;
512 513 514 515 516 517

        GETVALUE2b( i_packet_property >> 4, i_media_object_number, 0 );
        GETVALUE2b( i_packet_property >> 2, i_tmp, 0 );
        GETVALUE2b( i_packet_property, i_replicated_data_length, 0 );

        if( i_replicated_data_length > 1 ) // should be at least 8 bytes
518
        {
519 520 521
            i_pts = (mtime_t)GetDWLE( p_peek + i_skip + 4 ) * 1000;
            i_skip += i_replicated_data_length;
            i_pts_delta = 0;
522

523
            i_media_object_offset = i_tmp;
524 525 526 527 528

            if( i_skip >= i_packet_size_left )
            {
                break;
            }
529
        }
530
        else if( i_replicated_data_length == 1 )
531
        {
532
            /* msg_Dbg( p_demux, "found compressed payload" ); */
533

534 535
            i_pts = (mtime_t)i_tmp * 1000;
            i_pts_delta = (mtime_t)p_peek[i_skip] * 1000; i_skip++;
536

537 538 539 540 541 542
            i_media_object_offset = 0;
        }
        else
        {
            i_pts = (mtime_t)i_packet_send_time * 1000;
            i_pts_delta = 0;
543

544 545
            i_media_object_offset = i_tmp;
        }
546

547
        i_pts = __MAX( i_pts - p_sys->p_fp->i_preroll * 1000, 0 );
548 549
        if( b_packet_multiple_payload )
        {
550
            GETVALUE2b( i_payload_length_type, i_payload_data_length, 0 );
551 552 553
        }
        else
        {
Rafaël Carré's avatar
Rafaël Carré committed
554 555
            i_payload_data_length = i_packet_length -
                                    i_packet_padding_length - i_skip;
556 557
        }

558
        if( i_payload_data_length < 0 || i_payload_data_length > i_packet_size_left )
559 560 561
        {
            break;
        }
562
#if 0
Laurent Aimar's avatar
Laurent Aimar committed
563
         msg_Dbg( p_demux,
564
                  "payload(%d/%d) stream_number:%d media_object_number:%d media_object_offset:%d replicated_data_length:%d payload_data_length %d",
Laurent Aimar's avatar
Laurent Aimar committed
565 566
                  i_payload + 1, i_payload_count, i_stream_number, i_media_object_number,
                  i_media_object_offset, i_replicated_data_length, i_payload_data_length );
567 568
#endif

Laurent Aimar's avatar
Laurent Aimar committed
569
        if( ( tk = p_sys->track[i_stream_number] ) == NULL )
570
        {
Laurent Aimar's avatar
Laurent Aimar committed
571
            msg_Warn( p_demux,
572 573 574 575
                      "undeclared stream[Id 0x%x]", i_stream_number );
            i_skip += i_payload_data_length;
            continue;   // over payload
        }
576

Laurent Aimar's avatar
Laurent Aimar committed
577
        if( !tk->p_es )
578 579 580 581
        {
            i_skip += i_payload_data_length;
            continue;
        }
582 583


584 585 586 587 588
        for( i_payload_data_pos = 0;
             i_payload_data_pos < i_payload_data_length &&
                    i_packet_size_left > 0;
             i_payload_data_pos += i_sub_payload_data_length )
        {
Laurent Aimar's avatar
Laurent Aimar committed
589
            block_t *p_frag;
590
            int i_read;
Laurent Aimar's avatar
Laurent Aimar committed
591

592 593
            // read sub payload length
            if( i_replicated_data_length == 1 )
594
            {
595 596
                i_sub_payload_data_length = p_peek[i_skip]; i_skip++;
                i_payload_data_pos++;
597
            }
598
            else
599
            {
600
                i_sub_payload_data_length = i_payload_data_length;
601 602
            }

603
            /* FIXME I don't use i_media_object_number, sould I ? */
Laurent Aimar's avatar
Laurent Aimar committed
604
            if( tk->p_frame && i_media_object_offset == 0 )
Gildas Bazin's avatar
 
Gildas Bazin committed
605
            {
606
                /* send complete packet to decoder */
Laurent Aimar's avatar
Laurent Aimar committed
607
                block_t *p_gather = block_ChainGather( tk->p_frame );
608

609 610
                if( p_gather->i_dts > VLC_TS_INVALID )
                    tk->i_time = p_gather->i_dts - VLC_TS_0;
Laurent Aimar's avatar
Laurent Aimar committed
611

Laurent Aimar's avatar
Laurent Aimar committed
612
                if( p_sys->i_time < 0 )
613
                    es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + tk->i_time );
Laurent Aimar's avatar
Laurent Aimar committed
614

Laurent Aimar's avatar
Laurent Aimar committed
615
                es_out_Send( p_demux->out, tk->p_es, p_gather );
616

Laurent Aimar's avatar
Laurent Aimar committed
617
                tk->p_frame = NULL;
618 619
            }

620
            i_read = i_sub_payload_data_length + i_skip;
Laurent Aimar's avatar
Laurent Aimar committed
621
            if( ( p_frag = stream_Block( p_demux->s, i_read ) ) == NULL )
622
            {
Laurent Aimar's avatar
Laurent Aimar committed
623 624
                msg_Warn( p_demux, "cannot read data" );
                return 0;
625
            }
626
            i_packet_size_left -= i_read;
627

Laurent Aimar's avatar
Laurent Aimar committed
628 629
            p_frag->p_buffer += i_skip;
            p_frag->i_buffer -= i_skip;
630

Laurent Aimar's avatar
Laurent Aimar committed
631
            if( tk->p_frame == NULL )
632
            {
633
                p_frag->i_pts = VLC_TS_0 + i_pts + i_payload * (mtime_t)i_pts_delta;
Laurent Aimar's avatar
Laurent Aimar committed
634
                if( tk->i_cat != VIDEO_ES )
635
                    p_frag->i_dts = VLC_TS_0 + p_frag->i_pts;
Gildas Bazin's avatar
 
Gildas Bazin committed
636 637
                else
                {
638 639
                    p_frag->i_dts = VLC_TS_0 + p_frag->i_pts;
                    p_frag->i_pts = VLC_TS_INVALID;
Gildas Bazin's avatar
 
Gildas Bazin committed
640
                }
641
            }
Laurent Aimar's avatar
Laurent Aimar committed
642

Laurent Aimar's avatar
Laurent Aimar committed
643
            block_ChainAppend( &tk->p_frame, p_frag );
644

645 646
            i_skip = 0;
            if( i_packet_size_left > 0 )
647
            {
Laurent Aimar's avatar
Laurent Aimar committed
648
                if( stream_Peek( p_demux->s, &p_peek, i_packet_size_left )
649
                                                         < i_packet_size_left )
650
                {
Laurent Aimar's avatar
Laurent Aimar committed
651 652
                    msg_Warn( p_demux, "cannot peek, EOF ?" );
                    return 0;
653
                }
654 655 656
            }
        }
    }
657

658 659
    if( i_packet_size_left > 0 )
    {
660
#ifdef ASF_DEBUG
Rafaël Carré's avatar
Rafaël Carré committed
661 662 663 664 665 666
        if( i_packet_size_left > i_packet_padding_length )
            msg_Warn( p_demux, "Didn't read %d bytes in the packet",
                            i_packet_size_left - i_packet_padding_length );
        else if( i_packet_size_left < i_packet_padding_length )
            msg_Warn( p_demux, "Read %d too much bytes in the packet",
                            i_packet_padding_length - i_packet_size_left );
667
#endif
668 669
        if( stream_Read( p_demux->s, NULL, i_packet_size_left )
                                                         < i_packet_size_left )
670
        {
671
            msg_Err( p_demux, "cannot skip data, EOF ?" );
Laurent Aimar's avatar
Laurent Aimar committed
672
            return 0;
673 674
        }
    }
675

Laurent Aimar's avatar
Laurent Aimar committed
676
    return 1;
677

678
loop_error_recovery:
Laurent Aimar's avatar
Laurent Aimar committed
679
    msg_Warn( p_demux, "unsupported packet header" );
680
    if( p_sys->p_fp->i_min_data_packet_size != p_sys->p_fp->i_max_data_packet_size )
681
    {
Laurent Aimar's avatar
Laurent Aimar committed
682 683
        msg_Err( p_demux, "unsupported packet header, fatal error" );
        return -1;
684
    }
685 686 687 688 689
    if( stream_Read( p_demux->s, NULL, i_data_packet_min ) != i_data_packet_min )
    {
        msg_Warn( p_demux, "cannot skip data, EOF ?" );
        return 0;
    }
690

Laurent Aimar's avatar
Laurent Aimar committed
691
    return 1;
692
}
693

694 695 696 697 698 699 700 701 702 703 704 705 706
/*****************************************************************************
 *
 *****************************************************************************/
static int DemuxInit( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;

    /* init context */
    p_sys->i_time   = -1;
    p_sys->i_length = 0;
    p_sys->i_bitrate = 0;
    p_sys->p_root   = NULL;
    p_sys->p_fp     = NULL;
707
    p_sys->b_index  = 0;
708
    p_sys->i_track  = 0;
709
    for( int i = 0; i < 128; i++ )
710 711 712 713 714 715 716 717
    {
        p_sys->track[i] = NULL;
    }
    p_sys->i_data_begin = -1;
    p_sys->i_data_end   = -1;
    p_sys->meta         = NULL;

    /* Now load all object ( except raw data ) */
Laurent Aimar's avatar
Laurent Aimar committed
718
    bool b_seekable;
719
    stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &b_seekable );
720
    if( !(p_sys->p_root = ASF_ReadObjectRoot(p_demux->s, b_seekable)) )
721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739
    {
        msg_Warn( p_demux, "ASF plugin discarded (not a valid file)" );
        return VLC_EGENERIC;
    }
    p_sys->p_fp = p_sys->p_root->p_fp;

    if( p_sys->p_fp->i_min_data_packet_size != p_sys->p_fp->i_max_data_packet_size )
    {
        msg_Warn( p_demux, "ASF plugin discarded (invalid file_properties object)" );
        goto error;
    }

    p_sys->i_track = ASF_CountObject( p_sys->p_root->p_hdr,
                                      &asf_object_stream_properties_guid );
    if( p_sys->i_track <= 0 )
    {
        msg_Warn( p_demux, "ASF plugin discarded (cannot find any stream!)" );
        goto error;
    }
Laurent Aimar's avatar
Laurent Aimar committed
740
    msg_Dbg( p_demux, "found %d streams", p_sys->i_track );
741

742
    /* check if index is available */
Laurent Aimar's avatar
Laurent Aimar committed
743 744 745
    asf_object_index_t *p_index = ASF_FindObject( p_sys->p_root,
                                                  &asf_object_index_guid, 0 );
    const bool b_index = p_index && p_index->i_index_entry_count;
746

747 748 749
    /* Find the extended header if any */
    asf_object_t *p_hdr_ext = ASF_FindObject( p_sys->p_root->p_hdr,
                                              &asf_object_header_extension_guid, 0 );
750

751 752 753 754
    asf_object_language_list_t *p_languages = NULL;
    if( p_hdr_ext )
        p_languages = ASF_FindObject( p_hdr_ext, &asf_object_language_list, 0 );

Laurent Aimar's avatar
Laurent Aimar committed
755
    for( unsigned i_stream = 0; i_stream < p_sys->i_track; i_stream++ )
756 757 758
    {
        asf_track_t    *tk;
        asf_object_stream_properties_t *p_sp;
759
        asf_object_extended_stream_properties_t *p_esp;
760
        bool b_access_selected;
761 762 763 764

        p_sp = ASF_FindObject( p_sys->p_root->p_hdr,
                               &asf_object_stream_properties_guid,
                               i_stream );
765
        p_esp = NULL;
766 767 768 769 770 771 772 773 774

        tk = p_sys->track[p_sp->i_stream_number] = malloc( sizeof( asf_track_t ) );
        memset( tk, 0, sizeof( asf_track_t ) );

        tk->i_time = -1;
        tk->p_sp = p_sp;
        tk->p_es = NULL;
        tk->p_frame = NULL;

775 776 777 778 779 780 781 782 783 784 785
        /* Check (in case of mms) if this track is selected (ie will receive data) */
        if( !stream_Control( p_demux->s, STREAM_CONTROL_ACCESS, ACCESS_GET_PRIVATE_ID_STATE,
                             p_sp->i_stream_number, &b_access_selected ) &&
            !b_access_selected )
        {
            tk->i_cat = UNKNOWN_ES;
            msg_Dbg( p_demux, "ignoring not selected stream(ID:%d) (by access)",
                     p_sp->i_stream_number );
            continue;
        }

786 787 788 789 790
        /* Find the associated extended_stream_properties if any */
        if( p_hdr_ext )
        {
            int i_ext_stream = ASF_CountObject( p_hdr_ext,
                                                &asf_object_extended_stream_properties );
791
            for( int i = 0; i < i_ext_stream; i++ )
792 793 794 795 796 797 798 799 800 801 802 803
            {
                asf_object_t *p_tmp =
                    ASF_FindObject( p_hdr_ext,
                                    &asf_object_extended_stream_properties, i );
                if( p_tmp->ext_stream.i_stream_number == p_sp->i_stream_number )
                {
                    p_esp = &p_tmp->ext_stream;
                    break;
                }
            }
        }

804 805
        es_format_t fmt;

806 807 808
        if( ASF_CmpGUID( &p_sp->i_stream_type, &asf_object_stream_type_audio ) &&
            p_sp->i_type_specific_data_length >= sizeof( WAVEFORMATEX ) - 2 )
        {
809 810
            uint8_t *p_data = p_sp->p_type_specific_data;
            int i_format;
811 812

            es_format_Init( &fmt, AUDIO_ES, 0 );
813 814
            i_format = GetWLE( &p_data[0] );
            wf_tag_to_fourcc( i_format, &fmt.i_codec, NULL );
815 816 817 818 819 820
            fmt.audio.i_channels        = GetWLE(  &p_data[2] );
            fmt.audio.i_rate      = GetDWLE( &p_data[4] );
            fmt.i_bitrate         = GetDWLE( &p_data[8] ) * 8;
            fmt.audio.i_blockalign      = GetWLE(  &p_data[12] );
            fmt.audio.i_bitspersample   = GetWLE(  &p_data[14] );

821 822 823
            if( p_sp->i_type_specific_data_length > sizeof( WAVEFORMATEX ) &&
                i_format != WAVE_FORMAT_MPEGLAYER3 &&
                i_format != WAVE_FORMAT_MPEG )
824 825
            {
                fmt.i_extra = __MIN( GetWLE( &p_data[16] ),
826 827
                                     p_sp->i_type_specific_data_length -
                                     sizeof( WAVEFORMATEX ) );
828
                fmt.p_extra = malloc( fmt.i_extra );
829 830
                memcpy( fmt.p_extra, &p_data[sizeof( WAVEFORMATEX )],
                        fmt.i_extra );
831 832 833 834 835
            }

            msg_Dbg( p_demux, "added new audio stream(codec:0x%x,ID:%d)",
                    GetWLE( p_data ), p_sp->i_stream_number );
        }
836 837 838 839
        else if( ASF_CmpGUID( &p_sp->i_stream_type,
                              &asf_object_stream_type_video ) &&
                 p_sp->i_type_specific_data_length >= 11 +
                 sizeof( BITMAPINFOHEADER ) )
840 841 842 843
        {
            uint8_t      *p_data = &p_sp->p_type_specific_data[11];

            es_format_Init( &fmt, VIDEO_ES,
844 845
                            VLC_FOURCC( p_data[16], p_data[17],
                                        p_data[18], p_data[19] ) );
846 847 848
            fmt.video.i_width = GetDWLE( p_data + 4 );
            fmt.video.i_height= GetDWLE( p_data + 8 );

849 850 851 852 853
            if( p_esp && p_esp->i_average_time_per_frame > 0 )
            {
                fmt.video.i_frame_rate = 10000000;
                fmt.video.i_frame_rate_base = p_esp->i_average_time_per_frame;
            }
854 855 856 857 858

            if( fmt.i_codec == VLC_FOURCC( 'D','V','R',' ') )
            {
                /* DVR-MS special ASF */
                fmt.i_codec = VLC_FOURCC( 'm','p','g','2' ) ;
859
                fmt.b_packetized = false;
860 861
            }

862 863
            if( p_sp->i_type_specific_data_length > 11 +
                sizeof( BITMAPINFOHEADER ) )
864 865
            {
                fmt.i_extra = __MIN( GetDWLE( p_data ),
866 867
                                     p_sp->i_type_specific_data_length - 11 -
                                     sizeof( BITMAPINFOHEADER ) );
868
                fmt.p_extra = malloc( fmt.i_extra );
869 870
                memcpy( fmt.p_extra, &p_data[sizeof( BITMAPINFOHEADER )],
                        fmt.i_extra );
871 872
            }

873 874 875 876
            /* Look for an aspect ratio */
            if( p_sys->p_root->p_metadata )
            {
                asf_object_metadata_t *p_meta = p_sys->p_root->p_metadata;
877 878
                int i_aspect_x = 0, i_aspect_y = 0;
                unsigned int i;
879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899

                for( i = 0; i < p_meta->i_record_entries_count; i++ )
                {
                    if( !strcmp( p_meta->record[i].psz_name, "AspectRatioX" ) )
                    {
                        if( (!i_aspect_x && !p_meta->record[i].i_stream) ||
                            p_meta->record[i].i_stream ==
                            p_sp->i_stream_number )
                            i_aspect_x = p_meta->record[i].i_val;
                    }
                    if( !strcmp( p_meta->record[i].psz_name, "AspectRatioY" ) )
                    {
                        if( (!i_aspect_y && !p_meta->record[i].i_stream) ||
                            p_meta->record[i].i_stream ==
                            p_sp->i_stream_number )
                            i_aspect_y = p_meta->record[i].i_val;
                    }
                }

                if( i_aspect_x && i_aspect_y )
                {
900 901
                    fmt.video.i_sar_num = i_aspect_x;
                    fmt.video.i_sar_den = i_aspect_y;
902
                }
903
            }
904

905 906 907
            /* If there is a video track then use the index for seeking */
            p_sys->b_index = b_index;

908 909 910
            msg_Dbg( p_demux, "added new video stream(ID:%d)",
                     p_sp->i_stream_number );
        }
911 912 913 914 915 916 917 918 919
        else if( ASF_CmpGUID( &p_sp->i_stream_type, &asf_object_extended_stream_header ) &&
            p_sp->i_type_specific_data_length >= 64 )
        {
            /* Now follows a 64 byte header of which we don't know much */
            guid_t  *p_ref  = (guid_t *)p_sp->p_type_specific_data;
            uint8_t *p_data = p_sp->p_type_specific_data + 64;
            unsigned int i_data = p_sp->i_type_specific_data_length - 64;

            msg_Dbg( p_demux, "Ext stream header detected. datasize = %d", p_sp->i_type_specific_data_length );
920
            if( ASF_CmpGUID( p_ref, &asf_object_extended_stream_type_audio ) &&
921 922 923 924 925
                i_data >= sizeof( WAVEFORMATEX ) - 2)
            {
                int      i_format;
                es_format_Init( &fmt, AUDIO_ES, 0 );
                i_format = GetWLE( &p_data[0] );
926
                if( i_format == 0 )
927
                    fmt.i_codec = VLC_CODEC_A52;
928 929 930 931 932 933 934
                else
                    wf_tag_to_fourcc( i_format, &fmt.i_codec, NULL );
                fmt.audio.i_channels        = GetWLE(  &p_data[2] );
                fmt.audio.i_rate      = GetDWLE( &p_data[4] );
                fmt.i_bitrate         = GetDWLE( &p_data[8] ) * 8;
                fmt.audio.i_blockalign      = GetWLE(  &p_data[12] );
                fmt.audio.i_bitspersample   = GetWLE(  &p_data[14] );
935
                fmt.b_packetized = true;
936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951

                if( p_sp->i_type_specific_data_length > sizeof( WAVEFORMATEX ) &&
                    i_format != WAVE_FORMAT_MPEGLAYER3 &&
                    i_format != WAVE_FORMAT_MPEG )
                {
                    fmt.i_extra = __MIN( GetWLE( &p_data[16] ),
                                         p_sp->i_type_specific_data_length -
                                         sizeof( WAVEFORMATEX ) );
                    fmt.p_extra = malloc( fmt.i_extra );
                    memcpy( fmt.p_extra, &p_data[sizeof( WAVEFORMATEX )],
                        fmt.i_extra );
                }

                msg_Dbg( p_demux, "added new audio stream (codec:0x%x,ID:%d)",
                    i_format, p_sp->i_stream_number );
            }
952 953 954 955 956 957 958 959 960 961 962 963 964
            else
            {
                es_format_Init( &fmt, UNKNOWN_ES, 0 );
            }
        }
        else
        {
            es_format_Init( &fmt, UNKNOWN_ES, 0 );
        }

        tk->i_cat = fmt.i_cat;
        if( fmt.i_cat != UNKNOWN_ES )
        {
965 966 967 968 969 970 971 972 973 974
            if( p_esp && p_languages &&
                p_esp->i_language_index >= 0 &&
                p_esp->i_language_index < p_languages->i_language )
            {
                fmt.psz_language = strdup( p_languages->ppsz_language[p_esp->i_language_index] );
                char *p;
                if( fmt.psz_language && (p = strchr( fmt.psz_language, '-' )) )
                    *p = '\0';
            }

975
            tk->p_es = es_out_Add( p_demux->out, &fmt );
976
        }
977 978 979 980 981
        else
        {
            msg_Dbg( p_demux, "ignoring unknown stream(ID:%d)",
                     p_sp->i_stream_number );
        }
982
        es_format_Clean( &fmt );
983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016
    }

    p_sys->i_data_begin = p_sys->p_root->p_data->i_object_pos + 50;
    if( p_sys->p_root->p_data->i_object_size != 0 )
    { /* local file */
        p_sys->i_data_end = p_sys->p_root->p_data->i_object_pos +
                                    p_sys->p_root->p_data->i_object_size;
    }
    else
    { /* live/broacast */
        p_sys->i_data_end = -1;
    }

    /* go to first packet */
    stream_Seek( p_demux->s, p_sys->i_data_begin );

    /* try to calculate movie time */
    if( p_sys->p_fp->i_data_packets_count > 0 )
    {
        int64_t i_count;
        int64_t i_size = stream_Size( p_demux->s );

        if( p_sys->i_data_end > 0 && i_size > p_sys->i_data_end )
        {
            i_size = p_sys->i_data_end;
        }

        /* real number of packets */
        i_count = ( i_size - p_sys->i_data_begin ) /
                  p_sys->p_fp->i_min_data_packet_size;

        /* calculate the time duration in micro-s */
        p_sys->i_length = (mtime_t)p_sys->p_fp->i_play_duration / 10 *
                   (mtime_t)i_count /
1017 1018 1019
                   (mtime_t)p_sys->p_fp->i_data_packets_count - p_sys->p_fp->i_preroll * 1000;
        if( p_sys->i_length < 0 )
            p_sys->i_length = 0;
1020 1021 1022 1023 1024 1025 1026

        if( p_sys->i_length > 0 )
        {
            p_sys->i_bitrate = 8 * i_size * (int64_t)1000000 / p_sys->i_length;
        }
    }

1027
    /* Create meta information */
1028 1029
    p_sys->meta = vlc_meta_New();

Laurent Aimar's avatar
Laurent Aimar committed
1030
    asf_object_content_description_t *p_cd;
1031 1032 1033 1034 1035
    if( ( p_cd = ASF_FindObject( p_sys->p_root->p_hdr,
                                 &asf_object_content_description_guid, 0 ) ) )
    {
        if( p_cd->psz_title && *p_cd->psz_title )
        {
1036
            vlc_meta_SetTitle( p_sys->meta, p_cd->psz_title );
1037
        }
Clément Stenac's avatar
Clément Stenac committed
1038
        if( p_cd->psz_artist && *p_cd->psz_artist )
1039
        {
Clément Stenac's avatar
Clément Stenac committed
1040
             vlc_meta_SetArtist( p_sys->meta, p_cd->psz_artist );
1041 1042 1043
        }
        if( p_cd->psz_copyright && *p_cd->psz_copyright )
        {
1044
            vlc_meta_SetCopyright( p_sys->meta, p_cd->psz_copyright );
1045 1046 1047
        }
        if( p_cd->psz_description && *p_cd->psz_description )
        {
1048
            vlc_meta_SetDescription( p_sys->meta, p_cd->psz_description );
1049 1050 1051
        }
        if( p_cd->psz_rating && *p_cd->psz_rating )
        {
1052
            vlc_meta_SetRating( p_sys->meta, p_cd->psz_rating );
1053 1054
        }
    }
Clément Stenac's avatar
Clément Stenac committed
1055
    /// \tood Fix Child meta for ASF tracks
1056
#if 0
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084
    for( i_stream = 0, i = 0; i < 128; i++ )
    {
        asf_object_codec_list_t *p_cl = ASF_FindObject( p_sys->p_root->p_hdr,
                                                        &asf_object_codec_list_guid, 0 );

        if( p_sys->track[i] )
        {
            vlc_meta_t *tk = vlc_meta_New();
            TAB_APPEND( p_sys->meta->i_track, p_sys->meta->track, tk );

            if( p_cl && i_stream < p_cl->i_codec_entries_count )
            {
                if( p_cl->codec[i_stream].psz_name &&
                    *p_cl->codec[i_stream].psz_name )
                {
                    vlc_meta_Add( tk, VLC_META_CODEC_NAME,
                                  p_cl->codec[i_stream].psz_name );
                }
                if( p_cl->codec[i_stream].psz_description &&
                    *p_cl->codec[i_stream].psz_description )
                {
                    vlc_meta_Add( tk, VLC_META_CODEC_DESCRIPTION,
                                  p_cl->codec[i_stream].psz_description );
                }
            }
            i_stream++;
        }
    }
1085
#endif
1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129
    return VLC_SUCCESS;

error:
    ASF_FreeObjectRoot( p_demux->s, p_sys->p_root );
    return VLC_EGENERIC;
}
/*****************************************************************************
 *
 *****************************************************************************/
static void DemuxEnd( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    int         i;

    if( p_sys->p_root )
    {
        ASF_FreeObjectRoot( p_demux->s, p_sys->p_root );
        p_sys->p_root = NULL;
    }
    if( p_sys->meta )
    {
        vlc_meta_Delete( p_sys->meta );
        p_sys->meta = NULL;
    }

    for( i = 0; i < 128; i++ )
    {
        asf_track_t *tk = p_sys->track[i];

        if( tk )
        {
            if( tk->p_frame )
            {
                block_ChainRelease( tk->p_frame );
            }
            if( tk->p_es )
            {
                es_out_Del( p_demux->out, tk->p_es );
            }
            free( tk );
        }
        p_sys->track[i] = 0;
    }
}
1130