bintext.c 12.4 KB
Newer Older
Peter Ross's avatar
Peter Ross committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
/*
 * Binary text demuxer
 * eXtended BINary text (XBIN) demuxer
 * Artworx Data Format demuxer
 * iCEDraw File demuxer
 * Copyright (c) 2010 Peter Ross <pross@xvid.org>
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/**
 * @file
 * Binary text demuxer
 * eXtended BINary text (XBIN) demuxer
 * Artworx Data Format demuxer
 * iCEDraw File demuxer
 */

#include "libavutil/intreadwrite.h"
34 35
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
Peter Ross's avatar
Peter Ross committed
36
#include "avformat.h"
37
#include "internal.h"
Peter Ross's avatar
Peter Ross committed
38 39 40 41
#include "sauce.h"
#include "libavcodec/bintext.h"

typedef struct {
42 43 44 45 46 47
    const AVClass *class;
    int chars_per_frame; /**< characters to send decoder per frame;
                              set by private options as characters per second, and then
                              converted to characters per frame at runtime */
    char *video_size;    /**< video size (WxH pixels) (private option) */
    char *framerate;     /**< frames per second (private option) */
Peter Ross's avatar
Peter Ross committed
48 49 50
    uint64_t fsize;  /**< file size less metadata buffer */
} BinDemuxContext;

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
static AVStream * init_stream(AVFormatContext *s)
{
    BinDemuxContext *bin = s->priv_data;
    AVStream *st = avformat_new_stream(s, NULL);
    if (!st)
        return NULL;
    st->codec->codec_tag   = 0;
    st->codec->codec_type  = AVMEDIA_TYPE_VIDEO;

    if (bin->video_size) {
        if (av_parse_video_size(&st->codec->width, &st->codec->height, bin->video_size) < 0) {
            av_log(s, AV_LOG_ERROR, "Could not parse video size: '%s'\n", bin->video_size);
            return NULL;
        }
    } else {
        st->codec->width  = (80<<3);
        st->codec->height = (25<<4);
    }

    if (bin->framerate) {
        AVRational framerate;
        if (av_parse_video_rate(&framerate, bin->framerate) < 0) {
            av_log(s, AV_LOG_ERROR, "Could not parse framerate: '%s'\n", bin->framerate);
            return NULL;
        }
        avpriv_set_pts_info(st, 60, framerate.den, framerate.num);
    } else {
        avpriv_set_pts_info(st, 60, 1, 25);
    }

    /* simulate tty display speed */
    bin->chars_per_frame = FFMAX(av_q2d(st->time_base) * bin->chars_per_frame, 1);

    return st;
}

Peter Ross's avatar
Peter Ross committed
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
#if CONFIG_BINTEXT_DEMUXER | CONFIG_ADF_DEMUXER | CONFIG_IDF_DEMUXER
/**
 * Given filesize and width, calculate height (assume font_height of 16)
 */
static void calculate_height(AVCodecContext *avctx, uint64_t fsize)
{
    avctx->height = (fsize / ((avctx->width>>3)*2)) << 4;
}
#endif

#if CONFIG_BINTEXT_DEMUXER
static const uint8_t next_magic[]={
    0x1A, 0x1B, '[', '0', ';', '3', '0', ';', '4', '0', 'm', 'N', 'E', 'X', 'T', 0x00
};

static int next_tag_read(AVFormatContext *avctx, uint64_t *fsize)
{
104
    AVIOContext *pb = avctx->pb;
Peter Ross's avatar
Peter Ross committed
105 106
    char buf[36];
    int len;
107
    uint64_t start_pos = avio_size(pb) - 256;
Peter Ross's avatar
Peter Ross committed
108

109
    avio_seek(pb, start_pos, SEEK_SET);
110
    if (avio_read(pb, buf, sizeof(next_magic)) != sizeof(next_magic))
Peter Ross's avatar
Peter Ross committed
111 112 113
        return -1;
    if (memcmp(buf, next_magic, sizeof(next_magic)))
        return -1;
114
    if (avio_r8(pb) != 0x01)
Peter Ross's avatar
Peter Ross committed
115 116 117 118 119
        return -1;

    *fsize -= 256;

#define GET_EFI2_META(name,size) \
120
    len = avio_r8(pb); \
Peter Ross's avatar
Peter Ross committed
121 122
    if (len < 1 || len > size) \
        return -1; \
123
    if (avio_read(pb, buf, size) == size && *buf) { \
Peter Ross's avatar
Peter Ross committed
124
        buf[len] = 0; \
125
        av_dict_set(&avctx->metadata, name, buf, 0); \
Peter Ross's avatar
Peter Ross committed
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
    }

    GET_EFI2_META("filename",  12)
    GET_EFI2_META("author",    20)
    GET_EFI2_META("publisher", 20)
    GET_EFI2_META("title",     35)

    return 0;
}

