+ av_free(buf);
+ oggstream->page_count--;
+ return 0;
+}
+
+static int64_t ogg_granule_to_timestamp(OGGStreamContext *oggstream, int64_t granule)
+{
+ if (oggstream->kfgshift)
+ return (granule>>oggstream->kfgshift) +
+ (granule & ((1<<oggstream->kfgshift)-1));
+ else
+ return granule;
+}
+
+static int ogg_compare_granule(AVFormatContext *s, OGGPage *next, OGGPage *page)
+{
+ AVStream *st2 = s->streams[next->stream_index];
+ AVStream *st = s->streams[page->stream_index];
+ int64_t next_granule, cur_granule;
+
+ if (next->granule == -1 || page->granule == -1)
+ return 0;
+
+ next_granule = av_rescale_q(ogg_granule_to_timestamp(st2->priv_data, next->granule),
+ st2->time_base, AV_TIME_BASE_Q);
+ cur_granule = av_rescale_q(ogg_granule_to_timestamp(st->priv_data, page->granule),
+ st ->time_base, AV_TIME_BASE_Q);
+ return next_granule > cur_granule;
+}
+
+static int ogg_reset_cur_page(OGGStreamContext *oggstream)
+{
+ oggstream->page.granule = -1;
+ oggstream->page.flags = 0;
+ oggstream->page.segments_count = 0;
+ oggstream->page.size = 0;
+ return 0;
+}
+
+static int ogg_buffer_page(AVFormatContext *s, OGGStreamContext *oggstream)
+{
+ OGGContext *ogg = s->priv_data;
+ OGGPageList **p = &ogg->page_list;
+ OGGPageList *l = av_mallocz(sizeof(*l));
+
+ if (!l)
+ return AVERROR(ENOMEM);
+ l->page = oggstream->page;
+
+ oggstream->page_count++;
+ ogg_reset_cur_page(oggstream);
+
+ while (*p) {
+ if (ogg_compare_granule(s, &(*p)->page, &l->page))
+ break;
+ p = &(*p)->next;
+ }
+ l->next = *p;
+ *p = l;
+
+ return 0;
+}
+
+static int ogg_buffer_data(AVFormatContext *s, AVStream *st,
+ uint8_t *data, unsigned size, int64_t granule)
+{
+ OGGStreamContext *oggstream = st->priv_data;
+ int total_segments = size / 255 + 1;
+ uint8_t *p = data;
+ int i, segments, len, flush = 0;
+
+ // Handles VFR by flushing page because this frame needs to have a timestamp
+ if (st->codec->codec_id == CODEC_ID_THEORA &&
+ ogg_granule_to_timestamp(oggstream, granule) >
+ ogg_granule_to_timestamp(oggstream, oggstream->last_granule) + 1) {
+ if (oggstream->page.granule != -1)
+ ogg_buffer_page(s, oggstream);
+ flush = 1;
+ }
+
+ for (i = 0; i < total_segments; ) {
+ OGGPage *page = &oggstream->page;
+
+ segments = FFMIN(total_segments - i, 255 - page->segments_count);
+
+ if (i && !page->segments_count)
+ page->flags |= 1; // continued packet
+
+ memset(page->segments+page->segments_count, 255, segments - 1);
+ page->segments_count += segments - 1;
+
+ len = FFMIN(size, segments*255);
+ page->segments[page->segments_count++] = len - (segments-1)*255;
+ memcpy(page->data+page->size, p, len);
+ p += len;
+ size -= len;
+ i += segments;
+ page->size += len;
+
+ if (i == total_segments)
+ page->granule = granule;
+
+ if (page->segments_count == 255) {
+ ogg_buffer_page(s, oggstream);
+ }
+ }
+
+ if (flush && oggstream->page.granule != -1)
+ ogg_buffer_page(s, oggstream);
+
+ return 0;