FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/apvdec.c
Date: 2025-06-30 14:39:11
Exec Total Coverage
Lines: 74 107 69.2%
Functions: 4 4 100.0%
Branches: 31 68 45.6%

Line Branch Exec 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 3 static int apv_extract_header_info(GetByteContext *gbc)
49 {
50 3 APVHeaderInfo header, *info = &header;
51 int zero, byte;
52
53 3 info->pbu_type = bytestream2_get_byte(gbc);
54 3 info->group_id = bytestream2_get_be16(gbc);
55
56 3 zero = bytestream2_get_byte(gbc);
57
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (zero != 0)
58 return AVERROR_INVALIDDATA;
59
60
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (info->pbu_type == APV_PBU_ACCESS_UNIT_INFORMATION) {
61 unsigned int num_frames = bytestream2_get_be16(gbc);
62
63 if (num_frames < 1)
64 return AVERROR_INVALIDDATA;
65
66 info->pbu_type = bytestream2_get_byte(gbc);
67 if (info->pbu_type != APV_PBU_PRIMARY_FRAME &&
68 info->pbu_type != APV_PBU_NON_PRIMARY_FRAME &&
69 (info->pbu_type < APV_PBU_PREVIEW_FRAME || info->pbu_type > APV_PBU_ALPHA_FRAME))
70 return AVERROR_INVALIDDATA;
71
72 bytestream2_skip(gbc, 2); // group_id
73 zero = bytestream2_get_byte(gbc);
74 if (zero != 0)
75 return AVERROR_INVALIDDATA;
76
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 } else if (info->pbu_type != APV_PBU_PRIMARY_FRAME)
77 return AVERROR_INVALIDDATA;
78
79 3 info->profile_idc = bytestream2_get_byte(gbc);
80 3 info->level_idc = bytestream2_get_byte(gbc);
81
82 3 byte = bytestream2_get_byte(gbc);
83 3 info->band_idc = byte >> 3;
84 3 zero = byte & 7;
85
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (zero != 0)
86 return AVERROR_INVALIDDATA;
87
88 3 info->frame_width = bytestream2_get_be24(gbc);
89 3 info->frame_height = bytestream2_get_be24(gbc);
90
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 if (info->frame_width < 1 || info->frame_width > 65536 ||
91
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 info->frame_height < 1 || info->frame_height > 65536)
92 return AVERROR_INVALIDDATA;
93
94 3 byte = bytestream2_get_byte(gbc);
95 3 info->bit_depth_minus8 = byte & 0xf;
96
97
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (info->bit_depth_minus8 > 8) {
98 return AVERROR_INVALIDDATA;
99 }
100
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (info->bit_depth_minus8 % 2) {
101 // Odd bit depths are technically valid but not useful here.
102 return AVERROR_INVALIDDATA;
103 }
104
105 // Ignore capture_time_distance.
106 3 bytestream2_skip(gbc, 1);
107
108 3 zero = bytestream2_get_byte(gbc);
109
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (zero != 0)
110 return AVERROR_INVALIDDATA;
111
112 3 return 1;
113 }
114
115 7236 static int apv_probe(const AVProbeData *p)
116 {
117 GetByteContext gbc;
118 uint32_t au_size, signature, pbu_size;
119 int err;
120
121
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7236 times.
7236 if (p->buf_size < 28) {
122 // Too small to fit an APV header.
123 return 0;
124 }
125
126 7236 bytestream2_init(&gbc, p->buf, p->buf_size);
127
128 7236 au_size = bytestream2_get_be32(&gbc);
129
2/2
✓ Branch 0 taken 781 times.
✓ Branch 1 taken 6455 times.
7236 if (au_size < 24) {
130 // Too small.
131 781 return 0;
132 }
133 6455 signature = bytestream2_get_be32(&gbc);
134
2/2
✓ Branch 0 taken 6452 times.
✓ Branch 1 taken 3 times.
6455 if (signature != APV_SIGNATURE) {
135 // Signature is mandatory.
136 6452 return 0;
137 }
138 3 pbu_size = bytestream2_get_be32(&gbc);
139
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (pbu_size < 16) {
140 // Too small.
141 return 0;
142 }
143
144 3 err = apv_extract_header_info(&gbc);
145
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (err < 0) {
146 // Header does not look like APV.
147 return 0;
148 }
149 3 return AVPROBE_SCORE_MAX;
150 }
151
152 3 static int apv_read_header(AVFormatContext *s)
153 {
154 3 APVDemuxerContext *apv = s->priv_data;
155 AVStream *st;
156 GetByteContext gbc;
157 uint8_t buffer[12];
158 uint32_t au_size, signature, pbu_size;
159 int err, size;
160
161 3 err = ffio_ensure_seekback(s->pb, sizeof(buffer));
162
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (err < 0)
163 return err;
164 3 size = ffio_read_size(s->pb, buffer, sizeof(buffer));
165
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (size < 0)
166 return size;
167
168 3 bytestream2_init(&gbc, buffer, sizeof(buffer));
169
170 3 au_size = bytestream2_get_be32(&gbc);
171
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (au_size < 24) {
172 // Too small.
173 return AVERROR_INVALIDDATA;
174 }
175 3 signature = bytestream2_get_be32(&gbc);
176
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (signature != APV_SIGNATURE) {
177 // Signature is mandatory.
178 return AVERROR_INVALIDDATA;
179 }
180 3 pbu_size = bytestream2_get_be32(&gbc);
181
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (pbu_size < 16) {
182 // Too small.
183 return AVERROR_INVALIDDATA;
184 }
185
186 3 st = avformat_new_stream(s, NULL);
187
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!st)
188 return AVERROR(ENOMEM);
189
190 3 st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
191 3 st->codecpar->codec_id = AV_CODEC_ID_APV;
192
193 3 ffstream(st)->need_parsing = AVSTREAM_PARSE_HEADERS;
194 3 st->avg_frame_rate = apv->framerate;
195 3 avpriv_set_pts_info(st, 64, apv->framerate.den, apv->framerate.num);
196
197 3 avio_seek(s->pb, -size, SEEK_CUR);
198
199 3 return 0;
200 }
201
202 12 static int apv_read_packet(AVFormatContext *s, AVPacket *pkt)
203 {
204 uint32_t au_size, signature;
205 int ret;
206
207 12 au_size = avio_rb32(s->pb);
208
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 9 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
12 if (au_size == 0 && avio_feof(s->pb))
209 3 return AVERROR_EOF;
210
2/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
9 if (au_size < 24 || au_size > 1 << 24) {
211 av_log(s, AV_LOG_ERROR,
212 "APV AU has invalid size: %"PRIu32"\n", au_size);
213 return AVERROR_INVALIDDATA;
214 }
215
216 9 ret = av_get_packet(s->pb, pkt, au_size);
217
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (ret < 0)
218 return ret;
219 9 pkt->pos -= 4;
220 9 pkt->flags = AV_PKT_FLAG_KEY;
221
222 9 signature = AV_RB32(pkt->data);
223
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (signature != APV_SIGNATURE) {
224 av_log(s, AV_LOG_ERROR, "APV AU has invalid signature.\n");
225 return AVERROR_INVALIDDATA;
226 }
227
228 9 return 0;
229 }
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 };
256