asfdec.c 45.9 KB
Newer Older
Fabrice Bellard's avatar
Fabrice Bellard committed
1
/*
2
 * ASF compatible demuxer
3
 * Copyright (c) 2000, 2001 Fabrice Bellard
Fabrice Bellard's avatar
Fabrice Bellard committed
4
 *
5 6 7
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
Fabrice Bellard's avatar
Fabrice Bellard committed
8 9
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
Fabrice Bellard's avatar
Fabrice Bellard committed
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
Fabrice Bellard's avatar
Fabrice Bellard committed
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Fabrice Bellard's avatar
Fabrice Bellard committed
14 15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
Fabrice Bellard's avatar
Fabrice Bellard committed
16
 *
Fabrice Bellard's avatar
Fabrice Bellard committed
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Fabrice Bellard's avatar
Fabrice Bellard committed
20
 */
21

22 23
//#define DEBUG

24
#include "libavutil/common.h"
25
#include "libavutil/avstring.h"
26
#include "libavcodec/mpegaudio.h"
Fabrice Bellard's avatar
Fabrice Bellard committed
27
#include "avformat.h"
28
#include "riff.h"
29
#include "asf.h"
30
#include "asfcrypt.h"
31
#include "avlanguage.h"
Fabrice Bellard's avatar
Fabrice Bellard committed
32

33
void ff_mms_set_stream_selection(URLContext *h, AVFormatContext *format);
34

Michael Niedermayer's avatar
Michael Niedermayer committed
35 36 37
#undef NDEBUG
#include <assert.h>

38
#define ASF_MAX_STREAMS 127
39
#define FRAME_HEADER_SIZE 17
40
// Fix Me! FRAME_HEADER_SIZE may be different.
41

42
static const ff_asf_guid index_guid = {
Michael Niedermayer's avatar
Michael Niedermayer committed
43
    0x90, 0x08, 0x00, 0x33, 0xb1, 0xe5, 0xcf, 0x11, 0x89, 0xf4, 0x00, 0xa0, 0xc9, 0x03, 0x49, 0xcb
Fabrice Bellard's avatar
Fabrice Bellard committed
44 45
};

46
static const ff_asf_guid stream_bitrate_guid = { /* (http://get.to/sdp) */
47 48
    0xce, 0x75, 0xf8, 0x7b, 0x8d, 0x46, 0xd1, 0x11, 0x8d, 0x82, 0x00, 0x60, 0x97, 0xc9, 0xa2, 0xb2
};
Fabrice Bellard's avatar
Fabrice Bellard committed
49 50 51
/**********************************/
/* decoding */

52 53 54 55
static int guidcmp(const void *g1, const void *g2)
{
    return memcmp(g1, g2, sizeof(ff_asf_guid));
}
Fabrice Bellard's avatar
Fabrice Bellard committed
56 57

