[vlc-commits] mediacodec/videotoolbox: add hxxx_helper

Thomas Guillem git at videolan.org
Wed Mar 29 15:49:45 CEST 2017


vlc | branch: master | Thomas Guillem <thomas at gllm.fr> | Tue Mar 21 10:58:51 2017 +0100| [b6d73612d383b25ab12e2f8aad289d045200281a] | committer: Thomas Guillem

mediacodec/videotoolbox: add hxxx_helper

hxxx_helper is a H264/HEVC helper used by mediacodec and videtoolbox. For
mediacodec, it's used to convert (and validate) xvcC to AnnexB or used to
detect SPS/PPS change when decoding AnnexB. For videotoolbox, it's used to
validate avcC or to convert AnnexB to avcC (and detect SPS/PPS change) when
decoding AnnexB.

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

 modules/codec/Makefile.am        |  12 +-
 modules/codec/hxxx_helper.c      | 548 +++++++++++++++++++++++++++++++++++++++
 modules/codec/hxxx_helper.h      |  84 ++++++
 modules/codec/omxil/mediacodec.c | 342 ++++++++----------------
 modules/codec/videotoolbox.m     | 138 +++++-----
 5 files changed, 806 insertions(+), 318 deletions(-)

diff --git a/modules/codec/Makefile.am b/modules/codec/Makefile.am
index cc5c27d..f749853 100644
--- a/modules/codec/Makefile.am
+++ b/modules/codec/Makefile.am
@@ -315,9 +315,11 @@ liboggspots_plugin_la_LIBADD = $(OGGSPOTS_LIBS)
 EXTRA_LTLIBRARIES += liboggspots_plugin.la
 codec_LTLIBRARIES += $(LTLIBoggspots)
 
-libvideotoolbox_plugin_la_SOURCES = video_chroma/copy.c video_chroma/copy.h codec/videotoolbox.m \
-                                    packetizer/h264_nal.c packetizer/h264_nal.h \
-                                    packetizer/hxxx_nal.c packetizer/hxxx_nal.h
+libvideotoolbox_plugin_la_SOURCES = video_chroma/copy.c video_chroma/copy.h \
+	codec/videotoolbox.m codec/hxxx_helper.c codec/hxxx_helper.h \
+	packetizer/hxxx_nal.h packetizer/hxxx_nal.c \
+	packetizer/h264_nal.c packetizer/h264_nal.h \
+	packetizer/hevc_nal.c packetizer/hevc_nal.h
 if HAVE_OSX
 libvideotoolbox_plugin_la_OBJCFLAGS = $(AM_CFLAGS) -mmacosx-version-min=10.8
 endif
@@ -457,7 +459,9 @@ libmediacodec_plugin_la_SOURCES = codec/omxil/mediacodec.c codec/omxil/mediacode
 	video_chroma/copy.c \
 	video_output/android/utils.c video_output/android/utils.h \
 	video_output/android/display.h \
-	packetizer/hxxx_nal.h packetizer/h264_nal.c packetizer/h264_nal.h \
+	codec/hxxx_helper.c codec/hxxx_helper.h \
+	packetizer/hxxx_nal.h packetizer/hxxx_nal.c \
+	packetizer/h264_nal.c packetizer/h264_nal.h \
 	packetizer/hevc_nal.c packetizer/hevc_nal.h
 
 codec_LTLIBRARIES += $(LTLIBomxil) $(LTLIBomxil_vout)
