1/*
2 * Phanton 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 "avformat.h"
31#include "internal.h"
32
33typedef struct {
34    uint64_t pts;
35} CineDemuxContext;
36
37/** Compression */
38enum {
39    CC_RGB   = 0,  /**< Gray */
40    CC_LEAD  = 1,  /**< LEAD (M)JPEG */
41    CC_UNINT = 2   /**< Uninterpolated color image (CFA field indicates color ordering)  */
42};
43
44/** Color Filter Array */
45enum {
46    CFA_NONE      = 0,  /**< GRAY */
47    CFA_VRI       = 1,  /**< GBRG/RGGB */
48    CFA_VRIV6     = 2,  /**< BGGR/GRBG */
49    CFA_BAYER     = 3,  /**< GB/RG */
50    CFA_BAYERFLIP = 4,  /**< RG/GB */
51
52    CFA_TLGRAY    = 0x80000000,
53    CFA_TRGRAY    = 0x40000000,
54    CFA_BLGRAY    = 0x20000000,
55    CFA_BRGRAY    = 0x10000000
56};
57
58static int cine_read_probe(AVProbeData *p)
59{
60    int HeaderSize;
61    if (p->buf[0] == 'C' && p->buf[1] == 'I' &&  // Type
62        (HeaderSize = AV_RL16(p->buf + 2)) >= 0x2C &&  // HeaderSize
63        AV_RL16(p->buf + 4) <= CC_UNINT &&       // Compression
64        AV_RL16(p->buf + 6) <= 1 &&              // Version
65        AV_RL32(p->buf + 20) &&                  // ImageCount
66        AV_RL32(p->buf + 24) >= HeaderSize &&    // OffImageHeader
67        AV_RL32(p->buf + 28) >= HeaderSize &&    // OffSetup
68        AV_RL32(p->buf + 32) >= HeaderSize)      // OffImageOffsets
69        return AVPROBE_SCORE_MAX;
70    return 0;
71}
72
73static int set_metadata_int(AVDictionary **dict, const char *key, int value)
74{
75    if (value) {
76        char buf[64];
77        snprintf(buf, sizeof(buf), "%i", value);
78        return av_dict_set(dict, key, buf, 0);
79    }
80    return 0;
81}
82
83static int cine_read_header(AVFormatContext *avctx)
84{
85    AVIOContext *pb = avctx->pb;
86    AVStream *st;
87    unsigned int version, compression, offImageHeader, offSetup, offImageOffsets, biBitCount, length, CFA;
88    int vflip;
89    char *description;
90    uint64_t i;
91
92    st = avformat_new_stream(avctx, NULL);
93    if (!st)
94        return AVERROR(ENOMEM);
95    st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
96    st->codec->codec_id   = AV_CODEC_ID_RAWVIDEO;
97    st->codec->codec_tag  = 0;
98
99    /* CINEFILEHEADER structure */
100    avio_skip(pb, 4); // Type, Headersize
101
102    compression = avio_rl16(pb);
103    version     = avio_rl16(pb);
104    if (version != 1) {
105        avpriv_request_sample(avctx, "uknown version %i", version);
106        return AVERROR_INVALIDDATA;
107    }
108
109    avio_skip(pb, 12); // FirstMovieImage, TotalImageCount, FirstImageNumber
110
111    st->duration    = avio_rl32(pb);
112    offImageHeader  = avio_rl32(pb);
113    offSetup        = avio_rl32(pb);
114    offImageOffsets = avio_rl32(pb);
115
116    avio_skip(pb, 8); // TriggerTime
117
118    /* BITMAPINFOHEADER structure */
119    avio_seek(pb, offImageHeader, SEEK_SET);
120    avio_skip(pb, 4); //biSize
121    st->codec->width      = avio_rl32(pb);
122    st->codec->height     = avio_rl32(pb);
123
124    if (avio_rl16(pb) != 1) // biPlanes
125        return AVERROR_INVALIDDATA;
126
127    biBitCount = avio_rl16(pb);
128    if (biBitCount != 8 && biBitCount != 16 && biBitCount != 24 && biBitCount != 48) {
129        avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount);
130        return AVERROR_INVALIDDATA;
131    }
132
133    switch (avio_rl32(pb)) {
134    case BMP_RGB:
135        vflip = 0;
136        break;
137    case 0x100: /* BI_PACKED */
138        st->codec->codec_tag = MKTAG('B', 'I', 'T', 0);
139        vflip = 1;
140        break;
141    default:
142        avpriv_request_sample(avctx, "unknown bitmap compression");
143        return AVERROR_INVALIDDATA;
144    }
145
146    avio_skip(pb, 4); // biSizeImage
147
148    /* parse SETUP structure */
149    avio_seek(pb, offSetup, SEEK_SET);
150    avio_skip(pb, 140); // FrameRatae16 .. descriptionOld
151    if (avio_rl16(pb) != 0x5453)
152        return AVERROR_INVALIDDATA;
153    length = avio_rl16(pb);
154    if (length < 0x163C) {
155        avpriv_request_sample(avctx, "short SETUP header");
156        return AVERROR_INVALIDDATA;
157    }
158
159    avio_skip(pb, 616); // Binning .. bFlipH
160    if (!avio_rl32(pb) ^ vflip) {
161        st->codec->extradata  = av_strdup("BottomUp");
162        st->codec->extradata_size  = 9;
163    }
164
165    avio_skip(pb, 4); // Grid
166
167    avpriv_set_pts_info(st, 64, 1, avio_rl32(pb));
168
169    avio_skip(pb, 20); // Shutter .. bEnableColor
170
171    set_metadata_int(&st->metadata, "camera_version", avio_rl32(pb));
172    set_metadata_int(&st->metadata, "firmware_version", avio_rl32(pb));
173    set_metadata_int(&st->metadata, "software_version", avio_rl32(pb));
174    set_metadata_int(&st->metadata, "recording_timezone", avio_rl32(pb));
175
176    CFA = avio_rl32(pb);
177
178    set_metadata_int(&st->metadata, "brightness", avio_rl32(pb));
179    set_metadata_int(&st->metadata, "contrast", avio_rl32(pb));
180    set_metadata_int(&st->metadata, "gamma", avio_rl32(pb));
181
182    avio_skip(pb, 72); // Reserved1 .. WBView
183
184    st->codec->bits_per_coded_sample = avio_rl32(pb);
185
186    if (compression == CC_RGB) {
187        if (biBitCount == 8) {
188            st->codec->pix_fmt = AV_PIX_FMT_GRAY8;
189        } else if (biBitCount == 16) {
190            st->codec->pix_fmt = AV_PIX_FMT_GRAY16LE;
191        } else if (biBitCount == 24) {
192            st->codec->pix_fmt = AV_PIX_FMT_BGR24;
193        } else if (biBitCount == 48) {
194            st->codec->pix_fmt = AV_PIX_FMT_BGR48LE;
195        } else {
196            avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount);
197            return AVERROR_INVALIDDATA;
198        }
199    } else if (compression == CC_UNINT) {
200        switch (CFA & 0xFFFFFF) {
201        case CFA_BAYER:
202            if (biBitCount == 8) {
203                st->codec->pix_fmt = AV_PIX_FMT_BAYER_GBRG8;
204            } else if (biBitCount == 16) {
205                st->codec->pix_fmt = AV_PIX_FMT_BAYER_GBRG16LE;
206            } else {
207                avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount);
208                return AVERROR_INVALIDDATA;
209            }
210            break;
211        case CFA_BAYERFLIP:
212            if (biBitCount == 8) {
213                st->codec->pix_fmt = AV_PIX_FMT_BAYER_RGGB8;
214            } else if (biBitCount == 16) {
215                st->codec->pix_fmt = AV_PIX_FMT_BAYER_RGGB16LE;
216            } else {
217                avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount);
218                return AVERROR_INVALIDDATA;
219            }
220            break;
221        default:
222           avpriv_request_sample(avctx, "unsupported Color Field Array (CFA) %i", CFA & 0xFFFFFF);
223            return AVERROR_INVALIDDATA;
224        }
225    } else { //CC_LEAD
226        avpriv_request_sample(avctx, "unsupported compression %i", compression);
227        return AVERROR_INVALIDDATA;
228    }
229
230    avio_skip(pb, 696); // Conv8Min ... ImHeightAcq
231
232#define DESCRIPTION_SIZE 4096
233    description = av_malloc(DESCRIPTION_SIZE + 1);
234    if (!description)
235        return AVERROR(ENOMEM);
236    i = avio_get_str(pb, DESCRIPTION_SIZE, description, DESCRIPTION_SIZE + 1);
237    if (i < DESCRIPTION_SIZE)
238        avio_skip(pb, DESCRIPTION_SIZE - i);
239    if (description[0])
240        av_dict_set(&st->metadata, "description", description, AV_DICT_DONT_STRDUP_VAL);
241    else
242        av_free(description);
243
244    /* parse image offsets */
245    avio_seek(pb, offImageOffsets, SEEK_SET);
246    for (i = 0; i < st->duration; i++)
247        av_add_index_entry(st, avio_rl64(pb), i, 0, 0, AVINDEX_KEYFRAME);
248
249    return 0;
250}
251
252static int cine_read_packet(AVFormatContext *avctx, AVPacket *pkt)
253{
254    CineDemuxContext *cine = avctx->priv_data;
255    AVStream *st = avctx->streams[0];
256    AVIOContext *pb = avctx->pb;
257    int n, size, ret;
258
259    if (cine->pts >= st->duration)
260        return AVERROR_EOF;
261
262    avio_seek(pb, st->index_entries[cine->pts].pos, SEEK_SET);
263    n = avio_rl32(pb);
264    if (n < 8)
265        return AVERROR_INVALIDDATA;
266    avio_skip(pb, n - 8);
267    size = avio_rl32(pb);
268
269    ret = av_get_packet(pb, pkt, size);
270    if (ret < 0)
271        return ret;
272
273    pkt->pts = cine->pts++;
274    pkt->stream_index = 0;
275    pkt->flags |= AV_PKT_FLAG_KEY;
276    return 0;
277}
278
279static int cine_read_seek(AVFormatContext *avctx, int stream_index, int64_t timestamp, int flags)
280{
281    CineDemuxContext *cine = avctx->priv_data;
282
283    if ((flags & AVSEEK_FLAG_FRAME) || (flags & AVSEEK_FLAG_BYTE))
284        return AVERROR(ENOSYS);
285
286    if (!avctx->pb->seekable)
287        return AVERROR(EIO);
288
289    cine->pts = timestamp;
290    return 0;
291}
292
293AVInputFormat ff_cine_demuxer = {
294    .name           = "cine",
295    .long_name      = NULL_IF_CONFIG_SMALL("Phantom Cine"),
296    .priv_data_size = sizeof(CineDemuxContext),
297    .read_probe     = cine_read_probe,
298    .read_header    = cine_read_header,
299    .read_packet    = cine_read_packet,
300    .read_seek      = cine_read_seek,
301};
302