static int ogg_read_page(AVFormatContext *s, int *sid)
{
    AVIOContext *bc = s->pb;
    struct ogg *ogg = s->priv_data;
    struct ogg_stream *os;
    int ret, i = 0;
    int flags, nsegs;
    uint64_t gp;
    uint32_t serial;
    int size, idx;
    uint8_t sync[4];
    int sp = 0;

    ret = avio_read(bc, sync, 4);
    if (ret < 4)
        return ret < 0 ? ret : AVERROR_EOF;

    do {
        int c;

        if (sync[sp & 3] == 'O' &&
            sync[(sp + 1) & 3] == 'g' &&
            sync[(sp + 2) & 3] == 'g' && sync[(sp + 3) & 3] == 'S')
            break;

        if(!i && (bc->seekable & AVIO_SEEKABLE_NORMAL) && ogg->page_pos > 0) {
            memset(sync, 0, 4);
            avio_seek(bc, ogg->page_pos+4, SEEK_SET);
            ogg->page_pos = -1;
        }

        c = avio_r8(bc);

        if (avio_feof(bc))
            return AVERROR_EOF;

        sync[sp++ & 3] = c;
    } while (i++ < MAX_PAGE_SIZE);

    if (i >= MAX_PAGE_SIZE) {
        av_log(s, AV_LOG_INFO, "cannot find sync word\n");
        return AVERROR_INVALIDDATA;
    }

    if (avio_r8(bc) != 0) {      /* version */
        av_log (s, AV_LOG_ERROR, "ogg page, unsupported version\n");
        return AVERROR_INVALIDDATA;
    }

    flags  = avio_r8(bc);
    gp     = avio_rl64(bc);
    serial = avio_rl32(bc);
    avio_skip(bc, 8); /* seq, crc */
    nsegs  = avio_r8(bc);

    idx = ogg_find_stream(ogg, serial);
    if (idx < 0) {
        if (data_packets_seen(ogg))
            idx = ogg_replace_stream(s, serial, nsegs);
        else
            idx = ogg_new_stream(s, serial);

        if (idx < 0) {
            av_log(s, AV_LOG_ERROR, "failed to create or replace stream\n");
            return idx;
        }
    }

    os = ogg->streams + idx;
    ogg->page_pos =
    os->page_pos = avio_tell(bc) - 27;

    if (os->psize > 0) {
        ret = ogg_new_buf(ogg, idx);
        if (ret < 0)
            return ret;
    }

    ret = avio_read(bc, os->segments, nsegs);
    if (ret < nsegs)
        return ret < 0 ? ret : AVERROR_EOF;

    os->nsegs = nsegs;
    os->segp  = 0;

    size = 0;
    for (i = 0; i < nsegs; i++)
        size += os->segments[i];

    if (!(flags & OGG_FLAG_BOS))
        os->got_data = 1;

    if (flags & OGG_FLAG_CONT || os->incomplete) {
        if (!os->psize) {
            // If this is the very first segment we started
            // playback in the middle of a continuation packet.
            // Discard it since we missed the start of it.
            while (os->segp < os->nsegs) {
                int seg = os->segments[os->segp++];
                os->pstart += seg;
                if (seg < 255)
                    break;
            }
            os->sync_pos = os->page_pos;
        }
    } else {
        os->psize    = 0;
        os->sync_pos = os->page_pos;
    }

    if (os->bufsize - os->bufpos < size) {
        uint8_t *nb = av_malloc((os->bufsize *= 2) + AV_INPUT_BUFFER_PADDING_SIZE);
        if (!nb)
            return AVERROR(ENOMEM);
        memcpy(nb, os->buf, os->bufpos);
        av_free(os->buf);
        os->buf = nb;
    }

    ret = avio_read(bc, os->buf + os->bufpos, size);
    if (ret < size)
        return ret < 0 ? ret : AVERROR_EOF;

    os->bufpos += size;
    os->granule = gp;
    os->flags   = flags;

    memset(os->buf + os->bufpos, 0, AV_INPUT_BUFFER_PADDING_SIZE);
    if (sid)
        *sid = idx;

    return 0;
}