Coverage Report

Created: 2025-08-28 07:12

/src/ffmpeg/libavformat/oggparsevp8.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * On2 VP8 parser for Ogg
3
 * Copyright (C) 2013 James Almer
4
 *
5
 * This file is part of FFmpeg.
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
 */
21
22
#include "libavutil/intreadwrite.h"
23
24
#include "avformat.h"
25
#include "internal.h"
26
#include "oggdec.h"
27
28
1.34k
#define VP8_HEADER_SIZE 26
29
30
static int vp8_header(AVFormatContext *s, int idx)
31
1.58k
{
32
1.58k
    struct ogg *ogg = s->priv_data;
33
1.58k
    struct ogg_stream *os = ogg->streams + idx;
34
1.58k
    uint8_t *p = os->buf + os->pstart;
35
1.58k
    AVStream *st = s->streams[idx];
36
1.58k
    AVRational framerate;
37
38
1.58k
    if (os->psize < 7 || p[0] != 0x4f)
39
234
        return 0;
40
41
1.34k
    switch (p[5]){
42
1.34k
    case 0x01:
43
1.34k
        if (os->psize < VP8_HEADER_SIZE) {
44
0
            av_log(s, AV_LOG_ERROR, "Invalid OggVP8 header packet");
45
0
            return AVERROR_INVALIDDATA;
46
0
        }
47
48
1.34k
        if (p[6] != 1) {
49
13
            av_log(s, AV_LOG_WARNING,
50
13
                   "Unknown OggVP8 version %d.%d\n", p[6], p[7]);
51
13
            return AVERROR_INVALIDDATA;
52
13
        }
53
54
1.32k
        st->codecpar->width         = AV_RB16(p +  8);
55
1.32k
        st->codecpar->height        = AV_RB16(p + 10);
56
1.32k
        st->sample_aspect_ratio.num = AV_RB24(p + 12);
57
1.32k
        st->sample_aspect_ratio.den = AV_RB24(p + 15);
58
1.32k
        framerate.num               = AV_RB32(p + 18);
59
1.32k
        framerate.den               = AV_RB32(p + 22);
60
61
1.32k
        avpriv_set_pts_info(st, 64, framerate.den, framerate.num);
62
1.32k
        st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
63
1.32k
        st->codecpar->codec_id   = AV_CODEC_ID_VP8;
64
1.32k
        ffstream(st)->need_parsing = AVSTREAM_PARSE_HEADERS;
65
1.32k
        break;
66
0
    case 0x02:
67
0
        if (p[6] != 0x20)
68
0
            return AVERROR_INVALIDDATA;
69
0
        ff_vorbis_stream_comment(s, st, p + 7, os->psize - 7);
70
0
        break;
71
7
    default:
72
7
        av_log(s, AV_LOG_ERROR, "Unknown VP8 header type 0x%02X\n", p[5]);
73
7
        return AVERROR_INVALIDDATA;
74
1.34k
    }
75
76
1.32k
    return 1;
77
1.34k
}
78
79
static uint64_t vp8_gptopts(AVFormatContext *s, int idx,
80
                            uint64_t granule, int64_t *dts)
81
11.7k
{
82
11.7k
    struct ogg *ogg = s->priv_data;
83
11.7k
    struct ogg_stream *os = ogg->streams + idx;
84
85
11.7k
    int invcnt    = !((granule >> 30) & 3);
86
    // If page granule is that of an invisible vp8 frame, its pts will be
87
    // that of the end of the next visible frame. We subtract 1 for those
88
    // to prevent messing up pts calculations.
89
11.7k
    uint64_t pts  = (granule >> 32) - invcnt;
90
11.7k
    uint32_t dist = (granule >>  3) & 0x07ffffff;
91
92
11.7k
    if (!dist)
93
6.88k
        os->pflags |= AV_PKT_FLAG_KEY;
94
95
11.7k
    if (dts)
96
3.08k
        *dts = pts;
97
98
11.7k
    return pts;
99
11.7k
}
100
101
static int vp8_packet(AVFormatContext *s, int idx)
102
11.4k
{
103
11.4k
    struct ogg *ogg = s->priv_data;
104
11.4k
    struct ogg_stream *os = ogg->streams + idx;
105
11.4k
    uint8_t *p = os->buf + os->pstart;
106
107
11.4k
    if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) &&
108
11.4k
        !(os->flags & OGG_FLAG_EOS)) {
109
7.70k
        int seg;
110
7.70k
        int duration;
111
7.70k
        uint8_t *last_pkt = p;
112
7.70k
        uint8_t *next_pkt;
113
114
7.70k
        seg = os->segp;
115
7.70k
        duration = (last_pkt[0] >> 4) & 1;
116
7.70k
        next_pkt = last_pkt += os->psize;
117
44.5k
        for (; seg < os->nsegs; seg++) {
118
36.7k
            if (os->segments[seg] < 255) {
119
16.6k
                duration += (last_pkt[0] >> 4) & 1;
120
16.6k
                last_pkt  = next_pkt + os->segments[seg];
121
16.6k
            }
122
36.7k
            next_pkt += os->segments[seg];
123
36.7k
        }
124
7.70k
        os->lastpts =
125
7.70k
        os->lastdts = vp8_gptopts(s, idx, os->granule, NULL) - duration;
126
7.70k
        if(s->streams[idx]->start_time == AV_NOPTS_VALUE) {
127
198
            s->streams[idx]->start_time = os->lastpts;
128
198
            if (s->streams[idx]->duration && s->streams[idx]->duration != AV_NOPTS_VALUE)
129
41
                s->streams[idx]->duration -= s->streams[idx]->start_time;
130
198
        }
131
7.70k
    }
132
133
11.4k
    if (os->psize > 0)
134
4.13k
        os->pduration = (p[0] >> 4) & 1;
135
136
11.4k
    return 0;
137
11.4k
}
138
139
const struct ogg_codec ff_vp8_codec = {
140
    .magic     = "OVP80",
141
    .magicsize = 5,
142
    .header    = vp8_header,
143
    .packet    = vp8_packet,
144
    .gptopts   = vp8_gptopts,
145
    .nb_header = 1,
146
};