mpeg.c 33.5 KB
Newer Older
Fabrice Bellard's avatar
Fabrice Bellard committed
1
/*
2
 * MPEG1/2 demuxer
3
 * Copyright (c) 2000, 2001, 2002 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
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
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
 *
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

Fabrice Bellard's avatar
Fabrice Bellard committed
22
#include "avformat.h"
23
#include "internal.h"
24
#include "mpeg.h"
Fabrice Bellard's avatar
Fabrice Bellard committed
25

Clément Bœsch's avatar
Clément Bœsch committed
26 27 28
#if CONFIG_VOBSUB_DEMUXER
# include "subtitles.h"
# include "libavutil/bprint.h"
29
# include "libavutil/opt.h"
Clément Bœsch's avatar
Clément Bœsch committed
30 31
#endif

32
#include "libavutil/avassert.h"
33

Fabrice Bellard's avatar
Fabrice Bellard committed
34 35 36 37 38
/*********************************************/
/* demux code */

#define MAX_SYNC_SIZE 100000

39
static int check_pes(const uint8_t *p, const uint8_t *end)
40
{
41
    int pes1;
42 43 44 45 46 47 48 49 50 51 52 53 54 55
    int pes2 = (p[3] & 0xC0) == 0x80 &&
               (p[4] & 0xC0) != 0x40 &&
               ((p[4] & 0xC0) == 0x00 ||
                (p[4] & 0xC0) >> 2 == (p[6] & 0xF0));

    for (p += 3; p < end && *p == 0xFF; p++) ;
    if ((*p & 0xC0) == 0x40)
        p += 2;

    if ((*p & 0xF0) == 0x20)
        pes1 = p[0] & p[2] & p[4] & 1;
    else if ((*p & 0xF0) == 0x30)
        pes1 = p[0] & p[2] & p[4] & p[5] & p[7] & p[9] & 1;
    else
56 57
        pes1 = *p == 0x0F;

58
    return pes1 || pes2;
59 60
}

61 62
static int check_pack_header(const uint8_t *buf)
{
63 64 65
    return (buf[1] & 0xC0) == 0x40 || (buf[1] & 0xF0) == 0x20;
}

66 67
static int mpegps_probe(AVProbeData *p)
{
68
    uint32_t code = -1;
69
    int i;
70 71
    int sys = 0, pspack = 0, priv1 = 0, vid = 0;
    int audio = 0, invalid = 0, score = 0;
72
    int endpes = 0;
73

74 75
    for (i = 0; i < p->buf_size; i++) {
        code = (code << 8) + p->buf[i];
76
        if ((code & 0xffffff00) == 0x100) {
77
            int len  = p->buf[i + 1] << 8 | p->buf[i + 2];
78
            int pes  = endpes <= i && check_pes(p->buf + i, p->buf + p->buf_size);
79 80 81 82 83 84
            int pack = check_pack_header(p->buf + i);

            if (code == SYSTEM_HEADER_START_CODE)
                sys++;
            else if (code == PACK_START_CODE && pack)
                pspack++;
85 86
            else if ((code & 0xf0) == VIDEO_ID && pes) {
                endpes = i + len;
87
                vid++;
88
            }
89 90
            // skip pes payload to avoid start code emulation for private
            // and audio streams
91 92 93
            else if ((code & 0xe0) == AUDIO_ID &&  pes) {audio++; i+=len;}
            else if (code == PRIVATE_STREAM_1  &&  pes) {priv1++; i+=len;}
            else if (code == 0x1fd             &&  pes) vid++; //VC1
94

95 96 97
            else if ((code & 0xf0) == VIDEO_ID && !pes) invalid++;
            else if ((code & 0xe0) == AUDIO_ID && !pes) invalid++;
            else if (code == PRIVATE_STREAM_1  && !pes) invalid++;
98
        }
99
    }
100

101
    if (vid + audio > invalid + 1) /* invalid VDR files nd short PES streams */
102
        score = AVPROBE_SCORE_EXTENSION / 2;
103

104 105 106
//     av_log(NULL, AV_LOG_ERROR, "vid:%d aud:%d sys:%d pspack:%d invalid:%d size:%d \n",
//            vid, audio, sys, pspack, invalid, p->buf_size);

107
    if (sys > invalid && sys * 9 <= pspack * 10)
108
        return (audio > 12 || vid > 3 || pspack > 2) ? AVPROBE_SCORE_EXTENSION + 2
109
                                                     : AVPROBE_SCORE_EXTENSION / 2 + 1; // 1 more than mp3
110 111 112 113 114
    if (pspack > invalid && (priv1 + vid + audio) * 10 >= pspack * 9)
        return pspack > 2 ? AVPROBE_SCORE_EXTENSION + 2
                          : AVPROBE_SCORE_EXTENSION / 2; // 1 more than .mpg
    if ((!!vid ^ !!audio) && (audio > 4 || vid > 1) && !sys &&
        !pspack && p->buf_size > 2048 && vid + audio > invalid) /* PES stream */
115 116
        return (audio > 12 || vid > 3 + 2 * invalid) ? AVPROBE_SCORE_EXTENSION + 2
                                                     : AVPROBE_SCORE_EXTENSION / 2;
117 118 119

    // 02-Penguin.flac has sys:0 priv1:0 pspack:0 vid:0 audio:1
    // mp3_misidentified_2.mp3 has sys:0 priv1:0 pspack:0 vid:0 audio:6
120
    // Have\ Yourself\ a\ Merry\ Little\ Christmas.mp3 0 0 0 5 0 1 len:21618
121
    return score;
122 123
}

Fabrice Bellard's avatar
Fabrice Bellard committed
124
typedef struct MpegDemuxContext {
125
    AVClass *class;
126
    int32_t header_state;
127
    unsigned char psm_es_type[256];
128
    int sofdec;
129
    int dvd;
130
    int imkh_cctv;
Clément Bœsch's avatar
Clément Bœsch committed
131 132
#if CONFIG_VOBSUB_DEMUXER
    AVFormatContext *sub_ctx;
133
    FFDemuxSubtitlesQueue q[32];
134
    char *sub_name;
Clément Bœsch's avatar
Clément Bœsch committed
135
#endif
Fabrice Bellard's avatar
Fabrice Bellard committed
136 137
} MpegDemuxContext;

