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 |