meta-digi/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0071-v4l2codecs-vp8-add-web...

253 lines
8.1 KiB
Diff

From 6adaff584bc42f9bdf2e22aa3dab5f3f72626c03 Mon Sep 17 00:00:00 2001
From: Hugues Fruchet <hugues.fruchet@foss.st.com>
Date: Wed, 21 Aug 2024 17:35:34 +0200
Subject: [PATCH] v4l2codecs: vp8: add webp image support
Add WebP image decoding support to stateless V4L2 VP8 decoder.
A WebP image is composed of a RIFF header followed by a VP8 chunk.
Upstream-Status: Pending
---
gst-libs/gst/codecs/gstvp8decoder.c | 10 +++
sys/v4l2codecs/gstv4l2codecvp8dec.c | 127 ++++++++++++++++++++++++++-
sys/v4l2codecs/linux/v4l2-controls.h | 1 +
3 files changed, 136 insertions(+), 2 deletions(-)
diff --git a/gst-libs/gst/codecs/gstvp8decoder.c b/gst-libs/gst/codecs/gstvp8decoder.c
index 411f272..9ef51cf 100644
--- a/gst-libs/gst/codecs/gstvp8decoder.c
+++ b/gst-libs/gst/codecs/gstvp8decoder.c
@@ -35,6 +35,8 @@
GST_DEBUG_CATEGORY (gst_vp8_decoder_debug);
#define GST_CAT_DEFAULT gst_vp8_decoder_debug
+#define WEBP_RIFF_HEADER_SIZE 20
+
struct _GstVp8DecoderPrivate
{
gint width;
@@ -376,6 +378,8 @@ gst_vp8_decoder_handle_frame (GstVideoDecoder * decoder,
GstVp8Picture *picture = NULL;
GstFlowReturn ret = GST_FLOW_OK;
GstVp8DecoderOutputFrame output_frame;
+ GstCaps *webp_caps = gst_caps_intersect (gst_caps_new_empty_simple ("image/webp"),
+ self->input_state->caps);
GST_LOG_OBJECT (self,
"handle frame, PTS: %" GST_TIME_FORMAT ", DTS: %"
@@ -388,6 +392,12 @@ gst_vp8_decoder_handle_frame (GstVideoDecoder * decoder,
goto error;
}
+ if (!gst_caps_is_empty (webp_caps)) {
+ /* Drop WebP RIFF header */
+ map.data += WEBP_RIFF_HEADER_SIZE;
+ map.size -= WEBP_RIFF_HEADER_SIZE;
+ }
+
pres = gst_vp8_parser_parse_frame_header (&priv->parser,
&frame_hdr, map.data, map.size);
diff --git a/sys/v4l2codecs/gstv4l2codecvp8dec.c b/sys/v4l2codecs/gstv4l2codecvp8dec.c
index ba63a02..6cc5824 100644
--- a/sys/v4l2codecs/gstv4l2codecvp8dec.c
+++ b/sys/v4l2codecs/gstv4l2codecvp8dec.c
@@ -26,6 +26,8 @@
#include "gstv4l2codecpool.h"
#include "gstv4l2codecvp8dec.h"
#include "gstv4l2format.h"
+#include <gst/base/gstadapter.h>
+#include <gst/base/gstbytereader.h>
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
@@ -45,7 +47,8 @@ enum
static GstStaticPadTemplate sink_template =
GST_STATIC_PAD_TEMPLATE (GST_VIDEO_DECODER_SINK_NAME,
GST_PAD_SINK, GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("video/x-vp8")
+ GST_STATIC_CAPS ("video/x-vp8;"
+ "image/webp;")
);
static GstStaticPadTemplate alpha_template =
@@ -80,6 +83,10 @@ struct _GstV4l2CodecVp8Dec
GstMemory *bitstream;
GstMapInfo bitstream_map;
+
+ gboolean saw_header;
+ guint frame_size;
+ gboolean webp;
};
G_DEFINE_ABSTRACT_TYPE (GstV4l2CodecVp8Dec, gst_v4l2_codec_vp8_dec,
@@ -428,7 +435,8 @@ gst_v4l2_codec_vp8_dec_fill_frame_header (GstV4l2CodecVp8Dec * self,
(frame_hdr->show_frame ? V4L2_VP8_FRAME_FLAG_SHOW_FRAME : 0) |
(frame_hdr->mb_no_skip_coeff ? V4L2_VP8_FRAME_FLAG_MB_NO_SKIP_COEFF : 0) |
(frame_hdr->sign_bias_golden ? V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN : 0) |
- (frame_hdr->sign_bias_alternate ? V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT : 0),
+ (frame_hdr->sign_bias_alternate ? V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT : 0) |
+ (self->webp ? V4L2_VP8_FRAME_FLAG_WEBP : 0),
};
/* *INDENT-ON* */
@@ -579,6 +587,7 @@ gst_v4l2_codec_vp8_dec_decode_picture (GstVp8Decoder * decoder,
static void
gst_v4l2_codec_vp8_dec_reset_picture (GstV4l2CodecVp8Dec * self)
{
+ self->saw_header = FALSE;
if (self->bitstream) {
if (self->bitstream_map.memory)
gst_memory_unmap (self->bitstream, &self->bitstream_map);
@@ -854,6 +863,7 @@ gst_v4l2_codec_vp8_dec_get_property (GObject * object, guint prop_id,
static void
gst_v4l2_codec_vp8_dec_init (GstV4l2CodecVp8Dec * self)
{
+ self->saw_header = FALSE;
}
static void
@@ -879,6 +889,116 @@ gst_v4l2_codec_vp8_dec_class_init (GstV4l2CodecVp8DecClass * klass)
{
}
+static gboolean
+gst_v4l2_codec_vp8_dec_set_format (GstVideoDecoder * decoder,
+ GstVideoCodecState * state)
+{
+ GstV4l2CodecVp8Dec *self = GST_V4L2_CODEC_VP8_DEC (decoder);
+ GstVp8Decoder *vp8dec = GST_VP8_DECODER (decoder);
+ GstCaps *webp_caps = gst_caps_intersect (gst_caps_new_empty_simple ("image/webp"),
+ state->caps);
+
+ if (vp8dec->input_state)
+ gst_video_codec_state_unref (vp8dec->input_state);
+ vp8dec->input_state = gst_video_codec_state_ref (state);
+
+ if (!gst_caps_is_empty (webp_caps)) {
+ /* WebP requires parsing for right packetization */
+ gst_video_decoder_set_packetized (decoder, FALSE);
+ self->webp = TRUE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Webp image RIFF header
+ *
+ * 52 49 46 46 f6 00 00 00 57 45 42 50 56 50 38 20 RIFF....WEBPVP8
+ * ea 00 00 00 90 09 00 9d 01 2a 30 00 30 00 3e 35 .........*0.0.>5
+ * | \______/ \______/
+ * | | \__VP8 startcode
+ * | \__VP8 frame_tag
+ * |
+ * \__End of WebP RIFF header: 20 bytes, then VP8 chunk
+ *
+ * copied from gstwebpdec.c: gst_webp_dec_parse()
+ */
+static GstFlowReturn
+gst_v4l2_codec_vp8_dec_parse (GstVideoDecoder * decoder,
+ GstVideoCodecFrame * frame, GstAdapter * adapter, gboolean at_eos)
+{
+ gsize toadd = 0;
+ gsize size;
+ gconstpointer data;
+ GstByteReader reader;
+ GstV4l2CodecVp8Dec *self = (GstV4l2CodecVp8Dec *) decoder;
+
+ size = gst_adapter_available (adapter);
+ GST_DEBUG_OBJECT (decoder,
+ "parsing webp image data (%" G_GSIZE_FORMAT " bytes)", size);
+
+ if (at_eos) {
+ GST_DEBUG ("Flushing all data out");
+ toadd = size;
+
+ /* If we have leftover data, throw it away */
+ if (!self->saw_header)
+ goto drop_frame;
+ goto have_full_frame;
+ }
+
+ if (!self->saw_header) {
+ guint32 code;
+
+ if (size < 12)
+ goto need_more_data;
+
+ data = gst_adapter_map (adapter, size);
+ gst_byte_reader_init (&reader, data, size);
+
+ if (!gst_byte_reader_get_uint32_le (&reader, &code))
+ goto error;
+
+ if (code == GST_MAKE_FOURCC ('R', 'I', 'F', 'F')) {
+ if (!gst_byte_reader_get_uint32_le (&reader, &self->frame_size))
+ goto error;
+
+ if (!gst_byte_reader_get_uint32_le (&reader, &code))
+ goto error;
+
+ if (code == GST_MAKE_FOURCC ('W', 'E', 'B', 'P'))
+ self->saw_header = TRUE;
+ }
+ }
+
+ if (!self->saw_header)
+ goto error;
+
+ if (size >= (self->frame_size + 8)) {
+ toadd = self->frame_size + 8;
+ self->saw_header = FALSE;
+
+ goto have_full_frame;
+ }
+
+need_more_data:
+ return GST_VIDEO_DECODER_FLOW_NEED_DATA;
+
+have_full_frame:
+ if (toadd)
+ gst_video_decoder_add_to_frame (decoder, toadd);
+ return gst_video_decoder_have_frame (decoder);
+
+drop_frame:
+ gst_adapter_flush (adapter, size);
+ return GST_FLOW_OK;
+
+error:
+ GST_WARNING("parsing webp GST_FLOW_ERROR");
+ return GST_FLOW_ERROR;
+}
+
static void
gst_v4l2_codec_vp8_dec_subclass_init (GstV4l2CodecVp8DecClass * klass,
GstV4l2CodecDevice * device)
@@ -906,6 +1026,9 @@ gst_v4l2_codec_vp8_dec_subclass_init (GstV4l2CodecVp8DecClass * klass,
decoder_class->open = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_dec_open);
decoder_class->close = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_dec_close);
decoder_class->stop = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_dec_stop);
+ decoder_class->parse = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_dec_parse);
+ decoder_class->set_format =
+ GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_dec_set_format);
decoder_class->negotiate =
GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_dec_negotiate);
decoder_class->decide_allocation =
diff --git a/sys/v4l2codecs/linux/v4l2-controls.h b/sys/v4l2codecs/linux/v4l2-controls.h
index 7a94437..aaeaa58 100644
--- a/sys/v4l2codecs/linux/v4l2-controls.h
+++ b/sys/v4l2codecs/linux/v4l2-controls.h
@@ -1987,6 +1987,7 @@ struct v4l2_vp8_entropy_coder_state {
#define V4L2_VP8_FRAME_FLAG_MB_NO_SKIP_COEFF 0x08
#define V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN 0x10
#define V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT 0x20
+#define V4L2_VP8_FRAME_FLAG_WEBP 0x40
#define V4L2_VP8_FRAME_IS_KEY_FRAME(hdr) \
(!!((hdr)->flags & V4L2_VP8_FRAME_FLAG_KEY_FRAME))
--
2.25.1