diff --git a/modules/codec/hxxx_helper.c b/modules/codec/hxxx_helper.c
new file mode 100644
index 0000000..d36b1d0
--- /dev/null
+++ b/modules/codec/hxxx_helper.c
@@ -0,0 +1,548 @@
+/*****************************************************************************
+ * hxxx_helper.c: AnnexB / avcC helper for dumb decoders
+ *****************************************************************************
+ * Copyright (C) 2017 VLC authors and VideoLAN
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdint.h>
+#include <assert.h>
+
+#include <vlc_common.h>
+#include <vlc_bits.h>
+
+#include "hxxx_helper.h"
+#include "../packetizer/hxxx_nal.h"
+#include "../packetizer/h264_slice.h"
+
+void
+hxxx_helper_init(struct hxxx_helper *hh, vlc_object_t *p_obj,
+                 vlc_fourcc_t i_codec, bool b_need_xvcC)
+{
+    assert(i_codec == VLC_CODEC_H264 || i_codec == VLC_CODEC_HEVC);
+
+    memset(hh, 0, sizeof(struct hxxx_helper));
+    hh->p_obj = p_obj;
+    hh->i_codec = i_codec;
+    switch (i_codec)
+    {
+        case VLC_CODEC_H264:
+            break;
+    }
+    hh->b_need_xvcC = b_need_xvcC;
+}
+
+void
+hxxx_helper_clean(struct hxxx_helper *hh)
+{
+    switch (hh->i_codec)
+    {
+        case VLC_CODEC_H264:
+            for (size_t i = 0; i <= H264_SPS_ID_MAX; ++i)
+            {
+                struct hxxx_helper_nal *hnal = &hh->h264.sps_list[i];
+                if (hnal->b)
+                {
+                    block_Release(hnal->b);
+                    h264_release_sps(hnal->h264_sps);
+                }
+            }
+            for (size_t i = 0; i <= H264_PPS_ID_MAX; ++i)
+            {
+                struct hxxx_helper_nal *hnal = &hh->h264.pps_list[i];
+                if (hnal->b)
+                {
+                    block_Release(hnal->b);
+                    h264_release_pps(hnal->h264_pps);
+                }
+            }
+            break;
+        case VLC_CODEC_HEVC:
+            if (hh->hevc.p_annexb_config_nal)
+                block_Release(hh->hevc.p_annexb_config_nal);
+            break;
+        default:
+            vlc_assert_unreachable();
+    }
+}
+
+#define HELPER_FOREACH_NAL(it, p_nal_list, i_nal_count, i_nal_max) \
+    for (size_t ii = 0, i_nal_nb = 0; i < i_nal_max && i_nal_count > i_nal_nb; ++ii) \
+        if (p_nal_list[ii].b != NULL && (it = &p_nal_list[ii]) && ++i_nal_nb)
+
+static int
+helper_dup_buf(struct hxxx_helper_nal *p_nal,
+               const uint8_t *p_nal_buf, size_t i_nal_buf)
+{
+    if (!p_nal->b)
+    {
+        p_nal->b = block_Alloc(i_nal_buf);
+        if (!p_nal->b)
+            return VLC_ENOMEM;
+    }
+    else if (p_nal->b != NULL && i_nal_buf > p_nal->b->i_buffer)
+    {
+        block_t *b = block_TryRealloc(p_nal->b, 0, i_nal_buf);
+        if (b == NULL)
+            return VLC_ENOMEM;
+        p_nal->b = b;
+    }
+    memcpy(p_nal->b->p_buffer, p_nal_buf, i_nal_buf);
+    p_nal->b->i_buffer = i_nal_buf;
+    return VLC_SUCCESS;
+}
+
+static inline const struct hxxx_helper_nal *
+helper_search_nal(const struct hxxx_helper_nal *p_nal_list, size_t i_nal_count,
+                  size_t i_nal_max, const void *p_nal_buf, size_t i_nal_buf)
+{
+    size_t i_nal_nb = 0;
+    for (size_t i = 0; i < i_nal_max && i_nal_count > i_nal_nb; ++i)
+    {
+        const struct hxxx_helper_nal *p_nal = &p_nal_list[i];
+        if (p_nal->b == NULL)
+            continue;
+        i_nal_nb++;
+        const int i_diff = i_nal_buf - p_nal->b->i_buffer;
+        if (i_diff == 0 && memcmp(p_nal_buf, p_nal->b->p_buffer, i_nal_buf) == 0)
+            return p_nal;
+    }
+    return NULL;
+}
+#define helper_search_sps(hh, p_nal, i_nal) \
+    helper_search_nal(hh->h264.sps_list, hh->h264.i_sps_count, \
+                      H264_SPS_ID_MAX+1, p_nal, i_nal)
+#define helper_search_pps(hh, p_nal, i_nal) \
+    helper_search_nal(hh->h264.pps_list, hh->h264.i_pps_count, \
+                      H264_PPS_ID_MAX+1, p_nal, i_nal)
+
+static inline bool
+helper_nal_length_valid(struct hxxx_helper *hh)
+{
+    return hh->i_nal_length_size == 1 || hh->i_nal_length_size == 2
+        || hh->i_nal_length_size == 4;
+}
+
+static int
+h264_helper_parse_nal(struct hxxx_helper *hh, const uint8_t *p_buf, size_t i_buf,
+                      bool b_is_xvcC, bool *p_config_changed)
+{
+    const uint8_t *p_nal;
+    size_t i_nal;
+    hxxx_iterator_ctx_t it;
+    hxxx_iterator_init(&it, p_buf, i_buf, hh->i_nal_length_size);
+    bool (*pf_iterator)(hxxx_iterator_ctx_t *, const uint8_t **, size_t *) =
+        b_is_xvcC ? hxxx_iterate_next : hxxx_annexb_iterate_next;
+    *p_config_changed = false;
+
+    while (pf_iterator(&it, &p_nal, &i_nal))
+    {
+        if (i_nal < 2)
+            continue;
+
+        const enum h264_nal_unit_type_e i_nal_type = p_nal[0] & 0x1F;
+
+        if (i_nal_type == H264_NAL_SPS)
+        {
+            if (helper_search_sps(hh, p_nal, i_nal) != NULL)
+                continue;
+            h264_sequence_parameter_set_t *p_sps =
+                h264_decode_sps(p_nal, i_nal, true);
+            if (!p_sps)
+                return VLC_EGENERIC;
+
+            struct hxxx_helper_nal *hnal = &hh->h264.sps_list[p_sps->i_id];
+            if (helper_dup_buf(hnal, p_nal, i_nal))
+            {
+                h264_release_sps(p_sps);
+                return VLC_EGENERIC;
+            }
+            if (hnal->h264_sps)
+                h264_release_sps(hnal->h264_sps);
+            else
+                hh->h264.i_sps_count++;
+
+            hnal->h264_sps = p_sps;
+            *p_config_changed = true;
+            hh->h264.i_current_sps = p_sps->i_id;
+fprintf(stderr, "new SPS parsed: %u\n", p_sps->i_id);
+        }
+        else if (i_nal_type == H264_NAL_PPS)
+        {
+            if (helper_search_pps(hh, p_nal, i_nal) != NULL)
+                continue;
+            h264_picture_parameter_set_t *p_pps =
+                h264_decode_pps(p_nal, i_nal, true);
+            if (!p_pps)
+                return VLC_EGENERIC;
+
+            struct hxxx_helper_nal *hnal = &hh->h264.pps_list[p_pps->i_id];
+
+            if (helper_dup_buf(hnal, p_nal, i_nal))
+            {
+                h264_release_pps(p_pps);
+                return VLC_EGENERIC;
+            }
+            if (hnal->h264_pps)
+                h264_release_pps(hnal->h264_pps);
+            else
+                hh->h264.i_pps_count++;
+
+            hnal->h264_pps = p_pps;
+            *p_config_changed = true;
+fprintf(stderr, "new PPS parsed: %u\n", p_pps->i_id);
+        }
+        else if (i_nal_type <= H264_NAL_SLICE_IDR
+              && i_nal_type != H264_NAL_UNKNOWN)
+        {
+            if (hh->h264.i_sps_count > 1)
+            {
+                /* There is more than one SPS. Get the PPS id of the current
+                 * SLICE in order to get the current SPS id */
+
+                /* Get the PPS id from the slice: inspirated from
+                 * h264_decode_slice() */
+                bs_t s;
+                bs_init(&s, p_nal, i_nal);
+                bs_skip(&s, 8);
+                bs_read_ue(&s);
+                bs_read_ue(&s);
+                unsigned i_pps_id = bs_read_ue(&s);
+                if (i_pps_id > H264_PPS_ID_MAX)
+                    return VLC_EGENERIC;
+
+                struct hxxx_helper_nal *hpps = &hh->h264.pps_list[i_pps_id];
+                if (hpps->b == NULL)
+                    return VLC_EGENERIC;
+
+                struct hxxx_helper_nal *hsps =
+                    &hh->h264.sps_list[hpps->h264_pps->i_sps_id];
+                if (hsps->b == NULL)
+                    return VLC_EGENERIC;
+
+                assert(hpps->h264_pps->i_sps_id == hsps->h264_sps->i_id);
+                if (hsps->h264_sps->i_id != hh->h264.i_current_sps)
+                {
+                    hh->h264.i_current_sps = hsps->h264_sps->i_id;
+                    *p_config_changed = true;
+                }
+            }
+            break; /* No need to parse further NAL */
+        }
+    }
+    return VLC_SUCCESS;
+}
+
+static int
+h264_helper_set_extra(struct hxxx_helper *hh, const void *p_extra,
+                      size_t i_extra)
+{
+    if (i_extra == 0)
+    {
+        /* AnnexB case */
+        hh->i_nal_length_size = 4;
+        return VLC_SUCCESS;
+    }
+    else if (h264_isavcC(p_extra, i_extra))
+    {
+        bool b_unused;
+
+        hh->i_nal_length_size = (((uint8_t*)p_extra)[4] & 0x03) + 1;
+        if (!helper_nal_length_valid(hh))
+            return VLC_EGENERIC;
+        hh->b_is_xvcC = true;
+
+        if (hh->b_need_xvcC)
+            return h264_helper_parse_nal(hh, p_extra, i_extra, true, &b_unused);
+
+        size_t i_buf;
+        uint8_t *p_buf = h264_avcC_to_AnnexB_NAL(p_extra, i_extra, &i_buf,
+                                                 NULL);
+        if (!p_buf)
+        {
+            msg_Dbg(hh->p_obj, "h264_avcC_to_AnnexB_NAL failed");
+            return VLC_EGENERIC;
+        }
+        fprintf(stderr, "got annex b extra: %d (nal: %d)\n", i_buf, hh->i_nal_length_size);
+
+        /* XXX h264_AVC_to_AnnexB() works only with a i_nal_length_size of 4.
+         * If nal_length_size is smaller than 4, fallback to SW decoding. I
+         * don't know if it's worth the effort to fix h264_AVC_to_AnnexB() for
+         * a smaller nal_length_size. Indeed, this case will happen only with
+         * very small resolutions, where hardware decoders are not that useful.
+         * -Thomas */
+        if (hh->i_nal_length_size != 4)
+        {
+            msg_Dbg(hh->p_obj, "nal_length_size is too small");
+            free(p_buf);
+            return VLC_EGENERIC;
+        }
+
+        int i_ret = h264_helper_parse_nal(hh, p_buf, i_buf, false, &b_unused);
+        free(p_buf);
+        return i_ret;
+    }
+    else /* Can't handle extra that is not avcC */
+        return VLC_EGENERIC;
+}
+
+static int
+hevc_helper_set_extra(struct hxxx_helper *hh, const void *p_extra,
+                      size_t i_extra)
+{
+    if (i_extra == 0)
+    {
+        /* AnnexB case */
+        hh->i_nal_length_size = 4;
+        return VLC_SUCCESS;
+    }
+    else if (hevc_ishvcC(p_extra, i_extra))
+    {
+        hh->i_nal_length_size = hevc_getNALLengthSize(p_extra);
+        if (!helper_nal_length_valid(hh))
+            return VLC_EGENERIC;
+        hh->b_is_xvcC = true;
+
+        if (hh->b_need_xvcC)
+            return VLC_SUCCESS;
+
+        size_t i_buf;
+        uint8_t *p_buf = hevc_hvcC_to_AnnexB_NAL(p_extra, i_extra, &i_buf,
+                                                 NULL);
+        if (!p_buf)
+        {
+            msg_Dbg(hh->p_obj, "hevc_hvcC_to_AnnexB_NAL failed");
+            return VLC_EGENERIC;
+        }
+
+        hh->hevc.p_annexb_config_nal = p_buf;
+        hh->hevc.i_annexb_config_nal = i_buf;
+        return VLC_SUCCESS;
+    }
+    else /* Can't handle extra that is not avcC */
+        return VLC_EGENERIC;
+}
+
+static block_t *
+helper_process_block_xvcc2annexb(struct hxxx_helper *hh, block_t *p_block,
+                                 bool *p_config_changed)
+{
+    assert(helper_nal_length_valid(hh));
+    *p_config_changed = false;
+    h264_AVC_to_AnnexB(p_block->p_buffer, p_block->i_buffer,
+                       hh->i_nal_length_size);
+    return p_block;
+}
+
+static block_t *
+helper_process_block_h264_annexb(struct hxxx_helper *hh, block_t *p_block,
+                                 bool *p_config_changed)
+{
+    int i_ret = h264_helper_parse_nal(hh, p_block->p_buffer, p_block->i_buffer,
+                                      false, p_config_changed);
+    if (i_ret != VLC_SUCCESS)
+    {
+        block_Release(p_block);
+        return NULL;
+    }
+    return p_block;
+}
+
+static block_t *
+helper_process_block_h264_annexb2avcc(struct hxxx_helper *hh, block_t *p_block,
+                                      bool *p_config_changed)
+{
+    p_block = helper_process_block_h264_annexb(hh, p_block, p_config_changed);
+    return p_block ? hxxx_AnnexB_to_xVC(p_block, hh->i_nal_length_size) : NULL;
+}
+
+int
+hxxx_helper_set_extra(struct hxxx_helper *hh, const void *p_extra,
+                      size_t i_extra)
+{
+    int i_ret;
+    switch (hh->i_codec)
+    {
+        case VLC_CODEC_H264:
+            i_ret = h264_helper_set_extra(hh, p_extra, i_extra);
+            break;
+        case VLC_CODEC_HEVC:
+            i_ret = hevc_helper_set_extra(hh, p_extra, i_extra);
+            break;
+        default:
+            vlc_assert_unreachable();
+    }
+    if (i_ret != VLC_SUCCESS)
+        return i_ret;
+
+    if (hh->b_is_xvcC)
+    {
+        if (hh->b_need_xvcC)
+            hh->pf_process_block = NULL;
+        else
+            hh->pf_process_block = helper_process_block_xvcc2annexb;
+    }
+    else
+    {
+        switch (hh->i_codec)
+        {
+            case VLC_CODEC_H264:
+                if (hh->b_need_xvcC)
+                    hh->pf_process_block = helper_process_block_h264_annexb2avcc;
+                else
+                    hh->pf_process_block = helper_process_block_h264_annexb;
+                break;
+            case VLC_CODEC_HEVC:
+                if (hh->b_need_xvcC)
+                    return VLC_EGENERIC; /* TODO */
+                else
+                    hh->pf_process_block = NULL;
+            default:
+                vlc_assert_unreachable();
+        }
+    }
+    return VLC_SUCCESS;;
+}
+
+
+block_t *
+h264_helper_get_annexb_config(struct hxxx_helper *hh)
+{
+    static const uint8_t annexb_startcode[] = { 0x00, 0x00, 0x00, 0x01 };
+
+    if (hh->h264.i_sps_count == 0 || hh->h264.i_pps_count == 0)
+        return NULL;
+
+    const struct hxxx_helper_nal *pp_nal_lists[] = {
+        hh->h264.sps_list, hh->h264.pps_list };
+    const size_t p_nal_counts[] = { hh->h264.i_sps_count, hh->h264.i_pps_count };
+    const size_t p_nal_maxs[] = { H264_SPS_ID_MAX+1, H264_PPS_ID_MAX+1 };
+
+    block_t *p_block_list = NULL;
+    for (size_t i = 0; i < 2; ++i)
+    {
+        size_t i_nals_size = 0;
+        const struct hxxx_helper_nal *p_nal;
+        HELPER_FOREACH_NAL(p_nal, pp_nal_lists[i], p_nal_counts[i], p_nal_maxs[i])
+        {
+            i_nals_size += p_nal->b->i_buffer + sizeof annexb_startcode;
+        }
+
+        block_t *p_block = block_Alloc(i_nals_size);
+        if (p_block == NULL)
+        {
+            if (p_block_list != NULL)
+                block_Release(p_block_list);
+            return NULL;
+        }
+
+        p_block->i_buffer = 0;
+        HELPER_FOREACH_NAL(p_nal, pp_nal_lists[i], p_nal_counts[i], p_nal_maxs[i])
+        {
+            memcpy(&p_block->p_buffer[p_block->i_buffer], annexb_startcode,
+                   sizeof annexb_startcode);
+            p_block->i_buffer += sizeof annexb_startcode;
+            memcpy(&p_block->p_buffer[p_block->i_buffer], p_nal->b->p_buffer,
+                   p_nal->b->i_buffer);
+            p_block->i_buffer += p_nal->b->i_buffer;
+        }
+        if (p_block_list == NULL)
+            p_block_list = p_block;
+        else
+            p_block_list->p_next = p_block;
+    }
+
+    return p_block_list;
+}
+
+block_t *
+h264_helper_get_avcc_config(struct hxxx_helper *hh)
+{
+    const struct hxxx_helper_nal *p_nal;
+    size_t i = 0;
+    const uint8_t *pp_sps_bufs[hh->h264.i_sps_count];
+    size_t p_sps_sizes[hh->h264.i_sps_count];
+    HELPER_FOREACH_NAL(p_nal, hh->h264.sps_list, hh->h264.i_sps_count,
+                       H264_SPS_ID_MAX+1)
+    {
+        pp_sps_bufs[i] = p_nal->b->p_buffer;
+        p_sps_sizes[i] = p_nal->b->i_buffer;
+        ++i;
+    }
+
+    i = 0;
+    const uint8_t *pp_pps_bufs[hh->h264.i_pps_count];
+    size_t p_pps_sizes[hh->h264.i_pps_count];
+    HELPER_FOREACH_NAL(p_nal, hh->h264.pps_list, hh->h264.i_pps_count,
+                       H264_PPS_ID_MAX+1)
+    {
+        pp_pps_bufs[i] = p_nal->b->p_buffer;
+        p_pps_sizes[i] = p_nal->b->i_buffer;
+        ++i;
+    }
+    return h264_NAL_to_avcC(4, pp_sps_bufs, p_sps_sizes, hh->h264.i_sps_count,
+                            pp_pps_bufs, p_pps_sizes, hh->h264.i_pps_count);
+}
+
+static const struct hxxx_helper_nal *
+h264_helper_get_current_sps(struct hxxx_helper *hh)
+{
+    if (hh->h264.i_sps_count == 0)
+        return NULL;
+
+    const struct hxxx_helper_nal *hsps =
+        &hh->h264.sps_list[hh->h264.i_current_sps];
+    assert(hsps->b != NULL);
+    return hsps;
+}
+
+int
+h264_helper_get_current_picture_size(struct hxxx_helper *hh,
+                                     unsigned *p_w, unsigned *p_h,
+                                     unsigned *p_vw, unsigned *p_vh)
+{
+    const struct hxxx_helper_nal *hsps = h264_helper_get_current_sps(hh);
+    if (hsps == NULL)
+        return VLC_EGENERIC;
+    return h264_get_picture_size(hsps->h264_sps, p_w, p_h, p_vw, p_vh) ?
+           VLC_SUCCESS : VLC_EGENERIC;
+}
+
+int
+h264_helper_get_current_sar(struct hxxx_helper *hh, int *p_num, int *p_den)
+{
+    const struct hxxx_helper_nal *hsps = h264_helper_get_current_sps(hh);
+    if (hsps == NULL)
+        return VLC_EGENERIC;
+    *p_num = hsps->h264_sps->vui.i_sar_num;
+    *p_den = hsps->h264_sps->vui.i_sar_den;
+    return VLC_SUCCESS;
+}
+
+int
+h264_helper_get_current_dpb_values(struct hxxx_helper *hh,
+                                       uint8_t *p_depth, unsigned *p_delay)
+{
+    const struct hxxx_helper_nal *hsps = h264_helper_get_current_sps(hh);
+    if (hsps == NULL)
+        return VLC_EGENERIC;
+    return h264_get_dpb_values(hsps->h264_sps, p_depth, p_delay) ?
+           VLC_SUCCESS : VLC_EGENERIC;
+}
diff --git a/modules/codec/hxxx_helper.h b/modules/codec/hxxx_helper.h
new file mode 100644
index 0000000..a83f455
--- /dev/null
+++ b/modules/codec/hxxx_helper.h
@@ -0,0 +1,84 @@
+/*****************************************************************************
+ * hxxx_helper.h: AnnexB / avcC helper for dumb decoders
+ *****************************************************************************
+ * Copyright (C) 2017 VLC authors and VideoLAN
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_block.h>
+#include <vlc_fourcc.h>
+
+#include "../packetizer/h264_nal.h"
+#include "../packetizer/hevc_nal.h"
+
+struct hxxx_helper_nal
+{
+    block_t *b;
+    union {
+        h264_sequence_parameter_set_t   *h264_sps;
+        h264_picture_parameter_set_t    *h264_pps;
+    };
+};
+
+struct hxxx_helper
+{
+    vlc_object_t *p_obj; /* for logs */
+    vlc_fourcc_t i_codec;
+    bool b_need_xvcC; /* Need avcC or hvcC */
+
+    bool b_is_xvcC;
+    uint8_t i_nal_length_size;
+    union {
+        struct {
+            struct hxxx_helper_nal sps_list[H264_SPS_ID_MAX + 1];
+            struct hxxx_helper_nal pps_list[H264_PPS_ID_MAX + 1];
+            uint8_t i_current_sps;
+            uint8_t i_sps_count;
+            uint8_t i_pps_count;
+        } h264;
+        struct {
+            /* TODO: handle VPS/SPS/PPS */
+            void *p_annexb_config_nal;
+            size_t i_annexb_config_nal;
+        } hevc;
+    };
+    block_t * (*pf_process_block)(struct hxxx_helper *hh, block_t *p_block,
+                                  bool *p_config_changed);
+};
+
+void hxxx_helper_init(struct hxxx_helper *hh, vlc_object_t *p_obj,
+                      vlc_fourcc_t i_codec, bool b_need_xvcC);
+void hxxx_helper_clean(struct hxxx_helper *hh);
+
+int hxxx_helper_set_extra(struct hxxx_helper *hh, const void *p_extra,
+                          size_t i_extra);
+
+block_t *h264_helper_get_annexb_config(struct hxxx_helper *hh);
+
+block_t *h264_helper_get_avcc_config(struct hxxx_helper *hh);
+
+int h264_helper_get_current_picture_size(struct hxxx_helper *hh,
+                                         unsigned *p_w, unsigned *p_h,
+                                         unsigned *p_vw, unsigned *p_vh);
+
+int h264_helper_get_current_sar(struct hxxx_helper *hh, int *p_num, int *p_den);
+
+int h264_helper_get_current_dpb_values(struct hxxx_helper *hh,
+                                       uint8_t *p_depth, unsigned *pi_delay);
diff --git a/modules/codec/omxil/mediacodec.c b/modules/codec/omxil/mediacodec.c
index 3254b62..2ee1293 100644
--- a/modules/codec/omxil/mediacodec.c
+++ b/modules/codec/omxil/mediacodec.c
@@ -41,9 +41,7 @@
 #include <vlc_bits.h>
 
 #include "mediacodec.h"
