[FFmpeg-devel,v12,10/14] lavc/tiff: Support decoding of DNGs with single-component JPEGs

Submitted by velocityra@gmail.com on Aug. 9, 2019, 4:29 p.m.

Details

Message ID 20190809162959.17924-10-velocityra@gmail.com
State New
Headers show

Commit Message

velocityra@gmail.com Aug. 9, 2019, 4:29 p.m.
From: Nick Renieris <velocityra@gmail.com>

This enables decoding of DNG images generated by the 'DJI Zenmuse X7'
digital camera
Samples: https://www.dji.com/gr/zenmuse-x7/info#downloads

Signed-off-by: Nick Renieris <velocityra@gmail.com>
---
 libavcodec/tiff.c | 61 +++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 51 insertions(+), 10 deletions(-)

Comments

Michael Niedermayer Aug. 10, 2019, 12:19 p.m.
On Fri, Aug 09, 2019 at 07:29:55PM +0300, Nick Renieris wrote:
> From: Nick Renieris <velocityra@gmail.com>
> 
> This enables decoding of DNG images generated by the 'DJI Zenmuse X7'
> digital camera
> Samples: https://www.dji.com/gr/zenmuse-x7/info#downloads
> 
> Signed-off-by: Nick Renieris <velocityra@gmail.com>
> ---
>  libavcodec/tiff.c | 61 +++++++++++++++++++++++++++++++++++++++--------
>  1 file changed, 51 insertions(+), 10 deletions(-)

after this commit the code dies when used with ffplay with floating point 
exceptions

./ffplay pentax_k1_69.dng 
...
[tiff @ 0x7f8a68002540] Assuming black level pattern values are identical
Floating point exception (core dumped) vq=    0KB sq=    0B f=0/0   

[...]
velocityra@gmail.com Aug. 10, 2019, 9:57 p.m.
> after this commit the code dies when used with ffplay with floating point
> exceptions

I tried this on Ubuntu and I can't repro, it works fine.
Maybe something I changed locally fixed it, please re-test on the new
revision I'll send in a bit.
Michael Niedermayer Aug. 10, 2019, 11:29 p.m.
On Sun, Aug 11, 2019 at 12:57:17AM +0300, Nick Renieris wrote:
> > after this commit the code dies when used with ffplay with floating point
> > exceptions
> 
> I tried this on Ubuntu and I can't repro, it works fine.
> Maybe something I changed locally fixed it, please re-test on the new
> revision I'll send in a bit.

yes, it behaved a bit odd, ill retest with the new patches tomorrow (need to sleep)
some things i noticed in the previous ones though
it disappeared with some commits in the previous set too
it doesnt happen with ffmpeg
and i have this half useless backtrace:
#0  0x00000000004752cc in dng_decode.isra ()
#1  0x0000000000b73b67 in decode_frame ()
#2  0x00000000008668d7 in decode_receive_frame_internal ()
#3  0x0000000000867488 in avcodec_send_packet ()
#4  0x00000000007c002d in try_decode_frame ()
#5  0x00000000007c8ad8 in avformat_find_stream_info ()
#6  0x00000000004c2b09 in read_thread ()

ill provide better information tomorrow in case it still happens
with the latest set 

Thanks

[...]
Michael Niedermayer Aug. 11, 2019, 10:21 a.m.
On Sun, Aug 11, 2019 at 01:29:44AM +0200, Michael Niedermayer wrote:
> On Sun, Aug 11, 2019 at 12:57:17AM +0300, Nick Renieris wrote:
> > > after this commit the code dies when used with ffplay with floating point
> > > exceptions
> > 
> > I tried this on Ubuntu and I can't repro, it works fine.
> > Maybe something I changed locally fixed it, please re-test on the new
> > revision I'll send in a bit.
> 
> yes, it behaved a bit odd, ill retest with the new patches tomorrow (need to sleep)
> some things i noticed in the previous ones though
> it disappeared with some commits in the previous set too
> it doesnt happen with ffmpeg
> and i have this half useless backtrace:
> #0  0x00000000004752cc in dng_decode.isra ()
> #1  0x0000000000b73b67 in decode_frame ()
> #2  0x00000000008668d7 in decode_receive_frame_internal ()
> #3  0x0000000000867488 in avcodec_send_packet ()
> #4  0x00000000007c002d in try_decode_frame ()
> #5  0x00000000007c8ad8 in avformat_find_stream_info ()
> #6  0x00000000004c2b09 in read_thread ()
> 
> ill provide better information tomorrow in case it still happens
> with the latest set 

