Coverage Report

Created: 2026-04-01 07:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavformat/apvdec.c
Line
Count
Source
1
/*
2
 * This file is part of FFmpeg.
3
 *
4
 * FFmpeg is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8
 *
9
 * FFmpeg is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with FFmpeg; if not, write to the Free Software
16
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
 */
18
19
#include "libavutil/opt.h"
20
21
#include "libavcodec/apv.h"
22
#include "libavcodec/bytestream.h"
23
24
#include "avformat.h"
25
#include "avio_internal.h"
26
#include "demux.h"
27
#include "internal.h"
28
29
typedef struct APVDemuxerContext {
30
    const AVClass *class;     /**< Class for private options. */
31
    AVRational framerate;    /**< AVRational describing framerate, set by a private option. */
32
} APVDemuxerContext;
33
34
typedef struct APVHeaderInfo {
35
    uint8_t  pbu_type;
36
    uint16_t group_id;
37
38
    uint8_t  profile_idc;
39
    uint8_t  level_idc;
40
    uint8_t  band_idc;
41
42
    int      frame_width;
43
    int      frame_height;
44
45
    uint8_t  bit_depth_minus8;
46
} APVHeaderInfo;
47
48
static int apv_extract_header_info(GetByteContext *gbc)
49
1.16k
{
50
1.16k
    APVHeaderInfo header, *info = &header;
51
1.16k
    int zero, byte;
52
53
1.16k
    info->pbu_type = bytestream2_get_byte(gbc);
54
1.16k
    info->group_id = bytestream2_get_be16(gbc);
55
56
1.16k
    zero = bytestream2_get_byte(gbc);
57
1.16k
    if (zero != 0)
58
121
        return AVERROR_INVALIDDATA;
59
60
1.04k
    if (info->pbu_type == APV_PBU_ACCESS_UNIT_INFORMATION) {
61
611
        unsigned int num_frames = bytestream2_get_be16(gbc);
62
63
611
        if (num_frames < 1)
64
77
            return AVERROR_INVALIDDATA;
65
66
534
        info->pbu_type = bytestream2_get_byte(gbc);
67
534
        if (info->pbu_type != APV_PBU_PRIMARY_FRAME &&
68
472
            info->pbu_type != APV_PBU_NON_PRIMARY_FRAME &&
69
269
            (info->pbu_type < APV_PBU_PREVIEW_FRAME || info->pbu_type > APV_PBU_ALPHA_FRAME))
70
153
            return AVERROR_INVALIDDATA;
71
72
381
        bytestream2_skip(gbc, 2); // group_id
73
381
        zero = bytestream2_get_byte(gbc);
74
381
        if (zero != 0)
75
161
            return AVERROR_INVALIDDATA;
76
437
    } else if (info->pbu_type != APV_PBU_PRIMARY_FRAME)
77
85
        return AVERROR_INVALIDDATA;
78
79
572
    info->profile_idc = bytestream2_get_byte(gbc);
80
572
    info->level_idc   = bytestream2_get_byte(gbc);
81
82
572
    byte = bytestream2_get_byte(gbc);
83
572
    info->band_idc = byte >> 3;
84
572
    zero = byte & 7;
85
572
    if (zero != 0)
86
69
        return AVERROR_INVALIDDATA;
87
88
503
    info->frame_width  = bytestream2_get_be24(gbc);
89
503
    info->frame_height = bytestream2_get_be24(gbc);
90
503
    if (info->frame_width  < 1 || info->frame_width  > 65536 ||
91
353
        info->frame_height < 1 || info->frame_height > 65536)
92
271
        return AVERROR_INVALIDDATA;
93
94
232
    byte = bytestream2_get_byte(gbc);
95
232
    info->bit_depth_minus8  = byte & 0xf;
96
97
232
    if (info->bit_depth_minus8 > 8) {
98
61
        return AVERROR_INVALIDDATA;
99
61
    }
100
171
    if (info->bit_depth_minus8 % 2) {
101
        // Odd bit depths are technically valid but not useful here.
102
39
        return AVERROR_INVALIDDATA;
103
39
    }
104
105
    // Ignore capture_time_distance.
106
132
    bytestream2_skip(gbc, 1);
107
108
132
    zero = bytestream2_get_byte(gbc);
109
132
    if (zero != 0)
110
62
        return AVERROR_INVALIDDATA;
111
112
70
    return 1;
113
132
}
114
115
static int apv_probe(const AVProbeData *p)
116
959k
{
117
959k
    GetByteContext gbc;
118
959k
    uint32_t au_size, signature, pbu_size;
119
959k
    int err;
120
121
959k
    if (p->buf_size < 28) {
122
        // Too small to fit an APV header.
123
196k
        return 0;
124
196k
    }
125
126
762k
    bytestream2_init(&gbc, p->buf, p->buf_size);
127
128
762k
    au_size = bytestream2_get_be32(&gbc);
129
762k
    if (au_size < 24) {
130
        // Too small.
131
49.2k
        return 0;
132
49.2k
    }
133
713k
    signature = bytestream2_get_be32(&gbc);
134
713k
    if (signature != APV_SIGNATURE) {
135
        // Signature is mandatory.
136
712k
        return 0;
137
712k
    }
138
1.22k
    pbu_size = bytestream2_get_be32(&gbc);
139
1.22k
    if (pbu_size < 16) {
140
        // Too small.
141
51
        return 0;
142
51
    }
143
144
1.16k
    err = apv_extract_header_info(&gbc);
145
1.16k
    if (err < 0) {
146
        // Header does not look like APV.
147
1.09k
        return 0;
148
1.09k
    }
149
70
    return AVPROBE_SCORE_MAX;
150
1.16k
}
151
152
static int apv_read_header(AVFormatContext *s)
153
2.13k
{
154
2.13k
    APVDemuxerContext *apv = s->priv_data;
155
2.13k
    AVStream *st;
156
2.13k
    GetByteContext gbc;
157
2.13k
    uint8_t buffer[12];
158
2.13k
    uint32_t au_size, signature, pbu_size;
159
2.13k
    int err, size;
160
161
2.13k
    err = ffio_ensure_seekback(s->pb, sizeof(buffer));
162
2.13k
    if (err < 0)
163
0
        return err;
164
2.13k
    size = ffio_read_size(s->pb, buffer, sizeof(buffer));
165
2.13k
    if (size < 0)
166
136
        return size;
167
168
1.99k
    bytestream2_init(&gbc, buffer, sizeof(buffer));
169
170
1.99k
    au_size = bytestream2_get_be32(&gbc);
171
1.99k
    if (au_size < 24) {
172
        // Too small.
173
10
        return AVERROR_INVALIDDATA;
174
10
    }
175
1.98k
    signature = bytestream2_get_be32(&gbc);
176
1.98k
    if (signature != APV_SIGNATURE) {
177
        // Signature is mandatory.
178
80
        return AVERROR_INVALIDDATA;
179
80
    }
180
1.90k
    pbu_size = bytestream2_get_be32(&gbc);
181
1.90k
    if (pbu_size < 16) {
182
        // Too small.
183
5
        return AVERROR_INVALIDDATA;
184
5
    }
185
186
1.89k
    st = avformat_new_stream(s, NULL);
187
1.89k
    if (!st)
188
0
        return AVERROR(ENOMEM);
189
190
1.89k
    st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
191
1.89k
    st->codecpar->codec_id   = AV_CODEC_ID_APV;
192
193
1.89k
    ffstream(st)->need_parsing = AVSTREAM_PARSE_HEADERS;
194
1.89k
    st->avg_frame_rate = apv->framerate;
195
1.89k
    avpriv_set_pts_info(st, 64, apv->framerate.den, apv->framerate.num);
196
197
1.89k
    avio_seek(s->pb, -size, SEEK_CUR);
198
199
1.89k
    return 0;
200
1.89k
}
201
202
static int apv_read_packet(AVFormatContext *s, AVPacket *pkt)
203
130k
{
204
130k
    uint32_t au_size, signature;
205
130k
    int ret;
206
207
130k
    au_size = avio_rb32(s->pb);
208
130k
    if (au_size == 0 && avio_feof(s->pb))
209
2.82k
        return AVERROR_EOF;
210
127k
    if (au_size < 24 || au_size > 1 << 26) {
211
328
        av_log(s, AV_LOG_ERROR,
212
328
               "APV AU has invalid size: %"PRIu32"\n", au_size);
213
328
        return AVERROR_INVALIDDATA;
214
328
    }
215
216
127k
    ret = av_get_packet(s->pb, pkt, au_size);
217
127k
    if (ret < 0)
218
73
        return ret;
219
127k
    pkt->pos  -= 4;
220
127k
    pkt->flags = AV_PKT_FLAG_KEY;
221
222
127k
    signature = AV_RB32(pkt->data);
223
127k
    if (signature != APV_SIGNATURE) {
224
340
        av_log(s, AV_LOG_ERROR, "APV AU has invalid signature.\n");
225
340
        return AVERROR_INVALIDDATA;
226
340
    }
227
228
127k
    return 0;
229
127k
}
230
231
#define OFFSET(x) offsetof(APVDemuxerContext, x)
232
#define DEC AV_OPT_FLAG_DECODING_PARAM
233
static const AVOption apv_options[] = {
234
    { "framerate", "set frame rate", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, { .str = "30" }, 0, INT_MAX, DEC },
235
    { NULL },
236
};
237
238
static const AVClass apv_demuxer_class = {
239
    .class_name = "apv demuxer",
240
    .item_name  = av_default_item_name,
241
    .option     = apv_options,
242
    .version    = LIBAVUTIL_VERSION_INT,
243
};
244
245
const FFInputFormat ff_apv_demuxer = {
246
    .p.name         = "apv",
247
    .p.long_name    = NULL_IF_CONFIG_SMALL("APV raw bitstream"),
248
    .p.extensions   = "apv",
249
    .p.flags        = AVFMT_GENERIC_INDEX | AVFMT_NOTIMESTAMPS,
250
    .p.priv_class   = &apv_demuxer_class,
251
    .priv_data_size = sizeof(APVDemuxerContext),
252
    .read_probe     = apv_probe,
253
    .read_header    = apv_read_header,
254
    .read_packet    = apv_read_packet,
255
};