diff --git a/libavformat/microdvddec.c b/libavformat/microdvddec.c index 4055ee105a..5e37bc38a3 100644 --- a/libavformat/microdvddec.c +++ b/libavformat/microdvddec.c @@ -29,6 +29,8 @@ typedef struct { uint8_t lines[3][MAX_LINESIZE]; int64_t pos[3]; + AVPacket last_pkt; + int last_pkt_ready; } MicroDVDContext; @@ -96,7 +98,7 @@ static int get_duration(const char *buf) if (sscanf(buf, "{%d}{%d}", &frame_start, &frame_end) == 2) return frame_end - frame_start; - return 0; + return -1; } static int microdvd_read_packet(AVFormatContext *s, AVPacket *pkt) @@ -106,6 +108,13 @@ static int microdvd_read_packet(AVFormatContext *s, AVPacket *pkt) 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; + } + for (i=0; ilines); i++) { if (microdvd->lines[i][0]) { strcpy(buffer, microdvd->lines[i]); @@ -118,13 +127,40 @@ static int microdvd_read_packet(AVFormatContext *s, AVPacket *pkt) if (!len) len = ff_get_line(s->pb, buffer, sizeof(buffer)); - if (buffer[0] && !(res = av_new_packet(pkt, len))) { + 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) // TODO: handle "{}" duration + + 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; }