-#include "../../packetizer/h264_nal.h"
-#include "../../packetizer/hevc_nal.h"
-#include "../../packetizer/hxxx_nal.h"
+#include "../codec/hxxx_helper.h"
 #include <OMX_Core.h>
 #include <OMX_Component.h>
 #include "omxil_utils.h"
@@ -51,13 +49,6 @@
 
 #define BLOCK_FLAG_CSD (0x01 << BLOCK_FLAG_PRIVATE_SHIFT)
 
-/* Codec Specific Data */
-struct csd
-{
-    const uint8_t *p_buf;
-    size_t i_size;
-};
-
 #define DECODE_FLAG_RESTART (0x01)
 #define DECODE_FLASH_FLUSH (0x02)
 /**
@@ -84,7 +75,8 @@ struct decoder_sys_t
 
     /* Codec Specific Data buffer: sent in DecodeBlock after a start or a flush
      * with the BUFFER_FLAG_CODEC_CONFIG flag.*/
-    block_t **pp_csd;
+    #define MAX_CSD_COUNT 3
+    block_t *pp_csd[MAX_CSD_COUNT];
     size_t i_csd_count;
     size_t i_csd_send;
 
@@ -121,8 +113,7 @@ struct decoder_sys_t
             unsigned i_angle;
             unsigned int i_stride, i_slice_height;
             int i_pixel_format;
