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