mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-11-21 10:55:51 +02:00
790f793844
There are lots of files that don't need it: The number of object files that actually need it went down from 2011 to 884 here. Keep it for external users in order to not cause breakages. Also improve the other headers a bit while just at it. Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
350 lines
11 KiB
C
350 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 "libavutil/mem.h"
|
|
#include "libavcodec/bmp.h"
|
|
#include "libavutil/intfloat.h"
|
|
#include "avformat.h"
|
|
#include "demux.h"
|
|
#include "internal.h"
|
|
|
|
typedef struct {
|
|
uint64_t pts;
|
|
uint64_t maxsize;
|
|
} 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++) {
|
|
int64_t pos = avio_rl64(pb);
|
|
if (avio_feof(pb) || pos < 0)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
av_add_index_entry(st, pos, 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];
|
|
FFStream *const sti = ffstream(st);
|
|
AVIOContext *pb = avctx->pb;
|
|
int n, size, ret;
|
|
int64_t ret64;
|
|
|
|
if (cine->pts >= sti->nb_index_entries)
|
|
return AVERROR_EOF;
|
|
|
|
ret64 = avio_seek(pb, sti->index_entries[cine->pts].pos, SEEK_SET);
|
|
if (ret64 < 0)
|
|
return ret64;
|
|
n = avio_rl32(pb);
|
|
if (n < 8)
|
|
return AVERROR_INVALIDDATA;
|
|
avio_skip(pb, n - 8);
|
|
size = avio_rl32(pb);
|
|
if (avio_feof(pb) || size < 0)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
if (cine->maxsize && (uint64_t)sti->index_entries[cine->pts].pos + size + n > cine->maxsize)
|
|
size = cine->maxsize - sti->index_entries[cine->pts].pos - n;
|
|
|
|
ret = av_get_packet(pb, pkt, size);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (ret != size)
|
|
cine->maxsize = (uint64_t)sti->index_entries[cine->pts].pos + n + 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;
|
|
}
|
|
|
|
const FFInputFormat ff_cine_demuxer = {
|
|
.p.name = "cine",
|
|
.p.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,
|
|
};
|