static void predict_width(AVCodecContext *avctx, uint64_t fsize, int got_width)
{
    /** attempt to guess width */
    if (!got_width)
        avctx->width = fsize > 4000 ? (160<<3) : (80<<3);
}

143
static int bintext_read_header(AVFormatContext *s)
Peter Ross's avatar
Peter Ross committed
144 145
{
    BinDemuxContext *bin = s->priv_data;
146
    AVIOContext *pb = s->pb;
Peter Ross's avatar
Peter Ross committed
147

148
    AVStream *st = init_stream(s);
Peter Ross's avatar
Peter Ross committed
149 150
    if (!st)
        return AVERROR(ENOMEM);
151
    st->codec->codec_id    = AV_CODEC_ID_BINTEXT;
Peter Ross's avatar
Peter Ross committed
152 153

    st->codec->extradata_size = 2;
154
    st->codec->extradata = av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
Peter Ross's avatar
Peter Ross committed
155 156 157 158 159
    if (!st->codec->extradata)
        return AVERROR(ENOMEM);
    st->codec->extradata[0] = 16;
    st->codec->extradata[1] = 0;

160
    if (pb->seekable) {
Peter Ross's avatar
Peter Ross committed
161
        int got_width = 0;
162
        bin->fsize = avio_size(pb);
Peter Ross's avatar
Peter Ross committed
163 164
        if (ff_sauce_read(s, &bin->fsize, &got_width, 0) < 0)
            next_tag_read(s, &bin->fsize);
165
        if (!bin->video_size) {
Peter Ross's avatar
Peter Ross committed
166 167
            predict_width(st->codec, bin->fsize, got_width);
            calculate_height(st->codec, bin->fsize);
168
        }
169
        avio_seek(pb, 0, SEEK_SET);
Peter Ross's avatar
Peter Ross committed
170 171
    }
    return 0;
172
}
Peter Ross's avatar
Peter Ross committed
173 174 175 176 177 178 179 180 181 182 183 184 185 186
#endif /* CONFIG_BINTEXT_DEMUXER */

#if CONFIG_XBIN_DEMUXER
static int xbin_probe(AVProbeData *p)
{
    const uint8_t *d = p->buf;

    if (AV_RL32(d) == MKTAG('X','B','I','N') && d[4] == 0x1A &&
        AV_RL16(d+5) > 0 && AV_RL16(d+5) <= 160 &&
        d[9] > 0 && d[9] <= 32)
        return AVPROBE_SCORE_MAX;
    return 0;
}

187
static int xbin_read_header(AVFormatContext *s)
Peter Ross's avatar
Peter Ross committed
188 189
{
    BinDemuxContext *bin = s->priv_data;
190
    AVIOContext *pb = s->pb;
Peter Ross's avatar
Peter Ross committed
191 192
    char fontheight, flags;

193
    AVStream *st = init_stream(s);
Peter Ross's avatar
Peter Ross committed
194 195 196
    if (!st)
        return AVERROR(ENOMEM);

197
    avio_skip(pb, 5);
198 199
    st->codec->width   = avio_rl16(pb)<<3;
    st->codec->height  = avio_rl16(pb);
200
    fontheight         = avio_r8(pb);
Peter Ross's avatar
Peter Ross committed
201
    st->codec->height *= fontheight;
202
    flags              = avio_r8(pb);
Peter Ross's avatar
Peter Ross committed
203 204 205 206 207 208

    st->codec->extradata_size = 2;
    if ((flags & BINTEXT_PALETTE))
        st->codec->extradata_size += 48;
    if ((flags & BINTEXT_FONT))
        st->codec->extradata_size += fontheight * (flags & 0x10 ? 512 : 256);
209
    st->codec->codec_id    = flags & 4 ? AV_CODEC_ID_XBIN : AV_CODEC_ID_BINTEXT;
Peter Ross's avatar
Peter Ross committed
210

211
    st->codec->extradata = av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
Peter Ross's avatar
Peter Ross committed
212 213 214 215
    if (!st->codec->extradata)
        return AVERROR(ENOMEM);
    st->codec->extradata[0] = fontheight;
    st->codec->extradata[1] = flags;
216
    if (avio_read(pb, st->codec->extradata + 2, st->codec->extradata_size - 2) < 0)
Peter Ross's avatar
Peter Ross committed
217 218
        return AVERROR(EIO);

219
    if (pb->seekable) {
220
        bin->fsize = avio_size(pb) - 9 - st->codec->extradata_size;
Peter Ross's avatar
Peter Ross committed
221
        ff_sauce_read(s, &bin->fsize, NULL, 0);
222
        avio_seek(pb, 9 + st->codec->extradata_size, SEEK_SET);
Peter Ross's avatar
Peter Ross committed
223 224 225 226 227 228 229
    }

    return 0;
}
#endif /* CONFIG_XBIN_DEMUXER */