138
static int mpegps_read_header(AVFormatContext *s)
Fabrice Bellard's avatar
Fabrice Bellard committed
139 140
{
    MpegDemuxContext *m = s->priv_data;
141
    char buffer[7];
142
    int64_t last_pos = avio_tell(s->pb);
143

Fabrice Bellard's avatar
Fabrice Bellard committed
144
    m->header_state = 0xff;
145
    s->ctx_flags   |= AVFMTCTX_NOHEADER;
Fabrice Bellard's avatar
Fabrice Bellard committed
146

147 148 149 150 151 152
    avio_get_str(s->pb, 6, buffer, sizeof(buffer));
    if (!memcmp("IMKH", buffer, 4)) {
        m->imkh_cctv = 1;
    } else if (!memcmp("Sofdec", buffer, 6)) {
        m->sofdec = 1;
    } else
153 154
       avio_seek(s->pb, last_pos, SEEK_SET);

Fabrice Bellard's avatar
Fabrice Bellard committed
155 156 157 158
    /* no need to do more */
    return 0;
}

159
static int64_t get_pts(AVIOContext *pb, int c)
Fabrice Bellard's avatar
Fabrice Bellard committed
160
{
161 162
    uint8_t buf[5];

163 164
    buf[0] = c < 0 ? avio_r8(pb) : c;
    avio_read(pb, buf + 1, 4);
165 166

    return ff_parse_pes_pts(buf);
Fabrice Bellard's avatar
Fabrice Bellard committed
167 168
}

169
static int find_next_start_code(AVIOContext *pb, int *size_ptr,
170
                                int32_t *header_state)
Fabrice Bellard's avatar
Fabrice Bellard committed
171 172 173 174 175
{
    unsigned int state, v;
    int val, n;

    state = *header_state;
176
    n     = *size_ptr;
Fabrice Bellard's avatar
Fabrice Bellard committed
177
    while (n > 0) {
178
        if (avio_feof(pb))
Fabrice Bellard's avatar
Fabrice Bellard committed
179
            break;
180
        v = avio_r8(pb);
Fabrice Bellard's avatar
Fabrice Bellard committed
181 182 183
        n--;
        if (state == 0x000001) {
            state = ((state << 8) | v) & 0xffffff;
184
            val   = state;
Fabrice Bellard's avatar
Fabrice Bellard committed
185 186 187 188 189
            goto found;
        }
        state = ((state << 8) | v) & 0xffffff;
    }
    val = -1;
190 191

found:
Fabrice Bellard's avatar
Fabrice Bellard committed
192
    *header_state = state;
193
    *size_ptr     = n;
Fabrice Bellard's avatar
Fabrice Bellard committed
194 195 196
    return val;
}

197
/**
198
 * Extract stream types from a program stream map
199
 * According to ISO/IEC 13818-1 ('MPEG-2 Systems') table 2-35
200
 *
201 202
 * @return number of bytes occupied by PSM in the bitstream
 */
203
static long mpegps_psm_parse(MpegDemuxContext *m, AVIOContext *pb)
204 205 206
{
    int psm_length, ps_info_length, es_map_length;

207 208 209 210
    psm_length = avio_rb16(pb);
    avio_r8(pb);
    avio_r8(pb);
    ps_info_length = avio_rb16(pb);
211 212

    /* skip program_stream_info */
213
    avio_skip(pb, ps_info_length);
214
    /*es_map_length = */avio_rb16(pb);
215 216
    /* Ignore es_map_length, trust psm_length */
    es_map_length = psm_length - ps_info_length - 10;
217 218

    /* at least one es available? */
219
    while (es_map_length >= 4) {
220 221 222
        unsigned char type      = avio_r8(pb);
        unsigned char es_id     = avio_r8(pb);
        uint16_t es_info_length = avio_rb16(pb);
223

224 225 226
        /* remember mapping from stream id to stream type */
        m->psm_es_type[es_id] = type;
        /* skip program_stream_info */
227
        avio_skip(pb, es_info_length);
228 229
        es_map_length -= 4 + es_info_length;
    }
230
    avio_rb32(pb); /* crc32 */
231 232 233
    return 2 + psm_length;
}

234
/* read the next PES header. Return its position in ppos
235
 * (if not NULL), and its start code, pts and dts.
Fabrice Bellard's avatar
Fabrice Bellard committed
236 237
 */
static int mpegps_read_pes_header(AVFormatContext *s,
238
                                  int64_t *ppos, int *pstart_code,
239
                                  int64_t *ppts, int64_t *pdts)
