| /* |
| * FITS muxer |
| * Copyright (c) 2017 Paras Chadha |
| * |
| * This file is part of FFmpeg. |
| * |
| * FFmpeg is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * FFmpeg is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with FFmpeg; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| /** |
| * @file |
| * FITS muxer. |
| */ |
| |
| #include "internal.h" |
| |
| typedef struct FITSContext { |
| int first_image; |
| } FITSContext; |
| |
| static int fits_write_header(AVFormatContext *s) |
| { |
| FITSContext *fitsctx = s->priv_data; |
| fitsctx->first_image = 1; |
| return 0; |
| } |
| |
| /** |
| * Write one header line comprising of keyword and value(int) |
| * @param s AVFormat Context |
| * @param keyword pointer to the char array in which keyword is stored |
| * @param value the value corresponding to the keyword |
| * @param lines_written to keep track of lines written so far |
| * @return 0 |
| */ |
| static int write_keyword_value(AVFormatContext *s, const char *keyword, int value, int *lines_written) |
| { |
| int len, ret; |
| uint8_t header[80]; |
| |
| len = strlen(keyword); |
| memset(header, ' ', sizeof(header)); |
| memcpy(header, keyword, len); |
| |
| header[8] = '='; |
| header[9] = ' '; |
| |
| ret = snprintf(header + 10, 70, "%d", value); |
| memset(&header[ret + 10], ' ', sizeof(header) - (ret + 10)); |
| |
| avio_write(s->pb, header, sizeof(header)); |
| *lines_written += 1; |
| return 0; |
| } |
| |
| static int write_image_header(AVFormatContext *s) |
| { |
| AVStream *st = s->streams[0]; |
| AVCodecParameters *encctx = st->codecpar; |
| FITSContext *fitsctx = s->priv_data; |
| uint8_t buffer[80]; |
| int bitpix, naxis, naxis3 = 1, bzero = 0, rgb = 0, lines_written = 0, lines_left; |
| |
| switch (encctx->format) { |
| case AV_PIX_FMT_GRAY8: |
| bitpix = 8; |
| naxis = 2; |
| break; |
| case AV_PIX_FMT_GRAY16BE: |
| bitpix = 16; |
| naxis = 2; |
| bzero = 32768; |
| break; |
| case AV_PIX_FMT_GBRP: |
| case AV_PIX_FMT_GBRAP: |
| bitpix = 8; |
| naxis = 3; |
| rgb = 1; |
| if (encctx->format == AV_PIX_FMT_GBRP) { |
| naxis3 = 3; |
| } else { |
| naxis3 = 4; |
| } |
| break; |
| case AV_PIX_FMT_GBRP16BE: |
| case AV_PIX_FMT_GBRAP16BE: |
| bitpix = 16; |
| naxis = 3; |
| rgb = 1; |
| if (encctx->format == AV_PIX_FMT_GBRP16BE) { |
| naxis3 = 3; |
| } else { |
| naxis3 = 4; |
| } |
| bzero = 32768; |
| break; |
| default: |
| return AVERROR(EINVAL); |
| } |
| |
| if (fitsctx->first_image) { |
| memcpy(buffer, "SIMPLE = ", 10); |
| memset(buffer + 10, ' ', 70); |
| buffer[29] = 'T'; |
| avio_write(s->pb, buffer, sizeof(buffer)); |
| } else { |
| memcpy(buffer, "XTENSION= 'IMAGE '", 20); |
| memset(buffer + 20, ' ', 60); |
| avio_write(s->pb, buffer, sizeof(buffer)); |
| } |
| lines_written++; |
| |
| write_keyword_value(s, "BITPIX", bitpix, &lines_written); // no of bits per pixel |
| write_keyword_value(s, "NAXIS", naxis, &lines_written); // no of dimensions of image |
| write_keyword_value(s, "NAXIS1", encctx->width, &lines_written); // first dimension i.e. width |
| write_keyword_value(s, "NAXIS2", encctx->height, &lines_written); // second dimension i.e. height |
| |
| if (rgb) |
| write_keyword_value(s, "NAXIS3", naxis3, &lines_written); // third dimension to store RGBA planes |
| |
| if (!fitsctx->first_image) { |
| write_keyword_value(s, "PCOUNT", 0, &lines_written); |
| write_keyword_value(s, "GCOUNT", 1, &lines_written); |
| } else { |
| fitsctx->first_image = 0; |
| } |
| |
| /* |
| * Since FITS does not support unsigned 16 bit integers, |
| * BZERO = 32768 is used to store unsigned 16 bit integers as |
| * signed integers so that it can be read properly. |
| */ |
| if (bitpix == 16) |
| write_keyword_value(s, "BZERO", bzero, &lines_written); |
| |
| if (rgb) { |
| memcpy(buffer, "CTYPE3 = 'RGB '", 20); |
| memset(buffer + 20, ' ', 60); |
| avio_write(s->pb, buffer, sizeof(buffer)); |
| lines_written++; |
| } |
| |
| memcpy(buffer, "END", 3); |
| memset(buffer + 3, ' ', 77); |
| avio_write(s->pb, buffer, sizeof(buffer)); |
| lines_written++; |
| |
| lines_left = ((lines_written + 35) / 36) * 36 - lines_written; |
| memset(buffer, ' ', 80); |
| while (lines_left > 0) { |
| avio_write(s->pb, buffer, sizeof(buffer)); |
| lines_left--; |
| } |
| return 0; |
| } |
| |
| static int fits_write_packet(AVFormatContext *s, AVPacket *pkt) |
| { |
| int ret = write_image_header(s); |
| if (ret < 0) |
| return ret; |
| avio_write(s->pb, pkt->data, pkt->size); |
| return 0; |
| } |
| |
| AVOutputFormat ff_fits_muxer = { |
| .name = "fits", |
| .long_name = NULL_IF_CONFIG_SMALL("Flexible Image Transport System"), |
| .extensions = "fits", |
| .priv_data_size = sizeof(FITSContext), |
| .audio_codec = AV_CODEC_ID_NONE, |
| .video_codec = AV_CODEC_ID_FITS, |
| .write_header = fits_write_header, |
| .write_packet = fits_write_packet, |
| }; |