blob: 763b93ba2e178956bef1984ad06b011758699089
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 | |
54 | #define CFA_TLGRAY 0x80000000U |
55 | #define CFA_TRGRAY 0x40000000U |
56 | #define CFA_BLGRAY 0x20000000U |
57 | #define CFA_BRGRAY 0x10000000U |
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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; |
105 | st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO; |
106 | st->codecpar->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, "unknown 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->codecpar->width = avio_rl32(pb); |
131 | st->codecpar->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->codecpar->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->codecpar->extradata = av_strdup("BottomUp"); |
171 | st->codecpar->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->codecpar->bits_per_coded_sample = avio_rl32(pb); |
197 | |
198 | if (compression == CC_RGB) { |
199 | if (biBitCount == 8) { |
200 | st->codecpar->format = AV_PIX_FMT_GRAY8; |
201 | } else if (biBitCount == 16) { |
202 | st->codecpar->format = AV_PIX_FMT_GRAY16LE; |
203 | } else if (biBitCount == 24) { |
204 | st->codecpar->format = AV_PIX_FMT_BGR24; |
205 | } else if (biBitCount == 48) { |
206 | st->codecpar->format = 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->codecpar->format = AV_PIX_FMT_BAYER_GBRG8; |
216 | } else if (biBitCount == 16) { |
217 | st->codecpar->format = 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->codecpar->format = AV_PIX_FMT_BAYER_RGGB8; |
226 | } else if (biBitCount == 16) { |
227 | st->codecpar->format = 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 & AVIO_SEEKABLE_NORMAL)) |
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 | }; |
326 |