mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-11-21 10:55:51 +02:00
cea7c19cda
Those are private fields, no reason to have them exposed in a public header. Since there are some (semi-)public fields located after these, even though this section is supposed to be private, keep some dummy padding there until the next major bump to preserve ABI compatibility.
334 lines
11 KiB
C
334 lines
11 KiB
C
/*
|
|
* Phantom Cine demuxer
|
|
* Copyright (c) 2010-2011 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
|
|
* Phantom Cine demuxer
|
|
* @author Peter Ross <pross@xvid.org>
|
|
*/
|
|
|
|
#include "libavutil/intreadwrite.h"
|
|
#include "libavcodec/bmp.h"
|
|
#include "libavutil/intfloat.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 */
|
|
};
|
|
|
|
#define CFA_TLGRAY 0x80000000U
|
|
#define CFA_TRGRAY 0x40000000U
|
|
#define CFA_BLGRAY 0x20000000U
|
|
#define CFA_BRGRAY 0x10000000U
|
|
|
|
static int cine_read_probe(const 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, int allow_zero)
|
|
{
|
|
if (value || allow_zero) {
|
|
return av_dict_set_int(dict, key, value, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int set_metadata_float(AVDictionary **dict, const char *key, float value, int allow_zero)
|
|
{
|
|
if (value != 0 || allow_zero) {
|
|
char tmp[64];
|
|
snprintf(tmp, sizeof(tmp), "%f", value);
|
|
return av_dict_set(dict, key, tmp, 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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
|
|
st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
|
|
st->codecpar->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, "unknown 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->codecpar->width = avio_rl32(pb);
|
|
st->codecpar->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) {
|
|
avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount);
|
|
return AVERROR_INVALIDDATA;
|
|
}
|
|
|
|
switch (avio_rl32(pb)) {
|
|
case BMP_RGB:
|
|
vflip = 0;
|
|
break;
|
|
case 0x100: /* BI_PACKED */
|
|
st->codecpar->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->codecpar->extradata = av_strdup("BottomUp");
|
|
if (!st->codecpar->extradata) {
|
|
st->codecpar->extradata_size = 0;
|
|
return AVERROR(ENOMEM);
|
|
}
|
|
st->codecpar->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), 0);
|
|
set_metadata_int(&st->metadata, "firmware_version", avio_rl32(pb), 0);
|
|
set_metadata_int(&st->metadata, "software_version", avio_rl32(pb), 0);
|
|
set_metadata_int(&st->metadata, "recording_timezone", avio_rl32(pb), 0);
|
|
|
|
CFA = avio_rl32(pb);
|
|
|
|
set_metadata_int(&st->metadata, "brightness", avio_rl32(pb), 1);
|
|
set_metadata_int(&st->metadata, "contrast", avio_rl32(pb), 1);
|
|
set_metadata_int(&st->metadata, "gamma", avio_rl32(pb), 1);
|
|
|
|
avio_skip(pb, 12 + 16); // Reserved1 .. AutoExpRect
|
|
set_metadata_float(&st->metadata, "wbgain[0].r", av_int2float(avio_rl32(pb)), 1);
|
|
set_metadata_float(&st->metadata, "wbgain[0].b", av_int2float(avio_rl32(pb)), 1);
|
|
avio_skip(pb, 36); // WBGain[1].. WBView
|
|
|
|
st->codecpar->bits_per_coded_sample = avio_rl32(pb);
|
|
|
|
if (compression == CC_RGB) {
|
|
if (biBitCount == 8) {
|
|
st->codecpar->format = AV_PIX_FMT_GRAY8;
|
|
} else if (biBitCount == 16) {
|
|
st->codecpar->format = AV_PIX_FMT_GRAY16LE;
|
|
} else if (biBitCount == 24) {
|
|
st->codecpar->format = AV_PIX_FMT_BGR24;
|
|
} else if (biBitCount == 48) {
|
|
st->codecpar->format = 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->codecpar->format = AV_PIX_FMT_BAYER_GBRG8;
|
|
} else if (biBitCount == 16) {
|
|
st->codecpar->format = 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->codecpar->format = AV_PIX_FMT_BAYER_RGGB8;
|
|
} else if (biBitCount == 16) {
|
|
st->codecpar->format = 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, 668); // Conv8Min ... Sensor
|
|
|
|
set_metadata_int(&st->metadata, "shutter_ns", avio_rl32(pb), 0);
|
|
|
|
avio_skip(pb, 24); // EDRShutterNs ... 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);
|
|
|
|
avio_skip(pb, 1176); // RisingEdge ... cmUser
|
|
|
|
set_metadata_int(&st->metadata, "enable_crop", avio_rl32(pb), 1);
|
|
set_metadata_int(&st->metadata, "crop_left", avio_rl32(pb), 1);
|
|
set_metadata_int(&st->metadata, "crop_top", avio_rl32(pb), 1);
|
|
set_metadata_int(&st->metadata, "crop_right", avio_rl32(pb), 1);
|
|
set_metadata_int(&st->metadata, "crop_bottom", avio_rl32(pb), 1);
|
|
|
|
/* parse image offsets */
|
|
avio_seek(pb, offImageOffsets, SEEK_SET);
|
|
for (i = 0; i < st->duration; i++) {
|
|
if (avio_feof(pb))
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
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->internal->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 & AVIO_SEEKABLE_NORMAL))
|
|
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,
|
|
};
|