Fabrice Bellard's avatar
Fabrice Bellard committed
240 241
{
    MpegDemuxContext *m = s->priv_data;
Fabrice Bellard's avatar
Fabrice Bellard committed
242
    int len, size, startcode, c, flags, header_len;
243
    int pes_ext, ext2_len, id_ext, skip;
244
    int64_t pts, dts;
245 246 247 248 249 250 251 252 253 254 255
    int64_t last_sync = avio_tell(s->pb);

error_redo:
    avio_seek(s->pb, last_sync, SEEK_SET);
redo:
    /* next start code (should be immediately after) */
    m->header_state = 0xff;
    size      = MAX_SYNC_SIZE;
    startcode = find_next_start_code(s->pb, &size, &m->header_state);
    last_sync = avio_tell(s->pb);
    if (startcode < 0) {
256
        if (avio_feof(s->pb))
257
            return AVERROR_EOF;
258
        // FIXME we should remember header_state
259 260 261
        return AVERROR(EAGAIN);
    }

Fabrice Bellard's avatar
Fabrice Bellard committed
262 263 264 265
    if (startcode == PACK_START_CODE)
        goto redo;
    if (startcode == SYSTEM_HEADER_START_CODE)
        goto redo;
266
    if (startcode == PADDING_STREAM) {
267
        avio_skip(s->pb, avio_rb16(s->pb));
268 269 270 271
        goto redo;
    }
    if (startcode == PRIVATE_STREAM_2) {
        if (!m->sofdec) {
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
            /* Need to detect whether this from a DVD or a 'Sofdec' stream */
            int len = avio_rb16(s->pb);
            int bytesread = 0;
            uint8_t *ps2buf = av_malloc(len);

            if (ps2buf) {
                bytesread = avio_read(s->pb, ps2buf, len);

                if (bytesread != len) {
                    avio_skip(s->pb, len - bytesread);
                } else {
                    uint8_t *p = 0;
                    if (len >= 6)
                        p = memchr(ps2buf, 'S', len - 5);

                    if (p)
                        m->sofdec = !memcmp(p+1, "ofdec", 5);

                    m->sofdec -= !m->sofdec;

                    if (m->sofdec < 0) {
                        if (len == 980  && ps2buf[0] == 0) {
                            /* PCI structure? */
                            uint32_t startpts = AV_RB32(ps2buf + 0x0d);
                            uint32_t endpts = AV_RB32(ps2buf + 0x11);
                            uint8_t hours = ((ps2buf[0x19] >> 4) * 10) + (ps2buf[0x19] & 0x0f);
                            uint8_t mins  = ((ps2buf[0x1a] >> 4) * 10) + (ps2buf[0x1a] & 0x0f);
                            uint8_t secs  = ((ps2buf[0x1b] >> 4) * 10) + (ps2buf[0x1b] & 0x0f);

                            m->dvd = (hours <= 23 &&
                                      mins  <= 59 &&
                                      secs  <= 59 &&
                                      (ps2buf[0x19] & 0x0f) < 10 &&
                                      (ps2buf[0x1a] & 0x0f) < 10 &&
                                      (ps2buf[0x1b] & 0x0f) < 10 &&
                                      endpts >= startpts);
                        } else if (len == 1018 && ps2buf[0] == 1) {
                            /* DSI structure? */
                            uint8_t hours = ((ps2buf[0x1d] >> 4) * 10) + (ps2buf[0x1d] & 0x0f);
                            uint8_t mins  = ((ps2buf[0x1e] >> 4) * 10) + (ps2buf[0x1e] & 0x0f);
                            uint8_t secs  = ((ps2buf[0x1f] >> 4) * 10) + (ps2buf[0x1f] & 0x0f);

                            m->dvd = (hours <= 23 &&
                                      mins  <= 59 &&
                                      secs  <= 59 &&
                                      (ps2buf[0x1d] & 0x0f) < 10 &&
                                      (ps2buf[0x1e] & 0x0f) < 10 &&
                                      (ps2buf[0x1f] & 0x0f) < 10);
                        }
                    }
322
                }
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340

                av_free(ps2buf);

                /* If this isn't a DVD packet or no memory
                 * could be allocated, just ignore it.
                 * If we did, move back to the start of the
                 * packet (plus 'length' field) */
                if (!m->dvd || avio_skip(s->pb, -(len + 2)) < 0) {
                    /* Skip back failed.
                     * This packet will be lost but that can't be helped
                     * if we can't skip back
                     */
                    goto redo;
                }
            } else {
                /* No memory */
                avio_skip(s->pb, len);
                goto redo;
341
            }
342 343 344 345
        } else if (!m->dvd) {
            int len = avio_rb16(s->pb);
            avio_skip(s->pb, len);
            goto redo;
346
        }
Fabrice Bellard's avatar
Fabrice Bellard committed
347
    }
348
    if (startcode == PROGRAM_STREAM_MAP) {
349
        mpegps_psm_parse(m, s->pb);
350 351
        goto redo;
    }
352

Fabrice Bellard's avatar
Fabrice Bellard committed
353 354 355
    /* find matching stream */
    if (!((startcode >= 0x1c0 && startcode <= 0x1df) ||
          (startcode >= 0x1e0 && startcode <= 0x1ef) ||
356 357 358
          (startcode == 0x1bd) ||
          (startcode == PRIVATE_STREAM_2) ||
          (startcode == 0x1fd)))
Fabrice Bellard's avatar
Fabrice Bellard committed
359
        goto redo;
Fabrice Bellard's avatar
Fabrice Bellard committed
360
    if (ppos) {
361
        *ppos = avio_tell(s->pb) - 4;
Fabrice Bellard's avatar
Fabrice Bellard committed
362
    }
363
    len = avio_rb16(s->pb);
Michael Niedermayer's avatar
Michael Niedermayer committed
364
    pts =
365
    dts = AV_NOPTS_VALUE;
366 367
    if (startcode != PRIVATE_STREAM_2)
    {
Fabrice Bellard's avatar
Fabrice Bellard committed
368
    /* stuffing */
369
    for (;;) {
Fabrice Bellard's avatar
Fabrice Bellard committed
370
        if (len < 1)
371
            goto error_redo;
372
        c = avio_r8(s->pb);
Fabrice Bellard's avatar
Fabrice Bellard committed
373 374
        len--;
        /* XXX: for mpeg1, should test only bit 7 */
375
        if (c != 0xff)
Fabrice Bellard's avatar
Fabrice Bellard committed
376 377 378 379
            break;
    }
    if ((c & 0xc0) == 0x40) {
        /* buffer scale & size */
380
        avio_r8(s->pb);
381
        c    = avio_r8(s->pb);
Fabrice Bellard's avatar
Fabrice Bellard committed
382 383
        len -= 2;
    }
384
    if ((c & 0xe0) == 0x20) {
385 386
        dts  =
        pts  = get_pts(s->pb, c);
Fabrice Bellard's avatar
Fabrice Bellard committed
387
        len -= 4;
388 389
        if (c & 0x10) {
            dts  = get_pts(s->pb, -1);
390 391
            len -= 5;
        }
Fabrice Bellard's avatar
Fabrice Bellard committed
392 393
    } else if ((c & 0xc0) == 0x80) {
        /* mpeg 2 PES */
394
        flags      = avio_r8(s->pb);
395
        header_len = avio_r8(s->pb);
396
        len       -= 2;
Fabrice Bellard's avatar
Fabrice Bellard committed
397
        if (header_len > len)
398
            goto error_redo;
Michael Niedermayer's avatar
Michael Niedermayer committed
399
        len -= header_len;
400
        if (flags & 0x80) {
401
            dts         = pts = get_pts(s->pb, -1);
Fabrice Bellard's avatar
Fabrice Bellard committed
402
            header_len -= 5;
403
            if (flags & 0x40) {
404
                dts         = get_pts(s->pb, -1);
405 406
                header_len -= 5;
            }
Fabrice Bellard's avatar
Fabrice Bellard committed
407
        }
408
        if (flags & 0x3f && header_len == 0) {
409 410 411
            flags &= 0xC0;
            av_log(s, AV_LOG_WARNING, "Further flags set but no bytes left\n");
        }
412
        if (flags & 0x01) { /* PES extension */
413
            pes_ext = avio_r8(s->pb);
414
            header_len--;
415 416 417
            /* Skip PES private data, program packet sequence counter
             * and P-STD buffer */
            skip  = (pes_ext >> 4) & 0xb;
418
            skip += skip & 0x9;
419
            if (pes_ext & 0x40 || skip > header_len) {
420
                av_log(s, AV_LOG_WARNING, "pes_ext %X is invalid\n", pes_ext);
421
                pes_ext = skip = 0;
422
            }
423
            avio_skip(s->pb, skip);
424 425 426
            header_len -= skip;

            if (pes_ext & 0x01) { /* PES extension 2 */
427
                ext2_len = avio_r8(s->pb);
428 429
                header_len--;
                if ((ext2_len & 0x7f) > 0) {
430
                    id_ext = avio_r8(s->pb);
431 432 433 434 435 436
                    if ((id_ext & 0x80) == 0)
                        startcode = ((startcode & 0xff) << 8) | id_ext;
                    header_len--;
                }
            }
        }
437
        if (header_len < 0)
Michael Niedermayer's avatar
Michael Niedermayer committed
438
            goto error_redo;
439
        avio_skip(s->pb, header_len);
440
    } else if (c != 0xf)
441
        goto redo;
442
    }
443

444
    if (startcode == PRIVATE_STREAM_1) {
445
        startcode = avio_r8(s->pb);
Fabrice Bellard's avatar
Fabrice Bellard committed
446 447
        len--;
    }
448
    if (len < 0)
449
        goto error_redo;
450
    if (dts != AV_NOPTS_VALUE && ppos) {
451
        int i;
452 453 454
        for (i = 0; i < s->nb_streams; i++) {
            if (startcode == s->streams[i]->id &&
                s->pb->seekable /* index useless on streams anyway */) {
455
                ff_reduce_index(s, i);
456 457
                av_add_index_entry(s->streams[i], *ppos, dts, 0, 0,
                                   AVINDEX_KEYFRAME /* FIXME keyframe? */);
458 459 460
            }
        }
    }
461

Fabrice Bellard's avatar
Fabrice Bellard committed
462
    *pstart_code = startcode;
463 464
    *ppts        = pts;
    *pdts        = dts;
Fabrice Bellard's avatar
Fabrice Bellard committed
465 466 467 468 469 470
    return len;
}

