]> git.sesse.net Git - ffmpeg/blob - libavformat/bintext.c
Merge remote-tracking branch 'qatar/master'
[ffmpeg] / libavformat / bintext.c
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 "libavutil/intreadwrite.h"
34 #include "avformat.h"
35 #include "sauce.h"
36 #include "libavcodec/bintext.h"
37
38 #define LINE_RATE 6000 /** characters per second */
39
40 typedef struct {
41     int chars_per_frame;
42     uint64_t fsize;  /**< file size less metadata buffer */
43 } BinDemuxContext;
44
45 #if CONFIG_BINTEXT_DEMUXER | CONFIG_ADF_DEMUXER | CONFIG_IDF_DEMUXER
46 /**
47  * Given filesize and width, calculate height (assume font_height of 16)
48  */
49 static void calculate_height(AVCodecContext *avctx, uint64_t fsize)
50 {
51     avctx->height = (fsize / ((avctx->width>>3)*2)) << 4;
52 }
53 #endif
54
55 #if CONFIG_BINTEXT_DEMUXER
56 static const uint8_t next_magic[]={
57     0x1A, 0x1B, '[', '0', ';', '3', '0', ';', '4', '0', 'm', 'N', 'E', 'X', 'T', 0x00
58 };
59
60 static int next_tag_read(AVFormatContext *avctx, uint64_t *fsize)
61 {
62     ByteIOContext *pb = avctx->pb;
63     char buf[36];
64     int len;
65     uint64_t start_pos = url_fsize(pb) - 256;
66
67     url_fseek(pb, start_pos, SEEK_SET);
68     if (get_buffer(pb, buf, sizeof(next_magic)) != sizeof(next_magic))
69         return -1;
70     if (memcmp(buf, next_magic, sizeof(next_magic)))
71         return -1;
72     if (get_byte(pb) != 0x01)
73         return -1;
74
75     *fsize -= 256;
76
77 #define GET_EFI2_META(name,size) \
78     len = get_byte(pb); \
79     if (len < 1 || len > size) \
80         return -1; \
81     if (get_buffer(pb, buf, size) == size && *buf) { \
82         buf[len] = 0; \
83         av_metadata_set2(&avctx->metadata, name, buf, 0); \
84     }
85
86     GET_EFI2_META("filename",  12)
87     GET_EFI2_META("author",    20)
88     GET_EFI2_META("publisher", 20)
89     GET_EFI2_META("title",     35)
90
91     return 0;
92 }
93
94 static void predict_width(AVCodecContext *avctx, uint64_t fsize, int got_width)
95 {
96     /** attempt to guess width */
97     if (!got_width)
98         avctx->width = fsize > 4000 ? (160<<3) : (80<<3);
99 }
100
101 static AVStream * init_stream(AVFormatContext *s,
102                               AVFormatParameters *ap)
103 {
104     BinDemuxContext *bin = s->priv_data;
105     AVStream *st = av_new_stream(s, 0);
106     if (!st)
107         return NULL;
108     st->codec->codec_tag   = 0;
109     st->codec->codec_type  = AVMEDIA_TYPE_VIDEO;
110
111     if (!ap->time_base.num) {
112         av_set_pts_info(st, 60, 1, 25);
113     } else {
114         av_set_pts_info(st, 60, ap->time_base.num, ap->time_base.den);
115     }
116
117     /* simulate tty display speed */
118     bin->chars_per_frame = FFMAX(av_q2d(st->time_base) * (ap->sample_rate ? ap->sample_rate : LINE_RATE), 1);
119
120     st->codec->width  = ap->width  ? ap->width  : (80<<3);
121     st->codec->height = ap->height ? ap->height : (25<<4);
122     return st;
123 }
124
125 static int bintext_read_header(AVFormatContext *s,
126                                AVFormatParameters *ap)
127 {
128     BinDemuxContext *bin = s->priv_data;
129     ByteIOContext *pb = s->pb;
130
131     AVStream *st = init_stream(s, ap);
132     if (!st)
133         return AVERROR(ENOMEM);
134     st->codec->codec_id    = CODEC_ID_BINTEXT;
135
136     st->codec->extradata_size = 2;
137     st->codec->extradata = av_malloc(st->codec->extradata_size);
138     if (!st->codec->extradata)
139         return AVERROR(ENOMEM);
140     st->codec->extradata[0] = 16;
141     st->codec->extradata[1] = 0;
142
143     if (!url_is_streamed(pb)) {
144         int got_width = 0;
145         bin->fsize = url_fsize(pb);
146         if (ff_sauce_read(s, &bin->fsize, &got_width, 0) < 0)
147             next_tag_read(s, &bin->fsize);
148         if (!ap->width)
149             predict_width(st->codec, bin->fsize, got_width);
150         if (!ap->height)
151             calculate_height(st->codec, bin->fsize);
152         url_fseek(pb, 0, SEEK_SET);
153     }
154     return 0;
155 };
156 #endif /* CONFIG_BINTEXT_DEMUXER */
157
158 #if CONFIG_XBIN_DEMUXER
159 static int xbin_probe(AVProbeData *p)
160 {
161     const uint8_t *d = p->buf;
162
163     if (AV_RL32(d) == MKTAG('X','B','I','N') && d[4] == 0x1A &&
164         AV_RL16(d+5) > 0 && AV_RL16(d+5) <= 160 &&
165         d[9] > 0 && d[9] <= 32)
166         return AVPROBE_SCORE_MAX;
167     return 0;
168 }
169
170 static int xbin_read_header(AVFormatContext *s,
171                            AVFormatParameters *ap)
172 {
173     BinDemuxContext *bin = s->priv_data;
174     ByteIOContext *pb = s->pb;
175     char fontheight, flags;
176     uint8_t *h;
177
178     AVStream *st = init_stream(s, ap);
179     if (!st)
180         return AVERROR(ENOMEM);
181
182     url_fskip(pb, 5);
183     st->codec->width   = get_le16(pb)<<3;
184     st->codec->height  = get_le16(pb);
185     fontheight         = get_byte(pb);
186     st->codec->height *= fontheight;
187     flags              = get_byte(pb);
188
189     st->codec->extradata_size = 2;
190     if ((flags & BINTEXT_PALETTE))
191         st->codec->extradata_size += 48;
192     if ((flags & BINTEXT_FONT))
193         st->codec->extradata_size += fontheight * (flags & 0x10 ? 512 : 256);
194     st->codec->codec_id    = flags & 4 ? CODEC_ID_XBIN : CODEC_ID_BINTEXT;
195
196     h = st->codec->extradata = av_malloc(st->codec->extradata_size);
197     if (!st->codec->extradata)
198         return AVERROR(ENOMEM);
199     st->codec->extradata[0] = fontheight;
200     st->codec->extradata[1] = flags;
201     if (get_buffer(pb, st->codec->extradata + 2, st->codec->extradata_size - 2) < 0)
202         return AVERROR(EIO);
203
204     if (!url_is_streamed(pb)) {
205         bin->fsize = url_fsize(pb) - 9 - st->codec->extradata_size;
206         ff_sauce_read(s, &bin->fsize, NULL, 0);
207         url_fseek(pb, 9 + st->codec->extradata_size, SEEK_SET);
208     }
209
210     return 0;
211 }
212 #endif /* CONFIG_XBIN_DEMUXER */
213
214 #if CONFIG_ADF_DEMUXER
215 static int adf_read_header(AVFormatContext *s,
216                            AVFormatParameters *ap)
217 {
218     BinDemuxContext *bin = s->priv_data;
219     ByteIOContext *pb = s->pb;
220     AVStream *st;
221
222     if (get_byte(pb) != 1)
223         return AVERROR_INVALIDDATA;
224
225     st = init_stream(s, ap);
226     if (!st)
227         return AVERROR(ENOMEM);
228     st->codec->codec_id    = CODEC_ID_BINTEXT;
229
230     st->codec->extradata_size = 2 + 48 + 4096;
231     st->codec->extradata = av_malloc(st->codec->extradata_size);
232     if (!st->codec->extradata)
233         return AVERROR(ENOMEM);
234     st->codec->extradata[0] = 16;
235     st->codec->extradata[1] = BINTEXT_PALETTE|BINTEXT_FONT;
236
237     if (get_buffer(pb, st->codec->extradata + 2, 24) < 0)
238         return AVERROR(EIO);
239     url_fskip(pb, 144);
240     if (get_buffer(pb, st->codec->extradata + 2 + 24, 24) < 0)
241         return AVERROR(EIO);
242     if (get_buffer(pb, st->codec->extradata + 2 + 48, 4096) < 0)
243         return AVERROR(EIO);
244
245     if (!url_is_streamed(pb)) {
246         int got_width = 0;
247         bin->fsize = url_fsize(pb) - 1 - 192 - 4096;
248         st->codec->width = 80<<3;
249         ff_sauce_read(s, &bin->fsize, &got_width, 0);
250         if (!ap->height)
251             calculate_height(st->codec, bin->fsize);
252         url_fseek(pb, 1 + 192 + 4096, SEEK_SET);
253     }
254     return 0;
255 }
256 #endif /* CONFIG_ADF_DEMUXER */
257
258 #if CONFIG_IDF_DEMUXER
259 static const uint8_t idf_magic[] = {
260     0x04, 0x31, 0x2e, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x15, 0x00
261 };
262
263 static int idf_probe(AVProbeData *p)
264 {
265     if (!memcmp(p->buf, idf_magic, FFMIN(sizeof(idf_magic), p->buf_size)))
266         return AVPROBE_SCORE_MAX;
267     return 0;
268 }
269
270 static int idf_read_header(AVFormatContext *s,
271                            AVFormatParameters *ap)
272 {
273     BinDemuxContext *bin = s->priv_data;
274     ByteIOContext *pb = s->pb;
275     AVStream *st;
276     int got_width = 0;
277
278     if (url_is_streamed(pb))
279         return AVERROR(EIO);
280
281     st = init_stream(s, ap);
282     if (!st)
283         return AVERROR(ENOMEM);
284     st->codec->codec_id    = CODEC_ID_IDF;
285
286     st->codec->extradata_size = 2 + 48 + 4096;
287     st->codec->extradata = av_malloc(st->codec->extradata_size);
288     if (!st->codec->extradata)
289         return AVERROR(ENOMEM);
290     st->codec->extradata[0] = 16;
291     st->codec->extradata[1] = BINTEXT_PALETTE|BINTEXT_FONT;
292
293     url_fseek(pb, url_fsize(pb) - 4096 - 48, SEEK_SET);
294
295     if (get_buffer(pb, st->codec->extradata + 2 + 48, 4096) < 0)
296         return AVERROR(EIO);
297     if (get_buffer(pb, st->codec->extradata + 2, 48) < 0)
298         return AVERROR(EIO);
299
300     bin->fsize = url_fsize(pb) - 12 - 4096 - 48;
301     ff_sauce_read(s, &bin->fsize, &got_width, 0);
302     if (!ap->height)
303         calculate_height(st->codec, bin->fsize);
304     url_fseek(pb, 12, SEEK_SET);
305     return 0;
306 }
307 #endif /* CONFIG_IDF_DEMUXER */
308
309 static int read_packet(AVFormatContext *s,
310                            AVPacket *pkt)
311 {
312     BinDemuxContext *bin = s->priv_data;
313
314     if (bin->fsize > 0) {
315         if (av_get_packet(s->pb, pkt, bin->fsize) < 0)
316             return AVERROR(EIO);
317         bin->fsize = -1; /* done */
318     } else if (!bin->fsize) {
319         if (url_feof(s->pb))
320             return AVERROR(EIO);
321         if (av_get_packet(s->pb, pkt, bin->chars_per_frame) < 0)
322             return AVERROR(EIO);
323     } else {
324         return AVERROR(EIO);
325     }
326
327     pkt->flags |= AV_PKT_FLAG_KEY;
328     return 0;
329 }
330
331 #if CONFIG_BINTEXT_DEMUXER
332 AVInputFormat ff_bintext_demuxer = {
333     "bin",
334     NULL_IF_CONFIG_SMALL("Binary text"),
335     sizeof(BinDemuxContext),
336     NULL,
337     bintext_read_header,
338     read_packet,
339     .extensions = "bin",
340 };
341 #endif
342
343 #if CONFIG_XBIN_DEMUXER
344 AVInputFormat ff_xbin_demuxer = {
345     "xbin",
346     NULL_IF_CONFIG_SMALL("eXtended BINary text (XBIN)"),
347     sizeof(BinDemuxContext),
348     xbin_probe,
349     xbin_read_header,
350     read_packet,
351 };
352 #endif
353
354 #if CONFIG_ADF_DEMUXER
355 AVInputFormat ff_adf_demuxer = {
356     "adf",
357     NULL_IF_CONFIG_SMALL("Artworx Data Format"),
358     sizeof(BinDemuxContext),
359     NULL,
360     adf_read_header,
361     read_packet,
362     .extensions = "adf",
363 };
364 #endif
365
366 #if CONFIG_IDF_DEMUXER
367 AVInputFormat ff_idf_demuxer = {
368     "idf",
369     NULL_IF_CONFIG_SMALL("iCE Draw File"),
370     sizeof(BinDemuxContext),
371     idf_probe,
372     idf_read_header,
373     read_packet,
374     .extensions = "idf",
375 };
376 #endif