-            uint8_t i_nal_length_size;
-            size_t i_h264_profile;
+            struct hxxx_helper hh;
             /* stores the inflight picture for each output buffer or NULL */
             picture_sys_t** pp_inflight_pictures;
             unsigned int i_inflight_pictures;
@@ -148,8 +139,7 @@ static void CleanDecoder(decoder_t *);
 static void CloseDecoder(vlc_object_t *);
 
 static int Video_OnNewBlock(decoder_t *, block_t **);
-static int VideoH264_OnNewBlock(decoder_t *, block_t **);
-static int VideoHEVC_OnNewBlock(decoder_t *, block_t **);
+static int VideoHXXX_OnNewBlock(decoder_t *, block_t **);
 static int VideoVC1_OnNewBlock(decoder_t *, block_t **);
 static void Video_OnFlush(decoder_t *);
 static int Video_ProcessOutput(decoder_t *, mc_api_out *, picture_t **,
@@ -208,204 +198,117 @@ static void CSDFree(decoder_t *p_dec)
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
 
-    if (p_sys->pp_csd)
-    {
-        for (unsigned int i = 0; i < p_sys->i_csd_count; ++i)
-            block_Release(p_sys->pp_csd[i]);
-        free(p_sys->pp_csd);
-        p_sys->pp_csd = NULL;
-    }
+    for (unsigned int i = 0; i < p_sys->i_csd_count; ++i)
+        block_Release(p_sys->pp_csd[i]);
     p_sys->i_csd_count = 0;
 }
 
-/* Create the p_sys->p_csd that will be sent from DecodeBlock */
-static int CSDDup(decoder_t *p_dec, const struct csd *p_csd, size_t i_count)
+/* Init the p_sys->p_csd that will be sent from DecodeBlock */
+static void CSDInit(decoder_t *p_dec, block_t *p_blocks, size_t i_count)
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
+    assert(i_count >= 0 && i_count <= 3);
 
     CSDFree(p_dec);
 