#if CONFIG_ADF_DEMUXER
230
static int adf_read_header(AVFormatContext *s)
Peter Ross's avatar
Peter Ross committed
231 232
{
    BinDemuxContext *bin = s->priv_data;
233
    AVIOContext *pb = s->pb;
Peter Ross's avatar
Peter Ross committed
234 235
    AVStream *st;

236
    if (avio_r8(pb) != 1)
Peter Ross's avatar
Peter Ross committed
237 238
        return AVERROR_INVALIDDATA;

239
    st = init_stream(s);
Peter Ross's avatar
Peter Ross committed
240 241
    if (!st)
        return AVERROR(ENOMEM);
242
    st->codec->codec_id    = AV_CODEC_ID_BINTEXT;
Peter Ross's avatar
Peter Ross committed
243 244

    st->codec->extradata_size = 2 + 48 + 4096;
245
    st->codec->extradata = av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
Peter Ross's avatar
Peter Ross committed
246 247 248 249 250
    if (!st->codec->extradata)
        return AVERROR(ENOMEM);
    st->codec->extradata[0] = 16;
    st->codec->extradata[1] = BINTEXT_PALETTE|BINTEXT_FONT;

251
    if (avio_read(pb, st->codec->extradata + 2, 24) < 0)
Peter Ross's avatar
Peter Ross committed
252
        return AVERROR(EIO);
253
    avio_skip(pb, 144);
254
    if (avio_read(pb, st->codec->extradata + 2 + 24, 24) < 0)
Peter Ross's avatar
Peter Ross committed
255
        return AVERROR(EIO);
256
    if (avio_read(pb, st->codec->extradata + 2 + 48, 4096) < 0)
Peter Ross's avatar
Peter Ross committed
257 258
        return AVERROR(EIO);

259
    if (pb->seekable) {
Peter Ross's avatar
Peter Ross committed
260
        int got_width = 0;
261
        bin->fsize = avio_size(pb) - 1 - 192 - 4096;
Peter Ross's avatar
Peter Ross committed
262 263
        st->codec->width = 80<<3;
        ff_sauce_read(s, &bin->fsize, &got_width, 0);
264
        if (!bin->video_size)
Peter Ross's avatar
Peter Ross committed
265
            calculate_height(st->codec, bin->fsize);
266
        avio_seek(pb, 1 + 192 + 4096, SEEK_SET);
Peter Ross's avatar
Peter Ross committed
267 268 269 270 271 272 273 274 275 276 277 278
    }
    return 0;
}
#endif /* CONFIG_ADF_DEMUXER */

#if CONFIG_IDF_DEMUXER
static const uint8_t idf_magic[] = {
    0x04, 0x31, 0x2e, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x15, 0x00
};

static int idf_probe(AVProbeData *p)
{
279 280 281
    if (p->buf_size < sizeof(idf_magic))
        return 0;
    if (!memcmp(p->buf, idf_magic, sizeof(idf_magic)))
Peter Ross's avatar
Peter Ross committed
282 283 284 285
        return AVPROBE_SCORE_MAX;
    return 0;
}

286
static int idf_read_header(AVFormatContext *s)
Peter Ross's avatar
Peter Ross committed
287 288
{
    BinDemuxContext *bin = s->priv_data;
289
    AVIOContext *pb = s->pb;
Peter Ross's avatar
Peter Ross committed
290 291 292
    AVStream *st;
    int got_width = 0;

293
    if (!pb->seekable)
Peter Ross's avatar
Peter Ross committed
294 295
        return AVERROR(EIO);

296
    st = init_stream(s);
Peter Ross's avatar
Peter Ross committed
297 298
    if (!st)
        return AVERROR(ENOMEM);
299
    st->codec->codec_id    = AV_CODEC_ID_IDF;
Peter Ross's avatar
Peter Ross committed
300 301

    st->codec->extradata_size = 2 + 48 + 4096;
302
    st->codec->extradata = av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
Peter Ross's avatar
Peter Ross committed
303 304 305 306 307
    if (!st->codec->extradata)
        return AVERROR(ENOMEM);
    st->codec->extradata[0] = 16;
    st->codec->extradata[1] = BINTEXT_PALETTE|BINTEXT_FONT;

308
    avio_seek(pb, avio_size(pb) - 4096 - 48, SEEK_SET);
Peter Ross's avatar
Peter Ross committed
309

310
    if (avio_read(pb, st->codec->extradata + 2 + 48, 4096) < 0)
Peter Ross's avatar
Peter Ross committed
311
        return AVERROR(EIO);
312
    if (avio_read(pb, st->codec->extradata + 2, 48) < 0)
Peter Ross's avatar
Peter Ross committed
313 314
        return AVERROR(EIO);

315
    bin->fsize = avio_size(pb) - 12 - 4096 - 48;
Peter Ross's avatar
Peter Ross committed
316
    ff_sauce_read(s, &bin->fsize, &got_width, 0);
317
    if (!bin->video_size)
Peter Ross's avatar
Peter Ross committed
318
        calculate_height(st->codec, bin->fsize);
319
    avio_seek(pb, 12, SEEK_SET);
Peter Ross's avatar
Peter Ross committed
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
    return 0;
}
#endif /* CONFIG_IDF_DEMUXER */

