[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