-    p_sys->pp_csd = malloc(i_count * sizeof(block_t *));
-    if (!p_sys->pp_csd)
-        return VLC_ENOMEM;
-
     for (size_t i = 0; i < i_count; ++i)
     {
-        p_sys->pp_csd[i] = block_Alloc(p_csd[i].i_size);
-        if (!p_sys->pp_csd[i])
-        {
-            CSDFree(p_dec);
-            return VLC_ENOMEM;
-        }
+        assert(p_blocks != NULL);
+        p_sys->pp_csd[i] = p_blocks;
         p_sys->pp_csd[i]->i_flags = BLOCK_FLAG_CSD;
-        memcpy(p_sys->pp_csd[i]->p_buffer, p_csd[i].p_buf, p_csd[i].i_size);
-        p_sys->i_csd_count++;
+        p_blocks = p_blocks->p_next;
+        p_sys->pp_csd[i]->p_next = NULL;
     }
 
+    p_sys->i_csd_count = i_count;
     p_sys->i_csd_send = 0;
-    return VLC_SUCCESS;
 }
 
-static bool CSDCmp(decoder_t *p_dec, struct csd *p_csd, size_t i_csd_count)
+static int CSDDup(decoder_t *p_dec, const void *p_buf, size_t i_buf)
 {
-    decoder_sys_t *p_sys = p_dec->p_sys;
-
-    if (p_sys->i_csd_count != i_csd_count)
-        return false;
-    for (size_t i = 0; i < i_csd_count; ++i)
-    {
-        if (p_sys->pp_csd[i]->i_buffer != p_csd[i].i_size
-         || memcmp(p_sys->pp_csd[i]->p_buffer, p_csd[i].p_buf,
-                   p_csd[i].i_size) != 0)
-            return false;
-    }
-    return true;
-}
+    block_t *p_block = block_Alloc(i_buf);
+    if (!p_block)
+        return VLC_ENOMEM;
+    memcpy(p_block->p_buffer, p_buf, i_buf);
 
-static inline uint8_t RestoreSyncCode(const uint8_t *p_bufhead,
-                                      const uint8_t **pp_buf, size_t *pi_size)
-{
-    *pp_buf -= 3;
-    *pi_size += 3;
-    if (*pp_buf > p_bufhead && (*pp_buf)[-1] == 0)
-    {
-        *pp_buf -= 1;
-        *pi_size += 1;
-        return 4;
-    }
-    return 3;
+    CSDInit(p_dec, p_block, 1);
+    return VLC_SUCCESS;
 }
 
 /* Fill the p_sys->p_csd struct with H264 Parameter Sets */
-static int H264SetCSD(decoder_t *p_dec, void *p_buf, size_t i_size,
-                      bool *p_size_changed)
+static int H264SetCSD(decoder_t *p_dec, bool *p_size_changed)
 {
-    const uint8_t *p_sps_buf = NULL, *p_pps_buf = NULL;
-    size_t i_sps_size = 0, i_pps_size = 0;
-
-    /* Check if p_buf contains a valid SPS PPS */
-    if (h264_AnnexB_get_spspps(p_buf, i_size,
-                        &p_sps_buf, &i_sps_size,
-                        &p_pps_buf, &i_pps_size,
-                        NULL, NULL) && i_sps_size > 0)
-    {
-        struct csd csd[2];
-        int i_csd_count = 0;
-
-        h264_sequence_parameter_set_t *p_sps
-            = h264_decode_sps(p_sps_buf, i_sps_size, true);
-        if (!p_sps)
-            return VLC_EGENERIC;
-
-        unsigned vsize[4];
-        (void) h264_get_picture_size(p_sps, &vsize[0], &vsize[1], &vsize[2],
-                                     &vsize[3]);
-
-        if (i_sps_size && RestoreSyncCode(p_buf, &p_sps_buf, &i_sps_size) == 4)
-        {
-            csd[i_csd_count].p_buf = p_sps_buf;
-            csd[i_csd_count].i_size = i_sps_size;
-            i_csd_count++;
-        }
-        if (i_pps_size && RestoreSyncCode(p_buf, &p_pps_buf, &i_pps_size) == 4)
-        {
-            csd[i_csd_count].p_buf = p_pps_buf;
-            csd[i_csd_count].i_size = i_pps_size;
-            i_csd_count++;
-        }
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    struct hxxx_helper *hh = &p_sys->video.hh;
+    assert(hh->h264.i_sps_count > 0 || hh->h264.i_pps_count > 0);
 
-        /* Compare the SPS PPS with the old one */
-        if (!CSDCmp(p_dec, csd, i_csd_count))
-        {
-            msg_Warn(p_dec, "New SPS/PPS found, id: %" PRIu8 " size: %ux%u "
-                     "sps: %d pps: %d", p_sps->i_id, vsize[0], vsize[1],
-                     i_sps_size, i_pps_size);
+    block_t *p_spspps_blocks = h264_helper_get_annexb_config(hh);
 
-            /* In most use cases, p_sys->p_csd[0] contains a SPS, and
-             * p_sys->p_csd[1] contains a PPS */
-            if (CSDDup(p_dec, csd, i_csd_count))
-            {
-                h264_release_sps(p_sps);
-                return VLC_ENOMEM;
-            }
+    if (p_spspps_blocks != NULL)
+        CSDInit(p_dec, p_spspps_blocks, 2);
 
-            if (p_size_changed)
-                *p_size_changed = (vsize[0] != p_dec->fmt_out.video.i_width
-                                || vsize[1] != p_dec->fmt_out.video.i_height);
+    unsigned i_w, i_h, i_vw, i_vh;
+    h264_helper_get_current_picture_size(hh, &i_w, &i_h, &i_vw, &i_vh);
 
-            p_dec->fmt_out.video.i_visible_width =
-            p_dec->fmt_out.video.i_width = vsize[0];
-            p_dec->fmt_out.video.i_visible_height =
-            p_dec->fmt_out.video.i_height = vsize[1];
+    if (p_size_changed)
+        *p_size_changed = (i_w != p_dec->fmt_out.video.i_width
+                        || i_h != p_dec->fmt_out.video.i_height);
 
-            h264_release_sps(p_sps);
+    p_dec->fmt_out.video.i_visible_width =
+    p_dec->fmt_out.video.i_width = i_w;
+    p_dec->fmt_out.video.i_visible_height =
+    p_dec->fmt_out.video.i_height = i_h;
+    return VLC_SUCCESS;
+}
 
-            return VLC_SUCCESS;
-        }
+/* Fill the p_sys->p_csd struct with HEVC Parameter Sets */
+static int HEVCSetCSD(decoder_t *p_dec, bool *p_size_changed)
+{
+    (void) p_size_changed;
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    struct hxxx_helper *hh = &p_sys->video.hh;
 
-        h264_release_sps(p_sps);
-    }
+    assert(hh->hevc.i_annexb_config_nal > 0);
 
-    return VLC_EGENERIC;
+    return CSDDup(p_dec, hh->hevc.p_annexb_config_nal,
+                  hh->hevc.i_annexb_config_nal);
 }
 
 static int ParseVideoExtraH264(decoder_t *p_dec, uint8_t *p_extra, int i_extra)
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
+    struct hxxx_helper *hh = &p_sys->video.hh;
 
