From 40f88796c69ae453e5fc4b8cd6432fa0ca85b923 Mon Sep 17 00:00:00 2001 From: Peter Ross Date: Sat, 15 Mar 2014 17:28:01 +1100 Subject: Phantom Cine demuxer (iteration 2014.3) Signed-off-by: Michael Niedermayer --- libavformat/cinedec.c | 300 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 libavformat/cinedec.c (limited to 'libavformat/cinedec.c') diff --git a/libavformat/cinedec.c b/libavformat/cinedec.c new file mode 100644 index 0000000000..003ca519bc --- /dev/null +++ b/libavformat/cinedec.c @@ -0,0 +1,300 @@ +/* + * Phanton Cine demuxer + * Copyright (c) 2010-2011 Peter Ross + * + * 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 + * Phantom Cine demuxer + * @author Peter Ross + */ + +#include "libavutil/intreadwrite.h" +#include "libavcodec/bmp.h" +#include "avformat.h" +#include "internal.h" + +typedef struct { + uint64_t pts; +} CineDemuxContext; + +/** Compression */ +enum { + CC_RGB = 0, /**< Gray */ + CC_LEAD = 1, /**< LEAD (M)JPEG */ + CC_UNINT = 2 /**< Uninterpolated color image (CFA field indicates color ordering) */ +}; + +/** Color Filter Array */ +enum { + CFA_NONE = 0, /**< GRAY */ + CFA_VRI = 1, /**< GBRG/RGGB */ + CFA_VRIV6 = 2, /**< BGGR/GRBG */ + CFA_BAYER = 3, /**< GB/RG */ + CFA_BAYERFLIP = 4, /**< RG/GB */ + + CFA_TLGRAY = 0x80000000, + CFA_TRGRAY = 0x40000000, + CFA_BLGRAY = 0x20000000, + CFA_BRGRAY = 0x10000000 +}; + +static int cine_read_probe(AVProbeData *p) +{ + int HeaderSize; + if (p->buf[0] == 'C' && p->buf[1] == 'I' && // Type + (HeaderSize = AV_RL16(p->buf + 2)) >= 0x2C && // HeaderSize + AV_RL16(p->buf + 4) <= CC_UNINT && // Compression + AV_RL16(p->buf + 6) <= 1 && // Version + AV_RL32(p->buf + 20) && // ImageCount + AV_RL32(p->buf + 24) >= HeaderSize && // OffImageHeader + AV_RL32(p->buf + 28) >= HeaderSize && // OffSetup + AV_RL32(p->buf + 32) >= HeaderSize) // OffImageOffsets + return AVPROBE_SCORE_MAX; + return 0; +} + +static int set_metadata_int(AVDictionary **dict, const char *key, int value) +{ + if (value) { + char buf[64]; + snprintf(buf, sizeof(buf - 1), "%i", value); + buf[sizeof(buf) - 1] = 0; + return av_dict_set(dict, key, buf, 0); + } + return 0; +} + +static int cine_read_header(AVFormatContext *avctx) +{ + AVIOContext *pb = avctx->pb; + AVStream *st; + unsigned int version, compression, offImageHeader, offSetup, offImageOffsets, biBitCount, length, CFA; + int vflip; + char *description; + uint64_t i; + + st = avformat_new_stream(avctx, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_RAWVIDEO; + st->codec->codec_tag = 0; + + /* CINEFILEHEADER structure */ + avio_skip(pb, 4); // Type, Headersize + + compression = avio_rl16(pb); + version = avio_rl16(pb); + if (version != 1) { + avpriv_request_sample(avctx, "uknown version %i", version); + return AVERROR_INVALIDDATA; + } + + avio_skip(pb, 12); // FirstMovieImage, TotalImageCount, FirstImageNumber + + st->duration = avio_rl32(pb); + offImageHeader = avio_rl32(pb); + offSetup = avio_rl32(pb); + offImageOffsets = avio_rl32(pb); + + avio_skip(pb, 8); // TriggerTime + + /* BITMAPINFOHEADER structure */ + avio_seek(pb, offImageHeader, SEEK_SET); + avio_skip(pb, 4); //biSize + st->codec->width = avio_rl32(pb); + st->codec->height = avio_rl32(pb); + + if (avio_rl16(pb) != 1) // biPlanes + return AVERROR_INVALIDDATA; + + biBitCount = avio_rl16(pb); + if (biBitCount != 8 && biBitCount != 16 && biBitCount != 24 && biBitCount != 48) + return AVERROR_INVALIDDATA; + + switch (avio_rl32(pb)) { + case BMP_RGB: + vflip = 0; + break; + case 0x100: /* BI_PACKED */ + st->codec->codec_tag = MKTAG('B', 'I', 'T', 0); + vflip = 1; + break; + default: + avpriv_request_sample(avctx, "unknown bitmap compression"); + return AVERROR_INVALIDDATA; + } + + avio_skip(pb, 4); // biSizeImage + + /* parse SETUP structure */ + avio_seek(pb, offSetup, SEEK_SET); + avio_skip(pb, 140); // FrameRatae16 .. descriptionOld + if (avio_rl16(pb) != 0x5453) + return AVERROR_INVALIDDATA; + length = avio_rl16(pb); + if (length < 0x163C) { + avpriv_request_sample(avctx, "short SETUP header"); + return AVERROR_INVALIDDATA; + } + + avio_skip(pb, 616); // Binning .. bFlipH + if (!avio_rl32(pb) ^ vflip) { + st->codec->extradata = av_strdup("BottomUp"); + st->codec->extradata_size = 9; + } + + avio_skip(pb, 4); // Grid + + avpriv_set_pts_info(st, 64, 1, avio_rl32(pb)); + + avio_skip(pb, 20); // Shutter .. bEnableColor + + set_metadata_int(&st->metadata, "camera_version", avio_rl32(pb)); + set_metadata_int(&st->metadata, "firmware_version", avio_rl32(pb)); + set_metadata_int(&st->metadata, "software_version", avio_rl32(pb)); + set_metadata_int(&st->metadata, "recording_timezone", avio_rl32(pb)); + + CFA = avio_rl32(pb); + + set_metadata_int(&st->metadata, "brightness", avio_rl32(pb)); + set_metadata_int(&st->metadata, "contrast", avio_rl32(pb)); + set_metadata_int(&st->metadata, "gamma", avio_rl32(pb)); + + avio_skip(pb, 72); // Reserved1 .. WBView + + st->codec->bits_per_coded_sample = avio_rl32(pb); + + if (compression == CC_RGB) { + if (biBitCount == 8) { + st->codec->pix_fmt = AV_PIX_FMT_GRAY8; + } else if (biBitCount == 16) { + st->codec->pix_fmt = AV_PIX_FMT_GRAY16LE; + } else if (biBitCount == 24) { + st->codec->pix_fmt = AV_PIX_FMT_BGR24; + } else if (biBitCount == 48) { + st->codec->pix_fmt = AV_PIX_FMT_BGR48LE; + } else { + avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount); + return AVERROR_INVALIDDATA; + } + } else if (compression == CC_UNINT) { + switch (CFA & 0xFFFFFF) { + case CFA_BAYER: + if (biBitCount == 8) { + st->codec->pix_fmt = AV_PIX_FMT_BAYER_GBRG8; + } else if (biBitCount == 16) { + st->codec->pix_fmt = AV_PIX_FMT_BAYER_GBRG16LE; + } else { + avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount); + return AVERROR_INVALIDDATA; + } + break; + case CFA_BAYERFLIP: + if (biBitCount == 8) { + st->codec->pix_fmt = AV_PIX_FMT_BAYER_RGGB8; + } else if (biBitCount == 16) { + st->codec->pix_fmt = AV_PIX_FMT_BAYER_RGGB16LE; + } else { + avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount); + return AVERROR_INVALIDDATA; + } + break; + default: + avpriv_request_sample(avctx, "unsupported Color Field Array (CFA) %i", CFA & 0xFFFFFF); + return AVERROR_INVALIDDATA; + } + } else { //CC_LEAD + avpriv_request_sample(avctx, "unsupported compression %i", compression); + return AVERROR_INVALIDDATA; + } + + avio_skip(pb, 696); // Conv8Min ... ImHeightAcq + +#define DESCRIPTION_SIZE 4096 + description = av_malloc(DESCRIPTION_SIZE + 1); + if (!description) + return AVERROR(ENOMEM); + i = avio_get_str(pb, DESCRIPTION_SIZE, description, DESCRIPTION_SIZE + 1); + if (i < DESCRIPTION_SIZE) + avio_skip(pb, DESCRIPTION_SIZE - i); + if (description[0]) + av_dict_set(&st->metadata, "description", description, AV_DICT_DONT_STRDUP_VAL); + else + av_free(description); + + /* parse image offsets */ + avio_seek(pb, offImageOffsets, SEEK_SET); + for (i = 0; i < st->duration; i++) + av_add_index_entry(st, avio_rl64(pb), i, 0, 0, AVINDEX_KEYFRAME); + + return 0; +} + +static int cine_read_packet(AVFormatContext *avctx, AVPacket *pkt) +{ + CineDemuxContext *cine = avctx->priv_data; + AVStream *st = avctx->streams[0]; + AVIOContext *pb = avctx->pb; + int n, size, ret; + + if (cine->pts >= st->duration) + return AVERROR_EOF; + + avio_seek(pb, st->index_entries[cine->pts].pos, SEEK_SET); + n = avio_rl32(pb); + if (n < 8) + return AVERROR_INVALIDDATA; + avio_skip(pb, n - 8); + size = avio_rl32(pb); + + ret = av_get_packet(pb, pkt, size); + if (ret < 0) + return ret; + + pkt->pts = cine->pts++; + pkt->stream_index = 0; + pkt->flags |= AV_PKT_FLAG_KEY; + return 0; +} + +static int cine_read_seek(AVFormatContext *avctx, int stream_index, int64_t timestamp, int flags) +{ + CineDemuxContext *cine = avctx->priv_data; + + if ((flags & AVSEEK_FLAG_FRAME) || (flags & AVSEEK_FLAG_BYTE)) + return AVERROR(ENOSYS); + + if (!avctx->pb->seekable) + return AVERROR(EIO); + + cine->pts = timestamp; + return 0; +} + +AVInputFormat ff_cine_demuxer = { + .name = "cine", + .long_name = NULL_IF_CONFIG_SMALL("Phantom Cine"), + .priv_data_size = sizeof(CineDemuxContext), + .read_probe = cine_read_probe, + .read_header = cine_read_header, + .read_packet = cine_read_packet, + .read_seek = cine_read_seek, +}; -- cgit v1.2.3