From patchwork Tue Jul 23 11:51:12 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: 14040 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 762E0449817 for ; Tue, 23 Jul 2019 14:58:39 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 51E8868A417; Tue, 23 Jul 2019 14:58:39 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr1-f68.google.com (mail-wr1-f68.google.com [209.85.221.68]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 2F07468A2C0 for ; Tue, 23 Jul 2019 14:58:33 +0300 (EEST) Received: by mail-wr1-f68.google.com with SMTP id y4so42895055wrm.2 for ; Tue, 23 Jul 2019 04:58:33 -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=IfEOCFwGC75M70r8Z5C8o9NO8E8mTQ/Upv7aEwU3lss=; b=gw3j72Vmu837hU7KlNJcv5hF0n45kAS7eOiW3kc4cPgUHtGK1lhECfVEsLFBmUkAiO ckAQwICo0ZExd+oiJw24sMos2Qbk8Wq5gzoOXtqJmyDCe3Opr3vCzEK1CsxRdXQrJQx2 vNZlEE+h1vzA9Ioc3yap/S+Jj7FKPJpLPNED3eP7M62s6sphOwyb5+FTDS9U7R8DIUeV AXxpjag8yOsvA/G9emqpCjhkwpTuSO2k59PSquS9ybUnhOYUemu3EXiVwh+evDQGhn/s HePhUz83xPr/8jWKWHFJfRXLzyA+G5oT9UeZGwVKVcdNNEMSNYiUd0Xj0bIWuMl4riMv vW/w== 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=IfEOCFwGC75M70r8Z5C8o9NO8E8mTQ/Upv7aEwU3lss=; b=DblPOXoIJDaTtMi0aET6ywY05+eRRTzSITNdqbgkMQAB8xqT1g+lUm3HnGsAKmk7oH ozUpIHrsU9Cl9iUyBNBmScltQw6bsFQ0gYoqJCVH/fG98CeGLIWoe9ssQA0EhhVYsbuQ 0BpYYMQmH09gaGtJCK1iq9Cpo842zsftUebMX0AwuBu+lc9MhyvAtA4Q/sgHA7PT1DkF qYwVYm7zHAgQ4oHgnAtQe4fijPHYFG4W4q8Sj7CTqS6CbdQg/Q//XLXuzFtFCbsFrWuK Em65YrBadA1HaCpddA61DK0WMmOyvP//fJixb/h3YPst9W8DC8dbqpKvUTTfzRHTwAzp XZQA== X-Gm-Message-State: APjAAAW5yIBMi3Qu4EzQoUDXVQCZFU2rBMRhqK2TgiS37/8+LEIXgTP4 pA65QxMFVRr/en/RMHDqq1ZOAeOg X-Google-Smtp-Source: APXvYqzi6q+R3ntgprZWNKrayMX6w6k/64b2OoUL/zslBGMb0sdeETiTAVyRxmmibIgHr3uHIwFncQ== X-Received: by 2002:adf:eacf:: with SMTP id o15mr6227390wrn.171.1563882685768; Tue, 23 Jul 2019 04:51:25 -0700 (PDT) Received: from localhost.localdomain (athedsl-290719.home.otenet.gr. [85.73.183.61]) by smtp.gmail.com with ESMTPSA id r5sm45788573wmh.35.2019.07.23.04.51.24 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 23 Jul 2019 04:51:25 -0700 (PDT) From: velocityra@gmail.com X-Google-Original-From: velocityra@gmail To: ffmpeg-devel@ffmpeg.org Date: Tue, 23 Jul 2019 14:51:12 +0300 Message-Id: <20190723115112.35800-2-velocityra@gmail.com> X-Mailer: git-send-email 2.21.0.windows.1 In-Reply-To: <20190723115112.35800-1-velocityra@gmail.com> References: <20190723115112.35800-1-velocityra@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 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 --- libavcodec/tiff.c | 328 +++++++++++++++++++++++++++++++++++++++++++++- libavcodec/tiff.h | 2 + 2 files changed, 323 insertions(+), 7 deletions(-) diff --git a/libavcodec/tiff.c b/libavcodec/tiff.c index c520d7df83..e62adb9fc1 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,11 +272,32 @@ 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, uint8_t c, int width, int offset) { + /* Handle DNG images with uncompressed strips (non-tiled) */ + if (s->tiff_type == TIFF_TYPE_DNG || s->tiff_type == TIFF_TYPE_CINEMADNG) { + int is_u16, pixel_size; + + is_u16 = (bpp > 8); + pixel_size = (is_u16 ? sizeof(uint16_t) : sizeof(uint8_t)); + + dng_blit(s, + dst + offset, + width * pixel_size, + src, + width * pixel_size, + width * pixel_size, + 1, + is_u16); + return; + } + switch (bpp) { case 1: while (--width >= 0) { @@ -712,6 +748,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 +1156,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 +1262,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 +1318,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 +1341,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 +1686,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 +1696,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 +1789,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 tiled uncompressed 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 +1968,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 +1981,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 +2017,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, };