diff options
Diffstat (limited to 'libavformat/mpeg.c')
-rw-r--r-- | libavformat/mpeg.c | 482 |
1 files changed, 413 insertions, 69 deletions
diff --git a/libavformat/mpeg.c b/libavformat/mpeg.c index 7430bb0113..63b35efceb 100644 --- a/libavformat/mpeg.c +++ b/libavformat/mpeg.c @@ -2,20 +2,20 @@ * MPEG1/2 demuxer * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -23,15 +23,21 @@ #include "internal.h" #include "mpeg.h" +#if CONFIG_VOBSUB_DEMUXER +# include "subtitles.h" +# include "libavutil/bprint.h" +#endif + #undef NDEBUG #include <assert.h> +#include "libavutil/avassert.h" /*********************************************/ /* demux code */ #define MAX_SYNC_SIZE 100000 -static int check_pes(uint8_t *p, uint8_t *end){ +static int check_pes(const uint8_t *p, const uint8_t *end){ int pes1; int pes2= (p[3] & 0xC0) == 0x80 && (p[4] & 0xC0) != 0x40 @@ -74,6 +80,7 @@ static int mpegps_probe(AVProbeData *p) // and audio streams 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 else if((code & 0xf0) == VIDEO_ID && !pes) invalid++; else if((code & 0xe0) == AUDIO_ID && !pes) invalid++; @@ -81,18 +88,19 @@ static int mpegps_probe(AVProbeData *p) } } - if(vid+audio > invalid) /* invalid VDR files nd short PES streams */ + if(vid+audio > invalid+1) /* invalid VDR files nd short PES streams */ score = AVPROBE_SCORE_EXTENSION / 2; if(sys>invalid && sys*9 <= pspack*10) - return pspack > 2 ? AVPROBE_SCORE_EXTENSION + 2 : AVPROBE_SCORE_EXTENSION / 2; // 1 more than .mpg + return (audio > 12 || vid > 3 || pspack > 2) ? AVPROBE_SCORE_EXTENSION + 2 : AVPROBE_SCORE_EXTENSION / 2; // 1 more than .mpg 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 */ - return (audio > 12 || vid > 3) ? AVPROBE_SCORE_EXTENSION + 2 : AVPROBE_SCORE_EXTENSION / 2; + return (audio > 12 || vid > 3 + 2*invalid) ? AVPROBE_SCORE_EXTENSION + 2 : AVPROBE_SCORE_EXTENSION / 2; //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 + //Have\ Yourself\ a\ Merry\ Little\ Christmas.mp3 0 0 0 5 0 1 len:21618 return score; } @@ -101,25 +109,30 @@ typedef struct MpegDemuxContext { int32_t header_state; unsigned char psm_es_type[256]; int sofdec; + int dvd; + int imkh_cctv; +#if CONFIG_VOBSUB_DEMUXER + AVFormatContext *sub_ctx; + FFDemuxSubtitlesQueue q; +#endif } MpegDemuxContext; static int mpegps_read_header(AVFormatContext *s) { MpegDemuxContext *m = s->priv_data; - const char *sofdec = "Sofdec"; - int v, i = 0; + char buffer[7]; + int64_t last_pos = avio_tell(s->pb); m->header_state = 0xff; s->ctx_flags |= AVFMTCTX_NOHEADER; - m->sofdec = -1; - do { - v = avio_r8(s->pb); - m->header_state = m->header_state << 8 | v; - m->sofdec++; - } while (v == sofdec[i] && i++ < 6); - - m->sofdec = (m->sofdec == 6) ? 1 : 0; + 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 + avio_seek(s->pb, last_pos, SEEK_SET); /* no need to do more */ return 0; @@ -144,7 +157,7 @@ static int find_next_start_code(AVIOContext *pb, int *size_ptr, state = *header_state; n = *size_ptr; while (n > 0) { - if (pb->eof_reached) + if (url_feof(pb)) break; v = avio_r8(pb); n--; @@ -218,7 +231,7 @@ static int mpegps_read_pes_header(AVFormatContext *s, startcode = find_next_start_code(s->pb, &size, &m->header_state); last_sync = avio_tell(s->pb); if (startcode < 0){ - if(s->pb->eof_reached) + if(url_feof(s->pb)) return AVERROR_EOF; //FIXME we should remember header_state return AVERROR(EAGAIN); @@ -233,21 +246,82 @@ static int mpegps_read_pes_header(AVFormatContext *s, goto redo; } if (startcode == PRIVATE_STREAM_2) { - len = avio_rb16(s->pb); if (!m->sofdec) { - while (len-- >= 6) { - if (avio_r8(s->pb) == 'S') { - uint8_t buf[5]; - avio_read(s->pb, buf, sizeof(buf)); - m->sofdec = !memcmp(buf, "ofdec", 5); - len -= sizeof(buf); - break; + /* 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); + } + } } + + 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; } - m->sofdec -= !m->sofdec; + } else if (!m->dvd) { + int len = avio_rb16(s->pb); + avio_skip(s->pb, len); + goto redo; } - avio_skip(s->pb, len); - goto redo; } if (startcode == PROGRAM_STREAM_MAP) { mpegps_psm_parse(m, s->pb); @@ -257,7 +331,9 @@ static int mpegps_read_pes_header(AVFormatContext *s, /* find matching stream */ if (!((startcode >= 0x1c0 && startcode <= 0x1df) || (startcode >= 0x1e0 && startcode <= 0x1ef) || - (startcode == 0x1bd) || (startcode == 0x1fd))) + (startcode == 0x1bd) || + (startcode == PRIVATE_STREAM_2) || + (startcode == 0x1fd))) goto redo; if (ppos) { *ppos = avio_tell(s->pb) - 4; @@ -265,6 +341,8 @@ static int mpegps_read_pes_header(AVFormatContext *s, len = avio_rb16(s->pb); pts = dts = AV_NOPTS_VALUE; + if (startcode != PRIVATE_STREAM_2) + { /* stuffing */ for(;;) { if (len < 1) @@ -338,22 +416,11 @@ static int mpegps_read_pes_header(AVFormatContext *s, } else if( c!= 0xf ) goto redo; + } - if (startcode == PRIVATE_STREAM_1 && !m->psm_es_type[startcode & 0xff]) { + if (startcode == PRIVATE_STREAM_1) { startcode = avio_r8(s->pb); len--; - if (startcode >= 0x80 && startcode <= 0xcf) { - /* audio: skip header */ - avio_r8(s->pb); - avio_r8(s->pb); - avio_r8(s->pb); - len -= 3; - if (startcode >= 0xb0 && startcode <= 0xbf) { - /* MLP/TrueHD audio has a 4-byte header */ - avio_r8(s->pb); - len--; - } - } } if(len<0) goto error_redo; @@ -380,20 +447,30 @@ static int mpegps_read_packet(AVFormatContext *s, MpegDemuxContext *m = s->priv_data; AVStream *st; int len, startcode, i, es_type, ret; + int lpcm_header_len = -1; //Init to supress warning + int request_probe= 0; enum AVCodecID codec_id = AV_CODEC_ID_NONE; enum AVMediaType type; int64_t pts, dts, dummy_pos; //dummy_pos is needed for the index building to work - uint8_t av_uninit(dvdaudio_substream_type); redo: len = mpegps_read_pes_header(s, &dummy_pos, &startcode, &pts, &dts); if (len < 0) return len; - if(startcode == 0x1bd) { - dvdaudio_substream_type = avio_r8(s->pb); - avio_skip(s->pb, 3); - len -= 4; + if (startcode >= 0x80 && startcode <= 0xcf) { + if(len < 4) + goto skip; + + /* audio: skip header */ + avio_r8(s->pb); + lpcm_header_len = avio_rb16(s->pb); + len -= 3; + if (startcode >= 0xb0 && startcode <= 0xbf) { + /* MLP/TrueHD audio has a 4-byte header */ + avio_r8(s->pb); + len--; + } } /* now find stream */ @@ -404,7 +481,6 @@ static int mpegps_read_packet(AVFormatContext *s, } es_type = m->psm_es_type[startcode & 0xff]; - if(es_type > 0 && es_type != STREAM_TYPE_PRIVATE_DATA){ if(es_type == STREAM_TYPE_VIDEO_MPEG1){ codec_id = AV_CODEC_ID_MPEG2VIDEO; type = AVMEDIA_TYPE_VIDEO; @@ -427,9 +503,9 @@ static int mpegps_read_packet(AVFormatContext *s, } else if(es_type == STREAM_TYPE_AUDIO_AC3){ codec_id = AV_CODEC_ID_AC3; type = AVMEDIA_TYPE_AUDIO; - } else { - goto skip; - } + } else if(m->imkh_cctv && es_type == 0x91){ + codec_id = AV_CODEC_ID_PCM_MULAW; + type = AVMEDIA_TYPE_AUDIO; } else if (startcode >= 0x1e0 && startcode <= 0x1ef) { static const unsigned char avs_seqh[4] = { 0, 0, 1, 0xb0 }; unsigned char buf[8]; @@ -438,8 +514,11 @@ static int mpegps_read_packet(AVFormatContext *s, if(!memcmp(buf, avs_seqh, 4) && (buf[6] != 0 || buf[7] != 1)) codec_id = AV_CODEC_ID_CAVS; else - codec_id = AV_CODEC_ID_PROBE; + request_probe= 1; type = AVMEDIA_TYPE_VIDEO; + } else if (startcode == PRIVATE_STREAM_2) { + type = AVMEDIA_TYPE_DATA; + codec_id = AV_CODEC_ID_DVD_NAV; } else if (startcode >= 0x1c0 && startcode <= 0x1df) { type = AVMEDIA_TYPE_AUDIO; codec_id = m->sofdec > 0 ? AV_CODEC_ID_ADPCM_ADX : AV_CODEC_ID_MP2; @@ -453,7 +532,11 @@ static int mpegps_read_packet(AVFormatContext *s, codec_id = AV_CODEC_ID_DTS; } else if (startcode >= 0xa0 && startcode <= 0xaf) { type = AVMEDIA_TYPE_AUDIO; - codec_id = AV_CODEC_ID_PCM_DVD; + if(lpcm_header_len == 6) { + codec_id = AV_CODEC_ID_MLP; + } else { + codec_id = AV_CODEC_ID_PCM_DVD; + } } else if (startcode >= 0xb0 && startcode <= 0xbf) { type = AVMEDIA_TYPE_AUDIO; codec_id = AV_CODEC_ID_TRUEHD; @@ -467,19 +550,6 @@ static int mpegps_read_packet(AVFormatContext *s, } else if (startcode >= 0xfd55 && startcode <= 0xfd5f) { type = AVMEDIA_TYPE_VIDEO; codec_id = AV_CODEC_ID_VC1; - } else if (startcode == 0x1bd) { - // check dvd audio substream type - type = AVMEDIA_TYPE_AUDIO; - switch(dvdaudio_substream_type & 0xe0) { - case 0xa0: codec_id = AV_CODEC_ID_PCM_DVD; - break; - case 0x80: if((dvdaudio_substream_type & 0xf8) == 0x88) - codec_id = AV_CODEC_ID_DTS; - else codec_id = AV_CODEC_ID_AC3; - break; - default: av_log(s, AV_LOG_ERROR, "Unknown 0x1bd sub-stream\n"); - goto skip; - } } else { skip: /* skip packet */ @@ -493,10 +563,24 @@ static int mpegps_read_packet(AVFormatContext *s, st->id = startcode; st->codec->codec_type = type; st->codec->codec_id = codec_id; + if (st->codec->codec_id == AV_CODEC_ID_PCM_MULAW) { + st->codec->channels = 1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + st->codec->sample_rate = 8000; + } + st->request_probe = request_probe; st->need_parsing = AVSTREAM_PARSE_FULL; found: if(st->discard >= AVDISCARD_ALL) goto skip; + if (startcode >= 0xa0 && startcode <= 0xaf) { + if (lpcm_header_len == 6 && st->codec->codec_id == AV_CODEC_ID_MLP) { + if (len < 6) + goto skip; + avio_skip(s->pb, 6); + len -=6; + } + } ret = av_get_packet(s->pb, pkt, len); pkt->pts = pts; pkt->dts = dts; @@ -547,3 +631,263 @@ AVInputFormat ff_mpegps_demuxer = { .read_timestamp = mpegps_read_dts, .flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT, }; + +#if CONFIG_VOBSUB_DEMUXER + +#define REF_STRING "# VobSub index file," + +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) +{ + int i, ret = 0, header_parsed = 0, langidx = 0; + MpegDemuxContext *vobsub = s->priv_data; + char *sub_name = NULL; + size_t fname_len; + char *ext, *header_str; + AVBPrint header; + int64_t delay = 0; + AVStream *st = NULL; + + sub_name = av_strdup(s->filename); + fname_len = strlen(sub_name); + ext = 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, sub_name); + ret = avformat_open_input(&vobsub->sub_ctx, sub_name, &ff_mpegps_demuxer, NULL); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Unable to open %s as MPEG subtitles\n", sub_name); + goto end; + } + + av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED); + while (!url_feof(s->pb)) { + char line[2048]; + int len = ff_get_line(s->pb, line, sizeof(line)); + + if (!len) + break; + + line[strcspn(line, "\r\n")] = 0; + + if (!strncmp(line, "id:", 3)) { + int n, stream_id = 0; + char id[64] = {0}; + + n = sscanf(line, "id: %63[^,], index: %u", id, &stream_id); + if (n != 2) { + 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; + } + + 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; + av_dict_set(&st->metadata, "language", id, 0); + av_log(s, AV_LOG_DEBUG, "IDX stream[%d] id=%s\n", stream_id, id); + header_parsed = 1; + + } else if (st && !strncmp(line, "timestamp:", 10)) { + AVPacket *sub; + int hh, mm, ss, ms; + int64_t pos, timestamp; + const char *p = line + 10; + + if (sscanf(p, "%02d:%02d:%02d:%03d, filepos: %"SCNx64, + &hh, &mm, &ss, &ms, &pos) != 5) { + av_log(s, AV_LOG_ERROR, "Unable to parse timestamp line '%s', " + "abort parsing\n", line); + break; + } + timestamp = (hh*3600LL + mm*60LL + ss) * 1000LL + ms + delay; + timestamp = av_rescale_q(timestamp, (AVRational){1,1000}, st->time_base); + + sub = ff_subtitles_queue_insert(&vobsub->q, "", 0, 0); + if (!sub) { + ret = AVERROR(ENOMEM); + goto end; + } + sub->pos = pos; + sub->pts = timestamp; + sub->stream_index = s->nb_streams - 1; + + } else if (st && !strncmp(line, "alt:", 4)) { + const char *p = line + 4; + + while (*p == ' ') + p++; + av_dict_set(&st->metadata, "title", p, 0); + av_log(s, AV_LOG_DEBUG, "IDX stream[%d] name=%s\n", st->id, p); + 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; + + ff_subtitles_queue_finalize(&vobsub->q); + + 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); + sub_st->codec->extradata_size = header.len; + } + av_free(header_str); + +end: + av_free(sub_name); + return ret; +} + +#define FAIL(r) do { ret = r; goto fail; } while (0) + +static int vobsub_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + MpegDemuxContext *vobsub = s->priv_data; + FFDemuxSubtitlesQueue *q = &vobsub->q; + AVIOContext *pb = vobsub->sub_ctx->pb; + int ret, psize, len16 = -1; + AVPacket idx_pkt; + + 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; + + ret = mpegps_read_pes_header(vobsub->sub_ctx, NULL, &startcode, &pts, &dts); + if (ret < 0) + FAIL(ret); + to_read = ret & 0xffff; + + /* this prevents reads above the current packet */ + if (pkt->size + to_read > psize) + break; + + /* if the len is computed, we check for overread */ + if (len16 != -1 && pkt->size + to_read > len16) + break; + + /* 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) + FAIL(ret); + + n = avio_read(pb, pkt->data + (pkt->size - to_read), to_read); + if (n < to_read) + pkt->size -= to_read - n; + + /* first chunk contains the total len of the packet to raise */ + if (len16 == -1 && n > 2) + len16 = AV_RB16(pkt->data); + } while (len16 != -1 && pkt->size != len16); + + pkt->pts = pkt->dts = idx_pkt.pts; + pkt->pos = idx_pkt.pos; + pkt->stream_index = idx_pkt.stream_index; + + av_free_packet(&idx_pkt); + return 0; + +fail: + av_free_packet(&idx_pkt); + return ret; +} + +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; + return ff_subtitles_queue_seek(&vobsub->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int vobsub_read_close(AVFormatContext *s) +{ + MpegDemuxContext *vobsub = s->priv_data; + ff_subtitles_queue_clean(&vobsub->q); + if (vobsub->sub_ctx) + avformat_close_input(&vobsub->sub_ctx); + return 0; +} + +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", +}; +#endif |