| 1 | /* |
| 2 | * Phantom Cine demuxer |
| 3 | * Copyright (c) 2010-2011 Peter Ross <pross@xvid.org> |
| 4 | * |
| 5 | * This file is part of FFmpeg. |
| 6 | * |
| 7 | * FFmpeg is free software; you can redistribute it and/or |
| 8 | * modify it under the terms of the GNU Lesser General Public |
| 9 | * License as published by the Free Software Foundation; either |
| 10 | * version 2.1 of the License, or (at your option) any later version. |
| 11 | * |
| 12 | * FFmpeg is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 | * Lesser General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU Lesser General Public |
| 18 | * License along with FFmpeg; if not, write to the Free Software |
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 20 | */ |
| 21 | |
| 22 | /** |
| 23 | * @file |
| 24 | * Phantom Cine demuxer |
| 25 | * @author Peter Ross <pross@xvid.org> |
| 26 | */ |
| 27 | |
| 28 | #include "libavutil/intreadwrite.h" |
| 29 | #include "libavcodec/bmp.h" |
| 30 | #include "libavutil/intfloat.h" |
| 31 | #include "avformat.h" |
| 32 | #include "internal.h" |
| 33 | |
| 34 | typedef struct { |
| 35 | uint64_t pts; |
| 36 | } CineDemuxContext; |
| 37 | |
| 38 | /** Compression */ |
| 39 | enum { |
| 40 | CC_RGB = 0, /**< Gray */ |
| 41 | CC_LEAD = 1, /**< LEAD (M)JPEG */ |
| 42 | CC_UNINT = 2 /**< Uninterpolated color image (CFA field indicates color ordering) */ |
| 43 | }; |
| 44 | |
| 45 | /** Color Filter Array */ |
| 46 | enum { |
| 47 | CFA_NONE = 0, /**< GRAY */ |
| 48 | CFA_VRI = 1, /**< GBRG/RGGB */ |
| 49 | CFA_VRIV6 = 2, /**< BGGR/GRBG */ |
| 50 | CFA_BAYER = 3, /**< GB/RG */ |
| 51 | CFA_BAYERFLIP = 4, /**< RG/GB */ |
| 52 | |
| 53 | CFA_TLGRAY = 0x80000000, |
| 54 | CFA_TRGRAY = 0x40000000, |
| 55 | CFA_BLGRAY = 0x20000000, |
| 56 | CFA_BRGRAY = 0x10000000 |
| 57 | }; |
| 58 | |
| 59 | static int cine_read_probe(AVProbeData *p) |
| 60 | { |
| 61 | int HeaderSize; |
| 62 | if (p->buf[0] == 'C' && p->buf[1] == 'I' && // Type |
| 63 | (HeaderSize = AV_RL16(p->buf + 2)) >= 0x2C && // HeaderSize |
| 64 | AV_RL16(p->buf + 4) <= CC_UNINT && // Compression |
| 65 | AV_RL16(p->buf + 6) <= 1 && // Version |
| 66 | AV_RL32(p->buf + 20) && // ImageCount |
| 67 | AV_RL32(p->buf + 24) >= HeaderSize && // OffImageHeader |
| 68 | AV_RL32(p->buf + 28) >= HeaderSize && // OffSetup |
| 69 | AV_RL32(p->buf + 32) >= HeaderSize) // OffImageOffsets |
| 70 | return AVPROBE_SCORE_MAX; |
| 71 | return 0; |
| 72 | } |
| 73 | |
| 74 | static int set_metadata_int(AVDictionary **dict, const char *key, int value, int allow_zero) |
| 75 | { |
| 76 | if (value || allow_zero) { |
| 77 | return av_dict_set_int(dict, key, value, 0); |
| 78 | } |
| 79 | return 0; |
| 80 | } |
| 81 | |
| 82 | static int set_metadata_float(AVDictionary **dict, const char *key, float value, int allow_zero) |
| 83 | { |
| 84 | if (value != 0 || allow_zero) { |
| 85 | char tmp[64]; |
| 86 | snprintf(tmp, sizeof(tmp), "%f", value); |
| 87 | return av_dict_set(dict, key, tmp, 0); |
| 88 | } |
| 89 | return 0; |
| 90 | } |
| 91 | |
| 92 | static int cine_read_header(AVFormatContext *avctx) |
| 93 | { |
| 94 | AVIOContext *pb = avctx->pb; |
| 95 | AVStream *st; |
| 96 | unsigned int version, compression, offImageHeader, offSetup, offImageOffsets, biBitCount, length, CFA; |
| 97 | int vflip; |
| 98 | char *description; |
| 99 | uint64_t i; |
| 100 | |
| 101 | st = avformat_new_stream(avctx, NULL); |
| 102 | if (!st) |
| 103 | return AVERROR(ENOMEM); |
| 104 | st->codec->codec_type = AVMEDIA_TYPE_VIDEO; |
| 105 | st->codec->codec_id = AV_CODEC_ID_RAWVIDEO; |
| 106 | st->codec->codec_tag = 0; |
| 107 | |
| 108 | /* CINEFILEHEADER structure */ |
| 109 | avio_skip(pb, 4); // Type, Headersize |
| 110 | |
| 111 | compression = avio_rl16(pb); |
| 112 | version = avio_rl16(pb); |
| 113 | if (version != 1) { |
| 114 | avpriv_request_sample(avctx, "uknown version %i", version); |
| 115 | return AVERROR_INVALIDDATA; |
| 116 | } |
| 117 | |
| 118 | avio_skip(pb, 12); // FirstMovieImage, TotalImageCount, FirstImageNumber |
| 119 | |
| 120 | st->duration = avio_rl32(pb); |
| 121 | offImageHeader = avio_rl32(pb); |
| 122 | offSetup = avio_rl32(pb); |
| 123 | offImageOffsets = avio_rl32(pb); |
| 124 | |
| 125 | avio_skip(pb, 8); // TriggerTime |
| 126 | |
| 127 | /* BITMAPINFOHEADER structure */ |
| 128 | avio_seek(pb, offImageHeader, SEEK_SET); |
| 129 | avio_skip(pb, 4); //biSize |
| 130 | st->codec->width = avio_rl32(pb); |
| 131 | st->codec->height = avio_rl32(pb); |
| 132 | |
| 133 | if (avio_rl16(pb) != 1) // biPlanes |
| 134 | return AVERROR_INVALIDDATA; |
| 135 | |
| 136 | biBitCount = avio_rl16(pb); |
| 137 | if (biBitCount != 8 && biBitCount != 16 && biBitCount != 24 && biBitCount != 48) { |
| 138 | avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount); |
| 139 | return AVERROR_INVALIDDATA; |
| 140 | } |
| 141 | |
| 142 | switch (avio_rl32(pb)) { |
| 143 | case BMP_RGB: |
| 144 | vflip = 0; |
| 145 | break; |
| 146 | case 0x100: /* BI_PACKED */ |
| 147 | st->codec->codec_tag = MKTAG('B', 'I', 'T', 0); |
| 148 | vflip = 1; |
| 149 | break; |
| 150 | default: |
| 151 | avpriv_request_sample(avctx, "unknown bitmap compression"); |
| 152 | return AVERROR_INVALIDDATA; |
| 153 | } |
| 154 | |
| 155 | avio_skip(pb, 4); // biSizeImage |
| 156 | |
| 157 | /* parse SETUP structure */ |
| 158 | avio_seek(pb, offSetup, SEEK_SET); |
| 159 | avio_skip(pb, 140); // FrameRatae16 .. descriptionOld |
| 160 | if (avio_rl16(pb) != 0x5453) |
| 161 | return AVERROR_INVALIDDATA; |
| 162 | length = avio_rl16(pb); |
| 163 | if (length < 0x163C) { |
| 164 | avpriv_request_sample(avctx, "short SETUP header"); |
| 165 | return AVERROR_INVALIDDATA; |
| 166 | } |
| 167 | |
| 168 | avio_skip(pb, 616); // Binning .. bFlipH |
| 169 | if (!avio_rl32(pb) ^ vflip) { |
| 170 | st->codec->extradata = av_strdup("BottomUp"); |
| 171 | st->codec->extradata_size = 9; |
| 172 | } |
| 173 | |
| 174 | avio_skip(pb, 4); // Grid |
| 175 | |
| 176 | avpriv_set_pts_info(st, 64, 1, avio_rl32(pb)); |
| 177 | |
| 178 | avio_skip(pb, 20); // Shutter .. bEnableColor |
| 179 | |
| 180 | set_metadata_int(&st->metadata, "camera_version", avio_rl32(pb), 0); |
| 181 | set_metadata_int(&st->metadata, "firmware_version", avio_rl32(pb), 0); |
| 182 | set_metadata_int(&st->metadata, "software_version", avio_rl32(pb), 0); |
| 183 | set_metadata_int(&st->metadata, "recording_timezone", avio_rl32(pb), 0); |
| 184 | |
| 185 | CFA = avio_rl32(pb); |
| 186 | |
| 187 | set_metadata_int(&st->metadata, "brightness", avio_rl32(pb), 1); |
| 188 | set_metadata_int(&st->metadata, "contrast", avio_rl32(pb), 1); |
| 189 | set_metadata_int(&st->metadata, "gamma", avio_rl32(pb), 1); |
| 190 | |
| 191 | avio_skip(pb, 12 + 16); // Reserved1 .. AutoExpRect |
| 192 | set_metadata_float(&st->metadata, "wbgain[0].r", av_int2float(avio_rl32(pb)), 1); |
| 193 | set_metadata_float(&st->metadata, "wbgain[0].b", av_int2float(avio_rl32(pb)), 1); |
| 194 | avio_skip(pb, 36); // WBGain[1].. WBView |
| 195 | |
| 196 | st->codec->bits_per_coded_sample = avio_rl32(pb); |
| 197 | |
| 198 | if (compression == CC_RGB) { |
| 199 | if (biBitCount == 8) { |
| 200 | st->codec->pix_fmt = AV_PIX_FMT_GRAY8; |
| 201 | } else if (biBitCount == 16) { |
| 202 | st->codec->pix_fmt = AV_PIX_FMT_GRAY16LE; |
| 203 | } else if (biBitCount == 24) { |
| 204 | st->codec->pix_fmt = AV_PIX_FMT_BGR24; |
| 205 | } else if (biBitCount == 48) { |
| 206 | st->codec->pix_fmt = AV_PIX_FMT_BGR48LE; |
| 207 | } else { |
| 208 | avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount); |
| 209 | return AVERROR_INVALIDDATA; |
| 210 | } |
| 211 | } else if (compression == CC_UNINT) { |
| 212 | switch (CFA & 0xFFFFFF) { |
| 213 | case CFA_BAYER: |
| 214 | if (biBitCount == 8) { |
| 215 | st->codec->pix_fmt = AV_PIX_FMT_BAYER_GBRG8; |
| 216 | } else if (biBitCount == 16) { |
| 217 | st->codec->pix_fmt = AV_PIX_FMT_BAYER_GBRG16LE; |
| 218 | } else { |
| 219 | avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount); |
| 220 | return AVERROR_INVALIDDATA; |
| 221 | } |
| 222 | break; |
| 223 | case CFA_BAYERFLIP: |
| 224 | if (biBitCount == 8) { |
| 225 | st->codec->pix_fmt = AV_PIX_FMT_BAYER_RGGB8; |
| 226 | } else if (biBitCount == 16) { |
| 227 | st->codec->pix_fmt = AV_PIX_FMT_BAYER_RGGB16LE; |
| 228 | } else { |
| 229 | avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount); |
| 230 | return AVERROR_INVALIDDATA; |
| 231 | } |
| 232 | break; |
| 233 | default: |
| 234 | avpriv_request_sample(avctx, "unsupported Color Field Array (CFA) %i", CFA & 0xFFFFFF); |
| 235 | return AVERROR_INVALIDDATA; |
| 236 | } |
| 237 | } else { //CC_LEAD |
| 238 | avpriv_request_sample(avctx, "unsupported compression %i", compression); |
| 239 | return AVERROR_INVALIDDATA; |
| 240 | } |
| 241 | |
| 242 | avio_skip(pb, 668); // Conv8Min ... Sensor |
| 243 | |
| 244 | set_metadata_int(&st->metadata, "shutter_ns", avio_rl32(pb), 0); |
| 245 | |
| 246 | avio_skip(pb, 24); // EDRShutterNs ... ImHeightAcq |
| 247 | |
| 248 | #define DESCRIPTION_SIZE 4096 |
| 249 | description = av_malloc(DESCRIPTION_SIZE + 1); |
| 250 | if (!description) |
| 251 | return AVERROR(ENOMEM); |
| 252 | i = avio_get_str(pb, DESCRIPTION_SIZE, description, DESCRIPTION_SIZE + 1); |
| 253 | if (i < DESCRIPTION_SIZE) |
| 254 | avio_skip(pb, DESCRIPTION_SIZE - i); |
| 255 | if (description[0]) |
| 256 | av_dict_set(&st->metadata, "description", description, AV_DICT_DONT_STRDUP_VAL); |
| 257 | else |
| 258 | av_free(description); |
| 259 | |
| 260 | avio_skip(pb, 1176); // RisingEdge ... cmUser |
| 261 | |
| 262 | set_metadata_int(&st->metadata, "enable_crop", avio_rl32(pb), 1); |
| 263 | set_metadata_int(&st->metadata, "crop_left", avio_rl32(pb), 1); |
| 264 | set_metadata_int(&st->metadata, "crop_top", avio_rl32(pb), 1); |
| 265 | set_metadata_int(&st->metadata, "crop_right", avio_rl32(pb), 1); |
| 266 | set_metadata_int(&st->metadata, "crop_bottom", avio_rl32(pb), 1); |
| 267 | |
| 268 | /* parse image offsets */ |
| 269 | avio_seek(pb, offImageOffsets, SEEK_SET); |
| 270 | for (i = 0; i < st->duration; i++) |
| 271 | av_add_index_entry(st, avio_rl64(pb), i, 0, 0, AVINDEX_KEYFRAME); |
| 272 | |
| 273 | return 0; |
| 274 | } |
| 275 | |
| 276 | static int cine_read_packet(AVFormatContext *avctx, AVPacket *pkt) |
| 277 | { |
| 278 | CineDemuxContext *cine = avctx->priv_data; |
| 279 | AVStream *st = avctx->streams[0]; |
| 280 | AVIOContext *pb = avctx->pb; |
| 281 | int n, size, ret; |
| 282 | |
| 283 | if (cine->pts >= st->duration) |
| 284 | return AVERROR_EOF; |
| 285 | |
| 286 | avio_seek(pb, st->index_entries[cine->pts].pos, SEEK_SET); |
| 287 | n = avio_rl32(pb); |
| 288 | if (n < 8) |
| 289 | return AVERROR_INVALIDDATA; |
| 290 | avio_skip(pb, n - 8); |
| 291 | size = avio_rl32(pb); |
| 292 | |
| 293 | ret = av_get_packet(pb, pkt, size); |
| 294 | if (ret < 0) |
| 295 | return ret; |
| 296 | |
| 297 | pkt->pts = cine->pts++; |
| 298 | pkt->stream_index = 0; |
| 299 | pkt->flags |= AV_PKT_FLAG_KEY; |
| 300 | return 0; |
| 301 | } |
| 302 | |
| 303 | static int cine_read_seek(AVFormatContext *avctx, int stream_index, int64_t timestamp, int flags) |
| 304 | { |
| 305 | CineDemuxContext *cine = avctx->priv_data; |
| 306 | |
| 307 | if ((flags & AVSEEK_FLAG_FRAME) || (flags & AVSEEK_FLAG_BYTE)) |
| 308 | return AVERROR(ENOSYS); |
| 309 | |
| 310 | if (!avctx->pb->seekable) |
| 311 | return AVERROR(EIO); |
| 312 | |
| 313 | cine->pts = timestamp; |
| 314 | return 0; |
| 315 | } |
| 316 | |
| 317 | AVInputFormat ff_cine_demuxer = { |
| 318 | .name = "cine", |
| 319 | .long_name = NULL_IF_CONFIG_SMALL("Phantom Cine"), |
| 320 | .priv_data_size = sizeof(CineDemuxContext), |
| 321 | .read_probe = cine_read_probe, |
| 322 | .read_header = cine_read_header, |
| 323 | .read_packet = cine_read_packet, |
| 324 | .read_seek = cine_read_seek, |
| 325 | }; |