[FFmpeg-devel] avcodec/tiff: Add support for recognizing DNG files

Submitted by velocityra@gmail.com on May 28, 2019, 6:34 p.m.

Details

Message ID 20190528183442.12376-1-velocityra@gmail.com
State New
Headers show

Commit Message

velocityra@gmail.com May 28, 2019, 6:34 p.m.
From: Nick Renieris <velocityra@gmail.com>

In DNG images with the .tiff extension, it solves the issue where the
TIFF thumbnail in IFD 0 was incorrectly parsed (related confusion: [1]).
Embedded thumbnails for DNG images can still be decoded with the added
"-dng_thumb" option.

Additionally:
 - Renamed TIFF_WHITE_LEVEL to DNG_WHITE_LEVEL since it is specified
   in the DNG spec.
 - Added/changed some comments to be more precise in differentiating
   between TIFF, TIFF/EP and DNG values.

Related to ticket: https://trac.ffmpeg.org/ticket/4364

---

[1]: https://superuser.com/questions/546879/creating-video-from-dng-images-with-ffmpeg

Signed-off-by: Nick Renieris <velocityra@gmail.com>
---
 libavcodec/tiff.c  | 36 +++++++++++++++++++++++++++++++++++-
 libavcodec/tiff.h  | 18 +++++++++++++-----
 libavformat/img2.c |  1 +
 3 files changed, 49 insertions(+), 6 deletions(-)

Comments

Paul B Mahol May 28, 2019, 8:13 p.m.
On 5/28/19, velocityra@gmail.com <velocityra@gmail.com> wrote:
> From: Nick Renieris <velocityra@gmail.com>
>
> In DNG images with the .tiff extension, it solves the issue where the
> TIFF thumbnail in IFD 0 was incorrectly parsed (related confusion: [1]).
> Embedded thumbnails for DNG images can still be decoded with the added
> "-dng_thumb" option.
>
> Additionally:
>  - Renamed TIFF_WHITE_LEVEL to DNG_WHITE_LEVEL since it is specified
>    in the DNG spec.
>  - Added/changed some comments to be more precise in differentiating
>    between TIFF, TIFF/EP and DNG values.
>
> Related to ticket: https://trac.ffmpeg.org/ticket/4364
>

This patch breaks decoding DNGs which are already supported.