static int read_packet(AVFormatContext *s,
                           AVPacket *pkt)
{
    BinDemuxContext *bin = s->priv_data;

    if (bin->fsize > 0) {
        if (av_get_packet(s->pb, pkt, bin->fsize) < 0)
            return AVERROR(EIO);
        bin->fsize = -1; /* done */
    } else if (!bin->fsize) {
        if (url_feof(s->pb))
            return AVERROR(EIO);
        if (av_get_packet(s->pb, pkt, bin->chars_per_frame) < 0)
            return AVERROR(EIO);
    } else {
        return AVERROR(EIO);
    }

    pkt->flags |= AV_PKT_FLAG_KEY;
    return 0;
}

346 347
#define OFFSET(x) offsetof(BinDemuxContext, x)
static const AVOption options[] = {
348
    { "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},
349 350 351 352 353 354 355 356 357 358 359 360 361
    { "video_size", "set video size, such as 640x480 or hd720.", OFFSET(video_size), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_DECODING_PARAM },
    { "framerate", "set framerate (frames per second)", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = "25"}, 0, 0, AV_OPT_FLAG_DECODING_PARAM },
    { NULL },
};

#define CLASS(name) \
(const AVClass[1]){{ \
    .class_name     = name, \
    .item_name      = av_default_item_name, \
    .option         = options, \
    .version        = LIBAVUTIL_VERSION_INT, \
}}

Peter Ross's avatar
Peter Ross committed
362 363
#if CONFIG_BINTEXT_DEMUXER
AVInputFormat ff_bintext_demuxer = {
364 365 366 367 368 369
    .name           = "bin",
    .long_name      = NULL_IF_CONFIG_SMALL("Binary text"),
    .priv_data_size = sizeof(BinDemuxContext),
    .read_header    = bintext_read_header,
    .read_packet    = read_packet,
    .extensions     = "bin",
370
    .priv_class     = CLASS("Binary text demuxer"),
Peter Ross's avatar
Peter Ross committed
371 372 373 374 375
};
#endif

#if CONFIG_XBIN_DEMUXER
AVInputFormat ff_xbin_demuxer = {
376 377 378 379 380 381
    .name           = "xbin",
    .long_name      = NULL_IF_CONFIG_SMALL("eXtended BINary text (XBIN)"),
    .priv_data_size = sizeof(BinDemuxContext),
    .read_probe     = xbin_probe,
    .read_header    = xbin_read_header,
    .read_packet    = read_packet,
382
    .priv_class     = CLASS("eXtended BINary text (XBIN) demuxer"),
Peter Ross's avatar
Peter Ross committed
383 384 385 386 387
};
#endif

#if CONFIG_ADF_DEMUXER
AVInputFormat ff_adf_demuxer = {
388 389 390 391 392 393
    .name           = "adf",
    .long_name      = NULL_IF_CONFIG_SMALL("Artworx Data Format"),
    .priv_data_size = sizeof(BinDemuxContext),
    .read_header    = adf_read_header,
    .read_packet    = read_packet,
    .extensions     = "adf",
394
    .priv_class     = CLASS("Artworx Data Format demuxer"),
Peter Ross's avatar
Peter Ross committed
395 396 397 398 399
};
#endif

#if CONFIG_IDF_DEMUXER
AVInputFormat ff_idf_demuxer = {
400 401 402 403 404 405 406
    .name           = "idf",
    .long_name      = NULL_IF_CONFIG_SMALL("iCE Draw File"),
    .priv_data_size = sizeof(BinDemuxContext),
    .read_probe     = idf_probe,
    .read_header    = idf_read_header,
    .read_packet    = read_packet,
    .extensions     = "idf",
407
    .priv_class     = CLASS("iCE Draw File demuxer"),
Peter Ross's avatar
Peter Ross committed
408 409
};
#endif