From patchwork Thu Jul 25 12:12:53 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: velocityra@gmail.com X-Patchwork-Id: 14067 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 68E37447D8A for ; Thu, 25 Jul 2019 15:13:29 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 4295968A913; Thu, 25 Jul 2019 15:13:29 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr1-f65.google.com (mail-wr1-f65.google.com [209.85.221.65]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 24BCA68A5FD for ; Thu, 25 Jul 2019 15:13:23 +0300 (EEST) Received: by mail-wr1-f65.google.com with SMTP id 31so50577564wrm.1 for ; Thu, 25 Jul 2019 05:13:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=O3afRILzCDmYcaHSUHdMOdicOIqR/27Bbak7DIHAVRs=; b=opw/oz8Uq4RhwvJNz6hLlJ0LsuiD55X0y0BNzmPiFMtdT03J90ECGk5bG1YstIoli2 u/kQ3fvbwpARqU5gxGkrbiPXlpmFabKb82hA95HPUy2wd9clc247ctnDQo+LRcMS5F4a 5L9XlVNeFCjfkZc/KX/y3RrJwIhGigbdGUbjdvfdqu4HnO8TCwiEgF6kISuq2pIUOZBh IyagGgVJF2jJQZ40YeySWvVOVNhGzBUPDEXxbBeS4vBtIt00gfLlH6XCuLaI84BnjLZJ XgJbBJATZz1v83bW4tTRqMstcKas7hFk2YVuOkOYnG5+TgKTKBWN6YMZTdqFiLcvLjEF 3b7A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=O3afRILzCDmYcaHSUHdMOdicOIqR/27Bbak7DIHAVRs=; b=Lsr9j4rvwhF6gt3fIhKRi98u0hsQSYoa2vXcyodiKfP4Oc3gJgCDiveRsWS6khhZTD Xg3N3P7vj3zIXAQH8VoY1D7dze/0yhLHsfQ5XO8HjTx6QlLSDyzY0enU68tYrIQ9ikLP mZk8o03gxQT4ZxoOYhpVvHaUtNmBApfyLnYP8YDMHKdHr3hd1r5VQcvkElmCTiF8CjL/ aAbaoyZqjULX97hZzXtwCR1SYfndJC10ty1rgGQvYVY5dcx9qCITZyICXKZaxTLx5dlN f1LeKSRcEneleIE2oRPLu/+ebyUw9S+jXKVrcHBzfOya03ky68mTpaAls/sHWyOo74TM nwvw== X-Gm-Message-State: APjAAAUwcyYg5wp7WthKFP9r/5OUlrTtV5E7W5X4ogwuuDf+79UWHy9X 9g8shDUWu2fSRPqMxvIpGzoa+LXX X-Google-Smtp-Source: APXvYqw4hAhOVejvNR2Zb+L/9BulwIFfIyy/5Gidy+UnK1o4oBowJ7hAtyTvrpYuk6OkhPRPVoo0CQ== X-Received: by 2002:a5d:48cf:: with SMTP id p15mr45025751wrs.151.1564056802013; Thu, 25 Jul 2019 05:13:22 -0700 (PDT) Received: from localhost.localdomain ([2a02:587:2428:db00:d890:dfd2:86e8:60f2]) by smtp.gmail.com with ESMTPSA id y6sm42454037wrp.12.2019.07.25.05.13.20 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 25 Jul 2019 05:13:21 -0700 (PDT) From: velocityra@gmail.com X-Google-Original-From: velocityra@gmail To: ffmpeg-devel@ffmpeg.org Date: Thu, 25 Jul 2019 15:12:53 +0300 Message-Id: <20190725121253.35148-2-velocityra@gmail.com> X-Mailer: git-send-email 2.21.0.windows.1 In-Reply-To: <20190725121253.35148-1-velocityra@gmail.com> References: <20190725105803.GC3219@michaelspb> <20190725121253.35148-1-velocityra@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v6 2/2] lavc/tiff: Decode embedded JPEGs in DNG images X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Nick Renieris Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" From: Nick Renieris Used a technique similar to lavc/tdsc.c for invoking the MJPEG decoder. This commit adds support for: - DNG tiles - DNG tile huffman lossless JPEG decoding - DNG 8-bpp ("packed" as dcraw calls it) decoding - DNG color scaling [1] - LinearizationTable tag - BlackLevel tag [1]: As specified in the DNG Specification - Chapter 5 Signed-off-by: Nick Renieris --- configure | 1 + libavcodec/Makefile | 2 +- libavcodec/tiff.c | 310 +++++++++++++++++++++++++++++++++++++++++++- libavcodec/tiff.h | 2 + 4 files changed, 307 insertions(+), 8 deletions(-) diff --git a/configure b/configure index 5a4f507246..54163822a8 100755 --- a/configure +++ b/configure @@ -2813,6 +2813,7 @@ theora_decoder_select="vp3_decoder" thp_decoder_select="mjpeg_decoder" tiff_decoder_suggest="zlib lzma" tiff_encoder_suggest="zlib" +tiff_decoder_select="mjpeg_decoder" truehd_decoder_select="mlp_parser" truehd_encoder_select="lpc audio_frame_queue" truemotion2_decoder_select="bswapdsp" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 3cd73fbcc6..f814c69996 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -616,7 +616,7 @@ OBJS-$(CONFIG_TARGA_ENCODER) += targaenc.o rle.o OBJS-$(CONFIG_TARGA_Y216_DECODER) += targa_y216dec.o OBJS-$(CONFIG_TDSC_DECODER) += tdsc.o OBJS-$(CONFIG_TIERTEXSEQVIDEO_DECODER) += tiertexseqv.o -OBJS-$(CONFIG_TIFF_DECODER) += tiff.o lzw.o faxcompr.o tiff_data.o tiff_common.o +OBJS-$(CONFIG_TIFF_DECODER) += tiff.o lzw.o faxcompr.o tiff_data.o tiff_common.o mjpegdec.o OBJS-$(CONFIG_TIFF_ENCODER) += tiffenc.o rle.o lzwenc.o tiff_data.o OBJS-$(CONFIG_TMV_DECODER) += tmv.o cga_data.o OBJS-$(CONFIG_TRUEHD_DECODER) += mlpdec.o mlpdsp.o diff --git a/libavcodec/tiff.c b/libavcodec/tiff.c index c520d7df83..4f6ba256c6 100644 --- a/libavcodec/tiff.c +++ b/libavcodec/tiff.c @@ -46,6 +46,7 @@ #include "mathops.h" #include "tiff.h" #include "tiff_data.h" +#include "mjpegdec.h" #include "thread.h" #include "get_bits.h" @@ -54,6 +55,10 @@ typedef struct TiffContext { AVCodecContext *avctx; GetByteContext gb; + /* JPEG decoding for DNG */ + AVCodecContext *avctx_mjpeg; // wrapper context for MJPEG + AVFrame *jpgframe; // decoded JPEG tile + int get_subimage; uint16_t get_page; int get_thumbnail; @@ -76,7 +81,9 @@ typedef struct TiffContext { int is_bayer; uint8_t pattern[4]; + unsigned black_level; unsigned white_level; + const uint16_t *dng_lut; // Pointer to DNG linearization table uint32_t sub_ifd; uint16_t cur_page; @@ -86,6 +93,14 @@ typedef struct TiffContext { int stripsizesoff, stripsize, stripoff, strippos; LZWState *lzw; + /* Tile support */ + int is_tiled; + int tile_byte_counts_offset, tile_offsets_offset; + int tile_width, tile_length; + int tile_count; + + int is_jpeg; // 0 - Not JPEG, 1 - JPEG, 2 - New JPEG + uint8_t *deinvert_buf; int deinvert_buf_size; uint8_t *yuv_line; @@ -257,6 +272,9 @@ static int add_metadata(int count, int type, }; } +static void av_always_inline dng_blit(TiffContext *s, uint8_t *dst, int dst_stride, + const uint8_t *src, int src_stride, int width, int height, int is_u16); + static void av_always_inline horizontal_fill(TiffContext *s, unsigned int bpp, uint8_t* dst, int usePtr, const uint8_t *src, @@ -712,6 +730,203 @@ static int tiff_unpack_strip(TiffContext *s, AVFrame *p, uint8_t *dst, int strid return 0; } +/** + * Map stored raw sensor values into linear reference values. + * See: DNG Specification - Chapter 5 + */ +static uint16_t av_always_inline dng_raw_to_linear16(uint16_t value, + const uint16_t *lut, + uint16_t black_level, + float scale_factor) { + // Lookup table lookup + if (lut) value = lut[value]; + + // Black level subtraction + value = av_clip_uint16_c((unsigned)value - black_level); + + // Color scaling + value = av_clip_uint16_c((unsigned)(((float)value * scale_factor) * 0xFFFF)); + + return value; +} + +static uint16_t av_always_inline dng_raw_to_linear8(uint16_t value, + const uint16_t *lut, + uint16_t black_level, + float scale_factor) { + return dng_raw_to_linear16(value, lut, black_level, scale_factor) >> 8; +} + +static void dng_blit(TiffContext *s, uint8_t *dst, int dst_stride, + const uint8_t *src, int src_stride, + int width, int height, int is_u16) +{ + int line, col; + float scale_factor; + + scale_factor = 1.0 / (s->white_level - s->black_level); + + if (is_u16) { + for (line = 0; line < height; line++) { + uint16_t *dst_u16 = (uint16_t *)dst; + uint16_t *src_u16 = (uint16_t *)src; + + for (col = 0; col < width; col++) + *dst_u16++ = dng_raw_to_linear16(*src_u16++, s->dng_lut, s->black_level, scale_factor); + + dst += dst_stride * sizeof(uint16_t); + src += src_stride * sizeof(uint16_t); + } + } else { + for (line = 0; line < height; line++) { + for (col = 0; col < width; col++) + *dst++ = dng_raw_to_linear8(*src++, s->dng_lut, s->black_level, scale_factor); + + dst += dst_stride; + src += src_stride; + } + } +} + +static int dng_decode_jpeg_tile(AVCodecContext *avctx, AVFrame *frame, + int tile_byte_count, int x, int y, int w, int h) +{ + TiffContext *s = avctx->priv_data; + AVPacket jpkt; + uint8_t *dst_data, *src_data; + uint32_t dst_offset; /* offset from dst buffer in pixels */ + int is_u16, pixel_size; + int ret; + + /* Prepare a packet and send to the MJPEG decoder */ + av_init_packet(&jpkt); + jpkt.data = (uint8_t*)s->gb.buffer; + jpkt.size = tile_byte_count; + + ret = avcodec_send_packet(s->avctx_mjpeg, &jpkt); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error submitting a packet for decoding\n"); + return ret; + } + + ret = avcodec_receive_frame(s->avctx_mjpeg, s->jpgframe); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "JPEG decoding error (%d).\n", ret); + + /* Normally skip, error if explode */ + if (avctx->err_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + else + return 0; + } + + /* Copy the outputted tile's pixels from 'jpgframe' to to 'frame' (final buffer */ + + is_u16 = (s->bpp > 8); + pixel_size = (is_u16 ? sizeof(uint16_t) : sizeof(uint8_t)); + + dst_offset = x + frame->linesize[0] * y / pixel_size; + dst_data = frame->data[0] + dst_offset * pixel_size; + src_data = s->jpgframe->data[0]; + + dng_blit(s, + dst_data, + frame->linesize[0] / pixel_size, + src_data, + s->jpgframe->linesize[0] / pixel_size, + w, + h, + is_u16); + + av_frame_unref(s->jpgframe); + + return 0; +} + +static int dng_decode_tiles(AVCodecContext *avctx, AVFrame *frame) +{ + TiffContext *s = avctx->priv_data; + int tile_idx; + int tile_offset_offset, tile_offset; + int tile_byte_count_offset, tile_byte_count; + int tile_count_x, tile_count_y; + int tile_width, tile_length; + int tile_x = 0, tile_y = 0; + int pos_x = 0, pos_y = 0; + int ret; + + /* Calculate tile counts (round up) */ + tile_count_x = (s->width + s->tile_width - 1) / s->tile_width; + tile_count_y = (s->height + s->tile_length - 1) / s->tile_length; + + /* Iterate over the number of tiles */ + for (tile_idx = 0; tile_idx < s->tile_count; tile_idx++) { + tile_x = tile_idx % tile_count_x; + tile_y = tile_idx / tile_count_x; + + if (tile_x == tile_count_x - 1) // If on the right edge + tile_width = s->width % s->tile_width; + else + tile_width = s->tile_width; + + if (tile_y == tile_count_y - 1) // If on the bottom edge + tile_length = s->height % s->tile_length; + else + tile_length = s->tile_length; + + /* Read tile offset */ + tile_offset_offset = s->tile_offsets_offset + tile_idx * sizeof(int); + bytestream2_seek(&s->gb, tile_offset_offset, SEEK_SET); + tile_offset = ff_tget_long(&s->gb, s->le); + + /* Read tile byte size */ + tile_byte_count_offset = s->tile_byte_counts_offset + tile_idx * sizeof(int); + bytestream2_seek(&s->gb, tile_byte_count_offset, SEEK_SET); + tile_byte_count = ff_tget_long(&s->gb, s->le); + + /* Seek to tile data */ + bytestream2_seek(&s->gb, tile_offset, SEEK_SET); + + /* Decode JPEG tile and copy it in the reference frame */ + ret = dng_decode_jpeg_tile(avctx, frame, tile_byte_count, pos_x, pos_y, tile_width, tile_length); + + if (ret < 0) + return ret; + + /* Advance current positions */ + pos_x += tile_width; + if (tile_x == tile_count_x - 1) { // If on the right edge + pos_x = 0; + pos_y += tile_length; + } + } + + return 0; +} + +static int dng_decode(AVCodecContext *avctx, AVFrame *frame, AVPacket *avpkt) { + int ret; + + TiffContext *s = avctx->priv_data; + + s->jpgframe->width = s->tile_width; + s->jpgframe->height = s->tile_length; + + s->avctx_mjpeg->width = s->tile_width; + s->avctx_mjpeg->height = s->tile_length; + + /* Decode all tiles in a frame */ + ret = dng_decode_tiles(avctx, frame); + if (ret < 0) + return ret; + + /* Frame is ready to be output */ + frame->pict_type = AV_PICTURE_TYPE_I; + frame->key_frame = 1; + + return avpkt->size; +} + static int init_image(TiffContext *s, ThreadFrame *frame) { int ret; @@ -923,7 +1138,7 @@ static void set_sar(TiffContext *s, unsigned tag, unsigned num, unsigned den) static int tiff_decode_tag(TiffContext *s, AVFrame *frame) { - unsigned tag, type, count, off, value = 0, value2 = 0; + unsigned tag, type, count, off, value = 0, value2 = 1; // value2 is a denominator so init. to 1 int i, start; int pos; int ret; @@ -1029,8 +1244,8 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) #endif case TIFF_JPEG: case TIFF_NEWJPEG: - avpriv_report_missing_feature(s->avctx, "JPEG compression"); - return AVERROR_PATCHWELCOME; + s->is_jpeg = TIFF_JPEG ? 1 : 2; + break; case TIFF_LZMA: #if CONFIG_LZMA break; @@ -1085,12 +1300,19 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) case TIFF_YRES: set_sar(s, tag, value, value2); break; + case TIFF_TILE_OFFSETS: + s->tile_offsets_offset = off; + s->tile_count = count; + s->is_tiled = 1; + break; case TIFF_TILE_BYTE_COUNTS: + s->tile_byte_counts_offset = off; + break; case TIFF_TILE_LENGTH: - case TIFF_TILE_OFFSETS: + s->tile_length = value; + break; case TIFF_TILE_WIDTH: - av_log(s->avctx, AV_LOG_ERROR, "Tiled images are not supported\n"); - return AVERROR_PATCHWELCOME; + s->tile_width = value; break; case TIFF_PREDICTOR: s->predictor = value; @@ -1101,6 +1323,32 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) else if (count > 1) s->sub_ifd = ff_tget(&s->gb, TIFF_LONG, s->le); /** Only get the first SubIFD */ break; + case DNG_LINEARIZATION_TABLE: { + uint32_t lut_offset = value; + uint32_t lut_size = count; + uint32_t lut_wanted_size = 1 << s->bpp; + if (lut_wanted_size != lut_size) + av_log(s->avctx, AV_LOG_WARNING, "DNG contains LUT with invalid size (%d), disabling LUT\n", lut_size); + else if (lut_offset >= bytestream2_size(&s->gb)) + av_log(s->avctx, AV_LOG_WARNING, "DNG contains LUT with invalid offset (%d), disabling LUT\n", lut_offset); + else + s->dng_lut = (uint16_t*)(s->gb.buffer + lut_offset); + break; + } + case DNG_BLACK_LEVEL: + if (count > 1) { /* Use the first value in the pattern (assume they're all the same) */ + if (type == TIFF_RATIONAL) { + value = ff_tget(&s->gb, TIFF_LONG, s->le); + value2 = ff_tget(&s->gb, TIFF_LONG, s->le); + + s->black_level = value / value2; + } else + s->black_level = ff_tget(&s->gb, type, s->le); + av_log(s->avctx, AV_LOG_WARNING, "Assuming black level pattern values are identical\n"); + } else { + s->black_level = value / value2; + } + break; case DNG_WHITE_LEVEL: s->white_level = value; break; @@ -1420,6 +1668,8 @@ static int decode_frame(AVCodecContext *avctx, } s->le = le; // TIFF_BPP is not a required tag and defaults to 1 + + s->tiff_type = TIFF_TYPE_TIFF; again: s->is_thumbnail = 0; s->bppcount = s->bpp = 1; @@ -1428,8 +1678,10 @@ again: s->fill_order = 0; s->white_level = 0; s->is_bayer = 0; + s->is_tiled = 0; + s->is_jpeg = 0; s->cur_page = 0; - s->tiff_type = TIFF_TYPE_TIFF; + s->dng_lut = NULL; free_geotags(s); // Reset these offsets so we can tell if they were set this frame @@ -1519,6 +1771,26 @@ again: return AVERROR_INVALIDDATA; } + /* Handle DNG images with JPEG-compressed tiles */ + + if (s->tiff_type == TIFF_TYPE_DNG || s->tiff_type == TIFF_TYPE_CINEMADNG) { + if (s->is_jpeg) { + if (s->is_bayer) { + if ((ret = dng_decode(avctx, (AVFrame*)data, avpkt)) > 0) + *got_frame = 1; + return ret; + } else { + av_log(avctx, AV_LOG_ERROR, "DNG JPG-compressed non-bayer-encoded images are not supported\n"); + return AVERROR_PATCHWELCOME; + } + } else if (s->is_tiled) { + av_log(avctx, AV_LOG_ERROR, "DNG uncompressed tiled images are not supported\n"); + return AVERROR_PATCHWELCOME; + } + } + + /* Handle TIFF images and DNG images with uncompressed strips (non-tiled) */ + planes = s->planar ? s->bppcount : 1; for (plane = 0; plane < planes; plane++) { uint8_t *five_planes = NULL; @@ -1678,6 +1950,8 @@ again: static av_cold int tiff_init(AVCodecContext *avctx) { TiffContext *s = avctx->priv_data; + const AVCodec *codec; + int ret; s->width = 0; s->height = 0; @@ -1689,6 +1963,26 @@ static av_cold int tiff_init(AVCodecContext *avctx) return AVERROR(ENOMEM); ff_ccitt_unpack_init(); + /* Allocate JPEG frame */ + s->jpgframe = av_frame_alloc(); + if (!s->jpgframe) + return AVERROR(ENOMEM); + + /* Prepare everything needed for JPEG decoding */ + codec = avcodec_find_decoder(AV_CODEC_ID_MJPEG); + if (!codec) + return AVERROR_BUG; + s->avctx_mjpeg = avcodec_alloc_context3(codec); + if (!s->avctx_mjpeg) + return AVERROR(ENOMEM); + s->avctx_mjpeg->flags = avctx->flags; + s->avctx_mjpeg->flags2 = avctx->flags2; + s->avctx_mjpeg->dct_algo = avctx->dct_algo; + s->avctx_mjpeg->idct_algo = avctx->idct_algo; + ret = ff_codec_open2_recursive(s->avctx_mjpeg, codec, NULL); + if (ret < 0) + return ret; + return 0; } @@ -1705,6 +1999,8 @@ static av_cold int tiff_end(AVCodecContext *avctx) s->yuv_line_size = 0; av_freep(&s->fax_buffer); s->fax_buffer_size = 0; + av_frame_free(&s->jpgframe); + avcodec_free_context(&s->avctx_mjpeg); return 0; } diff --git a/libavcodec/tiff.h b/libavcodec/tiff.h index 81913c6b1a..2184c2c829 100644 --- a/libavcodec/tiff.h +++ b/libavcodec/tiff.h @@ -101,6 +101,8 @@ enum TiffTags { enum DngTags { DNG_VERSION = 0xC612, DNG_BACKWARD_VERSION = 0xC613, + DNG_LINEARIZATION_TABLE = 0xC618, + DNG_BLACK_LEVEL = 0xC61A, DNG_WHITE_LEVEL = 0xC61D, };