-    if (h264_isavcC(p_extra, i_extra))
-    {
-        size_t i_size = 0;
-        uint8_t *p_buf = h264_avcC_to_AnnexB_NAL(p_extra, i_extra, &i_size,
-                                                 &p_sys->video.i_nal_length_size);
-
-        /* XXX h264_AVC_to_AnnexB() works only with a i_nal_length_size of 4.
-         * If nal_length_size is smaller than 4, fallback to SW decoding. I
-         * don't know if it's worth the effort to fix h264_AVC_to_AnnexB() for
-         * a smaller nal_length_size. Indeed, this case will happen only with
-         * very small resolutions, where MediaCodec is not that useful.
-         * -Thomas */
-        if (!p_buf || p_sys->video.i_nal_length_size != 4)
-        {
-            msg_Dbg(p_dec, "h264_avcC_to_AnnexB_NAL failed%s",
-                    p_buf ? ": nal_length_size too small" : "");
-            free(p_buf);
-            return VLC_EGENERIC;
-        }
-
-        int i_ret = H264SetCSD(p_dec, p_buf, i_size, NULL);
-        free(p_buf);
+    int i_ret = hxxx_helper_set_extra(hh, p_extra, i_extra);
+    if (i_ret != VLC_SUCCESS)
         return i_ret;
-    }
-    else
-        return H264SetCSD(p_dec, p_extra, i_extra, NULL);
+
+    assert(hh->pf_process_block != NULL);
+
+    if (hh->h264.i_sps_count > 0 || hh->h264.i_pps_count > 0)
+        return H264SetCSD(p_dec, NULL);
+    return VLC_SUCCESS;
 }
 
 static int ParseVideoExtraHEVC(decoder_t *p_dec, uint8_t *p_extra, int i_extra)
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
+    struct hxxx_helper *hh = &p_sys->video.hh;
 
-    if (hevc_ishvcC(p_extra, i_extra))
-    {
-        struct csd csd;
-        uint8_t *p_buf = hevc_hvcC_to_AnnexB_NAL(p_extra, i_extra, &csd.i_size,
-                                                 &p_sys->video.i_nal_length_size);
-        if (p_buf)
-        {
-            csd.p_buf = p_buf;
-            CSDDup(p_dec, &csd, 1);
-            free(p_buf);
-        }
-    }
-    /* FIXME: what to do with AnnexB ? */
+    int i_ret = hxxx_helper_set_extra(hh, p_extra, i_extra);
+    if (i_ret != VLC_SUCCESS || hh->pf_process_block == NULL)
+        return i_ret;
+
+    assert(hh->pf_process_block != NULL);
 
+    if (hh->hevc.i_annexb_config_nal > 0)
+        return HEVCSetCSD(p_dec, NULL);
     return VLC_SUCCESS;
 }
 
 static int ParseVideoExtraVc1(decoder_t *p_dec, uint8_t *p_extra, int i_extra)
 {
     int offset = 0;
-    struct csd csd;
 
     if (i_extra < 4)
         return VLC_EGENERIC;
@@ -423,10 +326,7 @@ static int ParseVideoExtraVc1(decoder_t *p_dec, uint8_t *p_extra, int i_extra)
     if (offset >= i_extra - 4)
         return VLC_EGENERIC;
 
-    csd.i_size = i_extra - offset;
-    csd.p_buf = p_extra + offset;
-
-    return CSDDup(p_dec, &csd, 1);
+    return CSDDup(p_dec, p_extra + offset, i_extra - offset);
 }
 
 static int ParseVideoExtraWmv3(decoder_t *p_dec, uint8_t *p_extra, int i_extra)
@@ -453,17 +353,13 @@ static int ParseVideoExtraWmv3(decoder_t *p_dec, uint8_t *p_extra, int i_extra)
         0x00, 0x00, 0x00, 0x00
     };
 
-    struct csd csd;
-
-    csd.i_size = sizeof(p_data);
     /* Adding extradata */
     memcpy(&p_data[8], p_extra, 4);
     /* Adding height and width, little endian */
     SetDWLE(&(p_data[12]), p_dec->fmt_in.video.i_height);
     SetDWLE(&(p_data[16]), p_dec->fmt_in.video.i_width);
 
-    csd.p_buf = p_data;
-    return CSDDup(p_dec, &csd, 1);
+    return CSDDup(p_dec, p_data, sizeof(p_data));
 }
 
 static int ParseVideoExtra(decoder_t *p_dec)
@@ -545,7 +441,7 @@ static int StartMediaCodec(decoder_t *p_dec)
     decoder_sys_t *p_sys = p_dec->p_sys;
     union mc_api_args args;
 
-    if (((p_sys->api.i_quirks & MC_API_QUIRKS_NEED_CSD) && !p_sys->pp_csd))
+    if (((p_sys->api.i_quirks & MC_API_QUIRKS_NEED_CSD) && !p_sys->i_csd_count))
     {
         msg_Warn(p_dec, "waiting for extra data for codec %4.4s",
                  (const char *)&p_dec->fmt_in.i_codec);
@@ -564,20 +460,6 @@ static int StartMediaCodec(decoder_t *p_dec)
         args.video.i_height = p_dec->fmt_out.video.i_height;
         args.video.i_angle = p_sys->video.i_angle;
 
-        /* Configure again if h264 profile changed */
-        if (p_dec->fmt_in.i_codec == VLC_CODEC_H264
-         && !p_sys->video.i_h264_profile)
-        {
-            uint8_t i_profile;
-            if (h264_get_profile_level(&p_dec->fmt_in, &i_profile, NULL, NULL))
-            {
-                p_sys->video.i_h264_profile = i_profile;
-                if (p_sys->api.configure(&p_sys->api,
-                                         p_sys->video.i_h264_profile) != 0)
-                    return VLC_EGENERIC;
-            }
-        }
-
         args.video.p_surface = p_sys->video.p_surface;
         args.video.p_jsurface = p_sys->video.p_jsurface;
         args.video.b_tunneled_playback = args.video.p_surface ?
@@ -750,10 +632,10 @@ static int OpenDecoder(vlc_object_t *p_this, pf_MediaCodecApi_init pf_init)
         switch (p_dec->fmt_in.i_codec)
         {
         case VLC_CODEC_H264:
-            p_sys->pf_on_new_block = VideoH264_OnNewBlock;
-            break;
         case VLC_CODEC_HEVC:
-            p_sys->pf_on_new_block = VideoHEVC_OnNewBlock;
+            p_sys->pf_on_new_block = VideoHXXX_OnNewBlock;
+            hxxx_helper_init(&p_sys->video.hh, VLC_OBJECT(p_dec),
+                             p_dec->fmt_in.i_codec, false);
             break;
         case VLC_CODEC_VC1:
             p_sys->pf_on_new_block = VideoVC1_OnNewBlock;
@@ -764,7 +646,6 @@ static int OpenDecoder(vlc_object_t *p_this, pf_MediaCodecApi_init pf_init)
         }
         p_sys->pf_on_flush = Video_OnFlush;
         p_sys->pf_process_output = Video_ProcessOutput;
-        p_sys->video.i_h264_profile = i_h264_profile;
 
         p_sys->video.timestamp_fifo = timestamp_FifoNew(32);
         if (!p_sys->video.timestamp_fifo)
@@ -802,23 +683,14 @@ static int OpenDecoder(vlc_object_t *p_this, pf_MediaCodecApi_init pf_init)
         }
     }
 
-    if (p_dec->fmt_in.i_extra)
-    {
-        /* Try first to configure specific Video CSD */
-        if (p_dec->fmt_in.i_cat == VIDEO_ES)
-            if (ParseVideoExtra(p_dec) != VLC_SUCCESS)
-                goto bailout;
-
-        /* Set default CSD if ParseVideoExtra failed to configure one */
-        if (!p_sys->pp_csd)
-        {
-            struct csd csd;
+    /* Try first to configure specific Video CSD */
+    if (p_dec->fmt_in.i_cat == VIDEO_ES)
+        if (ParseVideoExtra(p_dec) != VLC_SUCCESS)
+            goto bailout;
 
-            csd.p_buf = p_dec->fmt_in.p_extra;
-            csd.i_size = p_dec->fmt_in.i_extra;
-            CSDDup(p_dec, &csd, 1);
-        }
-    }
+    /* Set default CSD if ParseVideoExtra failed to configure one */
+    if (!p_sys->i_csd_count && p_dec->fmt_in.i_extra)
+        CSDDup(p_dec, p_dec->fmt_in.p_extra, p_dec->fmt_in.i_extra);
 
     i_ret = StartMediaCodec(p_dec);
     switch (i_ret)
