[FFmpeg-cvslog] avcodec/exr: add multipart support

Paul B Mahol git at videolan.org
Tue Feb 16 04:51:26 EET 2021


ffmpeg | branch: master | Paul B Mahol <onemda at gmail.com> | Tue Feb 16 02:52:42 2021 +0100| [b1eb15c1cf6d57fa807f5e7972f98c55586d75db] | committer: Paul B Mahol

avcodec/exr: add multipart support

> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=b1eb15c1cf6d57fa807f5e7972f98c55586d75db
---

 libavcodec/exr.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 83 insertions(+), 6 deletions(-)

diff --git a/libavcodec/exr.c b/libavcodec/exr.c
index d94291ce73..7d46e8e027 100644
--- a/libavcodec/exr.c
+++ b/libavcodec/exr.c
@@ -143,6 +143,8 @@ typedef struct EXRContext {
 
     EXRTileAttribute tile_attr; /* header data attribute of tile */
     int is_tile; /* 0 if scanline, 1 if tile */
+    int is_multipart;
+    int current_part;
 
     int is_luma;/* 1 if there is an Y plane */
 
@@ -153,10 +155,12 @@ typedef struct EXRContext {
     EXRChannel *channels;
     int nb_channels;
     int current_channel_offset;
+    uint32_t chunk_count;
 
     EXRThreadData *thread_data;
 
     const char *layer;
+    int selected_part;
 
     enum AVColorTransferCharacteristic apply_trc_type;
     float gamma;
@@ -1015,6 +1019,8 @@ static int decode_block(AVCodecContext *avctx, void *tdata,
             return AVERROR_INVALIDDATA;
 
         src  = buf + line_offset + 20;
+        if (s->is_multipart)
+            src += 4;
 
         tile_x = AV_RL32(src - 20);
         tile_y = AV_RL32(src - 16);
@@ -1050,6 +1056,8 @@ static int decode_block(AVCodecContext *avctx, void *tdata,
             return AVERROR_INVALIDDATA;
 
         src  = buf + line_offset + 8;
+        if (s->is_multipart)
+            src += 4;
         line = AV_RL32(src - 8);
 
         if (line < s->ymin || line > s->ymax)
@@ -1266,6 +1274,21 @@ static int decode_block(AVCodecContext *avctx, void *tdata,
     return 0;
 }
 
+static void skip_header_chunk(EXRContext *s)
+{
+    while (bytestream2_get_bytes_left(&s->gb) > 0) {
+        if (!bytestream2_peek_byte(&s->gb))
+            break;
+
+        // Process unknown variables
+        for (int i = 0; i < 2; i++) // value_name and value_type
+            while (bytestream2_get_byte(&s->gb) != 0);
+
+        // Skip variable length
+        bytestream2_skip(&s->gb, bytestream2_get_le32(&s->gb));
+    }
+}
+
 /**
  * Check if the variable name corresponds to its data type.
  *
@@ -1334,7 +1357,9 @@ static int decode_header(EXRContext *s, AVFrame *frame)
     s->tile_attr.xSize    = -1;
     s->tile_attr.ySize    = -1;
     s->is_tile            = 0;
+    s->is_multipart       = 0;
     s->is_luma            = 0;
+    s->current_part       = 0;
 
     if (bytestream2_get_bytes_left(&s->gb) < 10) {
         av_log(s->avctx, AV_LOG_ERROR, "Header too short to parse.\n");
@@ -1359,18 +1384,50 @@ static int decode_header(EXRContext *s, AVFrame *frame)
 
     if (flags & 0x02)
         s->is_tile = 1;
+    if (flags & 0x10)
+        s->is_multipart = 1;
     if (flags & 0x08) {
         avpriv_report_missing_feature(s->avctx, "deep data");
         return AVERROR_PATCHWELCOME;
     }
-    if (flags & 0x10) {
-        avpriv_report_missing_feature(s->avctx, "multipart");
-        return AVERROR_PATCHWELCOME;
-    }
 
     // Parse the header
-    while (bytestream2_get_bytes_left(&s->gb) > 0 && *s->gb.buffer) {
+    while (bytestream2_get_bytes_left(&s->gb) > 0) {
         int var_size;
+
+        while (s->is_multipart && s->current_part < s->selected_part &&
+               bytestream2_get_bytes_left(&s->gb) > 0) {
+            if (bytestream2_peek_byte(&s->gb)) {
+                skip_header_chunk(s);
+            } else {
+                bytestream2_skip(&s->gb, 1);
+                if (!bytestream2_peek_byte(&s->gb))
+                    break;
+            }
+            bytestream2_skip(&s->gb, 1);
+            s->current_part++;
+        }
+
+        if (!bytestream2_peek_byte(&s->gb)) {
+            if (!s->is_multipart)
+                break;
+            bytestream2_skip(&s->gb, 1);
+            if (s->current_part == s->selected_part) {
+                while (bytestream2_get_bytes_left(&s->gb) > 0) {
+                    if (bytestream2_peek_byte(&s->gb)) {
+                        skip_header_chunk(s);
+                    } else {
+                        bytestream2_skip(&s->gb, 1);
+                        if (!bytestream2_peek_byte(&s->gb))
+                            break;
+                    }
+                }
+            }
+            if (!bytestream2_peek_byte(&s->gb))
+                break;
+            s->current_part++;
+        }
+
         if ((var_size = check_header_variable(s, "channels",
                                               "chlist", 38)) >= 0) {
             GetByteContext ch_gb;
@@ -1593,9 +1650,11 @@ static int decode_header(EXRContext *s, AVFrame *frame)
 
             if (s->compression == EXR_UNKN)
                 s->compression = bytestream2_get_byte(&s->gb);
-            else
+            else {
+                bytestream2_skip(&s->gb, 1);
                 av_log(s->avctx, AV_LOG_WARNING,
                        "Found more than one compression attribute.\n");
+            }
 
             continue;
         } else if ((var_size = check_header_variable(s, "tiles",
@@ -1646,6 +1705,22 @@ static int decode_header(EXRContext *s, AVFrame *frame)
             s->avctx->framerate.num = bytestream2_get_le32(&s->gb);
             s->avctx->framerate.den = bytestream2_get_le32(&s->gb);
 
+            continue;
+        } else if ((var_size = check_header_variable(s, "chunkCount",
+                                                     "int", 23)) >= 0) {
+
+            s->chunk_count = bytestream2_get_le32(&s->gb);
+
+            continue;
+        } else if ((var_size = check_header_variable(s, "type",
+                                                     "string", 16)) >= 0) {
+            uint8_t key[256] = { 0 };
+
+            bytestream2_get_buffer(&s->gb, key, FFMIN(sizeof(key) - 1, var_size));
+            if (strncmp("scanlineimage", key, var_size) &&
+                strncmp("tiledimage", key, var_size))
+                return AVERROR_PATCHWELCOME;
+
             continue;
         }
 
@@ -1941,6 +2016,8 @@ static av_cold int decode_end(AVCodecContext *avctx)
 static const AVOption options[] = {
     { "layer", "Set the decoding layer", OFFSET(layer),
         AV_OPT_TYPE_STRING, { .str = "" }, 0, 0, VD },
+    { "part",  "Set the decoding part", OFFSET(selected_part),
+        AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VD },
     { "gamma", "Set the float gamma value when decoding", OFFSET(gamma),
         AV_OPT_TYPE_FLOAT, { .dbl = 1.0f }, 0.001, FLT_MAX, VD },
 



More information about the ffmpeg-cvslog mailing list