You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

236 lines
6.4KB

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