static int mpegps_read_packet(AVFormatContext *s,
                              AVPacket *pkt)
{
471
    MpegDemuxContext *m = s->priv_data;
Fabrice Bellard's avatar
Fabrice Bellard committed
472
    AVStream *st;
473
    int len, startcode, i, es_type, ret;
Lou Logan's avatar
Lou Logan committed
474
    int lpcm_header_len = -1; //Init to suppress warning
475
    int request_probe= 0;
476
    enum AVCodecID codec_id = AV_CODEC_ID_NONE;
477
    enum AVMediaType type;
478
    int64_t pts, dts, dummy_pos; // dummy_pos is needed for the index building to work
Fabrice Bellard's avatar
Fabrice Bellard committed
479

480
redo:
481
    len = mpegps_read_pes_header(s, &dummy_pos, &startcode, &pts, &dts);
Fabrice Bellard's avatar
Fabrice Bellard committed
482 483
    if (len < 0)
        return len;
484

485
    if (startcode >= 0x80 && startcode <= 0xcf) {
486
        if (len < 4)
487 488 489 490
            goto skip;

        /* audio: skip header */
        avio_r8(s->pb);
491
        lpcm_header_len = avio_rb16(s->pb);
492 493 494 495 496 497 498 499
        len -= 3;
        if (startcode >= 0xb0 && startcode <= 0xbf) {
            /* MLP/TrueHD audio has a 4-byte header */
            avio_r8(s->pb);
            len--;
        }
    }

Fabrice Bellard's avatar
Fabrice Bellard committed
500
    /* now find stream */
501
    for (i = 0; i < s->nb_streams; i++) {
Fabrice Bellard's avatar
Fabrice Bellard committed
502 503 504 505
        st = s->streams[i];
        if (st->id == startcode)
            goto found;
    }
506 507

    es_type = m->psm_es_type[startcode & 0xff];
508
        if (es_type == STREAM_TYPE_VIDEO_MPEG1) {
509
            codec_id = AV_CODEC_ID_MPEG2VIDEO;
510 511
            type     = AVMEDIA_TYPE_VIDEO;
        } else if (es_type == STREAM_TYPE_VIDEO_MPEG2) {
512
            codec_id = AV_CODEC_ID_MPEG2VIDEO;
513 514 515
            type     = AVMEDIA_TYPE_VIDEO;
        } else if (es_type == STREAM_TYPE_AUDIO_MPEG1 ||
                   es_type == STREAM_TYPE_AUDIO_MPEG2) {
516
            codec_id = AV_CODEC_ID_MP3;
517 518
            type     = AVMEDIA_TYPE_AUDIO;
        } else if (es_type == STREAM_TYPE_AUDIO_AAC) {
519
            codec_id = AV_CODEC_ID_AAC;
520 521
            type     = AVMEDIA_TYPE_AUDIO;
        } else if (es_type == STREAM_TYPE_VIDEO_MPEG4) {
522
            codec_id = AV_CODEC_ID_MPEG4;
523 524
            type     = AVMEDIA_TYPE_VIDEO;
        } else if (es_type == STREAM_TYPE_VIDEO_H264) {
525
            codec_id = AV_CODEC_ID_H264;
526 527
            type     = AVMEDIA_TYPE_VIDEO;
        } else if (es_type == STREAM_TYPE_AUDIO_AC3) {
528
            codec_id = AV_CODEC_ID_AC3;
529
            type     = AVMEDIA_TYPE_AUDIO;
530
        } else if (m->imkh_cctv && es_type == 0x91) {
531
            codec_id = AV_CODEC_ID_PCM_MULAW;
532
            type     = AVMEDIA_TYPE_AUDIO;
533
    } else if (startcode >= 0x1e0 && startcode <= 0x1ef) {
534 535
        static const unsigned char avs_seqh[4] = { 0, 0, 1, 0xb0 };
        unsigned char buf[8];
536

537
        avio_read(s->pb, buf, 8);
538
        avio_seek(s->pb, -8, SEEK_CUR);
539
        if (!memcmp(buf, avs_seqh, 4) && (buf[6] != 0 || buf[7] != 1))
540
            codec_id = AV_CODEC_ID_CAVS;
541
        else
542
            request_probe= 1;
543
        type = AVMEDIA_TYPE_VIDEO;
544 545 546
    } else if (startcode == PRIVATE_STREAM_2) {
        type = AVMEDIA_TYPE_DATA;
        codec_id = AV_CODEC_ID_DVD_NAV;
547
    } else if (startcode >= 0x1c0 && startcode <= 0x1df) {
548
        type     = AVMEDIA_TYPE_AUDIO;
549 550 551 552
        if (m->sofdec > 0) {
            codec_id = AV_CODEC_ID_ADPCM_ADX;
            // Auto-detect AC-3
            request_probe = 50;
553
        } else if (m->imkh_cctv && startcode == 0x1c0 && len > 80) {
554 555
            codec_id = AV_CODEC_ID_PCM_ALAW;
            request_probe = 50;
556 557
        } else {
            codec_id = AV_CODEC_ID_MP2;
558 559
            if (m->imkh_cctv)
                request_probe = 25;
560
        }
561
    } else if (startcode >= 0x80 && startcode <= 0x87) {
562
        type     = AVMEDIA_TYPE_AUDIO;
563
        codec_id = AV_CODEC_ID_AC3;
564 565
    } else if ((startcode >= 0x88 && startcode <= 0x8f) ||
               (startcode >= 0x98 && startcode <= 0x9f)) {
566
        /* 0x90 - 0x97 is reserved for SDDS in DVD specs */
567
        type     = AVMEDIA_TYPE_AUDIO;
568
        codec_id = AV_CODEC_ID_DTS;
569
    } else if (startcode >= 0xa0 && startcode <= 0xaf) {
570
        type     = AVMEDIA_TYPE_AUDIO;
571
        if (lpcm_header_len == 6) {
572
            codec_id = AV_CODEC_ID_MLP;
573
        } else {
574
            codec_id = AV_CODEC_ID_PCM_DVD;
575
        }
576
    } else if (startcode >= 0xb0 && startcode <= 0xbf) {
577
        type     = AVMEDIA_TYPE_AUDIO;
578
        codec_id = AV_CODEC_ID_TRUEHD;
579 580
    } else if (startcode >= 0xc0 && startcode <= 0xcf) {
        /* Used for both AC-3 and E-AC-3 in EVOB files */
581
        type     = AVMEDIA_TYPE_AUDIO;
582
        codec_id = AV_CODEC_ID_AC3;
583
    } else if (startcode >= 0x20 && startcode <= 0x3f) {
584
        type     = AVMEDIA_TYPE_SUBTITLE;
585
        codec_id = AV_CODEC_ID_DVD_SUBTITLE;
586
    } else if (startcode >= 0xfd55 && startcode <= 0xfd5f) {
587
        type     = AVMEDIA_TYPE_VIDEO;
588
        codec_id = AV_CODEC_ID_VC1;
589
    } else {
590
skip:
591
        /* skip packet */
592
        avio_skip(s->pb, len);
593 594
        goto redo;
    }
595
    /* no stream found: add a new stream */
596
    st = avformat_new_stream(s, NULL);
597
    if (!st)
598
        goto skip;
599
    st->id                = startcode;
600
    st->codec->codec_type = type;
601
    st->codec->codec_id   = codec_id;
602 603
    if (   st->codec->codec_id == AV_CODEC_ID_PCM_MULAW
        || st->codec->codec_id == AV_CODEC_ID_PCM_ALAW) {
604 605 606 607
        st->codec->channels = 1;
        st->codec->channel_layout = AV_CH_LAYOUT_MONO;
        st->codec->sample_rate = 8000;
    }
608
    st->request_probe     = request_probe;
609 610 611 612
    st->need_parsing      = AVSTREAM_PARSE_FULL;

found:
    if (st->discard >= AVDISCARD_ALL)
613
        goto skip;
614
    if (startcode >= 0xa0 && startcode <= 0xaf) {
615
      if (st->codec->codec_id == AV_CODEC_ID_MLP) {
616 617 618 619 620
            if (len < 6)
                goto skip;
            avio_skip(s->pb, 6);
            len -=6;
      }
621
    }
622
    ret = av_get_packet(s->pb, pkt, len);
623 624 625 626

    pkt->pts          = pts;
    pkt->dts          = dts;
    pkt->pos          = dummy_pos;
627
    pkt->stream_index = st->index;
628 629 630

    if (s->debug & FF_FDEBUG_TS)
        av_log(s, AV_LOG_TRACE, "%d: pts=%0.3f dts=%0.3f size=%d\n",
631 632
            pkt->stream_index, pkt->pts / 90000.0, pkt->dts / 90000.0,
            pkt->size);
633

634
    return (ret < 0) ? ret : 0;
Fabrice Bellard's avatar
Fabrice Bellard committed
635 636
}