@@ -889,6 +761,10 @@ static void CleanDecoder(decoder_t *p_dec)
 
     if (p_dec->fmt_in.i_cat == VIDEO_ES)
     {
+        if (p_dec->fmt_in.i_codec == VLC_CODEC_H264
+         || p_dec->fmt_in.i_codec == VLC_CODEC_HEVC)
+            hxxx_helper_clean(&p_sys->video.hh);
+
         if (p_sys->video.timestamp_fifo)
             timestamp_FifoRelease(p_sys->video.timestamp_fifo);
     }
@@ -1602,21 +1478,33 @@ static int Video_OnNewBlock(decoder_t *p_dec, block_t **pp_block)
     return 1;
 }
 
-static int VideoH264_OnNewBlock(decoder_t *p_dec, block_t **pp_block)
+static int VideoHXXX_OnNewBlock(decoder_t *p_dec, block_t **pp_block)
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
-    block_t *p_block = *pp_block;
-    bool b_size_changed;
-
-    assert(p_dec->fmt_in.i_codec == VLC_CODEC_H264 && p_block);
+    struct hxxx_helper *hh = &p_sys->video.hh;
+    bool b_config_changed;
 
-    if (p_sys->video.i_nal_length_size)
-    {
-        h264_AVC_to_AnnexB(p_block->p_buffer, p_block->i_buffer,
-                           p_sys->video.i_nal_length_size);
-    } else if (H264SetCSD(p_dec, p_block->p_buffer, p_block->i_buffer,
-                          &b_size_changed) == VLC_SUCCESS)
+    *pp_block = hh->pf_process_block(hh, *pp_block, &b_config_changed);
+    if (!*pp_block)
+        return 0;
+    if (b_config_changed)
     {
+        bool b_size_changed;
+        int i_ret;
+        switch (p_dec->fmt_in.i_codec)
+        {
+        case VLC_CODEC_H264:
+            if (hh->h264.i_sps_count > 0 || hh->h264.i_pps_count > 0)
+                i_ret = H264SetCSD(p_dec, &b_size_changed);
+            else
+                i_ret = VLC_EGENERIC;
+            break;
+        case VLC_CODEC_HEVC:
+            i_ret = HEVCSetCSD(p_dec, &b_size_changed);
+            break;
+        }
+        if (i_ret != VLC_SUCCESS)
+            return i_ret;
         if (b_size_changed || !p_sys->api.b_started)
         {
             if (p_sys->api.b_started)
@@ -1626,29 +1514,13 @@ static int VideoH264_OnNewBlock(decoder_t *p_dec, block_t **pp_block)
         } else
         {
             msg_Err(p_dec, "SPS/PPS changed during playback. Flush it");
-            p_sys->i_decode_flags  |= DECODE_FLASH_FLUSH;
+            p_sys->i_decode_flags |= DECODE_FLASH_FLUSH;
         }
     }
 
     return Video_OnNewBlock(p_dec, pp_block);
 }
 
