Palettized QuickTime video in Matroska has hitherto not been recognized whatsoever, and the "palette" used has been completely random.

The patch for matroskadec.c fixes this issue by adding a palette side data packet in matroska_deliver_packet(), much in the same way as it's done in mov.c.

The change to mov.c consists mainly of moving the palette handling from the mov_parse_stsd_video() function to a new ff_get_qtpalette() function in the new file qtpalette.c, which is shared by both matroskadec.c and mov.c.

Since the ff_get_qtpalette() function has to parse the video sample description in two different ways, i.e. 1) by using the in-memory private data when called from matroskadec.c, and 2) by reading from the file when called from mov.c, it has to use a separate variable for each of these sources. It may seem slightly redundant, but it is unfortunately necessary. I would prefer that nobody touches what I've been doing, since it's working fine right now, and it's very easy to break things if you try to "improve it". Believe me, I've been there.

In matroskadec.c, I'm also putting the palette in 'extradata', like it's done for V_MS/VFW/FOURCC; this is a requirement in order for MPlayer to use the correct palette. And why is that, you may wonder. Well, it's because for some mysterious reason, MPlayer adds *another* palette side data packet *after* the one added in matroskadec.c. It uses whatever is in extradata as the palette when adding this packet.

Video samples for testing are available at
https://drive.google.com/open?id=0B3_pEBoLs0faWElmM2FnLTZYNlk.

--
Mats Peterson
http://matsp888.no-ip.org/~mats/
>From cd6cf2da13e1f0119a9e5bc7d4f789f6b776c887 Mon Sep 17 00:00:00 2001
From: Mats Peterson <matsp...@yahoo.com>
Date: Sun, 27 Dec 2015 05:24:22 +0100
Subject: [PATCH v7] lavf: palettized QuickTime video in Matroska

Palettized QuickTime video in Matroska has hitherto not been recognized
whatsoever, and the "palette" used has been completely random.

The patch for matroskadec.c fixes this issue by adding a palette side
data packet in matroska_deliver_packet(), much in the same way as it's
done in mov.c.

The change to mov.c consists mainly of moving the palette handling from
the mov_parse_stsd_video() function to a new ff_get_qtpalette() function
in the new file qtpalette.c, which is shared by both matroskadec.c and
mov.c.

Since the ff_get_qtpalette() function has to parse the video sample
description in two different ways, i.e. 1) by using the in-memory
private data when called from matroskadec.c, and 2) by reading from the
file when called from mov.c, it has to use a separate variable for each
of these sources. It may seem slightly redundant, but it is
unfortunately necessary. I would prefer that nobody touches what I've
been doing, since it's working fine right now, and it's very easy to
break things if you try to "improve it". Believe me, I've been there.

In matroskadec.c, I'm also putting the palette in 'extradata', like it's
done for V_MS/VFW/FOURCC; this is a requirement in order for MPlayer to
use the correct palette. And why is that, you may wonder. Well, it's
because for some mysterious reason, MPlayer adds *another* palette side
data packet *after* the one added in matroskadec.c. It uses whatever is
in extradata as the palette when adding this packet.

Video samples for testing are available at
https://drive.google.com/open?id=0B3_pEBoLs0faWElmM2FnLTZYNlk.

---
 libavformat/Makefile      |    1 +
 libavformat/matroskadec.c |   30 ++++++++++-
 libavformat/mov.c         |   83 +++++------------------------
 libavformat/qtpalette.c   |  128 +++++++++++++++++++++++++++++++++++++++++++++
 libavformat/qtpalette.h   |    6 ++-
 5 files changed, 176 insertions(+), 72 deletions(-)
 create mode 100644 libavformat/qtpalette.c

diff --git a/libavformat/Makefile b/libavformat/Makefile
index 110e9e3..e03c73e 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -18,6 +18,7 @@ OBJS = allformats.o         \
        mux.o                \
        options.o            \
        os_support.o         \
+       qtpalette.o          \
        riff.o               \
        sdp.o                \
        url.o                \
diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c
index c574749..cc3cdd0 100644
--- a/libavformat/matroskadec.c
+++ b/libavformat/matroskadec.c
@@ -64,6 +64,8 @@
 #include <zlib.h>
 #endif
 