637
static int64_t mpegps_read_dts(AVFormatContext *s, int stream_index,
638
                               int64_t *ppos, int64_t pos_limit)
Fabrice Bellard's avatar
Fabrice Bellard committed
639 640 641 642 643
{
    int len, startcode;
    int64_t pos, pts, dts;

    pos = *ppos;
644
    if (avio_seek(s->pb, pos, SEEK_SET) < 0)
645 646
        return AV_NOPTS_VALUE;

647
    for (;;) {
648
        len = mpegps_read_pes_header(s, &pos, &startcode, &pts, &dts);
Fabrice Bellard's avatar
Fabrice Bellard committed
649
        if (len < 0) {
650 651
            if (s->debug & FF_FDEBUG_TS)
                av_log(s, AV_LOG_TRACE, "none (ret=%d)\n", len);
Fabrice Bellard's avatar
Fabrice Bellard committed
652 653
            return AV_NOPTS_VALUE;
        }
654
        if (startcode == s->streams[stream_index]->id &&
Fabrice Bellard's avatar
Fabrice Bellard committed
655 656 657
            dts != AV_NOPTS_VALUE) {
            break;
        }
658
        avio_skip(s->pb, len);
Fabrice Bellard's avatar
Fabrice Bellard committed
659
    }
660 661
    if (s->debug & FF_FDEBUG_TS)
        av_log(s, AV_LOG_TRACE, "pos=0x%"PRIx64" dts=0x%"PRIx64" %0.3f\n",
662
            pos, dts, dts / 90000.0);
Fabrice Bellard's avatar
Fabrice Bellard committed
663
    *ppos = pos;
664
    return dts;
Fabrice Bellard's avatar
Fabrice Bellard committed
665 666
}

