FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/bintext.c
Date: 2024-05-01 17:53:38
Exec Total Coverage
Lines: 35 200 17.5%
Functions: 5 12 41.7%
Branches: 17 150 11.3%

Line Branch Exec Source
1 /*
2 * Binary text demuxer
3 * eXtended BINary text (XBIN) demuxer
4 * Artworx Data Format demuxer
5 * iCEDraw File demuxer
6 * Copyright (c) 2010 Peter Ross <pross@xvid.org>
7 *
8 * This file is part of FFmpeg.
9 *
10 * FFmpeg is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * FFmpeg is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with FFmpeg; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 /**
26 * @file
27 * Binary text demuxer
28 * eXtended BINary text (XBIN) demuxer
29 * Artworx Data Format demuxer
30 * iCEDraw File demuxer
31 */
32
33 #include "config_components.h"
34
35 #include "libavutil/intreadwrite.h"
36 #include "libavutil/opt.h"
37 #include "libavutil/parseutils.h"
38 #include "avformat.h"
39 #include "demux.h"
40 #include "internal.h"
41 #include "sauce.h"
42 #include "libavcodec/bintext.h"
43
44 typedef struct {
45 const AVClass *class;
46 int chars_per_frame; /**< characters to send decoder per frame;
47 set by private options as characters per second, and then
48 converted to characters per frame at runtime */
49 int width, height; /**< video size (WxH pixels) (private option) */
50 AVRational framerate; /**< frames per second (private option) */
51 uint64_t fsize; /**< file size less metadata buffer */
52 } BinDemuxContext;
53
54 static AVStream * init_stream(AVFormatContext *s)
55 {
56 BinDemuxContext *bin = s->priv_data;
57 AVStream *st = avformat_new_stream(s, NULL);
58 if (!st)
59 return NULL;
60 st->codecpar->codec_tag = 0;
61 st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
62
63 if (!bin->width) {
64 st->codecpar->width = (80<<3);
65 st->codecpar->height = (25<<4);
66 }
67
68 avpriv_set_pts_info(st, 60, bin->framerate.den, bin->framerate.num);
69
70 /* simulate tty display speed */
71 bin->chars_per_frame = av_clip(av_q2d(st->time_base) * bin->chars_per_frame, 1, INT_MAX);
72
73 return st;
74 }
75
76 #if CONFIG_BINTEXT_DEMUXER | CONFIG_ADF_DEMUXER | CONFIG_IDF_DEMUXER
77 /**
78 * Given filesize and width, calculate height (assume font_height of 16)
79 */
80 2 static void calculate_height(AVCodecParameters *par, uint64_t fsize)
81 {
82 2 par->height = (fsize / ((par->width>>3)*2)) << 4;
83 2 }
84 #endif
85
86 #if CONFIG_BINTEXT_DEMUXER
87 static const uint8_t next_magic[]={
88 0x1A, 0x1B, '[', '0', ';', '3', '0', ';', '4', '0', 'm', 'N', 'E', 'X', 'T', 0x00
89 };
90
91 static int next_tag_read(AVFormatContext *avctx, uint64_t *fsize)
92 {
93 AVIOContext *pb = avctx->pb;
94 char buf[36];
95 int len;
96 uint64_t start_pos = avio_size(pb) - 256;
97
98 avio_seek(pb, start_pos, SEEK_SET);
99 if (avio_read(pb, buf, sizeof(next_magic)) != sizeof(next_magic))
100 return -1;
101 if (memcmp(buf, next_magic, sizeof(next_magic)))
102 return -1;
103 if (avio_r8(pb) != 0x01)
104 return -1;
105
106 *fsize -= 256;
107
108 #define GET_EFI2_META(name,size) \
109 len = avio_r8(pb); \
110 if (len < 1 || len > size) \
111 return -1; \
112 if (avio_read(pb, buf, size) == size && *buf) { \
113 buf[len] = 0; \
114 av_dict_set(&avctx->metadata, name, buf, 0); \
115 }
116
117 GET_EFI2_META("filename", 12)
118 GET_EFI2_META("author", 20)
119 GET_EFI2_META("publisher", 20)
120 GET_EFI2_META("title", 35)
121
122 return 0;
123 }
124
125 2 static void predict_width(AVCodecParameters *par, uint64_t fsize, int got_width)
126 {
127 /** attempt to guess width */
128
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!got_width)
129
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 par->width = fsize > 4000 ? (160<<3) : (80<<3);
130 2 }
131
132 7128 static int bin_probe(const AVProbeData *p)
133 {
134 7128 const uint8_t *d = p->buf;
135 7128 int magic = 0, sauce = 0;
136
137
2/2
✓ Branch 0 taken 7106 times.
✓ Branch 1 taken 22 times.
7128 if (p->buf_size > 256)
138 7106 magic = !memcmp(d + p->buf_size - 256, next_magic, sizeof(next_magic));
139
2/2
✓ Branch 0 taken 7122 times.
✓ Branch 1 taken 6 times.
7128 if (p->buf_size > 128)
140 7122 sauce = !memcmp(d + p->buf_size - 128, "SAUCE00", 7);
141
142
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7128 times.
7128 if (magic)
143 return AVPROBE_SCORE_EXTENSION + 1;
144
145
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 7126 times.
7128 if (av_match_ext(p->filename, "bin")) {
146 AVCodecParameters par;
147 2 int got_width = 0;
148 2 par.width = par.height = 0;
149
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (sauce)
150 return AVPROBE_SCORE_EXTENSION + 1;
151
152 2 predict_width(&par, p->buf_size, got_width);
153
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (par.width < 8)
154 return 0;
155 2 calculate_height(&par, p->buf_size);
156
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (par.height <= 0)
157 return 0;
158
159
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (par.width * par.height * 2 / (8*16) == p->buf_size)
160 return AVPROBE_SCORE_MAX / 2;
161 2 return 0;
162 }
163
164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7126 times.
7126 if (sauce)
165 return 1;
166
167 7126 return 0;
168 }
169
170
171 static int bintext_read_header(AVFormatContext *s)
172 {
173 BinDemuxContext *bin = s->priv_data;
174 AVIOContext *pb = s->pb;
175 int ret;
176 AVStream *st = init_stream(s);
177 if (!st)
178 return AVERROR(ENOMEM);
179 st->codecpar->codec_id = AV_CODEC_ID_BINTEXT;
180
181 if ((ret = ff_alloc_extradata(st->codecpar, 2)) < 0)
182 return ret;
183 st->codecpar->extradata[0] = 16;
184 st->codecpar->extradata[1] = 0;
185
186 if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
187 int got_width = 0;
188 bin->fsize = avio_size(pb);
189 if (ff_sauce_read(s, &bin->fsize, &got_width, 0) < 0)
190 next_tag_read(s, &bin->fsize);
191 if (!bin->width) {
192 predict_width(st->codecpar, bin->fsize, got_width);
193 if (st->codecpar->width < 8)
194 return AVERROR_INVALIDDATA;
195 calculate_height(st->codecpar, bin->fsize);
196 }
197 avio_seek(pb, 0, SEEK_SET);
198 }
199 return 0;
200 }
201 #endif /* CONFIG_BINTEXT_DEMUXER */
202
203 #if CONFIG_XBIN_DEMUXER
204 7128 static int xbin_probe(const AVProbeData *p)
205 {
206 7128 const uint8_t *d = p->buf;
207
208
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7128 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
7128 if (AV_RL32(d) == MKTAG('X','B','I','N') && d[4] == 0x1A &&
209 AV_RL16(d+5) > 0 && AV_RL16(d+5) <= 160 &&
210 d[9] > 0 && d[9] <= 32)
211 return AVPROBE_SCORE_MAX;
212 7128 return 0;
213 }
214
215 static int xbin_read_header(AVFormatContext *s)
216 {
217 BinDemuxContext *bin = s->priv_data;
218 AVIOContext *pb = s->pb;
219 char fontheight, flags;
220 int ret;
221 AVStream *st = init_stream(s);
222 if (!st)
223 return AVERROR(ENOMEM);
224
225 avio_skip(pb, 5);
226 st->codecpar->width = avio_rl16(pb)<<3;
227 st->codecpar->height = avio_rl16(pb);
228 fontheight = avio_r8(pb);
229 st->codecpar->height *= fontheight;
230 flags = avio_r8(pb);
231
232 st->codecpar->extradata_size = 2;
233 if ((flags & BINTEXT_PALETTE))
234 st->codecpar->extradata_size += 48;
235 if ((flags & BINTEXT_FONT))
236 st->codecpar->extradata_size += fontheight * (flags & 0x10 ? 512 : 256);
237 st->codecpar->codec_id = flags & 4 ? AV_CODEC_ID_XBIN : AV_CODEC_ID_BINTEXT;
238
239 ret = ff_alloc_extradata(st->codecpar, st->codecpar->extradata_size);
240 if (ret < 0)
241 return ret;
242 st->codecpar->extradata[0] = fontheight;
243 st->codecpar->extradata[1] = flags;
244 if (avio_read(pb, st->codecpar->extradata + 2, st->codecpar->extradata_size - 2) < 0)
245 return AVERROR(EIO);
246
247 if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
248 bin->fsize = avio_size(pb) - 9 - st->codecpar->extradata_size;
249 ff_sauce_read(s, &bin->fsize, NULL, 0);
250 avio_seek(pb, 9 + st->codecpar->extradata_size, SEEK_SET);
251 }
252
253 return 0;
254 }
255 #endif /* CONFIG_XBIN_DEMUXER */
256
257 #if CONFIG_ADF_DEMUXER
258 static int adf_read_header(AVFormatContext *s)
259 {
260 BinDemuxContext *bin = s->priv_data;
261 AVIOContext *pb = s->pb;
262 AVStream *st;
263 int ret;
264
265 if (avio_r8(pb) != 1)
266 return AVERROR_INVALIDDATA;
267
268 st = init_stream(s);
269 if (!st)
270 return AVERROR(ENOMEM);
271 st->codecpar->codec_id = AV_CODEC_ID_BINTEXT;
272
273 if ((ret = ff_alloc_extradata(st->codecpar, 2 + 48 + 4096)) < 0)
274 return ret;
275 st->codecpar->extradata[0] = 16;
276 st->codecpar->extradata[1] = BINTEXT_PALETTE|BINTEXT_FONT;
277
278 if (avio_read(pb, st->codecpar->extradata + 2, 24) < 0)
279 return AVERROR(EIO);
280 avio_skip(pb, 144);
281 if (avio_read(pb, st->codecpar->extradata + 2 + 24, 24) < 0)
282 return AVERROR(EIO);
283 if (avio_read(pb, st->codecpar->extradata + 2 + 48, 4096) < 0)
284 return AVERROR(EIO);
285
286 if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
287 int got_width = 0;
288 bin->fsize = avio_size(pb) - 1 - 192 - 4096;
289 st->codecpar->width = 80<<3;
290 ff_sauce_read(s, &bin->fsize, &got_width, 0);
291 if (st->codecpar->width < 8)
292 return AVERROR_INVALIDDATA;
293 if (!bin->width)
294 calculate_height(st->codecpar, bin->fsize);
295 avio_seek(pb, 1 + 192 + 4096, SEEK_SET);
296 }
297 return 0;
298 }
299 #endif /* CONFIG_ADF_DEMUXER */
300
301 #if CONFIG_IDF_DEMUXER
302 static const uint8_t idf_magic[] = {
303 0x04, 0x31, 0x2e, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x15, 0x00
304 };
305
306 7128 static int idf_probe(const AVProbeData *p)
307 {
308
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7128 times.
7128 if (p->buf_size < sizeof(idf_magic))
309 return 0;
310
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7128 times.
7128 if (!memcmp(p->buf, idf_magic, sizeof(idf_magic)))
311 return AVPROBE_SCORE_MAX;
312 7128 return 0;
313 }
314
315 static int idf_read_header(AVFormatContext *s)
316 {
317 BinDemuxContext *bin = s->priv_data;
318 AVIOContext *pb = s->pb;
319 AVStream *st;
320 int got_width = 0, ret;
321
322 if (!(pb->seekable & AVIO_SEEKABLE_NORMAL))
323 return AVERROR(EIO);
324
325 st = init_stream(s);
326 if (!st)
327 return AVERROR(ENOMEM);
328 st->codecpar->codec_id = AV_CODEC_ID_IDF;
329
330 if ((ret = ff_alloc_extradata(st->codecpar, 2 + 48 + 4096)) < 0)
331 return ret;
332 st->codecpar->extradata[0] = 16;
333 st->codecpar->extradata[1] = BINTEXT_PALETTE|BINTEXT_FONT;
334
335 avio_seek(pb, avio_size(pb) - 4096 - 48, SEEK_SET);
336
337 if (avio_read(pb, st->codecpar->extradata + 2 + 48, 4096) < 0)
338 return AVERROR(EIO);
339 if (avio_read(pb, st->codecpar->extradata + 2, 48) < 0)
340 return AVERROR(EIO);
341
342 bin->fsize = avio_size(pb) - 12 - 4096 - 48;
343 ff_sauce_read(s, &bin->fsize, &got_width, 0);
344 if (st->codecpar->width < 8)
345 return AVERROR_INVALIDDATA;
346 if (!bin->width)
347 calculate_height(st->codecpar, bin->fsize);
348 avio_seek(pb, 12, SEEK_SET);
349 return 0;
350 }
351 #endif /* CONFIG_IDF_DEMUXER */
352
353 static int read_packet(AVFormatContext *s,
354 AVPacket *pkt)
355 {
356 BinDemuxContext *bin = s->priv_data;
357
358 if (bin->fsize > 0) {
359 if (av_get_packet(s->pb, pkt, bin->fsize) < 0)
360 return AVERROR(EIO);
361 bin->fsize = -1; /* done */
362 } else if (!bin->fsize) {
363 if (avio_feof(s->pb))
364 return AVERROR(EIO);
365 if (av_get_packet(s->pb, pkt, bin->chars_per_frame) < 0)
366 return AVERROR(EIO);
367 } else {
368 return AVERROR(EIO);
369 }
370
371 pkt->flags |= AV_PKT_FLAG_KEY;
372 return 0;
373 }
374
375 #define OFFSET(x) offsetof(BinDemuxContext, x)
376 static const AVOption options[] = {
377 { "linespeed", "set simulated line speed (bytes per second)", OFFSET(chars_per_frame), AV_OPT_TYPE_INT, {.i64 = 6000}, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM},
378 { "video_size", "set video size, such as 640x480 or hd720.", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, AV_OPT_FLAG_DECODING_PARAM },
379 { "framerate", "set framerate (frames per second)", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
380 { NULL },
381 };
382
383 #define CLASS(name) \
384 (const AVClass[1]){{ \
385 .class_name = name, \
386 .item_name = av_default_item_name, \
387 .option = options, \
388 .version = LIBAVUTIL_VERSION_INT, \
389 }}
390
391 #if CONFIG_BINTEXT_DEMUXER
392 const FFInputFormat ff_bintext_demuxer = {
393 .p.name = "bin",
394 .p.long_name = NULL_IF_CONFIG_SMALL("Binary text"),
395 .p.priv_class = CLASS("Binary text demuxer"),
396 .priv_data_size = sizeof(BinDemuxContext),
397 .read_probe = bin_probe,
398 .read_header = bintext_read_header,
399 .read_packet = read_packet,
400 };
401 #endif
402
403 #if CONFIG_XBIN_DEMUXER
404 const FFInputFormat ff_xbin_demuxer = {
405 .p.name = "xbin",
406 .p.long_name = NULL_IF_CONFIG_SMALL("eXtended BINary text (XBIN)"),
407 .p.priv_class = CLASS("eXtended BINary text (XBIN) demuxer"),
408 .priv_data_size = sizeof(BinDemuxContext),
409 .read_probe = xbin_probe,
410 .read_header = xbin_read_header,
411 .read_packet = read_packet,
412 };
413 #endif
414
415 #if CONFIG_ADF_DEMUXER
416 const FFInputFormat ff_adf_demuxer = {
417 .p.name = "adf",
418 .p.long_name = NULL_IF_CONFIG_SMALL("Artworx Data Format"),
419 .p.extensions = "adf",
420 .p.priv_class = CLASS("Artworx Data Format demuxer"),
421 .priv_data_size = sizeof(BinDemuxContext),
422 .read_header = adf_read_header,
423 .read_packet = read_packet,
424 };
425 #endif
426
427 #if CONFIG_IDF_DEMUXER
428 const FFInputFormat ff_idf_demuxer = {
429 .p.name = "idf",
430 .p.long_name = NULL_IF_CONFIG_SMALL("iCE Draw File"),
431 .p.extensions = "idf",
432 .p.priv_class = CLASS("iCE Draw File demuxer"),
433 .priv_data_size = sizeof(BinDemuxContext),
434 .read_probe = idf_probe,
435 .read_header = idf_read_header,
436 .read_packet = read_packet,
437 };
438 #endif
439