> ---
>
> [1]:
> https://superuser.com/questions/546879/creating-video-from-dng-images-with-ffmpeg
>
> Signed-off-by: Nick Renieris <velocityra@gmail.com>
> ---
>  libavcodec/tiff.c  | 36 +++++++++++++++++++++++++++++++++++-
>  libavcodec/tiff.h  | 18 +++++++++++++-----
>  libavformat/img2.c |  1 +
>  3 files changed, 49 insertions(+), 6 deletions(-)
>
> diff --git a/libavcodec/tiff.c b/libavcodec/tiff.c
> index 79e6242549..9575fe92ce 100644
> --- a/libavcodec/tiff.c
> +++ b/libavcodec/tiff.c
> @@ -56,6 +56,7 @@ typedef struct TiffContext {
>
>      int get_subimage;
>      uint16_t get_page;
> +    int get_dng_thumb;
>
>      int width, height;
>      unsigned int bpp, bppcount;
> @@ -70,7 +71,9 @@ typedef struct TiffContext {
>      int predictor;
>      int fill_order;
>      uint32_t res[4];
> +    int is_thumbnail;
>
> +    int dng_mode; /** denotes that this is a DNG image */
>      int is_bayer;
>      uint8_t pattern[4];
>      unsigned white_level;
> @@ -948,6 +951,8 @@ static int tiff_decode_tag(TiffContext *s, AVFrame
> *frame)
>      }
>
>      switch (tag) {
> +    case TIFF_SUBFILE:
> +        s->is_thumbnail = (value != 0);
>      case TIFF_WIDTH:
>          s->width = value;
>          break;
> @@ -1088,7 +1093,7 @@ static int tiff_decode_tag(TiffContext *s, AVFrame
> *frame)
>      case TIFF_SUB_IFDS:
>          s->sub_ifd = value;
>          break;
> -    case TIFF_WHITE_LEVEL:
> +    case DNG_WHITE_LEVEL:
>          s->white_level = value;
>          break;
>      case TIFF_CFA_PATTERN_DIM:
> @@ -1339,6 +1344,20 @@ static int tiff_decode_tag(TiffContext *s, AVFrame
> *frame)
>      case TIFF_SOFTWARE_NAME:
>          ADD_METADATA(count, "software", NULL);
>          break;
> +    case DNG_VERSION:
> +        if (count == 4) {
> +            unsigned int ver[4];
> +            ver[0] = ff_tget(&s->gb, type, s->le);
> +            ver[1] = ff_tget(&s->gb, type, s->le);
> +            ver[2] = ff_tget(&s->gb, type, s->le);
> +            ver[3] = ff_tget(&s->gb, type, s->le);
> +
> +            av_log(s->avctx, AV_LOG_DEBUG, "DNG file, version
> %u.%u.%u.%u\n",
> +                ver[0], ver[1], ver[2], ver[3]);
> +
> +            s->dng_mode = 1;
> +        }
> +        break;
>      default:
>          if (s->avctx->err_recognition & AV_EF_EXPLODE) {
>              av_log(s->avctx, AV_LOG_ERROR,
> @@ -1387,6 +1406,7 @@ static int decode_frame(AVCodecContext *avctx,
>      s->le          = le;
>      // TIFF_BPP is not a required tag and defaults to 1
>  again:
> +    s->is_thumbnail = 0;
>      s->bppcount    = s->bpp = 1;
>      s->photometric = TIFF_PHOTOMETRIC_NONE;
>      s->compr       = TIFF_RAW;
> @@ -1394,6 +1414,7 @@ again:
>      s->white_level = 0;
>      s->is_bayer    = 0;
>      s->cur_page    = 0;
> +    s->dng_mode    = 0;
>      free_geotags(s);
>
>      // Reset these offsets so we can tell if they were set this frame
> @@ -1408,6 +1429,18 @@ again:
>              return ret;
>      }
>
> +    if (s->dng_mode) {
> +        if (!s->get_dng_thumb) {
> +            av_log(avctx, AV_LOG_ERROR, "DNG images are not supported\n");
> +            av_log(avctx, AV_LOG_INFO, "You can use -dng_thumb to decode an
> embedded TIFF thumbnail (if any) instead\n");
> +            return AVERROR_INVALIDDATA;
> +        }
> +        if (!s->is_thumbnail) {
> +            av_log(avctx, AV_LOG_INFO, "No embedded thumbnail present\n");
> +            return AVERROR_EOF;
> +        }
> +    }
> +
>      /** whether we should look for this IFD's SubIFD */
>      retry_for_subifd = s->sub_ifd && s->get_subimage;
>      /** whether we should look for this multi-page IFD's next page */
> @@ -1671,6 +1704,7 @@ static av_cold int tiff_end(AVCodecContext *avctx)
>  static const AVOption tiff_options[] = {
>      { "subimage", "decode subimage instead if available",
> OFFSET(get_subimage), AV_OPT_TYPE_BOOL, {.i64=0},  0, 1,
> AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM },
>      { "page", "page number of multi-page image to decode (starting from
> 1)", OFFSET(get_page), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX,
> AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM },
> +    { "dng_thumb", "decode TIFF thumbnail of DNG image",
> OFFSET(get_dng_thumb), AV_OPT_TYPE_BOOL, {.i64=0},  0, 1,
> AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM },
>      { NULL },
>  };
>
> diff --git a/libavcodec/tiff.h b/libavcodec/tiff.h
> index 4b08650108..3cede299f6 100644
> --- a/libavcodec/tiff.h
> +++ b/libavcodec/tiff.h
> @@ -20,7 +20,7 @@
>
>  /**
>   * @file
> - * TIFF tables
> + * TIFF constants & data structures
>   *
>   * For more information about the TIFF format, check the official docs at:
>   * http://partners.adobe.com/public/developer/tiff/index.html
> @@ -33,7 +33,7 @@
>  #include <stdint.h>
>  #include "tiff_common.h"
>
> -/** abridged list of TIFF tags */
> +/** abridged list of TIFF and TIFF/EP tags */
>  enum TiffTags {
>      TIFF_SUBFILE            = 0xfe,
>      TIFF_WIDTH              = 0x100,
> @@ -85,10 +85,17 @@ enum TiffTags {
>      TIFF_GEO_KEY_DIRECTORY  = 0x87AF,
>      TIFF_GEO_DOUBLE_PARAMS  = 0x87B0,
>      TIFF_GEO_ASCII_PARAMS   = 0x87B1,
> -    TIFF_WHITE_LEVEL        = 0xC61D,
>  };
>
> -/** list of TIFF compression types */
> +/** abridged list of DNG tags */
> +enum DngTags
> +{
> +    DNG_VERSION             = 0xC612,
> +    DNG_BACKWARD_VERSION    = 0xC613,
> +    DNG_WHITE_LEVEL         = 0xC61D,
> +};
> +
> +/** list of TIFF, TIFF/EP and DNG compression types */
>  enum TiffCompr {
>      TIFF_RAW = 1,
>      TIFF_CCITT_RLE,
> @@ -151,6 +158,7 @@ enum TiffGeoTagKey {
>      TIFF_VERTICAL_UNITS_GEOKEY               = 4099
>  };
>
> +/** list of TIFF, TIFF/AP and DNG PhotometricInterpretation
> (TIFF_PHOTOMETRIC) values */
>  enum TiffPhotometric {
>      TIFF_PHOTOMETRIC_NONE       = -1,
>      TIFF_PHOTOMETRIC_WHITE_IS_ZERO,      /* mono or grayscale, 0 is white
> */
> @@ -163,7 +171,7 @@ enum TiffPhotometric {
>      TIFF_PHOTOMETRIC_CIE_LAB    = 8,     /* 1976 CIE L*a*b* */
>      TIFF_PHOTOMETRIC_ICC_LAB,            /* ICC L*a*b* */
>      TIFF_PHOTOMETRIC_ITU_LAB,            /* ITU L*a*b* */
> -    TIFF_PHOTOMETRIC_CFA        = 32803, /* Color Filter Array (DNG) */
> +    TIFF_PHOTOMETRIC_CFA        = 32803, /* Color Filter Array (TIFF/AP and
> DNG) */
>      TIFF_PHOTOMETRIC_LOG_L      = 32844, /* CIE Log2(L) */
>      TIFF_PHOTOMETRIC_LOG_LUV,            /* CIE Log L*u*v* */
>      TIFF_PHOTOMETRIC_LINEAR_RAW = 34892, /* Linear Raw (DNG) */
> diff --git a/libavformat/img2.c b/libavformat/img2.c
> index 8432cc0955..16bc9d2abd 100644
> --- a/libavformat/img2.c
> +++ b/libavformat/img2.c
> @@ -51,6 +51,7 @@ const IdStrMap ff_img_tags[] = {
>      { AV_CODEC_ID_TARGA,      "tga"      },
>      { AV_CODEC_ID_TIFF,       "tiff"     },
>      { AV_CODEC_ID_TIFF,       "tif"      },
> +    { AV_CODEC_ID_TIFF,       "dng"      },
>      { AV_CODEC_ID_SGI,        "sgi"      },
>      { AV_CODEC_ID_PTX,        "ptx"      },
>      { AV_CODEC_ID_PCX,        "pcx"      },
> --
> 2.21.0.windows.1
>
>

Patch hide | download patch | download mbox

diff --git a/libavcodec/tiff.c b/libavcodec/tiff.c
index 79e6242549..9575fe92ce 100644
--- a/libavcodec/tiff.c
+++ b/libavcodec/tiff.c
@@ -56,6 +56,7 @@  typedef struct TiffContext {
 
     int get_subimage;
     uint16_t get_page;
+    int get_dng_thumb;
 
     int width, height;
     unsigned int bpp, bppcount;
@@ -70,7 +71,9 @@  typedef struct TiffContext {
     int predictor;
     int fill_order;
     uint32_t res[4];
+    int is_thumbnail;
 
+    int dng_mode; /** denotes that this is a DNG image */
     int is_bayer;
     uint8_t pattern[4];
     unsigned white_level;
@@ -948,6 +951,8 @@  static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
     }
 
     switch (tag) {
+    case TIFF_SUBFILE:
+        s->is_thumbnail = (value != 0);
     case TIFF_WIDTH:
         s->width = value;
         break;
@@ -1088,7 +1093,7 @@  static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
     case TIFF_SUB_IFDS:
         s->sub_ifd = value;
         break;
-    case TIFF_WHITE_LEVEL:
+    case DNG_WHITE_LEVEL:
         s->white_level = value;
         break;
     case TIFF_CFA_PATTERN_DIM:
@@ -1339,6 +1344,20 @@  static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
     case TIFF_SOFTWARE_NAME:
         ADD_METADATA(count, "software", NULL);
         break;
+    case DNG_VERSION:
+        if (count == 4) {
+            unsigned int ver[4];
+            ver[0] = ff_tget(&s->gb, type, s->le);
+            ver[1] = ff_tget(&s->gb, type, s->le);
+            ver[2] = ff_tget(&s->gb, type, s->le);
+            ver[3] = ff_tget(&s->gb, type, s->le);
+
+            av_log(s->avctx, AV_LOG_DEBUG, "DNG file, version %u.%u.%u.%u\n",
+                ver[0], ver[1], ver[2], ver[3]);
+
+            s->dng_mode = 1;
+        }
+        break;
     default:
         if (s->avctx->err_recognition & AV_EF_EXPLODE) {
             av_log(s->avctx, AV_LOG_ERROR,
@@ -1387,6 +1406,7 @@  static int decode_frame(AVCodecContext *avctx,
     s->le          = le;
     // TIFF_BPP is not a required tag and defaults to 1
 again:
+    s->is_thumbnail = 0;
     s->bppcount    = s->bpp = 1;
     s->photometric = TIFF_PHOTOMETRIC_NONE;
     s->compr       = TIFF_RAW;
@@ -1394,6 +1414,7 @@  again:
     s->white_level = 0;
     s->is_bayer    = 0;
     s->cur_page    = 0;
+    s->dng_mode    = 0;
     free_geotags(s);
 
     // Reset these offsets so we can tell if they were set this frame
@@ -1408,6 +1429,18 @@  again:
             return ret;
     }
 
+    if (s->dng_mode) {
+        if (!s->get_dng_thumb) {
+            av_log(avctx, AV_LOG_ERROR, "DNG images are not supported\n");
+            av_log(avctx, AV_LOG_INFO, "You can use -dng_thumb to decode an embedded TIFF thumbnail (if any) instead\n");
+            return AVERROR_INVALIDDATA;
+        }
+        if (!s->is_thumbnail) {
+            av_log(avctx, AV_LOG_INFO, "No embedded thumbnail present\n");
+            return AVERROR_EOF;
+        }
+    }
+
     /** whether we should look for this IFD's SubIFD */
     retry_for_subifd = s->sub_ifd && s->get_subimage;
     /** whether we should look for this multi-page IFD's next page */
@@ -1671,6 +1704,7 @@  static av_cold int tiff_end(AVCodecContext *avctx)
 static const AVOption tiff_options[] = {
     { "subimage", "decode subimage instead if available", OFFSET(get_subimage), AV_OPT_TYPE_BOOL, {.i64=0},  0, 1, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM },
     { "page", "page number of multi-page image to decode (starting from 1)", OFFSET(get_page), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM },
+    { "dng_thumb", "decode TIFF thumbnail of DNG image", OFFSET(get_dng_thumb), AV_OPT_TYPE_BOOL, {.i64=0},  0, 1, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM },
     { NULL },
 };
 
diff --git a/libavcodec/tiff.h b/libavcodec/tiff.h
index 4b08650108..3cede299f6 100644
--- a/libavcodec/tiff.h
+++ b/libavcodec/tiff.h
@@ -20,7 +20,7 @@ 
 
 /**
  * @file
- * TIFF tables
+ * TIFF constants & data structures
  *
  * For more information about the TIFF format, check the official docs at:
  * http://partners.adobe.com/public/developer/tiff/index.html
@@ -33,7 +33,7 @@ 
 #include <stdint.h>
 #include "tiff_common.h"
 
-/** abridged list of TIFF tags */
+/** abridged list of TIFF and TIFF/EP tags */
 enum TiffTags {
     TIFF_SUBFILE            = 0xfe,
     TIFF_WIDTH              = 0x100,
@@ -85,10 +85,17 @@  enum TiffTags {
     TIFF_GEO_KEY_DIRECTORY  = 0x87AF,
     TIFF_GEO_DOUBLE_PARAMS  = 0x87B0,
     TIFF_GEO_ASCII_PARAMS   = 0x87B1,
-    TIFF_WHITE_LEVEL        = 0xC61D,
 };
 
-/** list of TIFF compression types */
+/** abridged list of DNG tags */
+enum DngTags
+{
+    DNG_VERSION             = 0xC612,
+    DNG_BACKWARD_VERSION    = 0xC613,
+    DNG_WHITE_LEVEL         = 0xC61D,
+};
+
+/** list of TIFF, TIFF/EP and DNG compression types */
 enum TiffCompr {
     TIFF_RAW = 1,
     TIFF_CCITT_RLE,
@@ -151,6 +158,7 @@  enum TiffGeoTagKey {
     TIFF_VERTICAL_UNITS_GEOKEY               = 4099
 };
 
+/** list of TIFF, TIFF/AP and DNG PhotometricInterpretation (TIFF_PHOTOMETRIC) values */
 enum TiffPhotometric {
     TIFF_PHOTOMETRIC_NONE       = -1,
     TIFF_PHOTOMETRIC_WHITE_IS_ZERO,      /* mono or grayscale, 0 is white */
@@ -163,7 +171,7 @@  enum TiffPhotometric {
     TIFF_PHOTOMETRIC_CIE_LAB    = 8,     /* 1976 CIE L*a*b* */
     TIFF_PHOTOMETRIC_ICC_LAB,            /* ICC L*a*b* */
     TIFF_PHOTOMETRIC_ITU_LAB,            /* ITU L*a*b* */
-    TIFF_PHOTOMETRIC_CFA        = 32803, /* Color Filter Array (DNG) */
+    TIFF_PHOTOMETRIC_CFA        = 32803, /* Color Filter Array (TIFF/AP and DNG) */
     TIFF_PHOTOMETRIC_LOG_L      = 32844, /* CIE Log2(L) */
     TIFF_PHOTOMETRIC_LOG_LUV,            /* CIE Log L*u*v* */
     TIFF_PHOTOMETRIC_LINEAR_RAW = 34892, /* Linear Raw (DNG) */
diff --git a/libavformat/img2.c b/libavformat/img2.c
index 8432cc0955..16bc9d2abd 100644
--- a/libavformat/img2.c
+++ b/libavformat/img2.c
@@ -51,6 +51,7 @@  const IdStrMap ff_img_tags[] = {
     { AV_CODEC_ID_TARGA,      "tga"      },
     { AV_CODEC_ID_TIFF,       "tiff"     },
     { AV_CODEC_ID_TIFF,       "tif"      },
+    { AV_CODEC_ID_TIFF,       "dng"      },
     { AV_CODEC_ID_SGI,        "sgi"      },
     { AV_CODEC_ID_PTX,        "ptx"      },
     { AV_CODEC_ID_PCX,        "pcx"      },