667
AVInputFormat ff_mpegps_demuxer = {
668
    .name           = "mpeg",
669
    .long_name      = NULL_IF_CONFIG_SMALL("MPEG-PS (MPEG-2 Program Stream)"),
670 671 672 673 674
    .priv_data_size = sizeof(MpegDemuxContext),
    .read_probe     = mpegps_probe,
    .read_header    = mpegps_read_header,
    .read_packet    = mpegps_read_packet,
    .read_timestamp = mpegps_read_dts,
675
    .flags          = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT,
Fabrice Bellard's avatar
Fabrice Bellard committed
676
};
Clément Bœsch's avatar
Clément Bœsch committed
677 678 679 680

#if CONFIG_VOBSUB_DEMUXER

#define REF_STRING "# VobSub index file,"
681
#define MAX_LINE_SIZE 2048
Clément Bœsch's avatar
Clément Bœsch committed
682 683 684 685 686 687 688 689 690 691

static int vobsub_probe(AVProbeData *p)
{
    if (!strncmp(p->buf, REF_STRING, sizeof(REF_STRING) - 1))
        return AVPROBE_SCORE_MAX;
    return 0;
}

static int vobsub_read_header(AVFormatContext *s)
{
692
    int i, ret = 0, header_parsed = 0, langidx = 0;
Clément Bœsch's avatar
Clément Bœsch committed
693 694
    MpegDemuxContext *vobsub = s->priv_data;
    size_t fname_len;
695
    char *header_str;
Clément Bœsch's avatar
Clément Bœsch committed
696 697 698
    AVBPrint header;
    int64_t delay = 0;
    AVStream *st = NULL;
699 700 701
    int stream_id = -1;
    char id[64] = {0};
    char alt[MAX_LINE_SIZE] = {0};
702
    AVInputFormat *iformat;
Clément Bœsch's avatar
Clément Bœsch committed
703

704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
    if (!vobsub->sub_name) {
        char *ext;
        vobsub->sub_name = av_strdup(s->filename);
        if (!vobsub->sub_name) {
            ret = AVERROR(ENOMEM);
            goto end;
        }

        fname_len = strlen(vobsub->sub_name);
        ext = vobsub->sub_name - 3 + fname_len;
        if (fname_len < 4 || *(ext - 1) != '.') {
            av_log(s, AV_LOG_ERROR, "The input index filename is too short "
                   "to guess the associated .SUB file\n");
            ret = AVERROR_INVALIDDATA;
            goto end;
        }
        memcpy(ext, !strncmp(ext, "IDX", 3) ? "SUB" : "sub", 3);
        av_log(s, AV_LOG_VERBOSE, "IDX/SUB: %s -> %s\n", s->filename, vobsub->sub_name);
Clément Bœsch's avatar
Clément Bœsch committed
722
    }
723

724 725 726 727
    if (!(iformat = av_find_input_format("mpeg"))) {
        ret = AVERROR_DEMUXER_NOT_FOUND;
        goto end;
    }
728

729
    vobsub->sub_ctx = avformat_alloc_context();
730 731 732 733
    if (!vobsub->sub_ctx) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
734

735 736
    if ((ret = ff_copy_whitelists(vobsub->sub_ctx, s)) < 0)
        goto end;
737

738
    ret = avformat_open_input(&vobsub->sub_ctx, vobsub->sub_name, iformat, NULL);
Clément Bœsch's avatar
Clément Bœsch committed
739
    if (ret < 0) {
740
        av_log(s, AV_LOG_ERROR, "Unable to open %s as MPEG subtitles\n", vobsub->sub_name);
Clément Bœsch's avatar
Clément Bœsch committed
741 742 743 744
        goto end;
    }

    av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED);