-static int VideoHEVC_OnNewBlock(decoder_t *p_dec, block_t **pp_block)
-{
-    decoder_sys_t *p_sys = p_dec->p_sys;
-    block_t *p_block = *pp_block;
-
-    assert(p_dec->fmt_in.i_codec == VLC_CODEC_HEVC && p_block);
-
-    if (p_sys->video.i_nal_length_size)
-    {
-        h264_AVC_to_AnnexB(p_block->p_buffer, p_block->i_buffer,
-                           p_sys->video.i_nal_length_size);
-    }
-
-    return Video_OnNewBlock(p_dec, pp_block);
-}
-
 static int VideoVC1_OnNewBlock(decoder_t *p_dec, block_t **pp_block)
 {
     block_t *p_block = *pp_block;
diff --git a/modules/codec/videotoolbox.m b/modules/codec/videotoolbox.m
index c2de0e7..617433c 100644
--- a/modules/codec/videotoolbox.m
+++ b/modules/codec/videotoolbox.m
@@ -29,8 +29,7 @@
 #import <vlc_common.h>
 #import <vlc_plugin.h>
 #import <vlc_codec.h>
-#import "../packetizer/h264_nal.h"
-#import "../packetizer/hxxx_nal.h"
+#import "hxxx_helper.h"
 #import "../video_chroma/copy.h"
 #import <vlc_bits.h>
 #import <vlc_boxes.h>
@@ -82,12 +81,13 @@ vlc_module_end()
 #pragma mark - local prototypes
 
 static int ESDSCreate(decoder_t *, uint8_t *, uint32_t);
-static int avcCFromAnnexBCreate(decoder_t *, block_t *);
+static int avcCFromAnnexBCreate(decoder_t *);
 static int ExtradataInfoCreate(decoder_t *, CFStringRef, void *, size_t);
 static int DecodeBlock(decoder_t *, block_t *);
 static void PicReorder_pushSorted(decoder_t *, picture_t *);
 static picture_t *PicReorder_pop(decoder_t *, bool);
 static void PicReorder_flush(decoder_t *);
+static void PicReorder_setup(decoder_t *);
 static void Flush(decoder_t *);
 static void DecoderCallback(void *, void *, OSStatus, VTDecodeInfoFlags,
                             CVPixelBufferRef, CMTime, CMTime);
@@ -106,11 +106,10 @@ struct picture_sys_t {
 struct decoder_sys_t
 {
     CMVideoCodecType            codec;
-    uint8_t                     i_nal_length_size;
+    struct                      hxxx_helper hh;
 
     bool                        b_vt_feed;
     bool                        b_vt_flush;
-    bool                        b_is_avcc;
     VTDecompressionSessionRef   session;
     CMVideoFormatDescriptionRef videoFormatDescription;
     CFMutableDictionaryRef      decoderConfiguration;
@@ -498,24 +497,24 @@ static int SetupDecoderExtradata(decoder_t *p_dec)
 
     if (p_sys->codec == kCMVideoCodecType_H264)
     {
+        hxxx_helper_init(&p_sys->hh, VLC_OBJECT(p_dec),
+                         p_dec->fmt_in.i_codec, true);
+        int i_ret = hxxx_helper_set_extra(&p_sys->hh, p_dec->fmt_in.p_extra,
+                                          p_dec->fmt_in.i_extra);
+        if (i_ret != VLC_SUCCESS)
+            return i_ret;
+
         if (p_dec->fmt_in.p_extra)
         {
-            if (!h264_isavcC(p_dec->fmt_in.p_extra, p_dec->fmt_in.i_extra))
-                return VLC_EGENERIC;
             int i_ret = ExtradataInfoCreate(p_dec, CFSTR("avcC"),
                                             p_dec->fmt_in.p_extra,
                                             p_dec->fmt_in.i_extra);
             if (i_ret != VLC_SUCCESS)
                 return i_ret;
-            p_sys->b_is_avcc = true;
-            p_sys->i_pic_reorder_max = 5;
-        }
-        else
-        {
-            /* AnnexB case, we'll get extradata from first input blocks */
-            p_sys->b_is_avcc = false;
-        }
 
+            PicReorder_setup(p_dec);
+        }
+        /* else: AnnexB case, we'll get extradata from first input blocks */
     }
     else if (p_sys->codec == kCMVideoCodecType_MPEG4Video)
     {
@@ -569,7 +568,6 @@ static int OpenDecoder(vlc_object_t *p_this)
     p_sys->session = nil;
     p_sys->b_vt_feed = false;
     p_sys->b_vt_flush = false;
-    p_sys->b_is_avcc = false;
     p_sys->codec = codec;
     p_sys->videoFormatDescription = nil;
     p_sys->decoderConfiguration = nil;
@@ -737,63 +735,38 @@ static int ESDSCreate(decoder_t *p_dec, uint8_t *p_buf, uint32_t i_buf_size)
     return i_ret;
 }
 
-static int avcCFromAnnexBCreate(decoder_t *p_dec, block_t *p_block)
+static int avcCFromAnnexBCreate(decoder_t *p_dec)
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
 
-    /* get the SPS and PPS units from the NAL unit which is either
-     * part of the demuxer's avvC atom or the mid stream data block */
-    const uint8_t *p_sps_nal = NULL, *p_pps_nal = NULL;
-    size_t i_sps_nalsize = 0, i_pps_nalsize = 0;
-    if (!h264_AnnexB_get_spspps(p_block->p_buffer, p_block->i_buffer,
-                                &p_sps_nal, &i_sps_nalsize,
-                                &p_pps_nal, &i_pps_nalsize,
-                                NULL, NULL) || i_sps_nalsize == 0)
-    {
-        msg_Warn(p_dec, "sps pps detection failed");
+    if (p_sys->hh.h264.i_sps_count == 0 || p_sys->hh.h264.i_pps_count == 0)
         return VLC_EGENERIC;
-    }
-    assert(p_sps_nal);
 
-    /* Decode Sequence Parameter Set */
-    h264_sequence_parameter_set_t *p_sps_data;
-    if (!(p_sps_data = h264_decode_sps(p_sps_nal, i_sps_nalsize, true)))
-    {
-        msg_Warn(p_dec, "sps pps parsing failed");
-        return VLC_EGENERIC;
-    }
-
-    /* this data is more trust-worthy than what we receive
-     * from the demuxer, so we will use it to over-write
-     * the current values */
-    unsigned h264_width, h264_height, i_video_width, i_video_height;
-    h264_get_picture_size(p_sps_data, &h264_width, &h264_height,
-                          &i_video_width, &i_video_height);
+    unsigned i_h264_width, i_h264_height, i_video_width, i_video_height;
+    int i_sar_num, i_sar_den, i_ret;
+    i_ret = h264_helper_get_current_picture_size(&p_sys->hh,
+                                                 &i_h264_width, &i_h264_height,
+                                                 &i_video_width, &i_video_height);
+    if (i_ret != VLC_SUCCESS)
+        return i_ret;
+    i_ret = h264_helper_get_current_sar(&p_sys->hh, &i_sar_num, &i_sar_den);
+    if (i_ret != VLC_SUCCESS)
+        return i_ret;
+    PicReorder_setup(p_dec);
 
     p_dec->fmt_out.video.i_visible_width =
     p_dec->fmt_out.video.i_width = i_video_width;
     p_dec->fmt_out.video.i_visible_height =
     p_dec->fmt_out.video.i_height = i_video_height;
-    p_dec->fmt_out.video.i_sar_num = p_sps_data->vui.i_sar_num;
-    p_dec->fmt_out.video.i_sar_den = p_sps_data->vui.i_sar_den;
-
-    uint8_t i_depth;
-    unsigned i_delay;
-    if (h264_get_dpb_values(p_sps_data, &i_depth, &i_delay) == false)
-        i_depth = 4;
-    p_sys->i_pic_reorder_max = i_depth + 1;
-
-    h264_release_sps(p_sps_data);
+    p_dec->fmt_out.video.i_sar_num = i_sar_num;
+    p_dec->fmt_out.video.i_sar_den = i_sar_den;
 
-    p_sys->i_nal_length_size = 4; /* default to 4 bytes */
-    block_t *p_avcC = h264_NAL_to_avcC(p_sys->i_nal_length_size,
-                                       &p_sps_nal, &i_sps_nalsize, 1,
-                                       &p_pps_nal, &i_pps_nalsize, 1);
+    block_t *p_avcC = h264_helper_get_avcc_config(&p_sys->hh);
     if (!p_avcC)
         return VLC_EGENERIC;
 
-    int i_ret = ExtradataInfoCreate(p_dec, CFSTR("avcC"), p_avcC->p_buffer,
-                                    p_avcC->i_buffer);
+    i_ret = ExtradataInfoCreate(p_dec, CFSTR("avcC"), p_avcC->p_buffer,
+                                p_avcC->i_buffer);
     block_Release(p_avcC);
     return i_ret;
 }
@@ -825,17 +798,6 @@ static int ExtradataInfoCreate(decoder_t *p_dec, CFStringRef name, void *p_data,
     return VLC_SUCCESS;
 }
 
-static block_t *H264ProcessBlock(decoder_t *p_dec, block_t *p_block)
-{
-    decoder_sys_t *p_sys = p_dec->p_sys;
-    assert(p_block);
-
-    if (p_sys->b_is_avcc) /* FIXME: no change checks done for AVC ? */
-        return p_block;
-
-    return hxxx_AnnexB_to_xVC(p_block, p_sys->i_nal_length_size);
-}
-
 static CMSampleBufferRef VTSampleBufferCreate(decoder_t *p_dec,
                                               CMFormatDescriptionRef fmt_desc,
                                               block_t *p_block)
@@ -973,6 +935,17 @@ static void PicReorder_flush(decoder_t *p_dec)
     p_sys->p_pic_reorder = NULL;
 }
 
+static void PicReorder_setup(decoder_t *p_dec)
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    uint8_t i_depth;
+    unsigned i_delay;
+    if (h264_helper_get_current_dpb_values(&p_sys->hh, &i_depth, &i_delay)
+     != VLC_SUCCESS)
+        i_depth = 4;
+    p_sys->i_pic_reorder_max = i_depth + 1;
+}
+
 static void Flush(decoder_t *p_dec)
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
@@ -1025,13 +998,26 @@ static int DecodeBlock(decoder_t *p_dec, block_t *p_block)
         goto skip;
     }
 
-    if (!p_sys->session)
+    bool b_config_changed = false;
+    if (p_sys->codec == kCMVideoCodecType_H264 && p_sys->hh.pf_process_block)
+    {
+        p_block = p_sys->hh.pf_process_block(&p_sys->hh, p_block, &b_config_changed);
+        if (!p_block)
+            return VLCDEC_SUCCESS;
+    }
+
+    if (b_config_changed)
     {
         /* decoding didn't start yet, which is ok for H264, let's see
          * if we can use this block to get going */
-        assert(p_sys->codec == kCMVideoCodecType_H264 && !p_sys->b_is_avcc);
-        int i_ret = avcCFromAnnexBCreate(p_dec, p_block);
+        assert(p_sys->codec == kCMVideoCodecType_H264 && !p_sys->hh.b_is_xvcC);
+        if (p_sys->session)
+        {
+            msg_Dbg(p_dec, "SPS/PPS changed: restarting H264 decoder");
+            StopVideoToolbox(p_dec, true);
+        }
 
+        int i_ret = avcCFromAnnexBCreate(p_dec);
         if (i_ret == VLC_SUCCESS)
         {
             if ((p_block->i_flags & BLOCK_FLAG_TOP_FIELD_FIRST
@@ -1055,12 +1041,6 @@ static int DecodeBlock(decoder_t *p_dec, block_t *p_block)
         goto reload;
     }
 
-    if (p_sys->codec == kCMVideoCodecType_H264) {
-        p_block = H264ProcessBlock(p_dec, p_block);
-        if (!p_block)
-            return VLCDEC_SUCCESS;
-    }
-
     CMSampleBufferRef sampleBuffer =
         VTSampleBufferCreate(p_dec, p_sys->videoFormatDescription, p_block);
     if (unlikely(!sampleBuffer))



More information about the vlc-commits mailing list