Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * FITS image decoder | ||
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 image decoder | ||
25 | * | ||
26 | * Specification: https://fits.gsfc.nasa.gov/fits_standard.html Version 3.0 | ||
27 | * | ||
28 | * Support all 2d images alongwith, bzero, bscale and blank keywords. | ||
29 | * RGBA images are supported as NAXIS3 = 3 or 4 i.e. Planes in RGBA order. Also CTYPE = 'RGB ' should be present. | ||
30 | * Also to interpret data, values are linearly scaled using min-max scaling but not RGB images. | ||
31 | */ | ||
32 | |||
33 | #include "avcodec.h" | ||
34 | #include "codec_internal.h" | ||
35 | #include "decode.h" | ||
36 | #include <float.h> | ||
37 | #include "libavutil/intreadwrite.h" | ||
38 | #include "libavutil/intfloat.h" | ||
39 | #include "libavutil/dict.h" | ||
40 | #include "libavutil/opt.h" | ||
41 | #include "fits.h" | ||
42 | |||
43 | typedef struct FITSContext { | ||
44 | const AVClass *class; | ||
45 | int blank_val; | ||
46 | } FITSContext; | ||
47 | |||
48 | /** | ||
49 | * Calculate the data_min and data_max values from the data. | ||
50 | * This is called if the values are not present in the header. | ||
51 | * @param ptr8 pointer to the data | ||
52 | * @param header pointer to the header | ||
53 | * @param end pointer to end of packet | ||
54 | * @return 0 if calculated successfully otherwise AVERROR_INVALIDDATA | ||
55 | */ | ||
56 | 6 | static int fill_data_min_max(const uint8_t *ptr8, FITSHeader *header, const uint8_t *end) | |
57 | { | ||
58 | uint8_t t8; | ||
59 | int16_t t16; | ||
60 | int32_t t32; | ||
61 | int64_t t64; | ||
62 | float tflt; | ||
63 | double tdbl; | ||
64 | int i, j; | ||
65 | |||
66 | 6 | header->data_min = DBL_MAX; | |
67 | 6 | header->data_max = -DBL_MAX; | |
68 |
3/7✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
6 | switch (header->bitpix) { |
69 | #define CASE_N(a, t, rd) \ | ||
70 | case a: \ | ||
71 | for (i = 0; i < header->naxisn[1]; i++) { \ | ||
72 | for (j = 0; j < header->naxisn[0]; j++) { \ | ||
73 | t = rd; \ | ||
74 | if (!header->blank_found || t != header->blank) { \ | ||
75 | if (t > header->data_max) \ | ||
76 | header->data_max = t; \ | ||
77 | if (t < header->data_min) \ | ||
78 | header->data_min = t; \ | ||
79 | } \ | ||
80 | ptr8 += abs(a) >> 3; \ | ||
81 | } \ | ||
82 | } \ | ||
83 | break | ||
84 | |||
85 |
9/12✗ Branch 1 not taken.
✓ Branch 2 taken 26642 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 26640 times.
✓ Branch 7 taken 116 times.
✓ Branch 8 taken 26526 times.
✓ Branch 9 taken 26642 times.
✓ Branch 10 taken 346 times.
✓ Branch 11 taken 346 times.
✓ Branch 12 taken 2 times.
|
26990 | CASE_N(-64, tdbl, av_int2double(AV_RB64(ptr8))); |
86 |
9/12✗ Branch 1 not taken.
✓ Branch 2 taken 22236 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 22234 times.
✓ Branch 7 taken 104 times.
✓ Branch 8 taken 22132 times.
✓ Branch 9 taken 22236 times.
✓ Branch 10 taken 218 times.
✓ Branch 11 taken 218 times.
✓ Branch 12 taken 2 times.
|
22456 | CASE_N(-32, tflt, av_int2float(AV_RB32(ptr8))); |
87 | ✗ | CASE_N(8, t8, ptr8[0]); | |
88 | ✗ | CASE_N(16, t16, AV_RB16(ptr8)); | |
89 |
11/12✓ Branch 0 taken 131072 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 131054 times.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 298 times.
✓ Branch 5 taken 130756 times.
✓ Branch 6 taken 10 times.
✓ Branch 7 taken 131044 times.
✓ Branch 8 taken 131072 times.
✓ Branch 9 taken 512 times.
✓ Branch 10 taken 512 times.
✓ Branch 11 taken 2 times.
|
131586 | CASE_N(32, t32, AV_RB32(ptr8)); |
90 | ✗ | CASE_N(64, t64, AV_RB64(ptr8)); | |
91 | ✗ | default: | |
92 | ✗ | return AVERROR_INVALIDDATA; | |
93 | } | ||
94 | 6 | return 0; | |
95 | } | ||
96 | |||
97 | /** | ||
98 | * Read the fits header and store the values in FITSHeader pointed by header | ||
99 | * @param avctx AVCodec context | ||
100 | * @param ptr pointer to pointer to the data | ||
101 | * @param header pointer to the FITSHeader | ||
102 | * @param end pointer to end of packet | ||
103 | * @param metadata pointer to pointer to AVDictionary to store metadata | ||
104 | * @return 0 if calculated successfully otherwise AVERROR_INVALIDDATA | ||
105 | */ | ||
106 | 215 | static int fits_read_header(AVCodecContext *avctx, const uint8_t **ptr, FITSHeader *header, | |
107 | const uint8_t *end, AVDictionary **metadata) | ||
108 | { | ||
109 | 215 | const uint8_t *ptr8 = *ptr; | |
110 | int lines_read, bytes_left, i, ret; | ||
111 | size_t size; | ||
112 | |||
113 | 215 | lines_read = 1; // to account for first header line, SIMPLE or XTENSION which is not included in packet... | |
114 | 215 | avpriv_fits_header_init(header, STATE_BITPIX); | |
115 | do { | ||
116 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2613 times.
|
2613 | if (end - ptr8 < 80) |
117 | ✗ | return AVERROR_INVALIDDATA; | |
118 | 2613 | ret = avpriv_fits_header_parse_line(avctx, header, ptr8, &metadata); | |
119 | 2613 | ptr8 += 80; | |
120 | 2613 | lines_read++; | |
121 |
2/2✓ Branch 0 taken 2398 times.
✓ Branch 1 taken 215 times.
|
2613 | } while (!ret); |
122 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 215 times.
|
215 | if (ret < 0) |
123 | ✗ | return ret; | |
124 | |||
125 | 215 | bytes_left = (((lines_read + 35) / 36) * 36 - lines_read) * 80; | |
126 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 215 times.
|
215 | if (end - ptr8 < bytes_left) |
127 | ✗ | return AVERROR_INVALIDDATA; | |
128 | 215 | ptr8 += bytes_left; | |
129 | |||
130 |
6/8✓ Branch 0 taken 153 times.
✓ Branch 1 taken 62 times.
✓ Branch 2 taken 153 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 97 times.
✓ Branch 5 taken 56 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 97 times.
|
215 | if (header->rgb && (header->naxis != 3 || (header->naxisn[2] != 3 && header->naxisn[2] != 4))) { |
131 | ✗ | av_log(avctx, AV_LOG_ERROR, "File contains RGB image but NAXIS = %d and NAXIS3 = %d\n", header->naxis, header->naxisn[2]); | |
132 | ✗ | return AVERROR_INVALIDDATA; | |
133 | } | ||
134 | |||
135 |
3/4✓ Branch 0 taken 62 times.
✓ Branch 1 taken 153 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 62 times.
|
215 | if (!header->rgb && header->naxis != 2) { |
136 | ✗ | av_log(avctx, AV_LOG_ERROR, "unsupported number of dimensions, NAXIS = %d\n", header->naxis); | |
137 | ✗ | return AVERROR_INVALIDDATA; | |
138 | } | ||
139 | |||
140 |
4/6✓ Branch 0 taken 2 times.
✓ Branch 1 taken 213 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
|
215 | if (header->blank_found && (header->bitpix == -32 || header->bitpix == -64)) { |
141 | ✗ | av_log(avctx, AV_LOG_WARNING, "BLANK keyword found but BITPIX = %d\n. Ignoring BLANK", header->bitpix); | |
142 | ✗ | header->blank_found = 0; | |
143 | } | ||
144 | |||
145 | 215 | size = abs(header->bitpix) >> 3; | |
146 |
2/2✓ Branch 0 taken 583 times.
✓ Branch 1 taken 215 times.
|
798 | for (i = 0; i < header->naxis; i++) { |
147 |
2/4✓ Branch 0 taken 583 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 583 times.
|
583 | if (size == 0 || header->naxisn[i] > SIZE_MAX / size) { |
148 | ✗ | av_log(avctx, AV_LOG_ERROR, "unsupported size of FITS image"); | |
149 | ✗ | return AVERROR_INVALIDDATA; | |
150 | } | ||
151 | 583 | size *= header->naxisn[i]; | |
152 | } | ||
153 | |||
154 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 215 times.
|
215 | if (end - ptr8 < size) |
155 | ✗ | return AVERROR_INVALIDDATA; | |
156 | 215 | *ptr = ptr8; | |
157 | |||
158 |
5/6✓ Branch 0 taken 62 times.
✓ Branch 1 taken 153 times.
✓ Branch 2 taken 56 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 56 times.
|
215 | if (!header->rgb && (!header->data_min_found || !header->data_max_found)) { |
159 | 6 | ret = fill_data_min_max(ptr8, header, end); | |
160 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (ret < 0) { |
161 | ✗ | av_log(avctx, AV_LOG_ERROR, "invalid BITPIX, %d\n", header->bitpix); | |
162 | ✗ | return ret; | |
163 | } | ||
164 | } else { | ||
165 | /* | ||
166 | * instead of applying bscale and bzero to every element, | ||
167 | * we can do inverse transformation on data_min and data_max | ||
168 | */ | ||
169 | 209 | header->data_min = (header->data_min - header->bzero) / header->bscale; | |
170 | 209 | header->data_max = (header->data_max - header->bzero) / header->bscale; | |
171 | } | ||
172 |
3/4✓ Branch 0 taken 62 times.
✓ Branch 1 taken 153 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 62 times.
|
215 | if (!header->rgb && header->data_min >= header->data_max) { |
173 | ✗ | if (header->data_min > header->data_max) { | |
174 | ✗ | av_log(avctx, AV_LOG_ERROR, "data min/max (%g %g) is invalid\n", header->data_min, header->data_max); | |
175 | ✗ | return AVERROR_INVALIDDATA; | |
176 | } | ||
177 | ✗ | av_log(avctx, AV_LOG_WARNING, "data min/max indicates a blank image\n"); | |
178 | ✗ | header->data_max ++; | |
179 | } | ||
180 | |||
181 | 215 | return 0; | |
182 | } | ||
183 | |||
184 | 215 | static int fits_decode_frame(AVCodecContext *avctx, AVFrame *p, | |
185 | int *got_frame, AVPacket *avpkt) | ||
186 | { | ||
187 | 215 | const uint8_t *ptr8 = avpkt->data, *end; | |
188 | uint8_t t8; | ||
189 | int16_t t16; | ||
190 | int32_t t32; | ||
191 | int64_t t64; | ||
192 | float tflt; | ||
193 | double tdbl; | ||
194 | int ret, i, j, k; | ||
195 | 215 | const int map[] = {2, 0, 1, 3}; // mapping from GBRA -> RGBA as RGBA is to be stored in FITS file.. | |
196 | uint8_t *dst8; | ||
197 | uint16_t *dst16; | ||
198 | uint64_t t; | ||
199 | FITSHeader header; | ||
200 | 215 | FITSContext * fitsctx = avctx->priv_data; | |
201 | |||
202 | 215 | end = ptr8 + avpkt->size; | |
203 | 215 | p->metadata = NULL; | |
204 | 215 | ret = fits_read_header(avctx, &ptr8, &header, end, &p->metadata); | |
205 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 215 times.
|
215 | if (ret < 0) |
206 | ✗ | return ret; | |
207 | |||
208 |
2/2✓ Branch 0 taken 153 times.
✓ Branch 1 taken 62 times.
|
215 | if (header.rgb) { |
209 |
2/2✓ Branch 0 taken 97 times.
✓ Branch 1 taken 56 times.
|
153 | if (header.bitpix == 8) { |
210 |
2/2✓ Branch 0 taken 28 times.
✓ Branch 1 taken 69 times.
|
97 | if (header.naxisn[2] == 3) { |
211 | 28 | avctx->pix_fmt = AV_PIX_FMT_GBRP; | |
212 | } else { | ||
213 | 69 | avctx->pix_fmt = AV_PIX_FMT_GBRAP; | |
214 | } | ||
215 |
1/2✓ Branch 0 taken 56 times.
✗ Branch 1 not taken.
|
56 | } else if (header.bitpix == 16) { |
216 |
2/2✓ Branch 0 taken 28 times.
✓ Branch 1 taken 28 times.
|
56 | if (header.naxisn[2] == 3) { |
217 | 28 | avctx->pix_fmt = AV_PIX_FMT_GBRP16; | |
218 | } else { | ||
219 | 28 | avctx->pix_fmt = AV_PIX_FMT_GBRAP16; | |
220 | } | ||
221 | } else { | ||
222 | ✗ | av_log(avctx, AV_LOG_ERROR, "unsupported BITPIX = %d\n", header.bitpix); | |
223 | ✗ | return AVERROR_INVALIDDATA; | |
224 | } | ||
225 | } else { | ||
226 |
2/2✓ Branch 0 taken 28 times.
✓ Branch 1 taken 34 times.
|
62 | if (header.bitpix == 8) { |
227 | 28 | avctx->pix_fmt = AV_PIX_FMT_GRAY8; | |
228 | } else { | ||
229 | 34 | avctx->pix_fmt = AV_PIX_FMT_GRAY16; | |
230 | } | ||
231 | } | ||
232 | |||
233 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 215 times.
|
215 | if ((ret = ff_set_dimensions(avctx, header.naxisn[0], header.naxisn[1])) < 0) |
234 | ✗ | return ret; | |
235 | |||
236 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 215 times.
|
215 | if ((ret = ff_get_buffer(avctx, p, 0)) < 0) |
237 | ✗ | return ret; | |
238 | |||
239 | /* | ||
240 | * FITS stores images with bottom row first. Therefore we have | ||
241 | * to fill the image from bottom to top. | ||
242 | */ | ||
243 |
2/2✓ Branch 0 taken 153 times.
✓ Branch 1 taken 62 times.
|
215 | if (header.rgb) { |
244 |
2/3✓ Branch 0 taken 97 times.
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
|
153 | switch(header.bitpix) { |
245 | #define CASE_RGB(cas, dst, type, dref) \ | ||
246 | case cas: \ | ||
247 | for (k = 0; k < header.naxisn[2]; k++) { \ | ||
248 | for (i = 0; i < avctx->height; i++) { \ | ||
249 | dst = (type *) (p->data[map[k]] + (avctx->height - i - 1) * p->linesize[map[k]]); \ | ||
250 | for (j = 0; j < avctx->width; j++) { \ | ||
251 | t32 = dref(ptr8); \ | ||
252 | if (!header.blank_found || t32 != header.blank) { \ | ||
253 | t = t32 * header.bscale + header.bzero; \ | ||
254 | } else { \ | ||
255 | t = fitsctx->blank_val; \ | ||
256 | } \ | ||
257 | *dst++ = (type) t; \ | ||
258 | ptr8 += cas >> 3; \ | ||
259 | } \ | ||
260 | } \ | ||
261 | } \ | ||
262 | break | ||
263 | |||
264 |
7/10✗ Branch 0 not taken.
✓ Branch 1 taken 18994560 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 18994560 times.
✓ Branch 5 taken 59376 times.
✓ Branch 6 taken 59376 times.
✓ Branch 7 taken 360 times.
✓ Branch 8 taken 360 times.
✓ Branch 9 taken 97 times.
|
19054393 | CASE_RGB(8, dst8, uint8_t, *); |
265 |
7/10✗ Branch 0 not taken.
✓ Branch 1 taken 18679808 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 18679808 times.
✓ Branch 5 taken 54208 times.
✓ Branch 6 taken 54208 times.
✓ Branch 7 taken 196 times.
✓ Branch 8 taken 196 times.
✓ Branch 9 taken 56 times.
|
18734268 | CASE_RGB(16, dst16, uint16_t, AV_RB16); |
266 | } | ||
267 | } else { | ||
268 | 62 | double scale = header.data_max - header.data_min; | |
269 | |||
270 |
2/4✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 62 times.
|
62 | if (scale <= 0 || !isfinite(scale)) { |
271 | ✗ | scale = 1; | |
272 | } | ||
273 | 62 | scale = 1/scale; | |
274 | |||
275 |
5/7✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 28 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
62 | switch (header.bitpix) { |
276 | #define CASE_GRAY(cas, dst, type, t, rd) \ | ||
277 | case cas: \ | ||
278 | for (i = 0; i < avctx->height; i++) { \ | ||
279 | dst = (type *) (p->data[0] + (avctx->height-i-1)* p->linesize[0]); \ | ||
280 | for (j = 0; j < avctx->width; j++) { \ | ||
281 | t = rd; \ | ||
282 | if (!header.blank_found || t != header.blank) { \ | ||
283 | *dst++ = lrint(((t - header.data_min) * ((1 << (sizeof(type) * 8)) - 1)) * scale); \ | ||
284 | } else { \ | ||
285 | *dst++ = fitsctx->blank_val; \ | ||
286 | } \ | ||
287 | ptr8 += abs(cas) >> 3; \ | ||
288 | } \ | ||
289 | } \ | ||
290 | break | ||
291 | |||
292 |
5/8✗ Branch 1 not taken.
✓ Branch 2 taken 26642 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 26642 times.
✓ Branch 6 taken 346 times.
✓ Branch 7 taken 346 times.
✓ Branch 8 taken 2 times.
|
26990 | CASE_GRAY(-64, dst16, uint16_t, tdbl, av_int2double(AV_RB64(ptr8))); |
293 |
5/8✗ Branch 1 not taken.
✓ Branch 2 taken 22236 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 22236 times.
✓ Branch 6 taken 218 times.
✓ Branch 7 taken 218 times.
✓ Branch 8 taken 2 times.
|
22456 | CASE_GRAY(-32, dst16, uint16_t, tflt, av_int2float(AV_RB32(ptr8))); |
294 |
5/8✗ Branch 0 not taken.
✓ Branch 1 taken 2668544 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2668544 times.
✓ Branch 5 taken 7744 times.
✓ Branch 6 taken 7744 times.
✓ Branch 7 taken 28 times.
|
2676316 | CASE_GRAY(8, dst8, uint8_t, t8, ptr8[0]); |
295 |
5/8✗ Branch 0 not taken.
✓ Branch 1 taken 3160064 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3160064 times.
✓ Branch 5 taken 8512 times.
✓ Branch 6 taken 8512 times.
✓ Branch 7 taken 28 times.
|
3168604 | CASE_GRAY(16, dst16, uint16_t, t16, AV_RB16(ptr8)); |
296 |
7/8✓ Branch 0 taken 131072 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 131054 times.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 131072 times.
✓ Branch 5 taken 512 times.
✓ Branch 6 taken 512 times.
✓ Branch 7 taken 2 times.
|
131586 | CASE_GRAY(32, dst16, uint16_t, t32, AV_RB32(ptr8)); |
297 | ✗ | CASE_GRAY(64, dst16, uint16_t, t64, AV_RB64(ptr8)); | |
298 | ✗ | default: | |
299 | ✗ | av_log(avctx, AV_LOG_ERROR, "invalid BITPIX, %d\n", header.bitpix); | |
300 | ✗ | return AVERROR_INVALIDDATA; | |
301 | } | ||
302 | } | ||
303 | |||
304 | 215 | *got_frame = 1; | |
305 | |||
306 | 215 | return avpkt->size; | |
307 | } | ||
308 | |||
309 | static const AVOption fits_options[] = { | ||
310 | { "blank_value", "value that is used to replace BLANK pixels in data array", offsetof(FITSContext, blank_val), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 65535, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM}, | ||
311 | { NULL }, | ||
312 | }; | ||
313 | |||
314 | static const AVClass fits_decoder_class = { | ||
315 | .class_name = "FITS decoder", | ||
316 | .item_name = av_default_item_name, | ||
317 | .option = fits_options, | ||
318 | .version = LIBAVUTIL_VERSION_INT, | ||
319 | .category = AV_CLASS_CATEGORY_DECODER, | ||
320 | }; | ||
321 | |||
322 | const FFCodec ff_fits_decoder = { | ||
323 | .p.name = "fits", | ||
324 | .p.type = AVMEDIA_TYPE_VIDEO, | ||
325 | .p.id = AV_CODEC_ID_FITS, | ||
326 | .p.capabilities = AV_CODEC_CAP_DR1, | ||
327 | CODEC_LONG_NAME("Flexible Image Transport System"), | ||
328 | .p.priv_class = &fits_decoder_class, | ||
329 | .priv_data_size = sizeof(FITSContext), | ||
330 | FF_CODEC_DECODE_CB(fits_decode_frame), | ||
331 | }; | ||
332 |