#ifdef DEBUG
François Revol's avatar
François Revol committed
58
#define PRINT_IF_GUID(g,cmp) \
59
if (!guidcmp(g, &cmp)) \
60
    dprintf(NULL, "(GUID: %s) ", #cmp)
François Revol's avatar
François Revol committed
61

62
static void print_guid(const ff_asf_guid *g)
Fabrice Bellard's avatar
Fabrice Bellard committed
63 64
{
    int i;
65 66 67 68 69 70 71 72 73 74 75 76
    PRINT_IF_GUID(g, ff_asf_header);
    else PRINT_IF_GUID(g, ff_asf_file_header);
    else PRINT_IF_GUID(g, ff_asf_stream_header);
    else PRINT_IF_GUID(g, ff_asf_audio_stream);
    else PRINT_IF_GUID(g, ff_asf_audio_conceal_none);
    else PRINT_IF_GUID(g, ff_asf_video_stream);
    else PRINT_IF_GUID(g, ff_asf_video_conceal_none);
    else PRINT_IF_GUID(g, ff_asf_command_stream);
    else PRINT_IF_GUID(g, ff_asf_comment_header);
    else PRINT_IF_GUID(g, ff_asf_codec_comment_header);
    else PRINT_IF_GUID(g, ff_asf_codec_comment1_header);
    else PRINT_IF_GUID(g, ff_asf_data_header);
François Revol's avatar
François Revol committed
77
    else PRINT_IF_GUID(g, index_guid);
78 79 80 81 82 83 84 85
    else PRINT_IF_GUID(g, ff_asf_head1_guid);
    else PRINT_IF_GUID(g, ff_asf_head2_guid);
    else PRINT_IF_GUID(g, ff_asf_my_guid);
    else PRINT_IF_GUID(g, ff_asf_ext_stream_header);
    else PRINT_IF_GUID(g, ff_asf_extended_content_header);
    else PRINT_IF_GUID(g, ff_asf_ext_stream_embed_stream_header);
    else PRINT_IF_GUID(g, ff_asf_ext_stream_audio_stream);
    else PRINT_IF_GUID(g, ff_asf_metadata_header);
86
    else PRINT_IF_GUID(g, ff_asf_marker_header);
87
    else PRINT_IF_GUID(g, stream_bitrate_guid);
88
    else PRINT_IF_GUID(g, ff_asf_language_guid);
François Revol's avatar
François Revol committed
89
    else
90
        dprintf(NULL, "(GUID: unknown) ");
Michael Niedermayer's avatar
Michael Niedermayer committed
91
    for(i=0;i<16;i++)
92 93
        dprintf(NULL, " 0x%02x,", (*g)[i]);
    dprintf(NULL, "}\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
94
}
95
#undef PRINT_IF_GUID
96 97
#else
#define print_guid(g)
Fabrice Bellard's avatar
Fabrice Bellard committed
98 99
#endif

100
static void get_guid(ByteIOContext *s, ff_asf_guid *g)
Fabrice Bellard's avatar
Fabrice Bellard committed
101
{
Michael Niedermayer's avatar
Michael Niedermayer committed
102
    assert(sizeof(*g) == 16);
103
    get_buffer(s, *g, sizeof(*g));
Fabrice Bellard's avatar
Fabrice Bellard committed
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
}

#if 0
static void get_str16(ByteIOContext *pb, char *buf, int buf_size)
{
    int len, c;
    char *q;

    len = get_le16(pb);
    q = buf;
    while (len > 0) {
        c = get_le16(pb);
        if ((q - buf) < buf_size - 1)
            *q++ = c;
        len--;
    }
    *q = '\0';
}
#endif

static void get_str16_nolen(ByteIOContext *pb, int len, char *buf, int buf_size)
{
Zuxy Meng's avatar
Zuxy Meng committed
126
    char* q = buf;
127
    while (len > 1) {
Zuxy Meng's avatar
Zuxy Meng committed
128
        uint8_t tmp;
129 130 131 132
        uint32_t ch;

        GET_UTF16(ch, (len -= 2) >= 0 ? get_le16(pb) : 0, break;)
        PUT_UTF8(ch, tmp, if (q - buf < buf_size - 1) *q++ = tmp;)
Fabrice Bellard's avatar
Fabrice Bellard committed
133
    }
134 135
    if (len > 0)
        url_fskip(pb, len);
Fabrice Bellard's avatar
Fabrice Bellard committed
136 137 138
    *q = '\0';
}

Fabrice Bellard's avatar
Fabrice Bellard committed
139 140 141
static int asf_probe(AVProbeData *pd)
{
    /* check file header */
142
    if (!guidcmp(pd->buf, &ff_asf_header))
Fabrice Bellard's avatar
Fabrice Bellard committed
143 144 145 146 147
        return AVPROBE_SCORE_MAX;
    else
        return 0;
}

Michael Niedermayer's avatar
Michael Niedermayer committed
148 149 150 151 152 153 154 155 156 157
static int get_value(ByteIOContext *pb, int type){
    switch(type){
        case 2: return get_le32(pb);
        case 3: return get_le32(pb);
        case 4: return get_le64(pb);
        case 5: return get_le16(pb);
        default:return INT_MIN;
    }
}

158 159
static void get_tag(AVFormatContext *s, const char *key, int type, int len)
{
160 161
    char *value;

162
    if ((unsigned)len >= (UINT_MAX - 1)/2)
163 164
        return;

165
    value = av_malloc(2*len+1);
166 167 168
    if (!value)
        return;

Benoit Fouet's avatar
Benoit Fouet committed
169
    if (type == 0) {         // UTF16-LE
170
        get_str16_nolen(s->pb, len, value, 2*len + 1);
Benoit Fouet's avatar
Benoit Fouet committed
171
    } else if (type > 1 && type <= 5) {  // boolean or DWORD or QWORD or WORD
172
        uint64_t num = get_value(s->pb, type);
173
        snprintf(value, len, "%"PRIu64, num);
174 175
    } else {
        url_fskip(s->pb, len);
Benoit Fouet's avatar
Benoit Fouet committed
176
        av_freep(&value);
177
        av_log(s, AV_LOG_DEBUG, "Unsupported value type %d in tag %s.\n", type, key);
178 179
        return;
    }
180 181
    av_metadata_set2(&s->metadata, key, value, 0);
    av_freep(&value);
182 183
}

Fabrice Bellard's avatar
Fabrice Bellard committed
184 185
static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap)
{
Fabrice Bellard's avatar
Fabrice Bellard committed
186
    ASFContext *asf = s->priv_data;
187
    ff_asf_guid g;
188
    ByteIOContext *pb = s->pb;
Fabrice Bellard's avatar
Fabrice Bellard committed
189 190
    AVStream *st;
    ASFStream *asf_st;
191
    int size, i;
192
    int64_t gsize;
Michael Niedermayer's avatar
Michael Niedermayer committed
193
    AVRational dar[128];
194
    uint32_t bitrate[128];
Michael Niedermayer's avatar
Michael Niedermayer committed
195 196

    memset(dar, 0, sizeof(dar));
197
    memset(bitrate, 0, sizeof(bitrate));
Fabrice Bellard's avatar
Fabrice Bellard committed
198 199

    get_guid(pb, &g);
200
    if (guidcmp(&g, &ff_asf_header))
201
        return -1;
Fabrice Bellard's avatar
Fabrice Bellard committed
202 203 204 205
    get_le64(pb);
    get_le32(pb);
    get_byte(pb);
    get_byte(pb);
206
    memset(&asf->asfid2avid, -1, sizeof(asf->asfid2avid));
Fabrice Bellard's avatar
Fabrice Bellard committed
207
    for(;;) {
208
        uint64_t gpos= url_ftell(pb);
Fabrice Bellard's avatar
Fabrice Bellard committed
209 210
        get_guid(pb, &g);
        gsize = get_le64(pb);
211
        dprintf(s, "%08"PRIx64": ", gpos);
Fabrice Bellard's avatar
Fabrice Bellard committed
212
        print_guid(&g);
213
        dprintf(s, "  size=0x%"PRIx64"\n", gsize);
214
        if (!guidcmp(&g, &ff_asf_data_header)) {
215 216 217 218 219 220 221 222 223
            asf->data_object_offset = url_ftell(pb);
            // if not streaming, gsize is not unlimited (how?), and there is enough space in the file..
            if (!(asf->hdr.flags & 0x01) && gsize >= 100) {
                asf->data_object_size = gsize - 24;
            } else {
                asf->data_object_size = (uint64_t)-1;
            }
            break;
        }
Fabrice Bellard's avatar
Fabrice Bellard committed
224
        if (gsize < 24)
225
            return -1;
226
        if (!guidcmp(&g, &ff_asf_file_header)) {
227
            get_guid(pb, &asf->hdr.guid);
228 229
            asf->hdr.file_size          = get_le64(pb);
            asf->hdr.create_time        = get_le64(pb);
230
            asf->nb_packets             = get_le64(pb);
231
            asf->hdr.play_time          = get_le64(pb);
232
            asf->hdr.send_time          = get_le64(pb);
233 234 235 236 237 238
            asf->hdr.preroll            = get_le32(pb);
            asf->hdr.ignore             = get_le32(pb);
            asf->hdr.flags              = get_le32(pb);
            asf->hdr.min_pktsize        = get_le32(pb);
            asf->hdr.max_pktsize        = get_le32(pb);
            asf->hdr.max_bitrate        = get_le32(pb);
239
            s->packet_size = asf->hdr.max_pktsize;
240
        } else if (!guidcmp(&g, &ff_asf_stream_header)) {
241
            enum AVMediaType type;
242
            int type_specific_size, sizeX;
243
            uint64_t total_size;
Fabrice Bellard's avatar
Fabrice Bellard committed
244
            unsigned int tag1;
245
            int64_t pos1, pos2, start_time;
John Donaghy's avatar
John Donaghy committed
246
            int test_for_ext_stream_audio, is_dvr_ms_audio=0;
247

248 249 250 251 252
            if (s->nb_streams == ASF_MAX_STREAMS) {
                av_log(s, AV_LOG_ERROR, "too many streams\n");
                return AVERROR(EINVAL);
            }

Fabrice Bellard's avatar
Fabrice Bellard committed
253 254
            pos1 = url_ftell(pb);

255
            st = av_new_stream(s, 0);
Fabrice Bellard's avatar
Fabrice Bellard committed
256
            if (!st)
257
                return AVERROR(ENOMEM);
258
            av_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */
Fabrice Bellard's avatar
Fabrice Bellard committed
259 260
            asf_st = av_mallocz(sizeof(ASFStream));
            if (!asf_st)
261
                return AVERROR(ENOMEM);
Fabrice Bellard's avatar
Fabrice Bellard committed
262
            st->priv_data = asf_st;
263 264
            start_time = asf->hdr.preroll;

265 266
            asf_st->stream_language_index = 128; // invalid stream index means no language info

267
            if(!(asf->hdr.flags & 0x01)) { // if we aren't streaming...
268
                st->duration = asf->hdr.play_time /
269
                    (10000000 / 1000) - start_time;
270
            }
Fabrice Bellard's avatar
Fabrice Bellard committed
271
            get_guid(pb, &g);
272 273

            test_for_ext_stream_audio = 0;
274
            if (!guidcmp(&g, &ff_asf_audio_stream)) {
275
                type = AVMEDIA_TYPE_AUDIO;
276
            } else if (!guidcmp(&g, &ff_asf_video_stream)) {
277
                type = AVMEDIA_TYPE_VIDEO;
278 279 280
            } else if (!guidcmp(&g, &ff_asf_jfif_media)) {
                type = AVMEDIA_TYPE_VIDEO;
                st->codec->codec_id = CODEC_ID_MJPEG;
281
            } else if (!guidcmp(&g, &ff_asf_command_stream)) {
282
                type = AVMEDIA_TYPE_DATA;
283
            } else if (!guidcmp(&g, &ff_asf_ext_stream_embed_stream_header)) {
284
                test_for_ext_stream_audio = 1;
285
                type = AVMEDIA_TYPE_UNKNOWN;
Fabrice Bellard's avatar
Fabrice Bellard committed
286
            } else {
287
                return -1;
Fabrice Bellard's avatar
Fabrice Bellard committed
288 289 290
            }
            get_guid(pb, &g);
            total_size = get_le64(pb);
291
            type_specific_size = get_le32(pb);
Fabrice Bellard's avatar
Fabrice Bellard committed
292
            get_le32(pb);
293
            st->id = get_le16(pb) & 0x7f; /* stream id */
294
            // mapping of asf ID to AV stream ID;
295
            asf->asfid2avid[st->id] = s->nb_streams - 1;
296

Fabrice Bellard's avatar
Fabrice Bellard committed
297
            get_le32(pb);
298 299 300

            if (test_for_ext_stream_audio) {
                get_guid(pb, &g);
301
                if (!guidcmp(&g, &ff_asf_ext_stream_audio_stream)) {
302
                    type = AVMEDIA_TYPE_AUDIO;
John Donaghy's avatar
John Donaghy committed
303
                    is_dvr_ms_audio=1;
304 305 306 307 308 309 310 311 312
                    get_guid(pb, &g);
                    get_le32(pb);
                    get_le32(pb);
                    get_le32(pb);
                    get_guid(pb, &g);
                    get_le32(pb);
                }
            }

313
            st->codec->codec_type = type;
314
            if (type == AVMEDIA_TYPE_AUDIO) {
315
                ff_get_wav_header(pb, st->codec, type_specific_size);
John Donaghy's avatar
John Donaghy committed
316 317 318
                if (is_dvr_ms_audio) {
                    // codec_id and codec_tag are unreliable in dvr_ms
                    // files. Set them later by probing stream.
319
                    st->codec->codec_id = CODEC_ID_PROBE;
John Donaghy's avatar
John Donaghy committed
320 321
                    st->codec->codec_tag = 0;
                }
322 323 324
                if (st->codec->codec_id == CODEC_ID_AAC) {
                    st->need_parsing = AVSTREAM_PARSE_NONE;
                } else {
Alex Converse's avatar
Alex Converse committed
325
                    st->need_parsing = AVSTREAM_PARSE_FULL;
326
                }
327 328
                /* We have to init the frame size at some point .... */
                pos2 = url_ftell(pb);
Michael Niedermayer's avatar
Michael Niedermayer committed
329
                if (gsize >= (pos2 + 8 - pos1 + 24)) {
330 331 332
                    asf_st->ds_span = get_byte(pb);
                    asf_st->ds_packet_size = get_le16(pb);
                    asf_st->ds_chunk_size = get_le16(pb);
333 334
                    get_le16(pb); //ds_data_size
                    get_byte(pb); //ds_silence_data
335 336 337 338 339 340
                }
                //printf("Descrambling: ps:%d cs:%d ds:%d s:%d  sd:%d\n",
                //       asf_st->ds_packet_size, asf_st->ds_chunk_size,
                //       asf_st->ds_data_size, asf_st->ds_span, asf_st->ds_silence_data);
                if (asf_st->ds_span > 1) {
                    if (!asf_st->ds_chunk_size
341 342
                        || (asf_st->ds_packet_size/asf_st->ds_chunk_size <= 1)
                        || asf_st->ds_packet_size % asf_st->ds_chunk_size)
343 344
                        asf_st->ds_span = 0; // disable descrambling
                }
345
                switch (st->codec->codec_id) {
Fabrice Bellard's avatar
Fabrice Bellard committed
346
                case CODEC_ID_MP3:
347
                    st->codec->frame_size = MPA_FRAME_SIZE;
348 349 350 351 352 353 354 355 356
                    break;
                case CODEC_ID_PCM_S16LE:
                case CODEC_ID_PCM_S16BE:
                case CODEC_ID_PCM_U16LE:
                case CODEC_ID_PCM_U16BE:
                case CODEC_ID_PCM_S8:
                case CODEC_ID_PCM_U8:
                case CODEC_ID_PCM_ALAW:
                case CODEC_ID_PCM_MULAW:
357
                    st->codec->frame_size = 1;
358 359 360
                    break;
                default:
                    /* This is probably wrong, but it prevents a crash later */
361
                    st->codec->frame_size = 1;
362 363
                    break;
                }
364
            } else if (type == AVMEDIA_TYPE_VIDEO &&
365
                       gsize - (url_ftell(pb) - pos1 + 24) >= 51) {
366
                get_le32(pb);
Fabrice Bellard's avatar
Fabrice Bellard committed
367 368 369
                get_le32(pb);
                get_byte(pb);
                size = get_le16(pb); /* size */
370
                sizeX= get_le32(pb); /* size */
371
                st->codec->width = get_le32(pb);
372
                st->codec->height = get_le32(pb);
373
                /* not available for asf */
Fabrice Bellard's avatar
Fabrice Bellard committed
374
                get_le16(pb); /* panes */
375
                st->codec->bits_per_coded_sample = get_le16(pb); /* depth */
Fabrice Bellard's avatar
Fabrice Bellard committed
376
                tag1 = get_le32(pb);
377
                url_fskip(pb, 20);
378
//                av_log(s, AV_LOG_DEBUG, "size:%d tsize:%d sizeX:%d\n", size, total_size, sizeX);
379
                size= sizeX;
380 381 382 383 384
                if (size > 40) {
                    st->codec->extradata_size = size - 40;
                    st->codec->extradata = av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
                    get_buffer(pb, st->codec->extradata, st->codec->extradata_size);
                }
385

386 387 388 389 390
                /* Extract palette from extradata if bpp <= 8 */
                /* This code assumes that extradata contains only palette */
                /* This is true for all paletted codecs implemented in ffmpeg */
                if (st->codec->extradata_size && (st->codec->bits_per_coded_sample <= 8)) {
                    st->codec->palctrl = av_mallocz(sizeof(AVPaletteControl));
391
#if HAVE_BIGENDIAN
392
                    for (i = 0; i < FFMIN(st->codec->extradata_size, AVPALETTE_SIZE)/4; i++)
393
                        st->codec->palctrl->palette[i] = av_bswap32(((uint32_t*)st->codec->extradata)[i]);
394
#else
395 396
                    memcpy(st->codec->palctrl->palette, st->codec->extradata,
                           FFMIN(st->codec->extradata_size, AVPALETTE_SIZE));
397
#endif
398 399
                    st->codec->palctrl->palette_changed = 1;
                }
400

401
                st->codec->codec_tag = tag1;
402
                st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, tag1);
403
                if(tag1 == MKTAG('D', 'V', 'R', ' ')){
Aurelien Jacobs's avatar
Aurelien Jacobs committed
404
                    st->need_parsing = AVSTREAM_PARSE_FULL;
405 406 407 408 409 410
                    // issue658 containse wrong w/h and MS even puts a fake seq header with wrong w/h in extradata while a correct one is in te stream. maximum lameness
                    st->codec->width  =
                    st->codec->height = 0;
                    av_freep(&st->codec->extradata);
                    st->codec->extradata_size=0;
                }
411 412
                if(st->codec->codec_id == CODEC_ID_H264)
                    st->need_parsing = AVSTREAM_PARSE_FULL_ONCE;
Fabrice Bellard's avatar
Fabrice Bellard committed
413 414 415
            }
            pos2 = url_ftell(pb);
            url_fskip(pb, gsize - (pos2 - pos1 + 24));
416
        } else if (!guidcmp(&g, &ff_asf_comment_header)) {
Fabrice Bellard's avatar
Fabrice Bellard committed
417 418 419 420 421 422 423
            int len1, len2, len3, len4, len5;

            len1 = get_le16(pb);
            len2 = get_le16(pb);
            len3 = get_le16(pb);
            len4 = get_le16(pb);
            len5 = get_le16(pb);
424 425 426 427
            get_tag(s, "title"    , 0, len1);
            get_tag(s, "author"   , 0, len2);
            get_tag(s, "copyright", 0, len3);
            get_tag(s, "comment"  , 0, len4);
428
            url_fskip(pb, len5);
429
        } else if (!guidcmp(&g, &stream_bitrate_guid)) {
430 431 432
            int stream_count = get_le16(pb);
            int j;

433 434
//            av_log(s, AV_LOG_ERROR, "stream bitrate properties\n");
//            av_log(s, AV_LOG_ERROR, "streams %d\n", streams);
435 436 437 438 439 440
            for(j = 0; j < stream_count; j++) {
                int flags, bitrate, stream_id;

                flags= get_le16(pb);
                bitrate= get_le32(pb);
                stream_id= (flags & 0x7f);
441
//                av_log(s, AV_LOG_ERROR, "flags: 0x%x stream id %d, bitrate %d\n", flags, stream_id, bitrate);
442
                asf->stream_bitrates[stream_id]= bitrate;
443
            }
444 445 446 447 448 449 450 451 452 453
        } else if (!guidcmp(&g, &ff_asf_language_guid)) {
            int j;
            int stream_count = get_le16(pb);
            for(j = 0; j < stream_count; j++) {
                char lang[6];
                unsigned int lang_len = get_byte(pb);
                get_str16_nolen(pb, lang_len, lang, sizeof(lang));
                if (j < 128)
                    av_strlcpy(asf->stream_languages[j], lang, sizeof(*asf->stream_languages));
            }
454
        } else if (!guidcmp(&g, &ff_asf_extended_content_header)) {
455 456 457 458 459 460 461 462
            int desc_count, i;

            desc_count = get_le16(pb);
            for(i=0;i<desc_count;i++) {
                    int name_len,value_type,value_len;
                    char name[1024];

                    name_len = get_le16(pb);
463 464
                    if (name_len%2)     // must be even, broken lavf versions wrote len-1
                        name_len += 1;
465 466 467
                    get_str16_nolen(pb, name_len, name, sizeof(name));
                    value_type = get_le16(pb);
                    value_len  = get_le16(pb);
468
                    if (!value_type && value_len%2)
469
                        value_len += 1;
470 471 472 473 474 475 476 477
                    /**
                     * My sample has that stream set to 0 maybe that mean the container.
                     * Asf stream count start at 1. I am using 0 to the container value since it's unused
                     */
                    if (!strcmp(name, "AspectRatioX")){
                        dar[0].num= get_value(s->pb, value_type);
                    } else if(!strcmp(name, "AspectRatioY")){
                        dar[0].den= get_value(s->pb, value_type);
478 479
                    } else
                        get_tag(s, name, value_type, value_len);
480
            }
481
        } else if (!guidcmp(&g, &ff_asf_metadata_header)) {
Michael Niedermayer's avatar
Michael Niedermayer committed
482 483 484 485 486 487 488 489 490 491 492 493 494
            int n, stream_num, name_len, value_len, value_type, value_num;
            n = get_le16(pb);

            for(i=0;i<n;i++) {
                char name[1024];

                get_le16(pb); //lang_list_index
                stream_num= get_le16(pb);
                name_len=   get_le16(pb);
                value_type= get_le16(pb);
                value_len=  get_le32(pb);

                get_str16_nolen(pb, name_len, name, sizeof(name));
495
//av_log(s, AV_LOG_ERROR, "%d %d %d %d %d <%s>\n", i, stream_num, name_len, value_type, value_len, name);
Diego Biurrun's avatar
Diego Biurrun committed
496
                value_num= get_le16(pb);//we should use get_value() here but it does not work 2 is le16 here but le32 elsewhere
Michael Niedermayer's avatar
Michael Niedermayer committed
497 498 499 500 501 502 503
                url_fskip(pb, value_len - 2);

                if(stream_num<128){
                    if     (!strcmp(name, "AspectRatioX")) dar[stream_num].num= value_num;
                    else if(!strcmp(name, "AspectRatioY")) dar[stream_num].den= value_num;
                }
            }
504
        } else if (!guidcmp(&g, &ff_asf_ext_stream_header)) {
505
            int ext_len, payload_ext_ct, stream_ct;
506
            uint32_t ext_d, leak_rate, stream_num;
507
            unsigned int stream_languageid_index;
508

509 510
            get_le64(pb); // starttime
            get_le64(pb); // endtime
511
            leak_rate = get_le32(pb); // leak-datarate
512 513 514 515 516 517 518
            get_le32(pb); // bucket-datasize
            get_le32(pb); // init-bucket-fullness
            get_le32(pb); // alt-leak-datarate
            get_le32(pb); // alt-bucket-datasize
            get_le32(pb); // alt-init-bucket-fullness
            get_le32(pb); // max-object-size
            get_le32(pb); // flags (reliable,seekable,no_cleanpoints?,resend-live-cleanpoints, rest of bits reserved)
519
            stream_num = get_le16(pb); // stream-num
520 521 522 523 524

            stream_languageid_index = get_le16(pb); // stream-language-id-index
            if (stream_num < 128)
                asf->streams[stream_num].stream_language_index = stream_languageid_index;

525 526 527
            get_le64(pb); // avg frametime in 100ns units
            stream_ct = get_le16(pb); //stream-name-count
            payload_ext_ct = get_le16(pb); //payload-extension-system-count
528

529 530 531
            if (stream_num < 128)
                bitrate[stream_num] = leak_rate;

532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
            for (i=0; i<stream_ct; i++){
                get_le16(pb);
                ext_len = get_le16(pb);
                url_fseek(pb, ext_len, SEEK_CUR);
            }

            for (i=0; i<payload_ext_ct; i++){
                get_guid(pb, &g);
                ext_d=get_le16(pb);
                ext_len=get_le32(pb);
                url_fseek(pb, ext_len, SEEK_CUR);
            }

            // there could be a optional stream properties object to follow
            // if so the next iteration will pick it up
547
            continue;
548
        } else if (!guidcmp(&g, &ff_asf_head1_guid)) {
Fabrice Bellard's avatar
Fabrice Bellard committed
549 550 551 552
            int v1, v2;
            get_guid(pb, &g);
            v1 = get_le32(pb);
            v2 = get_le16(pb);
553
            continue;
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
        } else if (!guidcmp(&g, &ff_asf_marker_header)) {
            int i, count, name_len;
            char name[1024];

            get_le64(pb);            // reserved 16 bytes
            get_le64(pb);            // ...
            count = get_le32(pb);    // markers count
            get_le16(pb);            // reserved 2 bytes
            name_len = get_le16(pb); // name length
            for(i=0;i<name_len;i++){
                get_byte(pb); // skip the name
            }

            for(i=0;i<count;i++){
                int64_t pres_time;
                int name_len;

                get_le64(pb);             // offset, 8 bytes
                pres_time = get_le64(pb); // presentation time
                get_le16(pb);             // entry length
                get_le32(pb);             // send time
                get_le32(pb);             // flags
                name_len = get_le32(pb);  // name length
                get_str16_nolen(pb, name_len * 2, name, sizeof(name));
                ff_new_chapter(s, i, (AVRational){1, 10000000}, pres_time, AV_NOPTS_VALUE, name );
            }
580
#if 0
581
        } else if (!guidcmp(&g, &ff_asf_codec_comment_header)) {
Fabrice Bellard's avatar
Fabrice Bellard committed
582 583 584 585 586 587
            int len, v1, n, num;
            char str[256], *q;
            char tag[16];

            get_guid(pb, &g);
            print_guid(&g);
588

Fabrice Bellard's avatar
Fabrice Bellard committed
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
            n = get_le32(pb);
            for(i=0;i<n;i++) {
                num = get_le16(pb); /* stream number */
                get_str16(pb, str, sizeof(str));
                get_str16(pb, str, sizeof(str));
                len = get_le16(pb);
                q = tag;
                while (len > 0) {
                    v1 = get_byte(pb);
                    if ((q - tag) < sizeof(tag) - 1)
                        *q++ = v1;
                    len--;
                }
                *q = '\0';
            }
#endif
        } else if (url_feof(pb)) {
606
            return -1;
Fabrice Bellard's avatar
Fabrice Bellard committed
607
        } else {
608 609 610 611 612 613 614 615 616
            if (!s->keylen) {
                if (!guidcmp(&g, &ff_asf_content_encryption)) {
                    av_log(s, AV_LOG_WARNING, "DRM protected stream detected, decoding will likely fail!\n");
                } else if (!guidcmp(&g, &ff_asf_ext_content_encryption)) {
                    av_log(s, AV_LOG_WARNING, "Ext DRM protected stream detected, decoding will likely fail!\n");
                } else if (!guidcmp(&g, &ff_asf_digital_signature)) {
                    av_log(s, AV_LOG_WARNING, "Digital signature detected, decoding will likely fail!\n");
                }
            }
Fabrice Bellard's avatar
Fabrice Bellard committed
617
        }
618 619 620
        if(url_ftell(pb) != gpos + gsize)
            av_log(s, AV_LOG_DEBUG, "gpos mismatch our pos=%"PRIu64", end=%"PRIu64"\n", url_ftell(pb)-gpos, gsize);
        url_fseek(pb, gpos + gsize, SEEK_SET);
Fabrice Bellard's avatar
Fabrice Bellard committed
621 622 623 624 625
    }
    get_guid(pb, &g);
    get_le64(pb);
    get_byte(pb);
    get_byte(pb);
626
    if (url_feof(pb))
627
        return -1;
628
    asf->data_offset = url_ftell(pb);
Fabrice Bellard's avatar
Fabrice Bellard committed
629 630
    asf->packet_size_left = 0;

Michael Niedermayer's avatar
Michael Niedermayer committed
631 632 633

    for(i=0; i<128; i++){
        int stream_num= asf->asfid2avid[i];
634
        if(stream_num>=0){
635 636 637
            AVStream *st = s->streams[stream_num];
            if (!st->codec->bit_rate)
                st->codec->bit_rate = bitrate[i];
638
            if (dar[i].num > 0 && dar[i].den > 0){
639 640
                av_reduce(&st->sample_aspect_ratio.num,
                          &st->sample_aspect_ratio.den,
641
                          dar[i].num, dar[i].den, INT_MAX);
642 643 644 645 646 647
            } else if ((dar[0].num > 0) && (dar[0].den > 0) && (st->codec->codec_type==CODEC_TYPE_VIDEO)) // Use ASF container value if the stream doesn't AR set.
                av_reduce(&st->sample_aspect_ratio.num,
                          &st->sample_aspect_ratio.den,
                          dar[0].num, dar[0].den, INT_MAX);

//av_log(s, AV_LOG_INFO, "i=%d, st->codec->codec_type:%d, dar %d:%d sar=%d:%d\n", i, st->codec->codec_type, dar[i].num, dar[i].den, st->sample_aspect_ratio.num, st->sample_aspect_ratio.den);
648 649 650 651 652 653 654 655

            // copy and convert language codes to the frontend
            if (asf->streams[i].stream_language_index < 128) {
                const char *rfc1766 = asf->stream_languages[asf->streams[i].stream_language_index];
                if (rfc1766 && strlen(rfc1766) > 1) {
                    const char primary_tag[3] = { rfc1766[0], rfc1766[1], '\0' }; // ignore country code if any
                    const char *iso6392 = av_convert_lang_to(primary_tag, AV_LANG_ISO639_2_BIBL);
                    if (iso6392)
656
                        av_metadata_set2(&st->metadata, "language", iso6392, 0);
657 658
                }
            }
Michael Niedermayer's avatar
Michael Niedermayer committed
659 660 661
        }
    }

Fabrice Bellard's avatar
Fabrice Bellard committed
662 663 664
    return 0;
}

