ffmpeg/libavformat/cinedec.c
Andreas Rheinhardt 790f793844 avutil/common: Don't auto-include mem.h
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>
2024-03-31 00:08:43 +01:00

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,
};