+#include "qtpalette.h"
+
 typedef enum {
     EBML_NONE,
     EBML_UINT,
@@ -312,6 +314,9 @@ typedef struct MatroskaDemuxContext {
 
     /* WebM DASH Manifest live flag/ */
     int is_live;
+
+    uint32_t palette[AVPALETTE_COUNT];
+    int has_palette;
 } MatroskaDemuxContext;
 
 typedef struct MatroskaBlock {
@@ -1856,7 +1861,7 @@ static int matroska_parse_tracks(AVFormatContext *s)
             fourcc           = st->codec->codec_tag;
             extradata_offset = FFMIN(track->codec_priv.size, 18);
         } else if (!strcmp(track->codec_id, "A_QUICKTIME")
-                   && (track->codec_priv.size >= 86)
+                   && (track->codec_priv.size >= 36)
                    && (track->codec_priv.data)) {
             fourcc = AV_RL32(track->codec_priv.data + 4);
             codec_id = ff_codec_get_id(ff_codec_movaudio_tags, fourcc);
@@ -1881,6 +1886,20 @@ static int matroska_parse_tracks(AVFormatContext *s)
                 av_log(matroska->ctx, AV_LOG_ERROR,
                        "mov FourCC not found %s.\n", buf);
             }
+            if (track->codec_priv.size >= 86) {
+                bit_depth = AV_RB16(track->codec_priv.data + 82);
+                if (ff_get_qtpalette(codec_id, track->codec_priv.data + 16,
+                        NULL, matroska->palette)) {
+                    bit_depth &= 0x1F;
+                    /* Behave like V_MS/VFW/FOURCC; copy the palette to
+                     * extradata */
+                    if (ff_alloc_extradata(st->codec, AVPALETTE_SIZE))
+                        return AVERROR(ENOMEM);
+                    memcpy(st->codec->extradata, matroska->palette,
+                            AVPALETTE_SIZE);
+                    matroska->has_palette = 1;
+                }
+            }
         } else if (codec_id == AV_CODEC_ID_PCM_S16BE) {
             switch (track->audio.bitdepth) {
             case  8:
@@ -2326,6 +2345,15 @@ static int matroska_deliver_packet(MatroskaDemuxContext *matroska,
     if (matroska->num_packets > 0) {
         memcpy(pkt, matroska->packets[0], sizeof(AVPacket));
         av_freep(&matroska->packets[0]);
+        if (matroska->has_palette) {
+            uint8_t *pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE);
+            if (!pal) {
+                av_log(matroska->ctx, AV_LOG_ERROR, "Cannot append palette to packet\n");
+            } else {
+                memcpy(pal, matroska->palette, AVPALETTE_SIZE);
+            }
+            matroska->has_palette = 0;
+        }
         if (matroska->num_packets > 1) {
             void *newpackets;
             memmove(&matroska->packets[0], &matroska->packets[1],
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 06e80c8..b0442c9 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -1754,10 +1754,14 @@ static int mov_codec_id(AVStream *st, uint32_t format)
 static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb,
                                  AVStream *st, MOVStreamContext *sc)
 {
+    uint8_t stsd[70];
     uint8_t codec_name[32];
-    unsigned int color_depth, len, j;
-    int color_greyscale;
-    int color_table_id;
+    int64_t pos;
+    unsigned int len;
+
+    pos = avio_tell(pb);
+    avio_read(pb, stsd, 70);
+    avio_seek(pb, pos, SEEK_SET);
 
     avio_rb16(pb); /* version */
     avio_rb16(pb); /* revision level */
@@ -1795,76 +1799,15 @@ static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb,
         st->codec->codec_id = AV_CODEC_ID_FLV1;
 
     st->codec->bits_per_coded_sample = avio_rb16(pb); /* depth */
-    color_table_id = avio_rb16(pb); /* colortable id */
-    av_log(c->fc, AV_LOG_TRACE, "depth %d, ctab id %d\n",
-            st->codec->bits_per_coded_sample, color_table_id);
-    /* figure out the palette situation */
-    color_depth     = st->codec->bits_per_coded_sample & 0x1F;
-    color_greyscale = st->codec->bits_per_coded_sample & 0x20;
-    /* Do not create a greyscale palette for cinepak */
-    if (color_greyscale && st->codec->codec_id == AV_CODEC_ID_CINEPAK)
-        return;
-
-    /* if the depth is 2, 4, or 8 bpp, file is palettized */
-    if ((color_depth == 2) || (color_depth == 4) || (color_depth == 8)) {
-        /* for palette traversal */
-        unsigned int color_start, color_count, color_end;
-        unsigned int a, r, g, b;
-
-        if (color_greyscale) {
-            int color_index, color_dec;
-            /* compute the greyscale palette */
-            st->codec->bits_per_coded_sample = color_depth;
-            color_count = 1 << color_depth;
-            color_index = 255;
-            color_dec   = 256 / (color_count - 1);
-            for (j = 0; j < color_count; j++) {
-                r = g = b = color_index;
-                sc->palette[j] = (0xFFU << 24) | (r << 16) | (g << 8) | (b);
-                color_index -= color_dec;
-                if (color_index < 0)
-                    color_index = 0;
-            }
-        } else if (color_table_id) {
-            const uint8_t *color_table;
-            /* if flag bit 3 is set, use the default palette */
-            color_count = 1 << color_depth;
-            if (color_depth == 2)
-                color_table = ff_qt_default_palette_4;
-            else if (color_depth == 4)
-                color_table = ff_qt_default_palette_16;
-            else
-                color_table = ff_qt_default_palette_256;
+    avio_rb16(pb); /* colortable id */
 
-            for (j = 0; j < color_count; j++) {
-                r = color_table[j * 3 + 0];
-                g = color_table[j * 3 + 1];
-                b = color_table[j * 3 + 2];
-                sc->palette[j] = (0xFFU << 24) | (r << 16) | (g << 8) | (b);
-            }
-        } else {
-            /* load the palette from the file */
-            color_start = avio_rb32(pb);
-            color_count = avio_rb16(pb);
-            color_end   = avio_rb16(pb);
-            if ((color_start <= 255) && (color_end <= 255)) {
-                for (j = color_start; j <= color_end; j++) {
-                    /* each A, R, G, or B component is 16 bits;
-                        * only use the top 8 bits */
-                    a = avio_r8(pb);
-                    avio_r8(pb);
-                    r = avio_r8(pb);
-                    avio_r8(pb);
-                    g = avio_r8(pb);
-                    avio_r8(pb);
-                    b = avio_r8(pb);
-                    avio_r8(pb);
-                    sc->palette[j] = (a << 24 ) | (r << 16) | (g << 8) | (b);
-                }
-            }
-        }
+    if (ff_get_qtpalette(st->codec->codec_id, stsd, pb, sc->palette)) {
+        st->codec->bits_per_coded_sample &= 0x1F;
         sc->has_palette = 1;
     }
+
+    av_free(stsd);
+    return 0;
 }
 
 static void mov_parse_stsd_audio(MOVContext *c, AVIOContext *pb,
diff --git a/libavformat/qtpalette.c b/libavformat/qtpalette.c
new file mode 100644
index 0000000..e57c6b7
--- /dev/null
+++ b/libavformat/qtpalette.c
@@ -0,0 +1,128 @@
+/*
+ * QuickTime palette handling
+ * Copyright (c) 2001 Fabrice Bellard
+ * Copyright (c) 2009 Baptiste Coudurier <baptiste dot coudurier at gmail dot com>
+ * Copyright (c) 2015 Mats Peterson
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include "avformat.h"
+#include "libavutil/intreadwrite.h"
+#include "qtpalette.h"
+
+/**
+ * Retrieve the palette (or "color table" in QuickTime terms), either
+ * from the video sample description, or from the default Macintosh
+ * palette.
+ */
+int ff_get_qtpalette(int codec_id, const uint8_t *stsd,
+        const AVIOContext *pb, uint32_t *palette)
+{
+    int tmp, bit_depth, greyscale, i;
+
+    /* Get the bit depth and greyscale state */
+    tmp = AV_RB16(stsd + 66);
+    bit_depth = tmp & 0x1F;
+    greyscale = tmp & 0x20;
+
+    /* Do not create a greyscale palette for Cinepak */
+    if (greyscale && codec_id == AV_CODEC_ID_CINEPAK)
+        return 0;
+
+    /* If the depth is 2, 4, or 8 bpp, file is palettized. */
+    if ((bit_depth == 2 || bit_depth == 4 || bit_depth == 8)) {
+        int color_table_id, color_count, color_start, color_end;
+        uint32_t a, r, g, b;
+
+        color_table_id = AV_RB16(stsd + 68);
+
+        if (greyscale) {
+            int color_index, color_dec;
+            /* compute the greyscale palette */
+            color_count = 1 << bit_depth;
+            color_index = 255;
+            color_dec   = 256 / (color_count - 1);
+            for (i = 0; i < color_count; i++) {
+                r = g = b = color_index;
+                palette[i] = (0xFFU << 24) | (r << 16) | (g << 8) | (b);
+                color_index -= color_dec;
+                if (color_index < 0)
+                    color_index = 0;
+            }
+        } else if (color_table_id) {
+            /* The color table ID is non-zero. Interpret this as
+             * being -1, which means use the default Macintosh
+             * color table */
+            const uint8_t *color_table;
+            color_count = 1 << bit_depth;
+            if (bit_depth == 2)
+                color_table = ff_qt_default_palette_4;
+            else if (bit_depth == 4)
+                color_table = ff_qt_default_palette_16;
+            else
+                color_table = ff_qt_default_palette_256;
+            for (i = 0; i < color_count; i++) {
+                r = color_table[i * 3 + 0];
+                g = color_table[i * 3 + 1];
+                b = color_table[i * 3 + 2];
+                palette[i] = (0xFFU << 24) | (r << 16) | (g << 8) | (b);
+            }
+        } else {
+            /* The color table ID is 0; the color table is in the sample
+             * description */
+            if (pb) {
+                color_start = avio_rb32(pb);
+                avio_rb16(pb); /* color table flags */
+                color_end = avio_rb16(pb);
+            } else {
+                color_start = AV_RB16(stsd + 70);
+                color_end = AV_RB16(stsd + 76);
+            }
+            if ((color_start <= 255) && (color_end <= 255)) {
+                uint8_t *p = stsd + 78;
+                for (i = color_start; i <= color_end; i++) {
+                    /* each A, R, G, or B component is 16 bits;
+                     * only use the top 8 bits */
+                    if (pb) {
+                        a = avio_r8(pb);
+                        avio_r8(pb);
+                        r = avio_r8(pb);
+                        avio_r8(pb);
+                        g = avio_r8(pb);
+                        avio_r8(pb);
+                        b = avio_r8(pb);
+                        avio_r8(pb);
+                    } else {
+                        a = *p++; p++;
+                        r = *p++; p++;
+                        g = *p++; p++;
+                        b = *p++; p++;
+                    }
+                    palette[i] = (a << 24 ) | (r << 16) | (g << 8) | (b);
+                }
+            }
+        }
+
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/libavformat/qtpalette.h b/libavformat/qtpalette.h
index 7d6802f..db214c5 100644
--- a/libavformat/qtpalette.h
+++ b/libavformat/qtpalette.h
@@ -23,7 +23,8 @@
 #ifndef AVFORMAT_QTPALETTE_H
 #define AVFORMAT_QTPALETTE_H
 
-#include <inttypes.h>
+#include <stdint.h>
+#include "avformat.h"
 
 static const uint8_t ff_qt_default_palette_4[4 * 3] = {
   0x93, 0x65, 0x5E,
@@ -310,4 +311,7 @@ static const uint8_t ff_qt_default_palette_256[256 * 3] = {
   /* 255, 0xFF */  0x00, 0x00, 0x00
 };
 
+int ff_get_qtpalette(int codec_id, const uint8_t *stsd,
+        const AVIOContext *pb, uint32_t *palette);
+
 #endif /* AVFORMAT_QTPALETTE_H */
-- 
1.7.10.4

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel

Reply via email to