diff --git a/sys/v4l2codecs/gstv4l2codech264dec.c b/sys/v4l2codecs/gstv4l2codech264dec.c new file mode 100644 index 0000000000..9a3428d780 --- /dev/null +++ b/sys/v4l2codecs/gstv4l2codech264dec.c @@ -0,0 +1,290 @@ +/* GStreamer + * Copyright (C) 2020 Nicolas Dufresne + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gstv4l2codech264dec.h" + +GST_DEBUG_CATEGORY_STATIC (v4l2_h264dec_debug); +#define GST_CAT_DEFAULT v4l2_h264dec_debug + +enum +{ + PROP_0, + PROP_LAST = PROP_0 +}; + +static GstStaticPadTemplate sink_template = +GST_STATIC_PAD_TEMPLATE (GST_VIDEO_DECODER_SINK_NAME, + GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-h264, " + "stream-format=(string) byte-stream, alignment=(string) au") + ); + +static GstStaticPadTemplate src_template = +GST_STATIC_PAD_TEMPLATE (GST_VIDEO_DECODER_SRC_NAME, + GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ NV12, YUY2 }"))); + +struct _GstV4l2CodecH264Dec +{ + GstH264Decoder parent; + GstV4l2Decoder *decoder; + GstVideoCodecState *output_state; + GstVideoFormat out_format; + gint width; + gint height; +}; + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstV4l2CodecH264Dec, + gst_v4l2_codec_h264_dec, GST_TYPE_H264_DECODER, + GST_DEBUG_CATEGORY_INIT (v4l2_h264dec_debug, "v4l2codecs-h264dec", 0, + "V4L2 stateless h264 decoder")); +#define parent_class gst_v4l2_codec_h264_dec_parent_class + +static gboolean +gst_v4l2_codec_h264_dec_open (GstVideoDecoder * decoder) +{ + return TRUE; +} + +static gboolean +gst_v4l2_codec_h264_dec_close (GstVideoDecoder * decoder) +{ + return TRUE; +} + +static gboolean +gst_v4l2_codec_h264_dec_stop (GstVideoDecoder * decoder) +{ + GstV4l2CodecH264Dec *self = GST_V4L2_CODEC_H264_DEC (decoder); + + if (self->output_state) + gst_video_codec_state_unref (self->output_state); + self->output_state = NULL; + + return GST_VIDEO_DECODER_CLASS (parent_class)->stop (decoder); +} + +static gboolean +gst_v4l2_codec_h264_dec_negotiate (GstVideoDecoder * decoder) +{ + GstV4l2CodecH264Dec *self = GST_V4L2_CODEC_H264_DEC (decoder); + GstH264Decoder *h264dec = GST_H264_DECODER (decoder); + + GST_DEBUG_OBJECT (self, "negotiate"); + + if (self->output_state) + gst_video_codec_state_unref (self->output_state); + + self->output_state = + gst_video_decoder_set_output_state (GST_VIDEO_DECODER (self), + self->out_format, self->width, self->height, h264dec->input_state); + + self->output_state->caps = gst_video_info_to_caps (&self->output_state->info); + + return GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder); +} + +static gboolean +gst_v4l2_codec_h264_dec_decide_allocation (GstVideoDecoder * decoder, + GstQuery * query) +{ + return GST_VIDEO_DECODER_CLASS (parent_class)->decide_allocation + (decoder, query); +} + +static gboolean +gst_v4l2_codec_h264_dec_new_sequence (GstH264Decoder * decoder, + const GstH264SPS * sps, gint max_dpb_size) +{ + /* TODO check if we need to setup a new format */ + return TRUE; +} + +static gboolean +gst_v4l2_codec_h264_dec_start_picture (GstH264Decoder * decoder, + GstH264Picture * picture, GstH264Slice * slice, GstH264Dpb * dpb) +{ + return FALSE; +} + +static GstFlowReturn +gst_v4l2_codec_h264_dec_output_picture (GstH264Decoder * decoder, + GstH264Picture * picture) +{ + return GST_FLOW_ERROR; +} + +static gboolean +gst_v4l2_codec_h264_dec_end_picture (GstH264Decoder * decoder, + GstH264Picture * picture) +{ + return TRUE; +} + +static gboolean +gst_v4l2_codec_h264_dec_decode_slice (GstH264Decoder * decoder, + GstH264Picture * picture, GstH264Slice * slice) +{ + return TRUE; +} + +static void +gst_v4l2_codec_h264_dec_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstV4l2CodecH264Dec *self = GST_V4L2_CODEC_H264_DEC (object); + GObject *dec = G_OBJECT (self->decoder); + + switch (prop_id) { + default: + gst_v4l2_decoder_set_property (dec, prop_id - PROP_LAST, value, pspec); + break; + } +} + +static void +gst_v4l2_codec_h264_dec_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstV4l2CodecH264Dec *self = GST_V4L2_CODEC_H264_DEC (object); + GObject *dec = G_OBJECT (self->decoder); + + switch (prop_id) { + default: + gst_v4l2_decoder_get_property (dec, prop_id - PROP_LAST, value, pspec); + break; + } +} + +static void +gst_v4l2_codec_h264_dec_init (GstV4l2CodecH264Dec * self) +{ +} + +static void +gst_v4l2_codec_h264_dec_subinit (GstV4l2CodecH264Dec * self, + GstV4l2CodecH264DecClass * klass) +{ + self->decoder = gst_v4l2_decoder_new (klass->device); +} + +static void +gst_v4l2_codec_h264_dec_dispose (GObject * object) +{ + GstV4l2CodecH264Dec *self = GST_V4L2_CODEC_H264_DEC (object); + + g_clear_object (&self->decoder); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_v4l2_codec_h264_dec_class_init (GstV4l2CodecH264DecClass * klass) +{ +} + +static void +gst_v4l2_codec_h264_dec_subclass_init (GstV4l2CodecH264DecClass * klass, + GstV4l2CodecDevice * device) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstVideoDecoderClass *decoder_class = GST_VIDEO_DECODER_CLASS (klass); + GstH264DecoderClass *h264decoder_class = GST_H264_DECODER_CLASS (klass); + + gobject_class->set_property = gst_v4l2_codec_h264_dec_set_property; + gobject_class->get_property = gst_v4l2_codec_h264_dec_get_property; + gobject_class->dispose = gst_v4l2_codec_h264_dec_dispose; + + gst_element_class_set_static_metadata (element_class, + "V4L2 Stateless H.264 Video Decoder", + "Codec/Decoder/Video/Hardware", + "A V4L2 based H.264 video decoder", + "Nicolas Dufresne "); + + gst_element_class_add_static_pad_template (element_class, &sink_template); + gst_element_class_add_static_pad_template (element_class, &src_template); + + decoder_class->open = GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_dec_open); + decoder_class->close = GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_dec_close); + decoder_class->stop = GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_dec_stop); + decoder_class->negotiate = + GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_dec_negotiate); + decoder_class->decide_allocation = + GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_dec_decide_allocation); + + h264decoder_class->new_sequence = + GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_dec_new_sequence); + h264decoder_class->output_picture = + GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_dec_output_picture); + h264decoder_class->start_picture = + GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_dec_start_picture); + h264decoder_class->decode_slice = + GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_dec_decode_slice); + h264decoder_class->end_picture = + GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_dec_end_picture); + + klass->device = device; + gst_v4l2_decoder_install_properties (gobject_class, PROP_LAST, device); +} + +void +gst_v4l2_codec_h264_dec_register (GstPlugin * plugin, + GstV4l2CodecDevice * device, guint rank) +{ + GTypeQuery type_query; + GTypeInfo type_info = { 0, }; + GType subtype; + gchar *type_name; + + g_type_query (GST_TYPE_V4L2_CODEC_H264_DEC, &type_query); + memset (&type_info, 0, sizeof (type_info)); + type_info.class_size = type_query.class_size; + type_info.instance_size = type_query.instance_size; + type_info.class_init = (GClassInitFunc) gst_v4l2_codec_h264_dec_subclass_init; + type_info.class_data = gst_mini_object_ref (GST_MINI_OBJECT (device)); + type_info.instance_init = (GInstanceInitFunc) gst_v4l2_codec_h264_dec_subinit; + GST_MINI_OBJECT_FLAG_SET (device, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED); + + /* The first decoder to be registered should use a constant name, like + * v4l2slh264dec, for any additional decoders, we create unique names. Decoder + * names may change between boots, so this should help gain stable names for + * the most common use cases. SL stands for state-less, we differentiate + * with v4l2h264dec as this element may not have the same properties */ + type_name = g_strdup ("v4l2slh264dec"); + + if (g_type_from_name (type_name) != 0) { + gchar *basename = g_path_get_basename (device->video_device_path); + g_free (type_name); + type_name = g_strdup_printf ("v4l2sl%sh264dec", basename); + g_free (basename); + } + + subtype = g_type_register_static (GST_TYPE_V4L2_CODEC_H264_DEC, type_name, + &type_info, 0); + + if (!gst_element_register (plugin, type_name, rank, subtype)) + GST_WARNING ("Failed to register plugin '%s'", type_name); + + g_free (type_name); +} diff --git a/sys/v4l2codecs/gstv4l2codech264dec.h b/sys/v4l2codecs/gstv4l2codech264dec.h new file mode 100644 index 0000000000..ccce690e4d --- /dev/null +++ b/sys/v4l2codecs/gstv4l2codech264dec.h @@ -0,0 +1,53 @@ +/* GStreamer + * Copyright (C) 2020 Nicolas Dufresne + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_V4L2_CODEC_H264_DEC_H__ +#define __GST_V4L2_CODEC_H264_DEC_H__ + +#define GST_USE_UNSTABLE_API +#include + +#include "gstv4l2decoder.h" + +G_BEGIN_DECLS + +#define GST_TYPE_V4L2_CODEC_H264_DEC (gst_v4l2_codec_h264_dec_get_type()) +#define GST_V4L2_CODEC_H264_DEC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4L2_CODEC_H264_DEC,GstV4l2CodecH264Dec)) +#define GST_V4L2_CODEC_H264_DEC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4L2_CODEC_H264_DEC,GstV4l2CodecH264DecClass)) +#define GST_V4L2_CODEC_H264_DEC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_V4L2_CODEC_H264_DEC, GstV4l2CodecH264DecClass)) +#define GST_IS_V4L2_CODEC_H264_DEC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2_CODEC_H264_DEC)) +#define GST_IS_V4L2_CODEC_H264_DEC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2_CODEC_H264_DEC)) + +typedef struct _GstV4l2CodecH264Dec GstV4l2CodecH264Dec; +typedef struct _GstV4l2CodecH264DecClass GstV4l2CodecH264DecClass; + +struct _GstV4l2CodecH264DecClass +{ + GstH264DecoderClass parent_class; + GstV4l2CodecDevice *device; +}; + +GType gst_v4l2_codec_h264_dec_get_type (void); +void gst_v4l2_codec_h264_dec_register (GstPlugin * plugin, + GstV4l2CodecDevice * device, + guint rank); + +G_END_DECLS + +#endif /* __GST_D3D11_H264_DEC_H__ */ diff --git a/sys/v4l2codecs/gstv4l2decoder.c b/sys/v4l2codecs/gstv4l2decoder.c index 451e49cae8..2771e8c034 100644 --- a/sys/v4l2codecs/gstv4l2decoder.c +++ b/sys/v4l2codecs/gstv4l2decoder.c @@ -128,7 +128,7 @@ gst_v4l2_decoder_open (GstV4l2Decoder * self) } gboolean -gst_v4l2_decoder_enum_input_fmt (GstV4l2Decoder * self, gint i, +gst_v4l2_decoder_enum_sink_fmt (GstV4l2Decoder * self, gint i, guint32 * out_fmt) { struct v4l2_fmtdesc fmtdesc = { i, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, }; diff --git a/sys/v4l2codecs/gstv4l2decoder.h b/sys/v4l2codecs/gstv4l2decoder.h index ef6efd1eea..4475595902 100644 --- a/sys/v4l2codecs/gstv4l2decoder.h +++ b/sys/v4l2codecs/gstv4l2decoder.h @@ -34,8 +34,8 @@ GstV4l2Decoder * gst_v4l2_decoder_new (GstV4l2CodecDevice * device); gboolean gst_v4l2_decoder_open (GstV4l2Decoder * decoder); -gboolean gst_v4l2_decoder_enum_input_fmt (GstV4l2Decoder * self, - gint i, guint32 * out_fmt); +gboolean gst_v4l2_decoder_enum_sink_fmt (GstV4l2Decoder * self, + gint i, guint32 * out_fmt); void gst_v4l2_decoder_install_properties (GObjectClass * gobject_class, gint prop_offset, diff --git a/sys/v4l2codecs/meson.build b/sys/v4l2codecs/meson.build index c63668dac4..efa0b15e39 100644 --- a/sys/v4l2codecs/meson.build +++ b/sys/v4l2codecs/meson.build @@ -1,6 +1,7 @@ v4l2codecs_sources = [ 'plugin.c', 'gstv4l2codecdevice.c', + 'gstv4l2codech264dec.c', 'gstv4l2decoder.c', ] diff --git a/sys/v4l2codecs/plugin.c b/sys/v4l2codecs/plugin.c index 623b117e8b..aafbcd008e 100644 --- a/sys/v4l2codecs/plugin.c +++ b/sys/v4l2codecs/plugin.c @@ -22,16 +22,61 @@ #include "config.h" #endif -#include +#include "gstv4l2codecdevice.h" +#include "gstv4l2codech264dec.h" +#include "gstv4l2decoder.h" +#include "linux/h264-ctrls.h" +#include "linux/media.h" #define GST_CAT_DEFAULT gstv4l2codecs_debug GST_DEBUG_CATEGORY (gstv4l2codecs_debug); +static void +register_video_decoder (GstPlugin * plugin, GstV4l2CodecDevice * device) +{ + GstV4l2Decoder *decoder = gst_v4l2_decoder_new (device); + gint i; + guint32 fmt; + + if (!gst_v4l2_decoder_open (decoder)) { + g_object_unref (decoder); + return; + } + + for (i = 0; gst_v4l2_decoder_enum_sink_fmt (decoder, i, &fmt); i++) { + switch (fmt) { + case V4L2_PIX_FMT_H264_SLICE: + GST_INFO_OBJECT (decoder, "Registering %s as H264 Decoder", + device->name); + gst_v4l2_codec_h264_dec_register (plugin, device, GST_RANK_PRIMARY + 1); + break; + default: + GST_FIXME_OBJECT (decoder, "%" GST_FOURCC_FORMAT " is not supported.", + GST_FOURCC_ARGS (fmt)); + break; + } + } + + g_object_unref (decoder); +} + static gboolean plugin_init (GstPlugin * plugin) { + GList *devices, *d; + GST_DEBUG_CATEGORY_INIT (gstv4l2codecs_debug, "v4l2codecs", 0, "V4L2 CODECs general debug"); + + devices = gst_v4l2_codec_find_devices (); + for (d = devices; d; d = g_list_next (d)) { + GstV4l2CodecDevice *device = d->data; + + if (device->function == MEDIA_ENT_F_PROC_VIDEO_DECODER) + register_video_decoder (plugin, device); + } + + gst_v4l2_codec_device_list_free (devices); return TRUE; }