745
    while (!avio_feof(s->pb)) {
746
        char line[MAX_LINE_SIZE];
Clément Bœsch's avatar
Clément Bœsch committed
747 748 749 750 751 752 753 754
        int len = ff_get_line(s->pb, line, sizeof(line));

        if (!len)
            break;

        line[strcspn(line, "\r\n")] = 0;

        if (!strncmp(line, "id:", 3)) {
755
            if (sscanf(line, "id: %63[^,], index: %u", id, &stream_id) != 2) {
Clément Bœsch's avatar
Clément Bœsch committed
756 757 758 759 760 761
                av_log(s, AV_LOG_WARNING, "Unable to parse index line '%s', "
                       "assuming 'id: und, index: 0'\n", line);
                strcpy(id, "und");
                stream_id = 0;
            }

762 763 764 765 766 767
            if (stream_id >= FF_ARRAY_ELEMS(vobsub->q)) {
                av_log(s, AV_LOG_ERROR, "Maximum number of subtitles streams reached\n");
                ret = AVERROR(EINVAL);
                goto end;
            }

Clément Bœsch's avatar
Clément Bœsch committed
768
            header_parsed = 1;
769 770 771 772 773
            alt[0] = '\0';
            /* We do not create the stream immediately to avoid adding empty
             * streams. See the following timestamp entry. */

            av_log(s, AV_LOG_DEBUG, "IDX stream[%d] id=%s\n", stream_id, id);
Clément Bœsch's avatar
Clément Bœsch committed
774

775
        } else if (!strncmp(line, "timestamp:", 10)) {
Clément Bœsch's avatar
Clément Bœsch committed
776 777 778 779 780
            AVPacket *sub;
            int hh, mm, ss, ms;
            int64_t pos, timestamp;
            const char *p = line + 10;

781
            if (stream_id == -1) {
782 783 784 785 786
                av_log(s, AV_LOG_ERROR, "Timestamp declared before any stream\n");
                ret = AVERROR_INVALIDDATA;
                goto end;
            }

787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
            if (!st || st->id != stream_id) {
                st = avformat_new_stream(s, NULL);
                if (!st) {
                    ret = AVERROR(ENOMEM);
                    goto end;
                }
                st->id = stream_id;
                st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
                st->codec->codec_id   = AV_CODEC_ID_DVD_SUBTITLE;
                avpriv_set_pts_info(st, 64, 1, 1000);
                av_dict_set(&st->metadata, "language", id, 0);
                if (alt[0])
                    av_dict_set(&st->metadata, "title", alt, 0);
            }

802
            if (sscanf(p, "%02d:%02d:%02d:%03d, filepos: %"SCNx64,
Clément Bœsch's avatar
Clément Bœsch committed
803 804 805
                       &hh, &mm, &ss, &ms, &pos) != 5) {
                av_log(s, AV_LOG_ERROR, "Unable to parse timestamp line '%s', "
                       "abort parsing\n", line);
806 807
                ret = AVERROR_INVALIDDATA;
                goto end;
Clément Bœsch's avatar
Clément Bœsch committed
808 809
            }
            timestamp = (hh*3600LL + mm*60LL + ss) * 1000LL + ms + delay;
810
            timestamp = av_rescale_q(timestamp, av_make_q(1, 1000), st->time_base);
Clément Bœsch's avatar
Clément Bœsch committed
811

812
            sub = ff_subtitles_queue_insert(&vobsub->q[s->nb_streams - 1], "", 0, 0);
Clément Bœsch's avatar
Clément Bœsch committed
813 814 815 816 817 818 819 820
            if (!sub) {
                ret = AVERROR(ENOMEM);
                goto end;
            }
            sub->pos = pos;
            sub->pts = timestamp;
            sub->stream_index = s->nb_streams - 1;

821
        } else if (!strncmp(line, "alt:", 4)) {
Clément Bœsch's avatar
Clément Bœsch committed
822 823 824 825
            const char *p = line + 4;

            while (*p == ' ')
                p++;
826
            av_log(s, AV_LOG_DEBUG, "IDX stream[%d] name=%s\n", stream_id, p);
827
            av_strlcpy(alt, p, sizeof(alt));
Clément Bœsch's avatar
Clément Bœsch committed
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
            header_parsed = 1;

        } else if (!strncmp(line, "delay:", 6)) {
            int sign = 1, hh = 0, mm = 0, ss = 0, ms = 0;
            const char *p = line + 6;

            while (*p == ' ')
                p++;
            if (*p == '-' || *p == '+') {
                sign = *p == '-' ? -1 : 1;
                p++;
            }
            sscanf(p, "%d:%d:%d:%d", &hh, &mm, &ss, &ms);
            delay = ((hh*3600LL + mm*60LL + ss) * 1000LL + ms) * sign;

        } else if (!strncmp(line, "langidx:", 8)) {
            const char *p = line + 8;

            if (sscanf(p, "%d", &langidx) != 1)
                av_log(s, AV_LOG_ERROR, "Invalid langidx specified\n");

        } else if (!header_parsed) {
            if (line[0] && line[0] != '#')
                av_bprintf(&header, "%s\n", line);
        }
    }

    if (langidx < s->nb_streams)
        s->streams[langidx]->disposition |= AV_DISPOSITION_DEFAULT;

858 859 860 861
    for (i = 0; i < s->nb_streams; i++) {
        vobsub->q[i].sort = SUB_SORT_POS_TS;
        ff_subtitles_queue_finalize(&vobsub->q[i]);
    }
Clément Bœsch's avatar
Clément Bœsch committed
862 863 864 865 866 867 868 869 870 871

    if (!av_bprint_is_complete(&header)) {
        av_bprint_finalize(&header, NULL);
        ret = AVERROR(ENOMEM);
        goto end;
    }
    av_bprint_finalize(&header, &header_str);
    for (i = 0; i < s->nb_streams; i++) {
        AVStream *sub_st = s->streams[i];
        sub_st->codec->extradata      = av_strdup(header_str);
872
        sub_st->codec->extradata_size = header.len;
Clément Bœsch's avatar
Clément Bœsch committed
873 874 875 876 877 878 879 880 881 882
    }
    av_free(header_str);

end:
    return ret;
}

