FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/apvdec.c
Date: 2025-05-09 16:55:38
Exec Total Coverage
Lines: 73 106 68.9%
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 "libavcodec/apv.h"
20 #include "libavcodec/bytestream.h"
21
22 #include "avformat.h"
23 #include "avio_internal.h"
24 #include "demux.h"
25 #include "internal.h"
26
27
28 typedef struct APVHeaderInfo {
29 uint8_t pbu_type;
30 uint16_t group_id;
31
32 uint8_t profile_idc;
33 uint8_t level_idc;
34 uint8_t band_idc;
35
36 int frame_width;
37 int frame_height;
38
39 uint8_t bit_depth_minus8;
40 } APVHeaderInfo;
41
42 2 static int apv_extract_header_info(GetByteContext *gbc)
43 {
44 2 APVHeaderInfo header, *info = &header;
45 int zero, byte;
46
47 2 info->pbu_type = bytestream2_get_byte(gbc);
48 2 info->group_id = bytestream2_get_be16(gbc);
49
50 2 zero = bytestream2_get_byte(gbc);
51
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (zero != 0)
52 return AVERROR_INVALIDDATA;
53
54
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (info->pbu_type == APV_PBU_ACCESS_UNIT_INFORMATION) {
55 unsigned int num_frames = bytestream2_get_be16(gbc);
56
57 if (num_frames < 1)
58 return AVERROR_INVALIDDATA;
59
60 info->pbu_type = bytestream2_get_byte(gbc);
61 if (info->pbu_type != APV_PBU_PRIMARY_FRAME &&
62 info->pbu_type != APV_PBU_NON_PRIMARY_FRAME &&
63 (info->pbu_type < APV_PBU_PREVIEW_FRAME || info->pbu_type > APV_PBU_ALPHA_FRAME))
64 return AVERROR_INVALIDDATA;
65
66 bytestream2_skip(gbc, 2); // group_id
67 zero = bytestream2_get_byte(gbc);
68 if (zero != 0)
69 return AVERROR_INVALIDDATA;
70
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 } else if (info->pbu_type != APV_PBU_PRIMARY_FRAME)
71 return AVERROR_INVALIDDATA;
72
73 2 info->profile_idc = bytestream2_get_byte(gbc);
74 2 info->level_idc = bytestream2_get_byte(gbc);
75
76 2 byte = bytestream2_get_byte(gbc);
77 2 info->band_idc = byte >> 3;
78 2 zero = byte & 7;
79
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (zero != 0)
80 return AVERROR_INVALIDDATA;
81
82 2 info->frame_width = bytestream2_get_be24(gbc);
83 2 info->frame_height = bytestream2_get_be24(gbc);
84
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if (info->frame_width < 1 || info->frame_width > 65536 ||
85
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 info->frame_height < 1 || info->frame_height > 65536)
86 return AVERROR_INVALIDDATA;
87
88 2 byte = bytestream2_get_byte(gbc);
89 2 info->bit_depth_minus8 = byte & 0xf;
90
91
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (info->bit_depth_minus8 > 8) {
92 return AVERROR_INVALIDDATA;
93 }
94
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (info->bit_depth_minus8 % 2) {
95 // Odd bit depths are technically valid but not useful here.
96 return AVERROR_INVALIDDATA;
97 }
98
99 // Ignore capture_time_distance.
100 2 bytestream2_skip(gbc, 1);
101
102 2 zero = bytestream2_get_byte(gbc);
103
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (zero != 0)
104 return AVERROR_INVALIDDATA;
105
106 2 return 1;
107 }
108
109 7215 static int apv_probe(const AVProbeData *p)
110 {
111 GetByteContext gbc;
112 uint32_t au_size, signature, pbu_size;
113 int err;
114
115
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7215 times.
7215 if (p->buf_size < 28) {
116 // Too small to fit an APV header.
117 return 0;
118 }
119
120 7215 bytestream2_init(&gbc, p->buf, p->buf_size);
121
122 7215 au_size = bytestream2_get_be32(&gbc);
123
2/2
✓ Branch 0 taken 778 times.
✓ Branch 1 taken 6437 times.
7215 if (au_size < 24) {
124 // Too small.
125 778 return 0;
126 }
127 6437 signature = bytestream2_get_be32(&gbc);
128
2/2
✓ Branch 0 taken 6435 times.
✓ Branch 1 taken 2 times.
6437 if (signature != APV_SIGNATURE) {
129 // Signature is mandatory.
130 6435 return 0;
131 }
132 2 pbu_size = bytestream2_get_be32(&gbc);
133
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (pbu_size < 16) {
134 // Too small.
135 return 0;
136 }
137
138 2 err = apv_extract_header_info(&gbc);
139
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (err < 0) {
140 // Header does not look like APV.
141 return 0;
142 }
143 2 return AVPROBE_SCORE_MAX;
144 }
145
146 2 static int apv_read_header(AVFormatContext *s)
147 {
148 AVStream *st;
149 GetByteContext gbc;
150 uint8_t buffer[12];
151 uint32_t au_size, signature, pbu_size;
152 int err, size;
153
154 2 err = ffio_ensure_seekback(s->pb, sizeof(buffer));
155
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (err < 0)
156 return err;
157 2 size = ffio_read_size(s->pb, buffer, sizeof(buffer));
158
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (size < 0)
159 return size;
160
161 2 bytestream2_init(&gbc, buffer, sizeof(buffer));
162
163 2 au_size = bytestream2_get_be32(&gbc);
164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (au_size < 24) {
165 // Too small.
166 return AVERROR_INVALIDDATA;
167 }
168 2 signature = bytestream2_get_be32(&gbc);
169
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (signature != APV_SIGNATURE) {
170 // Signature is mandatory.
171 return AVERROR_INVALIDDATA;
172 }
173 2 pbu_size = bytestream2_get_be32(&gbc);
174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (pbu_size < 16) {
175 // Too small.
176 return AVERROR_INVALIDDATA;
177 }
178
179 2 st = avformat_new_stream(s, NULL);
180
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!st)
181 return AVERROR(ENOMEM);
182
183 2 st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
184 2 st->codecpar->codec_id = AV_CODEC_ID_APV;
185
186 2 ffstream(st)->need_parsing = AVSTREAM_PARSE_HEADERS;
187 2 st->avg_frame_rate = (AVRational){ 30, 1 };
188 2 avpriv_set_pts_info(st, 64, 1, 30);
189
190 2 avio_seek(s->pb, -size, SEEK_CUR);
191
192 2 return 0;
193 }
194
195 8 static int apv_read_packet(AVFormatContext *s, AVPacket *pkt)
196 {
197 uint32_t au_size, signature;
198 int ret;
199
200 8 au_size = avio_rb32(s->pb);
201
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
8 if (au_size == 0 && avio_feof(s->pb))
202 2 return AVERROR_EOF;
203
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 if (au_size < 24 || au_size > 1 << 24) {
204 av_log(s, AV_LOG_ERROR,
205 "APV AU has invalid size: %"PRIu32"\n", au_size);
206 return AVERROR_INVALIDDATA;
207 }
208
209 6 ret = av_get_packet(s->pb, pkt, au_size);
210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (ret < 0)
211 return ret;
212 6 pkt->pos -= 4;
213 6 pkt->flags = AV_PKT_FLAG_KEY;
214
215 6 signature = AV_RB32(pkt->data);
216
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (signature != APV_SIGNATURE) {
217 av_log(s, AV_LOG_ERROR, "APV AU has invalid signature.\n");
218 return AVERROR_INVALIDDATA;
219 }
220
221 6 return 0;
222 }
223
224 const FFInputFormat ff_apv_demuxer = {
225 .p.name = "apv",
226 .p.long_name = NULL_IF_CONFIG_SMALL("APV raw bitstream"),
227 .p.extensions = "apv",
228 .p.flags = AVFMT_GENERIC_INDEX | AVFMT_NOTIMESTAMPS,
229 .read_probe = apv_probe,
230 .read_header = apv_read_header,
231 .read_packet = apv_read_packet,
232 };
233