665 666 667 668 669 670 671 672 673
#define DO_2BITS(bits, var, defval) \
    switch (bits & 3) \
    { \
    case 3: var = get_le32(pb); rsize += 4; break; \
    case 2: var = get_le16(pb); rsize += 2; break; \
    case 1: var = get_byte(pb); rsize++; break; \
    default: var = defval; break; \
    }

674 675 676 677
/**
 * Load a single ASF packet into the demuxer.
 * @param s demux context
 * @param pb context to read data from
Benoit Fouet's avatar
Benoit Fouet committed
678
 * @return 0 on success, <0 on error
679 680
 */
static int ff_asf_get_packet(AVFormatContext *s, ByteIOContext *pb)
Fabrice Bellard's avatar
Fabrice Bellard committed
681 682
{
    ASFContext *asf = s->priv_data;
683
    uint32_t packet_length, padsize;
684
    int rsize = 8;
685 686
    int c, d, e, off;

687 688 689
    // if we do not know packet size, allow skipping up to 32 kB
    off= 32768;
    if (s->packet_size > 0)
Reimar Döffinger's avatar
Reimar Döffinger committed
690
        off= (url_ftell(pb) - s->data_offset) % s->packet_size + 3;
691 692 693 694 695 696 697 698

    c=d=e=-1;
    while(off-- > 0){
        c=d; d=e;
        e= get_byte(pb);
        if(c == 0x82 && !d && !e)
            break;
    }
699

700
    if (c != 0x82) {
701 702 703 704 705 706 707 708
        /**
         * This code allows handling of -EAGAIN at packet boundaries (i.e.
         * if the packet sync code above triggers -EAGAIN). This does not
         * imply complete -EAGAIN handling support at random positions in
         * the stream.
         */
        if (url_ferror(pb) == AVERROR(EAGAIN))
            return AVERROR(EAGAIN);
709
        if (!url_feof(pb))
710
            av_log(s, AV_LOG_ERROR, "ff asf bad header %x  at:%"PRId64"\n", c, url_ftell(pb));
711
    }
712
    if ((c & 0x8f) == 0x82) {
713
        if (d || e) {
714
            if (!url_feof(pb))
715
                av_log(s, AV_LOG_ERROR, "ff asf bad non zero\n");
716
            return -1;
717
        }
718
        c= get_byte(pb);
719
        d= get_byte(pb);
720 721 722
        rsize+=3;
    }else{
        url_fseek(pb, -1, SEEK_CUR); //FIXME
Fabrice Bellard's avatar
Fabrice Bellard committed
723
    }
724

725 726
    asf->packet_flags    = c;
    asf->packet_property = d;
727

728
    DO_2BITS(asf->packet_flags >> 5, packet_length, s->packet_size);
729 730 731
    DO_2BITS(asf->packet_flags >> 1, padsize, 0); // sequence ignored
    DO_2BITS(asf->packet_flags >> 3, padsize, 0); // padding length

732
    //the following checks prevent overflows and infinite loops
733
    if(!packet_length || packet_length >= (1U<<29)){
734
        av_log(s, AV_LOG_ERROR, "invalid packet_length %d at:%"PRId64"\n", packet_length, url_ftell(pb));
735
        return -1;
736
    }
Michael Niedermayer's avatar
Michael Niedermayer committed
737
    if(padsize >= packet_length){
738
        av_log(s, AV_LOG_ERROR, "invalid padsize %d at:%"PRId64"\n", padsize, url_ftell(pb));
739
        return -1;
740 741
    }

742
    asf->packet_timestamp = get_le32(pb);
Fabrice Bellard's avatar
Fabrice Bellard committed
743
    get_le16(pb); /* duration */
744
    // rsize has at least 11 bytes which have to be present
745 746

    if (asf->packet_flags & 0x01) {
747
        asf->packet_segsizetype = get_byte(pb); rsize++;
748 749
        asf->packet_segments = asf->packet_segsizetype & 0x3f;
    } else {
750
        asf->packet_segments = 1;
751 752 753
        asf->packet_segsizetype = 0x80;
    }
    asf->packet_size_left = packet_length - padsize - rsize;
754 755
    if (packet_length < asf->hdr.min_pktsize)
        padsize += asf->hdr.min_pktsize - packet_length;
756
    asf->packet_padsize = padsize;
757
    dprintf(s, "packet: size=%d padsize=%d  left=%d\n", s->packet_size, asf->packet_padsize, asf->packet_size_left);
Fabrice Bellard's avatar
Fabrice Bellard committed
758 759 760
    return 0;
}

