| @@ -1,6 +1,7 @@ | |||
| /* | |||
| * MicroDVD subtitle demuxer | |||
| * Copyright (c) 2010 Aurelien Jacobs <aurel@gnuage.org> | |||
| * Copyright (c) 2012 Clément Bœsch <ubitux@gmail.com> | |||
| * | |||
| * 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; i<FF_ARRAY_ELEMS(microdvd->lines); 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_packet(AVFormatContext *s, AVPacket *pkt) | |||
| static int microdvd_read_header(AVFormatContext *s) | |||
| { | |||
| AVRational pts_info = (AVRational){ 2997, 125 }; /* default: 23.976 fps */ | |||
| MicroDVDContext *microdvd = s->priv_data; | |||
| char buffer[MAX_LINESIZE]; | |||
| int64_t pos = avio_tell(s->pb); | |||
| int i, len = 0, res = AVERROR_EOF; | |||
| // 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; | |||
| } | |||
| 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)); | |||
| for (i=0; i<FF_ARRAY_ELEMS(microdvd->lines); i++) { | |||
| if (microdvd->lines[i][0]) { | |||
| strcpy(buffer, microdvd->lines[i]); | |||
| pos = microdvd->pos[i]; | |||
| len = strlen(buffer); | |||
| microdvd->lines[i][0] = 0; | |||
| if (!len) | |||
| 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); | |||
| 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); | |||
| } | |||
| return res; | |||
| 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; | |||
| return ff_subtitles_queue_read_packet(µdvd->q, pkt); | |||
| } | |||
| 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, | |||
| }; | |||