diff --git a/libavformat/microdvddec.c b/libavformat/microdvddec.c index b1229fae01..b47457454d 100644 --- a/libavformat/microdvddec.c +++ b/libavformat/microdvddec.c @@ -1,6 +1,7 @@ /* * MicroDVD subtitle demuxer * Copyright (c) 2010 Aurelien Jacobs + * Copyright (c) 2012 Clément Bœsch * * This file is part of FFmpeg. * @@ -21,16 +22,14 @@ #include "avformat.h" #include "internal.h" +#include "subtitles.h" #include "libavutil/intreadwrite.h" #define MAX_LINESIZE 2048 typedef struct { - uint8_t lines[3][MAX_LINESIZE]; - int64_t pos[3]; - AVPacket last_pkt; - int last_pkt_ready; + FFDemuxSubtitlesQueue q; } MicroDVDContext; @@ -52,36 +51,6 @@ static int microdvd_probe(AVProbeData *p) return AVPROBE_SCORE_MAX; } -static int microdvd_read_header(AVFormatContext *s) -{ - AVRational pts_info = (AVRational){ 2997, 125 }; /* default: 23.976 fps */ - MicroDVDContext *microdvd = s->priv_data; - AVStream *st = avformat_new_stream(s, NULL); - int i, frame; - double fps; - char c; - - if (!st) - return AVERROR(ENOMEM); - for (i=0; ilines); i++) { - microdvd->pos[i] = avio_tell(s->pb); - ff_get_line(s->pb, microdvd->lines[i], sizeof(microdvd->lines[i])); - if ((sscanf(microdvd->lines[i], "{%d}{}%6lf", &frame, &fps) == 2 || - sscanf(microdvd->lines[i], "{%d}{%*d}%6lf", &frame, &fps) == 2) - && frame <= 1 && fps > 3 && fps < 100) - pts_info = av_d2q(fps, 100000); - if (sscanf(microdvd->lines[i], "{DEFAULT}{}%c", &c) == 1) { - st->codec->extradata = av_strdup(microdvd->lines[i] + 11); - st->codec->extradata_size = strlen(st->codec->extradata); - i--; - } - } - avpriv_set_pts_info(st, 64, pts_info.den, pts_info.num); - st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; - st->codec->codec_id = CODEC_ID_MICRODVD; - return 0; -} - static int64_t get_pts(const char *buf) { int frame; @@ -101,68 +70,67 @@ static int get_duration(const char *buf) return -1; } +static int microdvd_read_header(AVFormatContext *s) +{ + AVRational pts_info = (AVRational){ 2997, 125 }; /* default: 23.976 fps */ + MicroDVDContext *microdvd = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + int i = 0; + char line[MAX_LINESIZE]; + + if (!st) + return AVERROR(ENOMEM); + + while (!url_feof(s->pb)) { + AVPacket *sub; + int64_t pos = avio_tell(s->pb); + int len = ff_get_line(s->pb, line, sizeof(line)); + + if (!len) + break; + if (i < 3) { + int frame; + double fps; + char c; + + i++; + if ((sscanf(line, "{%d}{}%6lf", &frame, &fps) == 2 || + sscanf(line, "{%d}{%*d}%6lf", &frame, &fps) == 2) + && frame <= 1 && fps > 3 && fps < 100) + pts_info = av_d2q(fps, 100000); + if (!st->codec->extradata && sscanf(line, "{DEFAULT}{}%c", &c) == 1) { + st->codec->extradata = av_strdup(line + 11); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + st->codec->extradata_size = strlen(st->codec->extradata) + 1; + continue; + } + } + sub = ff_subtitles_queue_insert(µdvd->q, line, len, 0); + if (!sub) + return AVERROR(ENOMEM); + sub->pos = pos; + sub->pts = get_pts(sub->data); + sub->duration = get_duration(sub->data); + } + ff_subtitles_queue_finalize(µdvd->q); + avpriv_set_pts_info(st, 64, pts_info.den, pts_info.num); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = CODEC_ID_MICRODVD; + return 0; +} + static int microdvd_read_packet(AVFormatContext *s, AVPacket *pkt) { MicroDVDContext *microdvd = s->priv_data; - char buffer[MAX_LINESIZE]; - int64_t pos = avio_tell(s->pb); - int i, len = 0, res = AVERROR_EOF; + return ff_subtitles_queue_read_packet(µdvd->q, pkt); +} - // last packet has its duration set but couldn't be raised earlier - if (microdvd->last_pkt_ready) { - *pkt = microdvd->last_pkt; - microdvd->last_pkt_ready = 0; - return 0; - } - - for (i=0; ilines); i++) { - if (microdvd->lines[i][0]) { - strcpy(buffer, microdvd->lines[i]); - pos = microdvd->pos[i]; - len = strlen(buffer); - microdvd->lines[i][0] = 0; - break; - } - } - if (!len) - len = ff_get_line(s->pb, buffer, sizeof(buffer)); - - if (microdvd->last_pkt.duration == -1 && !buffer[0]) { - // if the previous subtitle line had no duration, last until the end of - // the presentation - microdvd->last_pkt.duration = 0; - *pkt = microdvd->last_pkt; - pkt->duration = -1; - res = 0; - } else if (buffer[0] && !(res = av_new_packet(pkt, len))) { - memcpy(pkt->data, buffer, len); - pkt->flags |= AV_PKT_FLAG_KEY; - pkt->pos = pos; - pkt->pts = pkt->dts = get_pts(buffer); - - if (pkt->pts != AV_NOPTS_VALUE) { - pkt->duration = get_duration(buffer); - if (microdvd->last_pkt.duration == -1) { - // previous packet wasn't raised because it was lacking the - // duration info, so set its duration with the new packet pts - // and raise it - AVPacket tmp_pkt; - - tmp_pkt = microdvd->last_pkt; - tmp_pkt.duration = pkt->pts - tmp_pkt.pts; - microdvd->last_pkt = *pkt; - microdvd->last_pkt_ready = pkt->duration != -1; - *pkt = tmp_pkt; - } else if (pkt->duration == -1) { - // no packet without duration queued, and current one is - // lacking the duration info, we need to parse another subtitle - // event. - microdvd->last_pkt = *pkt; - res = AVERROR(EAGAIN); - } - } - } - return res; +static int microdvd_read_close(AVFormatContext *s) +{ + MicroDVDContext *microdvd = s->priv_data; + ff_subtitles_queue_clean(µdvd->q); + return 0; } AVInputFormat ff_microdvd_demuxer = { @@ -172,5 +140,6 @@ AVInputFormat ff_microdvd_demuxer = { .read_probe = microdvd_probe, .read_header = microdvd_read_header, .read_packet = microdvd_read_packet, + .read_close = microdvd_read_close, .flags = AVFMT_GENERIC_INDEX, };