static int vobsub_read_packet(AVFormatContext *s, AVPacket *pkt)
{
    MpegDemuxContext *vobsub = s->priv_data;
883
    FFDemuxSubtitlesQueue *q;
Clément Bœsch's avatar
Clément Bœsch committed
884
    AVIOContext *pb = vobsub->sub_ctx->pb;
885
    int ret, psize, total_read = 0, i;
Clément Bœsch's avatar
Clément Bœsch committed
886 887
    AVPacket idx_pkt;

888 889 890 891
    int64_t min_ts = INT64_MAX;
    int sid = 0;
    for (i = 0; i < s->nb_streams; i++) {
        FFDemuxSubtitlesQueue *tmpq = &vobsub->q[i];
892 893 894
        int64_t ts;
        av_assert0(tmpq->nb_subs);
        ts = tmpq->subs[tmpq->current_sub_idx].pts;
895 896 897 898 899 900
        if (ts < min_ts) {
            min_ts = ts;
            sid = i;
        }
    }
    q = &vobsub->q[sid];
Clément Bœsch's avatar
Clément Bœsch committed
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922
    ret = ff_subtitles_queue_read_packet(q, &idx_pkt);
    if (ret < 0)
        return ret;

    /* compute maximum packet size using the next packet position. This is
     * useful when the len in the header is non-sense */
    if (q->current_sub_idx < q->nb_subs) {
        psize = q->subs[q->current_sub_idx].pos - idx_pkt.pos;
    } else {
        int64_t fsize = avio_size(pb);
        psize = fsize < 0 ? 0xffff : fsize - idx_pkt.pos;
    }

    avio_seek(pb, idx_pkt.pos, SEEK_SET);

    av_init_packet(pkt);
    pkt->size = 0;
    pkt->data = NULL;

    do {
        int n, to_read, startcode;
        int64_t pts, dts;
923 924
        int64_t old_pos = avio_tell(pb), new_pos;
        int pkt_size;
Clément Bœsch's avatar
Clément Bœsch committed
925 926

        ret = mpegps_read_pes_header(vobsub->sub_ctx, NULL, &startcode, &pts, &dts);
927 928 929
        if (ret < 0) {
            if (pkt->size) // raise packet even if incomplete
                break;
930
            goto fail;
931
        }
Clément Bœsch's avatar
Clément Bœsch committed
932
        to_read = ret & 0xffff;
933 934
        new_pos = avio_tell(pb);
        pkt_size = ret + (new_pos - old_pos);
Clément Bœsch's avatar
Clément Bœsch committed
935 936

        /* this prevents reads above the current packet */
937
        if (total_read + pkt_size > psize)
Clément Bœsch's avatar
Clément Bœsch committed
938
            break;
939
        total_read += pkt_size;
Clément Bœsch's avatar
Clément Bœsch committed
940 941 942 943 944 945 946

        /* the current chunk doesn't match the stream index (unlikely) */
        if ((startcode & 0x1f) != idx_pkt.stream_index)
            break;

        ret = av_grow_packet(pkt, to_read);
        if (ret < 0)
947
            goto fail;
Clément Bœsch's avatar
Clément Bœsch committed
948 949 950 951

        n = avio_read(pb, pkt->data + (pkt->size - to_read), to_read);
        if (n < to_read)
            pkt->size -= to_read - n;
952
    } while (total_read < psize);
Clément Bœsch's avatar
Clément Bœsch committed
953 954 955 956 957

    pkt->pts = pkt->dts = idx_pkt.pts;
    pkt->pos = idx_pkt.pos;
    pkt->stream_index = idx_pkt.stream_index;

958
    av_free_packet(&idx_pkt);
Clément Bœsch's avatar
Clément Bœsch committed
959
    return 0;
960 961

fail:
962
    av_free_packet(pkt);
963 964
    av_free_packet(&idx_pkt);
    return ret;
Clément Bœsch's avatar
Clément Bœsch committed
965 966 967 968 969 970
}

static int vobsub_read_seek(AVFormatContext *s, int stream_index,
                            int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
{
    MpegDemuxContext *vobsub = s->priv_data;
971 972 973 974 975

    /* Rescale requested timestamps based on the first stream (timebase is the
     * same for all subtitles stream within a .idx/.sub). Rescaling is done just
     * like in avformat_seek_file(). */
    if (stream_index == -1 && s->nb_streams != 1) {
976
        int i, ret = 0;
977 978 979 980 981 982 983 984
        AVRational time_base = s->streams[0]->time_base;
        ts = av_rescale_q(ts, AV_TIME_BASE_Q, time_base);
        min_ts = av_rescale_rnd(min_ts, time_base.den,
                                time_base.num * (int64_t)AV_TIME_BASE,
                                AV_ROUND_UP   | AV_ROUND_PASS_MINMAX);
        max_ts = av_rescale_rnd(max_ts, time_base.den,
                                time_base.num * (int64_t)AV_TIME_BASE,
                                AV_ROUND_DOWN | AV_ROUND_PASS_MINMAX);
985 986 987 988 989 990 991
        for (i = 0; i < s->nb_streams; i++) {
            int r = ff_subtitles_queue_seek(&vobsub->q[i], s, stream_index,
                                            min_ts, ts, max_ts, flags);
            if (r < 0)
                ret = r;
        }
        return ret;
992 993
    }

994 995
    if (stream_index == -1) // only 1 stream
        stream_index = 0;
996
    return ff_subtitles_queue_seek(&vobsub->q[stream_index], s, stream_index,
Clément Bœsch's avatar
Clément Bœsch committed
997 998 999 1000 1001
                                   min_ts, ts, max_ts, flags);
}

static int vobsub_read_close(AVFormatContext *s)
{
1002
    int i;
Clément Bœsch's avatar
Clément Bœsch committed
1003
    MpegDemuxContext *vobsub = s->priv_data;
1004 1005 1006

    for (i = 0; i < s->nb_streams; i++)
        ff_subtitles_queue_clean(&vobsub->q[i]);
Clément Bœsch's avatar
Clément Bœsch committed
1007 1008 1009 1010 1011
    if (vobsub->sub_ctx)
        avformat_close_input(&vobsub->sub_ctx);
    return 0;
}

1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
static const AVOption options[] = {
    { "sub_name", "URI for .sub file", offsetof(MpegDemuxContext, sub_name), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_DECODING_PARAM },
    { NULL }
};

static const AVClass vobsub_demuxer_class = {
    .class_name = "vobsub",
    .item_name  = av_default_item_name,
    .option     = options,
    .version    = LIBAVUTIL_VERSION_INT,
};

Clément Bœsch's avatar
Clément Bœsch committed
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034
AVInputFormat ff_vobsub_demuxer = {
    .name           = "vobsub",
    .long_name      = NULL_IF_CONFIG_SMALL("VobSub subtitle format"),
    .priv_data_size = sizeof(MpegDemuxContext),
    .read_probe     = vobsub_probe,
    .read_header    = vobsub_read_header,
    .read_packet    = vobsub_read_packet,
    .read_seek2     = vobsub_read_seek,
    .read_close     = vobsub_read_close,
    .flags          = AVFMT_SHOW_IDS,
    .extensions     = "idx",
1035
    .priv_class     = &vobsub_demuxer_class,
Clément Bœsch's avatar
Clément Bœsch committed
1036 1037
};
#endif