yading@11: /* yading@11: * Binary text demuxer yading@11: * eXtended BINary text (XBIN) demuxer yading@11: * Artworx Data Format demuxer yading@11: * iCEDraw File demuxer yading@11: * Copyright (c) 2010 Peter Ross yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: /** yading@11: * @file yading@11: * Binary text demuxer yading@11: * eXtended BINary text (XBIN) demuxer yading@11: * Artworx Data Format demuxer yading@11: * iCEDraw File demuxer yading@11: */ yading@11: yading@11: #include "libavutil/intreadwrite.h" yading@11: #include "libavutil/opt.h" yading@11: #include "libavutil/parseutils.h" yading@11: #include "avformat.h" yading@11: #include "internal.h" yading@11: #include "sauce.h" yading@11: #include "libavcodec/bintext.h" yading@11: yading@11: typedef struct { yading@11: const AVClass *class; yading@11: int chars_per_frame; /**< characters to send decoder per frame; yading@11: set by private options as characters per second, and then yading@11: converted to characters per frame at runtime */ yading@11: int width, height; /**< video size (WxH pixels) (private option) */ yading@11: AVRational framerate; /**< frames per second (private option) */ yading@11: uint64_t fsize; /**< file size less metadata buffer */ yading@11: } BinDemuxContext; yading@11: yading@11: static AVStream * init_stream(AVFormatContext *s) yading@11: { yading@11: BinDemuxContext *bin = s->priv_data; yading@11: AVStream *st = avformat_new_stream(s, NULL); yading@11: if (!st) yading@11: return NULL; yading@11: st->codec->codec_tag = 0; yading@11: st->codec->codec_type = AVMEDIA_TYPE_VIDEO; yading@11: yading@11: if (!bin->width) { yading@11: st->codec->width = (80<<3); yading@11: st->codec->height = (25<<4); yading@11: } yading@11: yading@11: avpriv_set_pts_info(st, 60, bin->framerate.den, bin->framerate.num); yading@11: yading@11: /* simulate tty display speed */ yading@11: bin->chars_per_frame = FFMAX(av_q2d(st->time_base) * bin->chars_per_frame, 1); yading@11: yading@11: return st; yading@11: } yading@11: yading@11: #if CONFIG_BINTEXT_DEMUXER | CONFIG_ADF_DEMUXER | CONFIG_IDF_DEMUXER yading@11: /** yading@11: * Given filesize and width, calculate height (assume font_height of 16) yading@11: */ yading@11: static void calculate_height(AVCodecContext *avctx, uint64_t fsize) yading@11: { yading@11: avctx->height = (fsize / ((avctx->width>>3)*2)) << 4; yading@11: } yading@11: #endif yading@11: yading@11: #if CONFIG_BINTEXT_DEMUXER yading@11: static const uint8_t next_magic[]={ yading@11: 0x1A, 0x1B, '[', '0', ';', '3', '0', ';', '4', '0', 'm', 'N', 'E', 'X', 'T', 0x00 yading@11: }; yading@11: yading@11: static int next_tag_read(AVFormatContext *avctx, uint64_t *fsize) yading@11: { yading@11: AVIOContext *pb = avctx->pb; yading@11: char buf[36]; yading@11: int len; yading@11: uint64_t start_pos = avio_size(pb) - 256; yading@11: yading@11: avio_seek(pb, start_pos, SEEK_SET); yading@11: if (avio_read(pb, buf, sizeof(next_magic)) != sizeof(next_magic)) yading@11: return -1; yading@11: if (memcmp(buf, next_magic, sizeof(next_magic))) yading@11: return -1; yading@11: if (avio_r8(pb) != 0x01) yading@11: return -1; yading@11: yading@11: *fsize -= 256; yading@11: yading@11: #define GET_EFI2_META(name,size) \ yading@11: len = avio_r8(pb); \ yading@11: if (len < 1 || len > size) \ yading@11: return -1; \ yading@11: if (avio_read(pb, buf, size) == size && *buf) { \ yading@11: buf[len] = 0; \ yading@11: av_dict_set(&avctx->metadata, name, buf, 0); \ yading@11: } yading@11: yading@11: GET_EFI2_META("filename", 12) yading@11: GET_EFI2_META("author", 20) yading@11: GET_EFI2_META("publisher", 20) yading@11: GET_EFI2_META("title", 35) yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static void predict_width(AVCodecContext *avctx, uint64_t fsize, int got_width) yading@11: { yading@11: /** attempt to guess width */ yading@11: if (!got_width) yading@11: avctx->width = fsize > 4000 ? (160<<3) : (80<<3); yading@11: } yading@11: yading@11: static int bintext_read_header(AVFormatContext *s) yading@11: { yading@11: BinDemuxContext *bin = s->priv_data; yading@11: AVIOContext *pb = s->pb; yading@11: yading@11: AVStream *st = init_stream(s); yading@11: if (!st) yading@11: return AVERROR(ENOMEM); yading@11: st->codec->codec_id = AV_CODEC_ID_BINTEXT; yading@11: yading@11: st->codec->extradata_size = 2; yading@11: st->codec->extradata = av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); yading@11: if (!st->codec->extradata) yading@11: return AVERROR(ENOMEM); yading@11: st->codec->extradata[0] = 16; yading@11: st->codec->extradata[1] = 0; yading@11: yading@11: if (pb->seekable) { yading@11: int got_width = 0; yading@11: bin->fsize = avio_size(pb); yading@11: if (ff_sauce_read(s, &bin->fsize, &got_width, 0) < 0) yading@11: next_tag_read(s, &bin->fsize); yading@11: if (!bin->width) { yading@11: predict_width(st->codec, bin->fsize, got_width); yading@11: calculate_height(st->codec, bin->fsize); yading@11: } yading@11: avio_seek(pb, 0, SEEK_SET); yading@11: } yading@11: return 0; yading@11: } yading@11: #endif /* CONFIG_BINTEXT_DEMUXER */ yading@11: yading@11: #if CONFIG_XBIN_DEMUXER yading@11: static int xbin_probe(AVProbeData *p) yading@11: { yading@11: const uint8_t *d = p->buf; yading@11: yading@11: if (AV_RL32(d) == MKTAG('X','B','I','N') && d[4] == 0x1A && yading@11: AV_RL16(d+5) > 0 && AV_RL16(d+5) <= 160 && yading@11: d[9] > 0 && d[9] <= 32) yading@11: return AVPROBE_SCORE_MAX; yading@11: return 0; yading@11: } yading@11: yading@11: static int xbin_read_header(AVFormatContext *s) yading@11: { yading@11: BinDemuxContext *bin = s->priv_data; yading@11: AVIOContext *pb = s->pb; yading@11: char fontheight, flags; yading@11: yading@11: AVStream *st = init_stream(s); yading@11: if (!st) yading@11: return AVERROR(ENOMEM); yading@11: yading@11: avio_skip(pb, 5); yading@11: st->codec->width = avio_rl16(pb)<<3; yading@11: st->codec->height = avio_rl16(pb); yading@11: fontheight = avio_r8(pb); yading@11: st->codec->height *= fontheight; yading@11: flags = avio_r8(pb); yading@11: yading@11: st->codec->extradata_size = 2; yading@11: if ((flags & BINTEXT_PALETTE)) yading@11: st->codec->extradata_size += 48; yading@11: if ((flags & BINTEXT_FONT)) yading@11: st->codec->extradata_size += fontheight * (flags & 0x10 ? 512 : 256); yading@11: st->codec->codec_id = flags & 4 ? AV_CODEC_ID_XBIN : AV_CODEC_ID_BINTEXT; yading@11: yading@11: st->codec->extradata = av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); yading@11: if (!st->codec->extradata) yading@11: return AVERROR(ENOMEM); yading@11: st->codec->extradata[0] = fontheight; yading@11: st->codec->extradata[1] = flags; yading@11: if (avio_read(pb, st->codec->extradata + 2, st->codec->extradata_size - 2) < 0) yading@11: return AVERROR(EIO); yading@11: yading@11: if (pb->seekable) { yading@11: bin->fsize = avio_size(pb) - 9 - st->codec->extradata_size; yading@11: ff_sauce_read(s, &bin->fsize, NULL, 0); yading@11: avio_seek(pb, 9 + st->codec->extradata_size, SEEK_SET); yading@11: } yading@11: yading@11: return 0; yading@11: } yading@11: #endif /* CONFIG_XBIN_DEMUXER */ yading@11: yading@11: #if CONFIG_ADF_DEMUXER yading@11: static int adf_read_header(AVFormatContext *s) yading@11: { yading@11: BinDemuxContext *bin = s->priv_data; yading@11: AVIOContext *pb = s->pb; yading@11: AVStream *st; yading@11: yading@11: if (avio_r8(pb) != 1) yading@11: return AVERROR_INVALIDDATA; yading@11: yading@11: st = init_stream(s); yading@11: if (!st) yading@11: return AVERROR(ENOMEM); yading@11: st->codec->codec_id = AV_CODEC_ID_BINTEXT; yading@11: yading@11: st->codec->extradata_size = 2 + 48 + 4096; yading@11: st->codec->extradata = av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); yading@11: if (!st->codec->extradata) yading@11: return AVERROR(ENOMEM); yading@11: st->codec->extradata[0] = 16; yading@11: st->codec->extradata[1] = BINTEXT_PALETTE|BINTEXT_FONT; yading@11: yading@11: if (avio_read(pb, st->codec->extradata + 2, 24) < 0) yading@11: return AVERROR(EIO); yading@11: avio_skip(pb, 144); yading@11: if (avio_read(pb, st->codec->extradata + 2 + 24, 24) < 0) yading@11: return AVERROR(EIO); yading@11: if (avio_read(pb, st->codec->extradata + 2 + 48, 4096) < 0) yading@11: return AVERROR(EIO); yading@11: yading@11: if (pb->seekable) { yading@11: int got_width = 0; yading@11: bin->fsize = avio_size(pb) - 1 - 192 - 4096; yading@11: st->codec->width = 80<<3; yading@11: ff_sauce_read(s, &bin->fsize, &got_width, 0); yading@11: if (!bin->width) yading@11: calculate_height(st->codec, bin->fsize); yading@11: avio_seek(pb, 1 + 192 + 4096, SEEK_SET); yading@11: } yading@11: return 0; yading@11: } yading@11: #endif /* CONFIG_ADF_DEMUXER */ yading@11: yading@11: #if CONFIG_IDF_DEMUXER yading@11: static const uint8_t idf_magic[] = { yading@11: 0x04, 0x31, 0x2e, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x15, 0x00 yading@11: }; yading@11: yading@11: static int idf_probe(AVProbeData *p) yading@11: { yading@11: if (p->buf_size < sizeof(idf_magic)) yading@11: return 0; yading@11: if (!memcmp(p->buf, idf_magic, sizeof(idf_magic))) yading@11: return AVPROBE_SCORE_MAX; yading@11: return 0; yading@11: } yading@11: yading@11: static int idf_read_header(AVFormatContext *s) yading@11: { yading@11: BinDemuxContext *bin = s->priv_data; yading@11: AVIOContext *pb = s->pb; yading@11: AVStream *st; yading@11: int got_width = 0; yading@11: yading@11: if (!pb->seekable) yading@11: return AVERROR(EIO); yading@11: yading@11: st = init_stream(s); yading@11: if (!st) yading@11: return AVERROR(ENOMEM); yading@11: st->codec->codec_id = AV_CODEC_ID_IDF; yading@11: yading@11: st->codec->extradata_size = 2 + 48 + 4096; yading@11: st->codec->extradata = av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); yading@11: if (!st->codec->extradata) yading@11: return AVERROR(ENOMEM); yading@11: st->codec->extradata[0] = 16; yading@11: st->codec->extradata[1] = BINTEXT_PALETTE|BINTEXT_FONT; yading@11: yading@11: avio_seek(pb, avio_size(pb) - 4096 - 48, SEEK_SET); yading@11: yading@11: if (avio_read(pb, st->codec->extradata + 2 + 48, 4096) < 0) yading@11: return AVERROR(EIO); yading@11: if (avio_read(pb, st->codec->extradata + 2, 48) < 0) yading@11: return AVERROR(EIO); yading@11: yading@11: bin->fsize = avio_size(pb) - 12 - 4096 - 48; yading@11: ff_sauce_read(s, &bin->fsize, &got_width, 0); yading@11: if (!bin->width) yading@11: calculate_height(st->codec, bin->fsize); yading@11: avio_seek(pb, 12, SEEK_SET); yading@11: return 0; yading@11: } yading@11: #endif /* CONFIG_IDF_DEMUXER */ yading@11: yading@11: static int read_packet(AVFormatContext *s, yading@11: AVPacket *pkt) yading@11: { yading@11: BinDemuxContext *bin = s->priv_data; yading@11: yading@11: if (bin->fsize > 0) { yading@11: if (av_get_packet(s->pb, pkt, bin->fsize) < 0) yading@11: return AVERROR(EIO); yading@11: bin->fsize = -1; /* done */ yading@11: } else if (!bin->fsize) { yading@11: if (url_feof(s->pb)) yading@11: return AVERROR(EIO); yading@11: if (av_get_packet(s->pb, pkt, bin->chars_per_frame) < 0) yading@11: return AVERROR(EIO); yading@11: } else { yading@11: return AVERROR(EIO); yading@11: } yading@11: yading@11: pkt->flags |= AV_PKT_FLAG_KEY; yading@11: return 0; yading@11: } yading@11: yading@11: #define OFFSET(x) offsetof(BinDemuxContext, x) yading@11: static const AVOption options[] = { yading@11: { "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}, yading@11: { "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 }, yading@11: { "framerate", "set framerate (frames per second)", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, AV_OPT_FLAG_DECODING_PARAM }, yading@11: { NULL }, yading@11: }; yading@11: yading@11: #define CLASS(name) \ yading@11: (const AVClass[1]){{ \ yading@11: .class_name = name, \ yading@11: .item_name = av_default_item_name, \ yading@11: .option = options, \ yading@11: .version = LIBAVUTIL_VERSION_INT, \ yading@11: }} yading@11: yading@11: #if CONFIG_BINTEXT_DEMUXER yading@11: AVInputFormat ff_bintext_demuxer = { yading@11: .name = "bin", yading@11: .long_name = NULL_IF_CONFIG_SMALL("Binary text"), yading@11: .priv_data_size = sizeof(BinDemuxContext), yading@11: .read_header = bintext_read_header, yading@11: .read_packet = read_packet, yading@11: .extensions = "bin", yading@11: .priv_class = CLASS("Binary text demuxer"), yading@11: }; yading@11: #endif yading@11: yading@11: #if CONFIG_XBIN_DEMUXER yading@11: AVInputFormat ff_xbin_demuxer = { yading@11: .name = "xbin", yading@11: .long_name = NULL_IF_CONFIG_SMALL("eXtended BINary text (XBIN)"), yading@11: .priv_data_size = sizeof(BinDemuxContext), yading@11: .read_probe = xbin_probe, yading@11: .read_header = xbin_read_header, yading@11: .read_packet = read_packet, yading@11: .priv_class = CLASS("eXtended BINary text (XBIN) demuxer"), yading@11: }; yading@11: #endif yading@11: yading@11: #if CONFIG_ADF_DEMUXER yading@11: AVInputFormat ff_adf_demuxer = { yading@11: .name = "adf", yading@11: .long_name = NULL_IF_CONFIG_SMALL("Artworx Data Format"), yading@11: .priv_data_size = sizeof(BinDemuxContext), yading@11: .read_header = adf_read_header, yading@11: .read_packet = read_packet, yading@11: .extensions = "adf", yading@11: .priv_class = CLASS("Artworx Data Format demuxer"), yading@11: }; yading@11: #endif yading@11: yading@11: #if CONFIG_IDF_DEMUXER yading@11: AVInputFormat ff_idf_demuxer = { yading@11: .name = "idf", yading@11: .long_name = NULL_IF_CONFIG_SMALL("iCE Draw File"), yading@11: .priv_data_size = sizeof(BinDemuxContext), yading@11: .read_probe = idf_probe, yading@11: .read_header = idf_read_header, yading@11: .read_packet = read_packet, yading@11: .extensions = "idf", yading@11: .priv_class = CLASS("iCE Draw File demuxer"), yading@11: }; yading@11: #endif