diff --git a/libavformat/oggdec.c b/libavformat/oggdec.c index 129268b390..75afc84099 100644 --- a/libavformat/oggdec.c +++ b/libavformat/oggdec.c @@ -116,7 +116,7 @@ ogg_reset (struct ogg * ogg) os->pstart = 0; os->psize = 0; os->granule = -1; - os->lastgp = -1; + os->lastpts = AV_NOPTS_VALUE; os->nsegs = 0; os->segp = 0; } @@ -288,7 +288,6 @@ ogg_read_page (AVFormatContext * s, int *str) if (get_buffer (bc, os->buf + os->bufpos, size) < size) return -1; - os->lastgp = os->granule; os->bufpos += size; os->granule = gp; os->flags = flags; @@ -303,7 +302,7 @@ static int ogg_packet (AVFormatContext * s, int *str, int *dstart, int *dsize) { struct ogg *ogg = s->priv_data; - int idx; + int idx, i; struct ogg_stream *os; int complete = 0; int segp = 0, psize = 0; @@ -393,6 +392,15 @@ ogg_packet (AVFormatContext * s, int *str, int *dstart, int *dsize) os->psize = 0; } + // determine whether there are more complete packets in this page + // if not, the page's granule will apply to this packet + os->page_end = 1; + for (i = os->segp; i < os->nsegs; i++) + if (os->segments[i] < 255) { + os->page_end = 0; + break; + } + os->seq++; if (os->segp == os->nsegs) ogg->curidx = -1; @@ -519,9 +527,20 @@ ogg_read_packet (AVFormatContext * s, AVPacket * pkt) return AVERROR(EIO); pkt->stream_index = idx; memcpy (pkt->data, os->buf + pstart, psize); - if (os->lastgp != -1LL){ - pkt->pts = ogg_gptopts (s, idx, os->lastgp); - os->lastgp = -1; + + if (os->lastpts != AV_NOPTS_VALUE) { + pkt->pts = os->lastpts; + os->lastpts = AV_NOPTS_VALUE; + } + if (os->page_end) { + if (os->granule != -1LL) { + if (os->codec && os->codec->granule_is_start) + pkt->pts = ogg_gptopts(s, idx, os->granule); + else + os->lastpts = ogg_gptopts(s, idx, os->granule); + os->granule = -1LL; + } else + av_log(s, AV_LOG_WARNING, "Packet is missing granule\n"); } pkt->flags = os->pflags; diff --git a/libavformat/oggdec.h b/libavformat/oggdec.h index cefde7e2fd..696233dd43 100644 --- a/libavformat/oggdec.h +++ b/libavformat/oggdec.h @@ -41,6 +41,11 @@ struct ogg_codec { int (*header)(AVFormatContext *, int); int (*packet)(AVFormatContext *, int); uint64_t (*gptopts)(AVFormatContext *, int, uint64_t); + /** + * 1 if granule is the start time of the associated packet. + * 0 if granule is the end time of the associated packet. + */ + int granule_is_start; }; struct ogg_stream { @@ -53,12 +58,14 @@ struct ogg_stream { unsigned int pduration; uint32_t serial; uint32_t seq; - uint64_t granule, lastgp; + uint64_t granule; + int64_t lastpts; int flags; const struct ogg_codec *codec; int header; int nsegs, segp; uint8_t segments[255]; + int page_end; ///< current packet is the last one completed in the page void *private; }; diff --git a/libavformat/oggparseogm.c b/libavformat/oggparseogm.c index f13f8c5242..beeb27d5bc 100644 --- a/libavformat/oggparseogm.c +++ b/libavformat/oggparseogm.c @@ -153,26 +153,30 @@ const struct ogg_codec ff_ogm_video_codec = { .magic = "\001video", .magicsize = 6, .header = ogm_header, - .packet = ogm_packet + .packet = ogm_packet, + .granule_is_start = 1, }; const struct ogg_codec ff_ogm_audio_codec = { .magic = "\001audio", .magicsize = 6, .header = ogm_header, - .packet = ogm_packet + .packet = ogm_packet, + .granule_is_start = 1, }; const struct ogg_codec ff_ogm_text_codec = { .magic = "\001text", .magicsize = 5, .header = ogm_header, - .packet = ogm_packet + .packet = ogm_packet, + .granule_is_start = 1, }; const struct ogg_codec ff_ogm_old_codec = { .magic = "\001Direct Show Samples embedded in Ogg", .magicsize = 35, .header = ogm_dshow_header, - .packet = ogm_packet + .packet = ogm_packet, + .granule_is_start = 1, }; diff --git a/libavformat/oggparsespeex.c b/libavformat/oggparsespeex.c index f6174749f2..14d2b38131 100644 --- a/libavformat/oggparsespeex.c +++ b/libavformat/oggparsespeex.c @@ -95,15 +95,16 @@ static int speex_packet(AVFormatContext *s, int idx) os->private = spxp; } - if (os->flags & OGG_FLAG_EOS && os->lastgp != -1 && os->granule > 0) { + if (os->flags & OGG_FLAG_EOS && os->lastpts != AV_NOPTS_VALUE && + os->granule > 0) { /* first packet of final page. we have to calculate the final packet duration here because it is the only place we know the next-to-last granule position. */ - spxp->final_packet_duration = os->granule - os->lastgp - + spxp->final_packet_duration = os->granule - os->lastpts - packet_size * (ogg_page_packets(os) - 1); } - if (!os->lastgp && os->granule > 0) + if (!os->lastpts && os->granule > 0) /* first packet */ os->pduration = os->granule - packet_size * (ogg_page_packets(os) - 1); else if (os->flags & OGG_FLAG_EOS && os->segp == os->nsegs &&