/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 | | }; |