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