also occurs with the latest patchset (minus the last 3 patches)

heres a proper backtrace:

Program received signal SIGFPE, Arithmetic exception.
[Switching to Thread 0x7fffdb6a5700 (LWP 19427)]
0x0000000000c4bad1 in dng_decode_tiles (avctx=0x7fffcc002540, frame=0x7fffcc003200) at libavcodec/tiff.c:947
947	    has_width_leftover = (s->width % s->tile_width != 0);
(gdb) bt
Python Exception <type 'exceptions.ImportError'> No module named gdb.frames: 
#0  0x0000000000c4bad1 in dng_decode_tiles (avctx=0x7fffcc002540, frame=0x7fffcc003200) at libavcodec/tiff.c:947
#1  0x0000000000c4bd95 in dng_decode (avctx=0x7fffcc002540, frame=0x7fffcc003200, avpkt=0x7fffcc003540) at libavcodec/tiff.c:1011
#2  0x0000000000c4fcc4 in decode_frame (avctx=0x7fffcc002540, data=0x7fffcc003200, got_frame=0x7fffdb6a46e8, avpkt=0x7fffcc003540) at libavcodec/tiff.c:1893
#3  0x00000000009005fa in decode_simple_internal (avctx=0x7fffcc002540, frame=0x7fffcc003200) at libavcodec/decode.c:433
#4  0x0000000000901281 in decode_simple_receive_frame (avctx=0x7fffcc002540, frame=0x7fffcc003200) at libavcodec/decode.c:629
#5  0x000000000090134c in decode_receive_frame_internal (avctx=0x7fffcc002540, frame=0x7fffcc003200) at libavcodec/decode.c:647
#6  0x00000000009015a7 in avcodec_send_packet (avctx=0x7fffcc002540, avpkt=0x7fffdb6a4890) at libavcodec/decode.c:705
#7  0x000000000083415b in try_decode_frame (s=0x7fffcc000940, st=0x7fffcc001c80, avpkt=0x7fffdb6a4a40, options=0x7fffcc001380) at libavformat/utils.c:3080
#8  0x000000000083752c in avformat_find_stream_info (ic=0x7fffcc000940, options=0x7fffcc001380) at libavformat/utils.c:3903
#9  0x0000000000422573 in read_thread (arg=0x7fffdb6a6040) at fftools/ffplay.c:2805
#10 0x00007ffff5f1f2ed in SDL_RunThread (data=0x2996dd0) at SDL2-2.0.4/src/thread/SDL_thread.c:282
#11 0x00007ffff5f7d1a9 in RunThread (data=<optimized out>) at SDL2-2.0.4/src/thread/pthread/SDL_systhread.c:75
#12 0x00007ffff0249184 in start_thread () from /lib/x86_64-linux-gnu/libpthread.so.0
#13 0x00007fffeff7603d in clone () from /lib/x86_64-linux-gnu/libc.so.6


[...]

Patch hide | download patch | download mbox

diff --git a/libavcodec/tiff.c b/libavcodec/tiff.c
index cf8965453c..ecd87c0b2f 100644
--- a/libavcodec/tiff.c
+++ b/libavcodec/tiff.c
@@ -274,7 +274,8 @@  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);
+                                      const uint8_t *src, int src_stride, int width, int height,
+                                      int is_single_comp, int is_u16);
 
 static void av_always_inline horizontal_fill(TiffContext *s,
                                              unsigned int bpp, uint8_t* dst,
@@ -698,6 +699,7 @@  static int tiff_unpack_strip(TiffContext *s, AVFrame *p, uint8_t *dst, int strid
                          0, // no stride, only 1 line
                          width / pixel_size_bytes * pixel_size_bits / s->bpp, // need to account for [1, 16] bpp
                          1,
+                         0, // single-component variation is only preset in JPEG-encoded DNGs
                          is_u16);
             }
 
