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.

232 lines
6.3KB

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