PageRenderTime 48ms CodeModel.GetById 19ms app.highlight 24ms RepoModel.GetById 2ms app.codeStats 0ms

/libavformat/bintext.c

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