@@ -795,18 +797,32 @@  static uint16_t av_always_inline dng_process_color8(uint16_t value,
 
 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 width, int height, int is_single_comp, int is_u16)
 {
     int line, col;
     float scale_factor;
 
     scale_factor = 1.0f / (s->white_level - s->black_level);
 
-    if (is_u16) {
-        for (line = 0; line < height; line++) {
+    if (is_single_comp) {
+        if (!is_u16)
+            return; /* <= 8bpp unsupported */
+
+        /* Image is double the width and half the height we need, each row comprises 2 rows of the output
+           (split vertically in the middle). */
+        for (line = 0; line < height / 2; line++) {
             uint16_t *dst_u16 = (uint16_t *)dst;
             uint16_t *src_u16 = (uint16_t *)src;
 
+            /* Blit first half of input row row to initial row of output */
+            for (col = 0; col < width; col++)
+                *dst_u16++ = dng_process_color16(*src_u16++, s->dng_lut, s->black_level, scale_factor);
+
+            /* Advance the destination pointer by a row (source pointer remains in the same place) */
+            dst += dst_stride * sizeof(uint16_t);
+            dst_u16 = (uint16_t *)dst;
+
+            /* Blit second half of input row row to next row of output */
             for (col = 0; col < width; col++)
                 *dst_u16++ = dng_process_color16(*src_u16++, s->dng_lut, s->black_level, scale_factor);
 
@@ -814,12 +830,27 @@  static void dng_blit(TiffContext *s, uint8_t *dst, int dst_stride,
             src += src_stride * sizeof(uint16_t);
         }
     } else {
-        for (line = 0; line < height; line++) {
-            for (col = 0; col < width; col++)
-                *dst++ = dng_process_color8(*src++, s->dng_lut, s->black_level, scale_factor);
+        /* Input and output image are the same size and the MJpeg decoder has done per-component
+           deinterleaving, so blitting here is straightforward. */
+        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_process_color16(*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_process_color8(*src++, s->dng_lut, s->black_level, scale_factor);
 
-            dst += dst_stride;
-            src += src_stride;
+                dst += dst_stride;
+                src += src_stride;
+            }
         }
     }
 }
@@ -831,7 +862,7 @@  static int dng_decode_jpeg_tile(AVCodecContext *avctx, AVFrame *frame,
     AVPacket jpkt;
     uint8_t *dst_data, *src_data;
     uint32_t dst_offset; /* offset from dst buffer in pixels */
-    int is_u16, pixel_size;
+    int is_single_comp, is_u16, pixel_size;
     int ret;
 
     /* Prepare a packet and send to the MJPEG decoder */
@@ -865,9 +896,18 @@  static int dng_decode_jpeg_tile(AVCodecContext *avctx, AVFrame *frame,
 
     /* Copy the outputted tile's pixels from 'jpgframe' to 'frame' (final buffer) */
 
+    /* See dng_blit for explanation */
+    is_single_comp = (s->avctx_mjpeg->width == w * 2 && s->avctx_mjpeg->height == h / 2);
+
     is_u16 = (s->bpp > 8);
     pixel_size = (is_u16 ? sizeof(uint16_t) : sizeof(uint8_t));
 
+    if (is_single_comp && !is_u16) {
+        av_log(s->avctx, AV_LOG_ERROR, "DNGs with bpp <= 8 and 1 component are unsupported\n");
+        av_frame_unref(s->jpgframe);
+        return AVERROR_PATCHWELCOME;
+    }
+
     dst_offset = x + frame->linesize[0] * y / pixel_size;
     dst_data = frame->data[0] + dst_offset * pixel_size;
     src_data = s->jpgframe->data[0];
@@ -879,6 +919,7 @@  static int dng_decode_jpeg_tile(AVCodecContext *avctx, AVFrame *frame,
              s->jpgframe->linesize[0] / pixel_size,
              w,
              h,
+             is_single_comp,
              is_u16);
 
     av_frame_unref(s->jpgframe);