FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/cinedec.c
Date: 2025-06-01 09:29:47
Exec Total Coverage
Lines: 125 204 61.3%
Functions: 5 6 83.3%
Branches: 45 120 37.5%

Line Branch Exec Source
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 "libavutil/mem.h"
30 #include "libavcodec/bmp.h"
31 #include "libavutil/intfloat.h"
32 #include "avformat.h"
33 #include "demux.h"
34 #include "internal.h"
35
36 typedef struct {
37 uint64_t pts;
38 uint64_t maxsize;
39 } CineDemuxContext;
40
41 /** Compression */
42 enum {
43 CC_RGB = 0, /**< Gray */
44 CC_LEAD = 1, /**< LEAD (M)JPEG */
45 CC_UNINT = 2 /**< Uninterpolated color image (CFA field indicates color ordering) */
46 };
47
48 /** Color Filter Array */
49 enum {
50 CFA_NONE = 0, /**< GRAY */
51 CFA_VRI = 1, /**< GBRG/RGGB */
52 CFA_VRIV6 = 2, /**< BGGR/GRBG */
53 CFA_BAYER = 3, /**< GB/RG */
54 CFA_BAYERFLIP = 4, /**< RG/GB */
55 CFA_BAYERFLIPB = 5, /**< GR/BG */
56 CFA_BAYERFLIPH = 6, /**< BG/GR */
57 };
58
59 #define CFA_TLGRAY 0x80000000U
60 #define CFA_TRGRAY 0x40000000U
61 #define CFA_BLGRAY 0x20000000U
62 #define CFA_BRGRAY 0x10000000U
63
64 7235 static int cine_read_probe(const AVProbeData *p)
65 {
66 int HeaderSize;
67
4/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 7226 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 8 times.
7235 if (p->buf[0] == 'C' && p->buf[1] == 'I' && // Type
68
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 (HeaderSize = AV_RL16(p->buf + 2)) >= 0x2C && // HeaderSize
69
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 AV_RL16(p->buf + 4) <= CC_UNINT && // Compression
70
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 AV_RL16(p->buf + 6) <= 1 && // Version
71
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 AV_RL32(p->buf + 20) && // ImageCount
72
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 AV_RL32(p->buf + 24) >= HeaderSize && // OffImageHeader
73
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 AV_RL32(p->buf + 28) >= HeaderSize && // OffSetup
74
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 AV_RL32(p->buf + 32) >= HeaderSize) // OffImageOffsets
75 1 return AVPROBE_SCORE_MAX;
76 7234 return 0;
77 }
78
79 13 static int set_metadata_int(AVDictionary **dict, const char *key, int value, int allow_zero)
80 {
81
3/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
13 if (value || allow_zero) {
82 13 return av_dict_set_int(dict, key, value, 0);
83 }
84 return 0;
85 }
86
87 2 static int set_metadata_float(AVDictionary **dict, const char *key, float value, int allow_zero)
88 {
89
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 if (value != 0 || allow_zero) {
90 char tmp[64];
91 2 snprintf(tmp, sizeof(tmp), "%f", value);
92 2 return av_dict_set(dict, key, tmp, 0);
93 }
94 return 0;
95 }
96
97 1 static int cine_read_header(AVFormatContext *avctx)
98 {
99 1 AVIOContext *pb = avctx->pb;
100 AVStream *st;
101 unsigned int version, compression, offImageHeader, offSetup, offImageOffsets, biBitCount, length, CFA;
102 int vflip;
103 char *description;
104 uint64_t i;
105
106 1 st = avformat_new_stream(avctx, NULL);
107
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!st)
108 return AVERROR(ENOMEM);
109 1 st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
110 1 st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
111 1 st->codecpar->codec_tag = 0;
112
113 /* CINEFILEHEADER structure */
114 1 avio_skip(pb, 4); // Type, Headersize
115
116 1 compression = avio_rl16(pb);
117 1 version = avio_rl16(pb);
118
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (version != 1) {
119 avpriv_request_sample(avctx, "unknown version %i", version);
120 return AVERROR_INVALIDDATA;
121 }
122
123 1 avio_skip(pb, 12); // FirstMovieImage, TotalImageCount, FirstImageNumber
124
125 1 st->duration = avio_rl32(pb);
126 1 offImageHeader = avio_rl32(pb);
127 1 offSetup = avio_rl32(pb);
128 1 offImageOffsets = avio_rl32(pb);
129
130 1 avio_skip(pb, 8); // TriggerTime
131
132 /* BITMAPINFOHEADER structure */
133 1 avio_seek(pb, offImageHeader, SEEK_SET);
134 1 avio_skip(pb, 4); //biSize
135 1 st->codecpar->width = avio_rl32(pb);
136 1 st->codecpar->height = avio_rl32(pb);
137
138
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (avio_rl16(pb) != 1) // biPlanes
139 return AVERROR_INVALIDDATA;
140
141 1 biBitCount = avio_rl16(pb);
142
1/8
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1 if (biBitCount != 8 && biBitCount != 16 && biBitCount != 24 && biBitCount != 48) {
143 avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount);
144 return AVERROR_INVALIDDATA;
145 }
146
147
1/3
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 switch (avio_rl32(pb)) {
148 1 case BMP_RGB:
149 1 vflip = 0;
150 1 break;
151 case 0x100: /* BI_PACKED */
152 st->codecpar->codec_tag = MKTAG('B', 'I', 'T', 0);
153 vflip = 1;
154 break;
155 default:
156 avpriv_request_sample(avctx, "unknown bitmap compression");
157 return AVERROR_INVALIDDATA;
158 }
159
160 1 avio_skip(pb, 4); // biSizeImage
161
162 /* parse SETUP structure */
163 1 avio_seek(pb, offSetup, SEEK_SET);
164 1 avio_skip(pb, 140); // FrameRatae16 .. descriptionOld
165
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (avio_rl16(pb) != 0x5453)
166 return AVERROR_INVALIDDATA;
167 1 length = avio_rl16(pb);
168
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (length < 0x163C) {
169 avpriv_request_sample(avctx, "short SETUP header");
170 return AVERROR_INVALIDDATA;
171 }
172
173 1 avio_skip(pb, 616); // Binning .. bFlipH
174
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (!avio_rl32(pb) ^ vflip) {
175 1 st->codecpar->extradata = av_strdup("BottomUp");
176
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!st->codecpar->extradata) {
177 st->codecpar->extradata_size = 0;
178 return AVERROR(ENOMEM);
179 }
180 1 st->codecpar->extradata_size = 9;
181 }
182
183 1 avio_skip(pb, 4); // Grid
184
185 1 avpriv_set_pts_info(st, 64, 1, avio_rl32(pb));
186
187 1 avio_skip(pb, 20); // Shutter .. bEnableColor
188
189 1 set_metadata_int(&st->metadata, "camera_version", avio_rl32(pb), 0);
190 1 set_metadata_int(&st->metadata, "firmware_version", avio_rl32(pb), 0);
191 1 set_metadata_int(&st->metadata, "software_version", avio_rl32(pb), 0);
192 1 set_metadata_int(&st->metadata, "recording_timezone", avio_rl32(pb), 0);
193
194 1 CFA = avio_rl32(pb);
195
196 1 set_metadata_int(&st->metadata, "brightness", avio_rl32(pb), 1);
197 1 set_metadata_int(&st->metadata, "contrast", avio_rl32(pb), 1);
198 1 set_metadata_int(&st->metadata, "gamma", avio_rl32(pb), 1);
199
200 1 avio_skip(pb, 12 + 16); // Reserved1 .. AutoExpRect
201 1 set_metadata_float(&st->metadata, "wbgain[0].r", av_int2float(avio_rl32(pb)), 1);
202 1 set_metadata_float(&st->metadata, "wbgain[0].b", av_int2float(avio_rl32(pb)), 1);
203 1 avio_skip(pb, 36); // WBGain[1].. WBView
204
205 1 st->codecpar->bits_per_coded_sample = avio_rl32(pb);
206
207
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (compression == CC_RGB) {
208 if (biBitCount == 8) {
209 st->codecpar->format = AV_PIX_FMT_GRAY8;
210 } else if (biBitCount == 16) {
211 st->codecpar->format = AV_PIX_FMT_GRAY16LE;
212 } else if (biBitCount == 24) {
213 st->codecpar->format = AV_PIX_FMT_BGR24;
214 } else if (biBitCount == 48) {
215 st->codecpar->format = AV_PIX_FMT_BGR48LE;
216 } else {
217 avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount);
218 return AVERROR_INVALIDDATA;
219 }
220
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 } else if (compression == CC_UNINT) {
221
1/5
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
1 switch (CFA & 0xFFFFFF) {
222 1 case CFA_BAYER:
223
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (biBitCount == 8) {
224 1 st->codecpar->format = AV_PIX_FMT_BAYER_GBRG8;
225 } else if (biBitCount == 16) {
226 st->codecpar->format = AV_PIX_FMT_BAYER_GBRG16LE;
227 } else {
228 avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount);
229 return AVERROR_INVALIDDATA;
230 }
231 1 break;
232 case CFA_BAYERFLIP:
233 if (biBitCount == 8) {
234 st->codecpar->format = AV_PIX_FMT_BAYER_RGGB8;
235 } else if (biBitCount == 16) {
236 st->codecpar->format = AV_PIX_FMT_BAYER_RGGB16LE;
237 } else {
238 avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount);
239 return AVERROR_INVALIDDATA;
240 }
241 break;
242 case CFA_BAYERFLIPB:
243 if (biBitCount == 8) {
244 st->codecpar->format = AV_PIX_FMT_BAYER_GRBG8;
245 } else if (biBitCount == 16) {
246 st->codecpar->format = AV_PIX_FMT_BAYER_GRBG16LE;
247 } else {
248 avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount);
249 return AVERROR_INVALIDDATA;
250 }
251 break;
252 case CFA_BAYERFLIPH:
253 if (biBitCount == 8) {
254 st->codecpar->format = AV_PIX_FMT_BAYER_BGGR8;
255 } else if (biBitCount == 16) {
256 st->codecpar->format = AV_PIX_FMT_BAYER_BGGR16LE;
257 } else {
258 avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount);
259 return AVERROR_INVALIDDATA;
260 }
261 break;
262 default:
263 avpriv_request_sample(avctx, "unsupported Color Field Array (CFA) %i", CFA & 0xFFFFFF);
264 return AVERROR_INVALIDDATA;
265 }
266 } else { //CC_LEAD
267 avpriv_request_sample(avctx, "unsupported compression %i", compression);
268 return AVERROR_INVALIDDATA;
269 }
270
271 1 avio_skip(pb, 668); // Conv8Min ... Sensor
272
273 1 set_metadata_int(&st->metadata, "shutter_ns", avio_rl32(pb), 0);
274
275 1 avio_skip(pb, 24); // EDRShutterNs ... ImHeightAcq
276
277 #define DESCRIPTION_SIZE 4096
278 1 description = av_malloc(DESCRIPTION_SIZE + 1);
279
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!description)
280 return AVERROR(ENOMEM);
281 1 i = avio_get_str(pb, DESCRIPTION_SIZE, description, DESCRIPTION_SIZE + 1);
282
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (i < DESCRIPTION_SIZE)
283 1 avio_skip(pb, DESCRIPTION_SIZE - i);
284
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (description[0])
285 av_dict_set(&st->metadata, "description", description, AV_DICT_DONT_STRDUP_VAL);
286 else
287 1 av_free(description);
288
289 1 avio_skip(pb, 1176); // RisingEdge ... cmUser
290
291 1 set_metadata_int(&st->metadata, "enable_crop", avio_rl32(pb), 1);
292 1 set_metadata_int(&st->metadata, "crop_left", avio_rl32(pb), 1);
293 1 set_metadata_int(&st->metadata, "crop_top", avio_rl32(pb), 1);
294 1 set_metadata_int(&st->metadata, "crop_right", avio_rl32(pb), 1);
295 1 set_metadata_int(&st->metadata, "crop_bottom", avio_rl32(pb), 1);
296
297 /* parse image offsets */
298 1 avio_seek(pb, offImageOffsets, SEEK_SET);
299
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 1 times.
101 for (i = 0; i < st->duration; i++) {
300 100 int64_t pos = avio_rl64(pb);
301
2/4
✓ Branch 1 taken 100 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 100 times.
100 if (avio_feof(pb) || pos < 0)
302 return AVERROR_INVALIDDATA;
303
304 100 av_add_index_entry(st, pos, i, 0, 0, AVINDEX_KEYFRAME);
305 }
306
307 1 return 0;
308 }
309
310 4 static int cine_read_packet(AVFormatContext *avctx, AVPacket *pkt)
311 {
312 4 CineDemuxContext *cine = avctx->priv_data;
313 4 AVStream *st = avctx->streams[0];
314 4 FFStream *const sti = ffstream(st);
315 4 AVIOContext *pb = avctx->pb;
316 int n, size, ret;
317 int64_t ret64;
318
319
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (cine->pts >= sti->nb_index_entries)
320 return AVERROR_EOF;
321
322 4 ret64 = avio_seek(pb, sti->index_entries[cine->pts].pos, SEEK_SET);
323
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (ret64 < 0)
324 return ret64;
325 4 n = avio_rl32(pb);
326
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (n < 8)
327 2 return AVERROR_INVALIDDATA;
328 2 avio_skip(pb, n - 8);
329 2 size = avio_rl32(pb);
330
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 if (avio_feof(pb) || size < 0)
331 return AVERROR_INVALIDDATA;
332
333
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 if (cine->maxsize && (uint64_t)sti->index_entries[cine->pts].pos + size + n > cine->maxsize)
334 size = cine->maxsize - sti->index_entries[cine->pts].pos - n;
335
336 2 ret = av_get_packet(pb, pkt, size);
337
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ret < 0)
338 return ret;
339
340
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (ret != size)
341 1 cine->maxsize = (uint64_t)sti->index_entries[cine->pts].pos + n + ret;
342
343 2 pkt->pts = cine->pts++;
344 2 pkt->stream_index = 0;
345 2 pkt->flags |= AV_PKT_FLAG_KEY;
346 2 return 0;
347 }
348
349 static int cine_read_seek(AVFormatContext *avctx, int stream_index, int64_t timestamp, int flags)
350 {
351 CineDemuxContext *cine = avctx->priv_data;
352
353 if ((flags & AVSEEK_FLAG_FRAME) || (flags & AVSEEK_FLAG_BYTE))
354 return AVERROR(ENOSYS);
355
356 if (!(avctx->pb->seekable & AVIO_SEEKABLE_NORMAL))
357 return AVERROR(EIO);
358
359 cine->pts = timestamp;
360 return 0;
361 }
362
363 const FFInputFormat ff_cine_demuxer = {
364 .p.name = "cine",
365 .p.long_name = NULL_IF_CONFIG_SMALL("Phantom Cine"),
366 .priv_data_size = sizeof(CineDemuxContext),
367 .read_probe = cine_read_probe,
368 .read_header = cine_read_header,
369 .read_packet = cine_read_packet,
370 .read_seek = cine_read_seek,
371 };
372