/src/ffmpeg/libavformat/fitsdec.c
Line | Count | Source |
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 "demux.h" |
28 | | #include "internal.h" |
29 | | #include "libavutil/opt.h" |
30 | | #include "libavcodec/fits.h" |
31 | | |
32 | 51.9k | #define FITS_BLOCK_SIZE 2880 |
33 | | |
34 | | typedef struct FITSContext { |
35 | | const AVClass *class; |
36 | | AVRational framerate; |
37 | | int first_image; |
38 | | } FITSContext; |
39 | | |
40 | | static int fits_probe(const AVProbeData *p) |
41 | 964k | { |
42 | 964k | const uint8_t *b = p->buf; |
43 | 964k | if (!memcmp(b, "SIMPLE = T", 30)) |
44 | 365 | return AVPROBE_SCORE_MAX - 1; |
45 | 964k | return 0; |
46 | 964k | } |
47 | | |
48 | | static int fits_read_header(AVFormatContext *s) |
49 | 2.31k | { |
50 | 2.31k | AVStream *st; |
51 | 2.31k | FITSContext * fits = s->priv_data; |
52 | | |
53 | 2.31k | st = avformat_new_stream(s, NULL); |
54 | 2.31k | if (!st) |
55 | 0 | return AVERROR(ENOMEM); |
56 | | |
57 | 2.31k | st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; |
58 | 2.31k | st->codecpar->codec_id = AV_CODEC_ID_FITS; |
59 | | |
60 | 2.31k | avpriv_set_pts_info(st, 64, fits->framerate.den, fits->framerate.num); |
61 | 2.31k | fits->first_image = 1; |
62 | 2.31k | return 0; |
63 | 2.31k | } |
64 | | |
65 | | /** |
66 | | * Parses header and checks that the current HDU contains image or not |
67 | | * It also stores the header in the avbuf and stores the size of data part in data_size |
68 | | * @param s pointer to AVFormat Context |
69 | | * @param fits pointer to FITSContext |
70 | | * @param header pointer to FITSHeader |
71 | | * @param pkt pointer to AVPacket to store the header |
72 | | * @param data_size to store the size of data part |
73 | | * @return 1 if image found, 0 if any other extension and AVERROR code otherwise |
74 | | */ |
75 | | static int is_image(AVFormatContext *s, FITSContext *fits, FITSHeader *header, |
76 | | AVPacket *pkt, uint64_t *data_size) |
77 | 5.20k | { |
78 | 5.20k | int i, ret, image = 0; |
79 | 5.20k | int64_t size = 0, t; |
80 | | |
81 | 17.7k | do { |
82 | 17.7k | const uint8_t *buf, *buf_end; |
83 | 17.7k | ret = av_append_packet(s->pb, pkt, FITS_BLOCK_SIZE); |
84 | 17.7k | if (ret < 0) { |
85 | 2.12k | return ret; |
86 | 15.6k | } else if (ret < FITS_BLOCK_SIZE) { |
87 | 891 | return AVERROR_INVALIDDATA; |
88 | 891 | } |
89 | | |
90 | 14.7k | ret = 0; |
91 | 14.7k | buf_end = pkt->data + pkt->size; |
92 | 14.7k | buf = buf_end - FITS_BLOCK_SIZE; |
93 | 503k | while(!ret && buf < buf_end) { |
94 | 488k | ret = avpriv_fits_header_parse_line(s, header, buf, NULL); |
95 | 488k | buf += 80; |
96 | 488k | } |
97 | 14.7k | } while (!ret); |
98 | 2.18k | if (ret < 0) |
99 | 715 | return ret; |
100 | | |
101 | 1.47k | image = fits->first_image || header->image_extension; |
102 | 1.47k | fits->first_image = 0; |
103 | | |
104 | 1.47k | if (header->groups) { |
105 | 35 | image = 0; |
106 | 35 | if (header->naxis > 1) |
107 | 5 | size = 1; |
108 | 1.43k | } else if (header->naxis) { |
109 | 1.06k | size = header->naxisn[0]; |
110 | 1.06k | } else { |
111 | 368 | image = 0; |
112 | 368 | } |
113 | | |
114 | 2.08k | for (i = 1; i < header->naxis; i++) { |
115 | 626 | if(size && header->naxisn[i] > UINT64_MAX / size) |
116 | 11 | return AVERROR_INVALIDDATA; |
117 | 615 | size *= header->naxisn[i]; |
118 | 615 | } |
119 | | |
120 | 1.46k | if(header->pcount > UINT64_MAX - size) |
121 | 1 | return AVERROR_INVALIDDATA; |
122 | 1.45k | size += header->pcount; |
123 | | |
124 | 1.45k | t = (abs(header->bitpix) >> 3) * ((int64_t) header->gcount); |
125 | 1.45k | if(size && t > INT64_MAX / size) |
126 | 77 | return AVERROR_INVALIDDATA; |
127 | 1.38k | size *= t; |
128 | | |
129 | 1.38k | if (!size) { |
130 | 426 | image = 0; |
131 | 956 | } else { |
132 | 956 | if(FITS_BLOCK_SIZE - 1 > INT64_MAX - size) |
133 | 0 | return AVERROR_INVALIDDATA; |
134 | 956 | size = ((size + FITS_BLOCK_SIZE - 1) / FITS_BLOCK_SIZE) * FITS_BLOCK_SIZE; |
135 | 956 | } |
136 | 1.38k | *data_size = size; |
137 | 1.38k | return image; |
138 | 1.38k | } |
139 | | |
140 | | static int fits_read_packet(AVFormatContext *s, AVPacket *pkt) |
141 | 4.62k | { |
142 | 4.62k | uint64_t size; |
143 | 4.62k | FITSContext *fits = s->priv_data; |
144 | 4.62k | FITSHeader header; |
145 | 4.62k | int ret; |
146 | | |
147 | 4.62k | if (fits->first_image) { |
148 | 3.32k | avpriv_fits_header_init(&header, STATE_SIMPLE); |
149 | 3.32k | } else { |
150 | 1.30k | avpriv_fits_header_init(&header, STATE_XTENSION); |
151 | 1.30k | } |
152 | | |
153 | 5.20k | while ((ret = is_image(s, fits, &header, pkt, &size)) == 0) { |
154 | 803 | int64_t pos = avio_skip(s->pb, size); |
155 | 803 | if (pos < 0) |
156 | 224 | return pos; |
157 | | |
158 | 579 | avpriv_fits_header_init(&header, STATE_XTENSION); |
159 | 579 | av_packet_unref(pkt); |
160 | 579 | } |
161 | 4.40k | if (ret < 0) |
162 | 3.82k | return ret; |
163 | | |
164 | 579 | pkt->stream_index = 0; |
165 | 579 | pkt->flags |= AV_PKT_FLAG_KEY; |
166 | 579 | pkt->duration = 1; |
167 | | // Header is sent with the first line removed... |
168 | 579 | pkt->data += 80; |
169 | 579 | pkt->size -= 80; |
170 | | |
171 | 579 | if (size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE - pkt->size) |
172 | 108 | return AVERROR(ERANGE); |
173 | | |
174 | 471 | ret = av_append_packet(s->pb, pkt, size); |
175 | 471 | if (ret < 0) |
176 | 120 | return ret; |
177 | | |
178 | 351 | return 0; |
179 | 471 | } |
180 | | |
181 | | static const AVOption fits_options[] = { |
182 | | { "framerate", "set the framerate", offsetof(FITSContext, framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "1"}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM}, |
183 | | { NULL }, |
184 | | }; |
185 | | |
186 | | static const AVClass fits_demuxer_class = { |
187 | | .class_name = "FITS demuxer", |
188 | | .item_name = av_default_item_name, |
189 | | .option = fits_options, |
190 | | .version = LIBAVUTIL_VERSION_INT, |
191 | | .category = AV_CLASS_CATEGORY_DEMUXER, |
192 | | }; |
193 | | |
194 | | const FFInputFormat ff_fits_demuxer = { |
195 | | .p.name = "fits", |
196 | | .p.long_name = NULL_IF_CONFIG_SMALL("Flexible Image Transport System"), |
197 | | .p.priv_class = &fits_demuxer_class, |
198 | | .p.flags = AVFMT_NOTIMESTAMPS, |
199 | | .priv_data_size = sizeof(FITSContext), |
200 | | .read_probe = fits_probe, |
201 | | .read_header = fits_read_header, |
202 | | .read_packet = fits_read_packet, |
203 | | }; |