• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * FITS demuxer
3  * Copyright (c) 2017 Paras Chadha
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  * FITS demuxer.
25  */
26 
27 #include "libavutil/avassert.h"
28 #include "libavutil/intreadwrite.h"
29 #include "internal.h"
30 #include "libavutil/opt.h"
31 #include "libavcodec/fits.h"
32 #include "libavutil/bprint.h"
33 
34 #define FITS_BLOCK_SIZE 2880
35 
36 typedef struct FITSContext {
37     const AVClass *class;
38     AVRational framerate;
39     int first_image;
40     int64_t pts;
41 } FITSContext;
42 
fits_probe(const AVProbeData * p)43 static int fits_probe(const AVProbeData *p)
44 {
45     const uint8_t *b = p->buf;
46     if (!memcmp(b, "SIMPLE  =                    T", 30))
47         return AVPROBE_SCORE_MAX - 1;
48     return 0;
49 }
50 
fits_read_header(AVFormatContext * s)51 static int fits_read_header(AVFormatContext *s)
52 {
53     AVStream *st;
54     FITSContext * fits = s->priv_data;
55 
56     st = avformat_new_stream(s, NULL);
57     if (!st)
58         return AVERROR(ENOMEM);
59 
60     st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
61     st->codecpar->codec_id = AV_CODEC_ID_FITS;
62 
63     avpriv_set_pts_info(st, 64, fits->framerate.den, fits->framerate.num);
64     fits->pts = 0;
65     fits->first_image = 1;
66     return 0;
67 }
68 
69 /**
70  * Parses header and checks that the current HDU contains image or not
71  * It also stores the header in the avbuf and stores the size of data part in data_size
72  * @param s pointer to AVFormat Context
73  * @param fits pointer to FITSContext
74  * @param header pointer to FITSHeader
75  * @param avbuf pointer to AVBPrint to store the header
76  * @param data_size to store the size of data part
77  * @return 1 if image found, 0 if any other extension and AVERROR_INVALIDDATA otherwise
78  */
is_image(AVFormatContext * s,FITSContext * fits,FITSHeader * header,AVBPrint * avbuf,uint64_t * data_size)79 static int64_t is_image(AVFormatContext *s, FITSContext *fits, FITSHeader *header,
80                          AVBPrint *avbuf, uint64_t *data_size)
81 {
82     int i, ret, image = 0;
83     char buf[FITS_BLOCK_SIZE] = { 0 };
84     int64_t buf_size = 0, size = 0, t;
85 
86     do {
87         ret = avio_read(s->pb, buf, FITS_BLOCK_SIZE);
88         if (ret < 0) {
89             return ret;
90         } else if (ret < FITS_BLOCK_SIZE) {
91             return AVERROR_INVALIDDATA;
92         }
93 
94         av_bprint_append_data(avbuf, buf, FITS_BLOCK_SIZE);
95         ret = 0;
96         buf_size = 0;
97         while(!ret && buf_size < FITS_BLOCK_SIZE) {
98             ret = avpriv_fits_header_parse_line(s, header, buf + buf_size, NULL);
99             buf_size += 80;
100         }
101     } while (!ret);
102     if (ret < 0)
103         return ret;
104 
105     image = fits->first_image || header->image_extension;
106     fits->first_image = 0;
107 
108     if (header->groups) {
109         image = 0;
110         if (header->naxis > 1)
111             size = 1;
112     } else if (header->naxis) {
113         size = header->naxisn[0];
114     } else {
115         image = 0;
116     }
117 
118     for (i = 1; i < header->naxis; i++) {
119         if(size && header->naxisn[i] > UINT64_MAX / size)
120             return AVERROR_INVALIDDATA;
121         size *= header->naxisn[i];
122     }
123 
124     if(header->pcount > UINT64_MAX - size)
125         return AVERROR_INVALIDDATA;
126     size += header->pcount;
127 
128     t = (abs(header->bitpix) >> 3) * ((int64_t) header->gcount);
129     if(size && t > INT64_MAX / size)
130         return AVERROR_INVALIDDATA;
131     size *= t;
132 
133     if (!size) {
134         image = 0;
135     } else {
136         if(FITS_BLOCK_SIZE - 1 > INT64_MAX - size)
137             return AVERROR_INVALIDDATA;
138         size = ((size + FITS_BLOCK_SIZE - 1) / FITS_BLOCK_SIZE) * FITS_BLOCK_SIZE;
139     }
140     *data_size = size;
141     return image;
142 }
143 
fits_read_packet(AVFormatContext * s,AVPacket * pkt)144 static int fits_read_packet(AVFormatContext *s, AVPacket *pkt)
145 {
146     int64_t pos, ret;
147     uint64_t size;
148     FITSContext *fits = s->priv_data;
149     FITSHeader header;
150     AVBPrint avbuf;
151     char *buf;
152 
153     if (fits->first_image) {
154         avpriv_fits_header_init(&header, STATE_SIMPLE);
155     } else {
156         avpriv_fits_header_init(&header, STATE_XTENSION);
157     }
158 
159     av_bprint_init(&avbuf, FITS_BLOCK_SIZE, AV_BPRINT_SIZE_UNLIMITED);
160     while ((ret = is_image(s, fits, &header, &avbuf, &size)) == 0) {
161         av_bprint_finalize(&avbuf, NULL);
162         pos = avio_skip(s->pb, size);
163         if (pos < 0)
164             return pos;
165 
166         av_bprint_init(&avbuf, FITS_BLOCK_SIZE, AV_BPRINT_SIZE_UNLIMITED);
167         avpriv_fits_header_init(&header, STATE_XTENSION);
168     }
169     if (ret < 0)
170         goto fail;
171 
172     if (!av_bprint_is_complete(&avbuf)) {
173         ret = AVERROR(ENOMEM);
174         goto fail;
175     }
176 
177     av_assert0(avbuf.len <= INT64_MAX && size <= INT64_MAX);
178     if (avbuf.len + size > INT_MAX - 80)  {
179         ret = AVERROR_INVALIDDATA;
180         goto fail;
181     }
182     // Header is sent with the first line removed...
183     ret = av_new_packet(pkt, avbuf.len - 80 + size);
184     if (ret < 0)
185         goto fail;
186 
187     pkt->stream_index = 0;
188     pkt->flags |= AV_PKT_FLAG_KEY;
189 
190     ret = av_bprint_finalize(&avbuf, &buf);
191     if (ret < 0) {
192         return ret;
193     }
194 
195     memcpy(pkt->data, buf + 80, avbuf.len - 80);
196     pkt->size = avbuf.len - 80;
197     av_freep(&buf);
198     ret = avio_read(s->pb, pkt->data + pkt->size, size);
199     if (ret < 0) {
200         return ret;
201     }
202 
203     pkt->size += ret;
204     pkt->pts = fits->pts;
205     fits->pts++;
206 
207     return 0;
208 
209 fail:
210     av_bprint_finalize(&avbuf, NULL);
211     return ret;
212 }
213 
214 static const AVOption fits_options[] = {
215     { "framerate", "set the framerate", offsetof(FITSContext, framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "1"}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM},
216     { NULL },
217 };
218 
219 static const AVClass fits_demuxer_class = {
220     .class_name = "FITS demuxer",
221     .item_name  = av_default_item_name,
222     .option     = fits_options,
223     .version    = LIBAVUTIL_VERSION_INT,
224 };
225 
226 AVInputFormat ff_fits_demuxer = {
227     .name           = "fits",
228     .long_name      = NULL_IF_CONFIG_SMALL("Flexible Image Transport System"),
229     .priv_data_size = sizeof(FITSContext),
230     .read_probe     = fits_probe,
231     .read_header    = fits_read_header,
232     .read_packet    = fits_read_packet,
233     .priv_class     = &fits_demuxer_class,
234     .raw_codec_id   = AV_CODEC_ID_FITS,
235 };
236