761 762 763 764
/**
 *
 * @return <0 if error
 */
765
static int asf_read_frame_header(AVFormatContext *s, ByteIOContext *pb){
766 767 768
    ASFContext *asf = s->priv_data;
    int rsize = 1;
    int num = get_byte(pb);
Michael Niedermayer's avatar
Michael Niedermayer committed
769
    int64_t ts0, ts1;
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785

    asf->packet_segments--;
    asf->packet_key_frame = num >> 7;
    asf->stream_index = asf->asfid2avid[num & 0x7f];
    // sequence should be ignored!
    DO_2BITS(asf->packet_property >> 4, asf->packet_seq, 0);
    DO_2BITS(asf->packet_property >> 2, asf->packet_frag_offset, 0);
    DO_2BITS(asf->packet_property, asf->packet_replic_size, 0);
//printf("key:%d stream:%d seq:%d offset:%d replic_size:%d\n", asf->packet_key_frame, asf->stream_index, asf->packet_seq, //asf->packet_frag_offset, asf->packet_replic_size);
    if (asf->packet_replic_size >= 8) {
        asf->packet_obj_size = get_le32(pb);
        if(asf->packet_obj_size >= (1<<24) || asf->packet_obj_size <= 0){
            av_log(s, AV_LOG_ERROR, "packet_obj_size invalid\n");
            return -1;
        }
        asf->packet_frag_timestamp = get_le32(pb); // timestamp
Michael Niedermayer's avatar
Michael Niedermayer committed
786 787 788 789 790 791 792 793 794 795 796 797 798 799
        if(asf->packet_replic_size >= 8+38+4){
//            for(i=0; i<asf->packet_replic_size-8; i++)
//                av_log(s, AV_LOG_DEBUG, "%02X ",get_byte(pb));
//            av_log(s, AV_LOG_DEBUG, "\n");
            url_fskip(pb, 10);
            ts0= get_le64(pb);
            ts1= get_le64(pb);
            url_fskip(pb, 12);
            get_le32(pb);
            url_fskip(pb, asf->packet_replic_size - 8 - 38 - 4);
            if(ts0!= -1) asf->packet_frag_timestamp= ts0/10000;
            else         asf->packet_frag_timestamp= AV_NOPTS_VALUE;
        }else
            url_fskip(pb, asf->packet_replic_size - 8);
800 801
        rsize += asf->packet_replic_size; // FIXME - check validity
    } else if (asf->packet_replic_size==1){
802
        // multipacket - frag_offset is beginning timestamp
803 804 805 806 807 808 809 810 811 812 813 814
        asf->packet_time_start = asf->packet_frag_offset;
        asf->packet_frag_offset = 0;
        asf->packet_frag_timestamp = asf->packet_timestamp;

        asf->packet_time_delta = get_byte(pb);
        rsize++;
    }else if(asf->packet_replic_size!=0){
        av_log(s, AV_LOG_ERROR, "unexpected packet_replic_size of %d\n", asf->packet_replic_size);
        return -1;
    }
    if (asf->packet_flags & 0x01) {
        DO_2BITS(asf->packet_segsizetype >> 6, asf->packet_frag_size, 0); // 0 is illegal
815 816 817 818
        if(asf->packet_frag_size > asf->packet_size_left - rsize){
            av_log(s, AV_LOG_ERROR, "packet_frag_size is invalid\n");
            return -1;
        }
819 820 821 822 823 824