mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2024-12-22 07:20:45 +00:00
703288cec6
I went through all codecs and put them into five basic categories: 1. JPEG range only 2. MPEG range only 3. Explicitly tagged 4. Broken (codec supports both but encoder ignores tags) 5. N/A (headerless or pseudo-formats) Filters in category 5 remain untouched. The rest gain an explicit assignment of their supported color ranges, with codecs in category 4 being set to MPEG-only for safety. It might be considered redundant to distinguish between 0 (category 5) and MPEG+JPEG (category 3), but in doing so we effectively communicate that we can guarantee that these tags will be encoded, which is distinct from the situation where there are some codecs that simply don't have tagging or implied semantics (e.g. rawvideo). A full list of codecs follows: JPEG range only: - amv - roqvideo MPEG range only: - asv1, asv2 - avui - cfhd - cljr - dnxhd - dvvideo - ffv1 - flv - h261, h263, h263p - {h263,vp8}_v4l2m2m - huffyuv, ffvhuff - jpeg2000 - libopenjpeg - libtheora - libwebp, libwebp_anim - libx262 - libxavs, libxavs2 - libxvid - mpeg1video, mpeg2video - mpeg2_qsv - mpeg2_vaapi - mpeg4, msmpeg4, msmpeg4v2, wmv1, wmv2 - mpeg4_omx - prores, prores_aw, prores_ks - rv10, rv20 - snow - speedhq - svq1 - tiff - utvideo Explicitly tagged (MPEG/JPEG): - {av1,h264,hevc}_nvenc - {av1,h264,hevc}_vaapi - {av1,h264,hevc,vp8,vp9,mpeg4}_mediacodec - {av1,h264,hevc,vp9}_qsv - h264_amf - {h264,hevc,prores}_videotoolbox - libaom-av1 - libkvazaar - libopenh264 - librav1e - libsvtav1 - libvpx, libvpx-vp9 - libx264 - libx265 - ljpeg - mjpeg - vc2 Broken (encoder ignores tags): - {av1,hevc}_amf - {h264,hevc,mpeg4}_v4l2m2m - h264_omx - libxeve - magicyuv - {vp8,vp9,mjpeg}_vaapi N/A: - ayuv, yuv4, y41p, v308, v210, v410, v408 (headerless) - pgmyuv (headerless) - rawvideo, bitpacked (headerless) - vnull, wrapped_avframe (pseudocodecs)
595 lines
20 KiB
C
595 lines
20 KiB
C
/*
|
|
* TIFF image encoder
|
|
* Copyright (c) 2007 Bartlomiej Wolowiec
|
|
*
|
|
* 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
|
|
* TIFF image encoder
|
|
* @author Bartlomiej Wolowiec
|
|
*/
|
|
|
|
#include "config.h"
|
|
#if CONFIG_ZLIB
|
|
#include <zlib.h>
|
|
#endif
|
|
|
|
#include "libavutil/log.h"
|
|
#include "libavutil/mem.h"
|
|
#include "libavutil/opt.h"
|
|
#include "libavutil/pixdesc.h"
|
|
#include "avcodec.h"
|
|
#include "bytestream.h"
|
|
#include "codec_internal.h"
|
|
#include "encode.h"
|
|
#include "lzw.h"
|
|
#include "rle.h"
|
|
#include "tiff.h"
|
|
#include "tiff_common.h"
|
|
#include "version.h"
|
|
|
|
#define TIFF_MAX_ENTRY 32
|
|
|
|
/** sizes of various TIFF field types (string size = 1)*/
|
|
static const uint8_t type_sizes2[14] = {
|
|
0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 4
|
|
};
|
|
|
|
typedef struct TiffEncoderContext {
|
|
AVClass *class; ///< for private options
|
|
AVCodecContext *avctx;
|
|
|
|
int width; ///< picture width
|
|
int height; ///< picture height
|
|
unsigned int bpp; ///< bits per pixel
|
|
int compr; ///< compression level
|
|
int bpp_tab_size; ///< bpp_tab size
|
|
enum TiffPhotometric photometric_interpretation; ///< photometric interpretation
|
|
int strips; ///< number of strips
|
|
uint32_t *strip_sizes;
|
|
unsigned int strip_sizes_size;
|
|
uint32_t *strip_offsets;
|
|
unsigned int strip_offsets_size;
|
|
uint8_t *yuv_line;
|
|
unsigned int yuv_line_size;
|
|
int rps; ///< row per strip
|
|
uint8_t entries[TIFF_MAX_ENTRY * 12]; ///< entries in header
|
|
int num_entries; ///< number of entries
|
|
uint8_t **buf; ///< actual position in buffer
|
|
uint8_t *buf_start; ///< pointer to first byte in buffer
|
|
int buf_size; ///< buffer size
|
|
uint16_t subsampling[2]; ///< YUV subsampling factors
|
|
struct LZWEncodeState *lzws; ///< LZW encode state
|
|
uint32_t dpi; ///< image resolution in DPI
|
|
} TiffEncoderContext;
|
|
|
|
/**
|
|
* Check free space in buffer.
|
|
*
|
|
* @param s Tiff context
|
|
* @param need Needed bytes
|
|
* @return 0 - ok, 1 - no free space
|
|
*/
|
|
static inline int check_size(TiffEncoderContext *s, uint64_t need)
|
|
{
|
|
if (s->buf_size < *s->buf - s->buf_start + need) {
|
|
*s->buf = s->buf_start + s->buf_size + 1;
|
|
av_log(s->avctx, AV_LOG_ERROR, "Buffer is too small\n");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Put n values to buffer.
|
|
*
|
|
* @param p pointer to pointer to output buffer
|
|
* @param n number of values
|
|
* @param val pointer to values
|
|
* @param type type of values
|
|
* @param flip = 0 - normal copy, >0 - flip
|
|
*/
|
|
static void tnput(uint8_t **p, int n, const uint8_t *val, enum TiffTypes type,
|
|
int flip)
|
|
{
|
|
int i;
|
|
#if HAVE_BIGENDIAN
|
|
flip ^= ((int[]) { 0, 0, 0, 1, 3, 3 })[type];
|
|
#endif
|
|
for (i = 0; i < n * type_sizes2[type]; i++)
|
|
*(*p)++ = val[i ^ flip];
|
|
}
|
|
|
|
/**
|
|
* Add entry to directory in tiff header.
|
|
*
|
|
* @param s Tiff context
|
|
* @param tag tag that identifies the entry
|
|
* @param type entry type
|
|
* @param count the number of values
|
|
* @param ptr_val pointer to values
|
|
*/
|
|
static int add_entry(TiffEncoderContext *s, enum TiffTags tag,
|
|
enum TiffTypes type, int count, const void *ptr_val)
|
|
{
|
|
uint8_t *entries_ptr = s->entries + 12 * s->num_entries;
|
|
|
|
av_assert0(s->num_entries < TIFF_MAX_ENTRY);
|
|
|
|
bytestream_put_le16(&entries_ptr, tag);
|
|
bytestream_put_le16(&entries_ptr, type);
|
|
bytestream_put_le32(&entries_ptr, count);
|
|
|
|
if (type_sizes[type] * (int64_t)count <= 4) {
|
|
tnput(&entries_ptr, count, ptr_val, type, 0);
|
|
} else {
|
|
bytestream_put_le32(&entries_ptr, *s->buf - s->buf_start);
|
|
if (check_size(s, count * (int64_t)type_sizes2[type]))
|
|
return AVERROR_INVALIDDATA;
|
|
tnput(s->buf, count, ptr_val, type, 0);
|
|
}
|
|
|
|
s->num_entries++;
|
|
return 0;
|
|
}
|
|
|
|
static int add_entry1(TiffEncoderContext *s,
|
|
enum TiffTags tag, enum TiffTypes type, int val)
|
|
{
|
|
uint16_t w = val;
|
|
uint32_t dw = val;
|
|
return add_entry(s, tag, type, 1,
|
|
type == TIFF_SHORT ? (void *)&w : (void *)&dw);
|
|
}
|
|
|
|
/**
|
|
* Encode one strip in tiff file.
|
|
*
|
|
* @param s Tiff context
|
|
* @param src input buffer
|
|
* @param dst output buffer
|
|
* @param n size of input buffer
|
|
* @param compr compression method
|
|
* @return number of output bytes. If an output error is encountered, a negative
|
|
* value corresponding to an AVERROR error code is returned.
|
|
*/
|
|
static int encode_strip(TiffEncoderContext *s, const int8_t *src,
|
|
uint8_t *dst, int n, int compr)
|
|
{
|
|
switch (compr) {
|
|
#if CONFIG_ZLIB
|
|
case TIFF_DEFLATE:
|
|
case TIFF_ADOBE_DEFLATE:
|
|
{
|
|
unsigned long zlen = s->buf_size - (*s->buf - s->buf_start);
|
|
if (compress(dst, &zlen, src, n) != Z_OK) {
|
|
av_log(s->avctx, AV_LOG_ERROR, "Compressing failed\n");
|
|
return AVERROR_EXTERNAL;
|
|
}
|
|
return zlen;
|
|
}
|
|
#endif
|
|
case TIFF_RAW:
|
|
if (check_size(s, n))
|
|
return AVERROR(EINVAL);
|
|
memcpy(dst, src, n);
|
|
return n;
|
|
case TIFF_PACKBITS:
|
|
return ff_rle_encode(dst, s->buf_size - (*s->buf - s->buf_start),
|
|
src, 1, n, 2, 0xff, -1, 0);
|
|
case TIFF_LZW:
|
|
return ff_lzw_encode(s->lzws, src, n);
|
|
default:
|
|
av_log(s->avctx, AV_LOG_ERROR, "Unsupported compression method: %d\n",
|
|
compr);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
}
|
|
|
|
static void pack_yuv(TiffEncoderContext *s, const AVFrame *p,
|
|
uint8_t *dst, int lnum)
|
|
{
|
|
int i, j, k;
|
|
int w = (s->width - 1) / s->subsampling[0] + 1;
|
|
const uint8_t *pu = &p->data[1][lnum / s->subsampling[1] * p->linesize[1]];
|
|
const uint8_t *pv = &p->data[2][lnum / s->subsampling[1] * p->linesize[2]];
|
|
if (s->width % s->subsampling[0] || s->height % s->subsampling[1]) {
|
|
for (i = 0; i < w; i++) {
|
|
for (j = 0; j < s->subsampling[1]; j++)
|
|
for (k = 0; k < s->subsampling[0]; k++)
|
|
*dst++ = p->data[0][FFMIN(lnum + j, s->height-1) * p->linesize[0] +
|
|
FFMIN(i * s->subsampling[0] + k, s->width-1)];
|
|
*dst++ = *pu++;
|
|
*dst++ = *pv++;
|
|
}
|
|
}else{
|
|
for (i = 0; i < w; i++) {
|
|
for (j = 0; j < s->subsampling[1]; j++)
|
|
for (k = 0; k < s->subsampling[0]; k++)
|
|
*dst++ = p->data[0][(lnum + j) * p->linesize[0] +
|
|
i * s->subsampling[0] + k];
|
|
*dst++ = *pu++;
|
|
*dst++ = *pv++;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define ADD_ENTRY(s, tag, type, count, ptr_val) \
|
|
do { \
|
|
ret = add_entry(s, tag, type, count, ptr_val); \
|
|
if (ret < 0) \
|
|
goto fail; \
|
|
} while (0)
|
|
|
|
#define ADD_ENTRY1(s, tag, type, val) \
|
|
do { \
|
|
ret = add_entry1(s, tag, type, val); \
|
|
if (ret < 0) \
|
|
goto fail; \
|
|
} while (0)
|
|
|
|
static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
|
|
const AVFrame *pict, int *got_packet)
|
|
{
|
|
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->pix_fmt);
|
|
TiffEncoderContext *s = avctx->priv_data;
|
|
const AVFrame *const p = pict;
|
|
int i;
|
|
uint8_t *ptr;
|
|
uint8_t *offset;
|
|
uint32_t strips;
|
|
int bytes_per_row;
|
|
uint32_t res[2] = { s->dpi, 1 }; // image resolution (72/1)
|
|
uint16_t bpp_tab[4];
|
|
int ret = 0;
|
|
int is_yuv = 0, alpha = 0;
|
|
int shift_h, shift_v;
|
|
int packet_size;
|
|
|
|
s->width = avctx->width;
|
|
s->height = avctx->height;
|
|
s->subsampling[0] = 1;
|
|
s->subsampling[1] = 1;
|
|
|
|
if (!desc)
|
|
return AVERROR(EINVAL);
|
|
|
|
avctx->bits_per_coded_sample =
|
|
s->bpp = av_get_bits_per_pixel(desc);
|
|
s->bpp_tab_size = desc->nb_components;
|
|
|
|
switch (avctx->pix_fmt) {
|
|
case AV_PIX_FMT_RGBA64LE:
|
|
case AV_PIX_FMT_RGBA:
|
|
alpha = 1;
|
|
case AV_PIX_FMT_RGB48LE:
|
|
case AV_PIX_FMT_RGB24:
|
|
s->photometric_interpretation = TIFF_PHOTOMETRIC_RGB;
|
|
break;
|
|
case AV_PIX_FMT_GRAY8:
|
|
avctx->bits_per_coded_sample = 0x28;
|
|
case AV_PIX_FMT_GRAY8A:
|
|
case AV_PIX_FMT_YA16LE:
|
|
alpha = avctx->pix_fmt == AV_PIX_FMT_GRAY8A || avctx->pix_fmt == AV_PIX_FMT_YA16LE;
|
|
case AV_PIX_FMT_GRAY16LE:
|
|
case AV_PIX_FMT_MONOBLACK:
|
|
s->photometric_interpretation = TIFF_PHOTOMETRIC_BLACK_IS_ZERO;
|
|
break;
|
|
case AV_PIX_FMT_PAL8:
|
|
s->photometric_interpretation = TIFF_PHOTOMETRIC_PALETTE;
|
|
break;
|
|
case AV_PIX_FMT_MONOWHITE:
|
|
s->photometric_interpretation = TIFF_PHOTOMETRIC_WHITE_IS_ZERO;
|
|
break;
|
|
case AV_PIX_FMT_YUV420P:
|
|
case AV_PIX_FMT_YUV422P:
|
|
case AV_PIX_FMT_YUV440P:
|
|
case AV_PIX_FMT_YUV444P:
|
|
case AV_PIX_FMT_YUV410P:
|
|
case AV_PIX_FMT_YUV411P:
|
|
av_pix_fmt_get_chroma_sub_sample(avctx->pix_fmt, &shift_h, &shift_v);
|
|
s->photometric_interpretation = TIFF_PHOTOMETRIC_YCBCR;
|
|
s->subsampling[0] = 1 << shift_h;
|
|
s->subsampling[1] = 1 << shift_v;
|
|
is_yuv = 1;
|
|
break;
|
|
default:
|
|
av_log(s->avctx, AV_LOG_ERROR,
|
|
"This colors format is not supported\n");
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
for (i = 0; i < s->bpp_tab_size; i++)
|
|
bpp_tab[i] = desc->comp[i].depth;
|
|
|
|
if (s->compr == TIFF_DEFLATE ||
|
|
s->compr == TIFF_ADOBE_DEFLATE ||
|
|
s->compr == TIFF_LZW)
|
|
// best choice for DEFLATE
|
|
s->rps = s->height;
|
|
else
|
|
// suggest size of strip
|
|
s->rps = FFMAX(8192 / (((s->width * s->bpp) >> 3) + 1), 1);
|
|
// round rps up
|
|
s->rps = ((s->rps - 1) / s->subsampling[1] + 1) * s->subsampling[1];
|
|
|
|
strips = (s->height - 1) / s->rps + 1;
|
|
|
|
bytes_per_row = (((s->width - 1) / s->subsampling[0] + 1) * s->bpp *
|
|
s->subsampling[0] * s->subsampling[1] + 7) >> 3;
|
|
packet_size = avctx->height * bytes_per_row * 2 +
|
|
avctx->height * 4 + FF_INPUT_BUFFER_MIN_SIZE;
|
|
|
|
if ((ret = ff_alloc_packet(avctx, pkt, packet_size)) < 0)
|
|
return ret;
|
|
ptr = pkt->data;
|
|
s->buf_start = pkt->data;
|
|
s->buf = &ptr;
|
|
s->buf_size = pkt->size;
|
|
|
|
if (check_size(s, 8)) {
|
|
ret = AVERROR(EINVAL);
|
|
goto fail;
|
|
}
|
|
|
|
// write header
|
|
bytestream_put_le16(&ptr, 0x4949);
|
|
bytestream_put_le16(&ptr, 42);
|
|
|
|
offset = ptr;
|
|
bytestream_put_le32(&ptr, 0);
|
|
|
|
if (strips > INT_MAX / FFMAX(sizeof(s->strip_sizes[0]), sizeof(s->strip_offsets[0]))) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
av_fast_padded_mallocz(&s->strip_sizes , &s->strip_sizes_size , sizeof(s->strip_sizes [0]) * strips);
|
|
av_fast_padded_mallocz(&s->strip_offsets, &s->strip_offsets_size, sizeof(s->strip_offsets[0]) * strips);
|
|
|
|
if (!s->strip_sizes || !s->strip_offsets) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
|
|
if (is_yuv) {
|
|
av_fast_padded_malloc(&s->yuv_line, &s->yuv_line_size, bytes_per_row);
|
|
if (s->yuv_line == NULL) {
|
|
av_log(s->avctx, AV_LOG_ERROR, "Not enough memory\n");
|
|
ret = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
#if CONFIG_ZLIB
|
|
if (s->compr == TIFF_DEFLATE || s->compr == TIFF_ADOBE_DEFLATE) {
|
|
uint8_t *zbuf;
|
|
int zlen, zn;
|
|
int j;
|
|
|
|
zlen = bytes_per_row * s->rps;
|
|
zbuf = av_malloc(zlen);
|
|
if (!zbuf) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
s->strip_offsets[0] = ptr - pkt->data;
|
|
zn = 0;
|
|
for (j = 0; j < s->rps; j++) {
|
|
if (is_yuv) {
|
|
pack_yuv(s, p, s->yuv_line, j);
|
|
memcpy(zbuf + zn, s->yuv_line, bytes_per_row);
|
|
j += s->subsampling[1] - 1;
|
|
} else
|
|
memcpy(zbuf + j * bytes_per_row,
|
|
p->data[0] + j * p->linesize[0], bytes_per_row);
|
|
zn += bytes_per_row;
|
|
}
|
|
ret = encode_strip(s, zbuf, ptr, zn, s->compr);
|
|
av_free(zbuf);
|
|
if (ret < 0) {
|
|
av_log(s->avctx, AV_LOG_ERROR, "Encode strip failed\n");
|
|
goto fail;
|
|
}
|
|
ptr += ret;
|
|
s->strip_sizes[0] = ptr - pkt->data - s->strip_offsets[0];
|
|
} else
|
|
#endif
|
|
{
|
|
if (s->compr == TIFF_LZW) {
|
|
s->lzws = av_malloc(ff_lzw_encode_state_size);
|
|
if (!s->lzws) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
}
|
|
for (i = 0; i < s->height; i++) {
|
|
if (s->strip_sizes[i / s->rps] == 0) {
|
|
if (s->compr == TIFF_LZW) {
|
|
ff_lzw_encode_init(s->lzws, ptr,
|
|
s->buf_size - (*s->buf - s->buf_start),
|
|
12, FF_LZW_TIFF, 0);
|
|
}
|
|
s->strip_offsets[i / s->rps] = ptr - pkt->data;
|
|
}
|
|
if (is_yuv) {
|
|
pack_yuv(s, p, s->yuv_line, i);
|
|
ret = encode_strip(s, s->yuv_line, ptr, bytes_per_row, s->compr);
|
|
i += s->subsampling[1] - 1;
|
|
} else
|
|
ret = encode_strip(s, p->data[0] + i * p->linesize[0],
|
|
ptr, bytes_per_row, s->compr);
|
|
if (ret < 0) {
|
|
av_log(s->avctx, AV_LOG_ERROR, "Encode strip failed\n");
|
|
goto fail;
|
|
}
|
|
s->strip_sizes[i / s->rps] += ret;
|
|
ptr += ret;
|
|
if (s->compr == TIFF_LZW &&
|
|
(i == s->height - 1 || i % s->rps == s->rps - 1)) {
|
|
ret = ff_lzw_encode_flush(s->lzws);
|
|
s->strip_sizes[(i / s->rps)] += ret;
|
|
ptr += ret;
|
|
}
|
|
}
|
|
if (s->compr == TIFF_LZW)
|
|
av_freep(&s->lzws);
|
|
}
|
|
|
|
s->num_entries = 0;
|
|
|
|
ADD_ENTRY1(s, TIFF_SUBFILE, TIFF_LONG, 0);
|
|
ADD_ENTRY1(s, TIFF_WIDTH, TIFF_LONG, s->width);
|
|
ADD_ENTRY1(s, TIFF_HEIGHT, TIFF_LONG, s->height);
|
|
|
|
if (s->bpp_tab_size)
|
|
ADD_ENTRY(s, TIFF_BPP, TIFF_SHORT, s->bpp_tab_size, bpp_tab);
|
|
|
|
ADD_ENTRY1(s, TIFF_COMPR, TIFF_SHORT, s->compr);
|
|
ADD_ENTRY1(s, TIFF_PHOTOMETRIC, TIFF_SHORT, s->photometric_interpretation);
|
|
ADD_ENTRY(s, TIFF_STRIP_OFFS, TIFF_LONG, strips, s->strip_offsets);
|
|
|
|
if (s->bpp_tab_size)
|
|
ADD_ENTRY1(s, TIFF_SAMPLES_PER_PIXEL, TIFF_SHORT, s->bpp_tab_size);
|
|
|
|
ADD_ENTRY1(s, TIFF_ROWSPERSTRIP, TIFF_LONG, s->rps);
|
|
ADD_ENTRY(s, TIFF_STRIP_SIZE, TIFF_LONG, strips, s->strip_sizes);
|
|
ADD_ENTRY(s, TIFF_XRES, TIFF_RATIONAL, 1, res);
|
|
if (avctx->sample_aspect_ratio.num > 0 &&
|
|
avctx->sample_aspect_ratio.den > 0) {
|
|
AVRational y = av_mul_q(av_make_q(s->dpi, 1),
|
|
avctx->sample_aspect_ratio);
|
|
res[0] = y.num;
|
|
res[1] = y.den;
|
|
}
|
|
ADD_ENTRY(s, TIFF_YRES, TIFF_RATIONAL, 1, res);
|
|
ADD_ENTRY1(s, TIFF_RES_UNIT, TIFF_SHORT, 2);
|
|
|
|
if (!(avctx->flags & AV_CODEC_FLAG_BITEXACT))
|
|
ADD_ENTRY(s, TIFF_SOFTWARE_NAME, TIFF_STRING,
|
|
strlen(LIBAVCODEC_IDENT) + 1, LIBAVCODEC_IDENT);
|
|
|
|
if (avctx->pix_fmt == AV_PIX_FMT_PAL8) {
|
|
uint16_t pal[256 * 3];
|
|
for (i = 0; i < 256; i++) {
|
|
uint32_t rgb = *(uint32_t *) (p->data[1] + i * 4);
|
|
pal[i] = ((rgb >> 16) & 0xff) * 257;
|
|
pal[i + 256] = ((rgb >> 8) & 0xff) * 257;
|
|
pal[i + 512] = (rgb & 0xff) * 257;
|
|
}
|
|
ADD_ENTRY(s, TIFF_PAL, TIFF_SHORT, 256 * 3, pal);
|
|
}
|
|
if (alpha)
|
|
ADD_ENTRY1(s,TIFF_EXTRASAMPLES, TIFF_SHORT, 2);
|
|
if (is_yuv) {
|
|
/** according to CCIR Recommendation 601.1 */
|
|
uint32_t refbw[12] = { 15, 1, 235, 1, 128, 1, 240, 1, 128, 1, 240, 1 };
|
|
ADD_ENTRY(s, TIFF_YCBCR_SUBSAMPLING, TIFF_SHORT, 2, s->subsampling);
|
|
if (avctx->chroma_sample_location == AVCHROMA_LOC_TOPLEFT)
|
|
ADD_ENTRY1(s, TIFF_YCBCR_POSITIONING, TIFF_SHORT, 2);
|
|
ADD_ENTRY(s, TIFF_REFERENCE_BW, TIFF_RATIONAL, 6, refbw);
|
|
}
|
|
// write offset to dir
|
|
bytestream_put_le32(&offset, ptr - pkt->data);
|
|
|
|
if (check_size(s, 6 + s->num_entries * 12)) {
|
|
ret = AVERROR(EINVAL);
|
|
goto fail;
|
|
}
|
|
bytestream_put_le16(&ptr, s->num_entries); // write tag count
|
|
bytestream_put_buffer(&ptr, s->entries, s->num_entries * 12);
|
|
bytestream_put_le32(&ptr, 0);
|
|
|
|
pkt->size = ptr - pkt->data;
|
|
*got_packet = 1;
|
|
|
|
fail:
|
|
return ret < 0 ? ret : 0;
|
|
}
|
|
|
|
static av_cold int encode_init(AVCodecContext *avctx)
|
|
{
|
|
TiffEncoderContext *s = avctx->priv_data;
|
|
|
|
#if !CONFIG_ZLIB
|
|
if (s->compr == TIFF_DEFLATE) {
|
|
av_log(avctx, AV_LOG_ERROR,
|
|
"Deflate compression needs zlib compiled in\n");
|
|
return AVERROR(ENOSYS);
|
|
}
|
|
#endif
|
|
|
|
s->avctx = avctx;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static av_cold int encode_close(AVCodecContext *avctx)
|
|
{
|
|
TiffEncoderContext *s = avctx->priv_data;
|
|
|
|
av_freep(&s->strip_sizes);
|
|
av_freep(&s->strip_offsets);
|
|
av_freep(&s->yuv_line);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define OFFSET(x) offsetof(TiffEncoderContext, x)
|
|
#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
|
|
static const AVOption options[] = {
|
|
{"dpi", "set the image resolution (in dpi)", OFFSET(dpi), AV_OPT_TYPE_INT, {.i64 = 72}, 1, 0x10000, AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_ENCODING_PARAM},
|
|
{ "compression_algo", NULL, OFFSET(compr), AV_OPT_TYPE_INT, { .i64 = TIFF_PACKBITS }, TIFF_RAW, TIFF_DEFLATE, VE, .unit = "compression_algo" },
|
|
{ "packbits", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TIFF_PACKBITS }, 0, 0, VE, .unit = "compression_algo" },
|
|
{ "raw", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TIFF_RAW }, 0, 0, VE, .unit = "compression_algo" },
|
|
{ "lzw", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TIFF_LZW }, 0, 0, VE, .unit = "compression_algo" },
|
|
{ "deflate", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TIFF_DEFLATE }, 0, 0, VE, .unit = "compression_algo" },
|
|
{ NULL },
|
|
};
|
|
|
|
static const AVClass tiffenc_class = {
|
|
.class_name = "TIFF encoder",
|
|
.item_name = av_default_item_name,
|
|
.option = options,
|
|
.version = LIBAVUTIL_VERSION_INT,
|
|
};
|
|
|
|
const FFCodec ff_tiff_encoder = {
|
|
.p.name = "tiff",
|
|
CODEC_LONG_NAME("TIFF image"),
|
|
.p.type = AVMEDIA_TYPE_VIDEO,
|
|
.p.id = AV_CODEC_ID_TIFF,
|
|
.p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS |
|
|
AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
|
|
.priv_data_size = sizeof(TiffEncoderContext),
|
|
.init = encode_init,
|
|
.close = encode_close,
|
|
FF_CODEC_ENCODE_CB(encode_frame),
|
|
.p.pix_fmts = (const enum AVPixelFormat[]) {
|
|
AV_PIX_FMT_RGB24, AV_PIX_FMT_RGB48LE, AV_PIX_FMT_PAL8,
|
|
AV_PIX_FMT_RGBA, AV_PIX_FMT_RGBA64LE,
|
|
AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY8A, AV_PIX_FMT_GRAY16LE, AV_PIX_FMT_YA16LE,
|
|
AV_PIX_FMT_MONOBLACK, AV_PIX_FMT_MONOWHITE,
|
|
AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P,
|
|
AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P,
|
|
AV_PIX_FMT_NONE
|
|
},
|
|
.color_ranges = AVCOL_RANGE_MPEG,
|
|
.p.priv_class = &tiffenc_class,
|
|
};
|