From 9f880b37fc7e64324da98d63b54cb9a1caca7d9c Mon Sep 17 00:00:00 2001 From: Adrian Fiergolski Date: Mon, 8 Nov 2021 13:18:49 +0100 Subject: [PATCH] avtp: rvf: add AVTP RVF de-payload support Add AVTP Raw Video Format de-payload support. The element supports only GRAY16_LE output format, so: - active pixels (no vertical blanking), - progressive mode, - 8 and 16-bit pixel depth, - mono pixel format, - grayscale colorspace. Part-of: --- .../gst-docs/symbols/symbol_index.json | 3 + .../docs/plugins/gst_plugins_cache.json | 23 + .../gst-plugins-bad/ext/avtp/gstavtp.c | 4 +- .../ext/avtp/gstavtprvfdepay.c | 710 ++++++++++++++++++ .../ext/avtp/gstavtprvfdepay.h | 76 ++ .../gst-plugins-bad/ext/avtp/meson.build | 1 + 6 files changed, 816 insertions(+), 1 deletion(-) create mode 100644 subprojects/gst-plugins-bad/ext/avtp/gstavtprvfdepay.c create mode 100644 subprojects/gst-plugins-bad/ext/avtp/gstavtprvfdepay.h diff --git a/subprojects/gst-docs/symbols/symbol_index.json b/subprojects/gst-docs/symbols/symbol_index.json index 18bacc1a21..381b2bd0e6 100644 --- a/subprojects/gst-docs/symbols/symbol_index.json +++ b/subprojects/gst-docs/symbols/symbol_index.json @@ -7355,6 +7355,8 @@ "GstAvtpCrfSync!src", "GstAvtpCvfDepay", "GstAvtpCvfDepay!src", + "GstAvtpRvfDepay", + "GstAvtpRvfDepay!src", "GstAvtpCvfPay", "GstAvtpCvfPay!sink", "GstAvtpRvfPay", @@ -34940,6 +34942,7 @@ "element-avtpcrfcheck", "element-avtpcrfsync", "element-avtpcvfdepay", + "element-avtprvfdepay", "element-avtpcvfpay", "element-avtprvfpay", "element-avtpsink", diff --git a/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json b/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json index 3db594158f..86df735be8 100644 --- a/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json +++ b/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json @@ -3601,6 +3601,29 @@ }, "rank": "none" }, + "avtprvfdepay": { + "author": "Adrian Fiergolski ", + "description": "Extracts raw video from RVF AVTPDUs", + "hierarchy": [ + "GstAvtpRvfDepay", + "GstAvtpVfDepayBase", + "GstAvtpBaseDepayload", + "GstElement", + "GstObject", + "GInitiallyUnowned", + "GObject" + ], + "klass": "Codec/Depayloader/Network/AVTP", + "long-name": "AVTP Raw Video Format (RVF) depayloader", + "pad-templates": { + "src": { + "caps": "video/x-raw:\n format: { GRAY16_LE }\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n framerate: [ 0/1, 2147483647/1 ]\n", + "direction": "src", + "presence": "always" + } + }, + "rank": "none" + }, "avtprvfpay": { "author": "Adrian Fiergolski ", "description": "Payload-encode raw video into RVF AVTPDU (IEEE 1722)", diff --git a/subprojects/gst-plugins-bad/ext/avtp/gstavtp.c b/subprojects/gst-plugins-bad/ext/avtp/gstavtp.c index 8e6e170fc2..e6a9b1c29f 100644 --- a/subprojects/gst-plugins-bad/ext/avtp/gstavtp.c +++ b/subprojects/gst-plugins-bad/ext/avtp/gstavtp.c @@ -149,7 +149,7 @@ * Each element has its own configuration properties, with some being common * to several elements. Basic properties are: * - * * streamid (avtpaafpay, avtprvfpay, avtpcvfpay, avtpcvfdepay, + * * streamid (avtpaafpay, avtprvfpay, avtpcvfpay, avtprvfdepay, avtpcvfdepay, * avtprvfdepay, avtpcrfsync, avtpcrfcheck): Stream ID associated with the * stream. * @@ -241,6 +241,7 @@ #include "gstavtpaafdepay.h" #include "gstavtpaafpay.h" #include "gstavtpcvfdepay.h" +#include "gstavtprvfdepay.h" #include "gstavtpcvfpay.h" #include "gstavtprvfpay.h" #include "gstavtpsink.h" @@ -259,6 +260,7 @@ plugin_init (GstPlugin * plugin) ret |= GST_ELEMENT_REGISTER (avtpsrc, plugin); ret |= GST_ELEMENT_REGISTER (avtprvfpay, plugin); ret |= GST_ELEMENT_REGISTER (avtpcvfpay, plugin); + ret |= GST_ELEMENT_REGISTER (avtprvfdepay, plugin); ret |= GST_ELEMENT_REGISTER (avtpcvfdepay, plugin); ret |= GST_ELEMENT_REGISTER (avtpcrfsync, plugin); ret |= GST_ELEMENT_REGISTER (avtpcrfcheck, plugin); diff --git a/subprojects/gst-plugins-bad/ext/avtp/gstavtprvfdepay.c b/subprojects/gst-plugins-bad/ext/avtp/gstavtprvfdepay.c new file mode 100644 index 0000000000..a163975b2b --- /dev/null +++ b/subprojects/gst-plugins-bad/ext/avtp/gstavtprvfdepay.c @@ -0,0 +1,710 @@ +/* + * GStreamer AVTP Plugin + * Copyright (c) 2021, Fastree3D + * Adrian Fiergolski + * + * This library 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 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +/** + * SECTION:element-avtprvfdepay + * @see_also: avtprvfpay + * + * De-payload RVF AVTPDUs into x-raw video according + * to IEEE 1722-2016. For detailed information see + * https://standards.ieee.org/standard/1722-2016.html. + * + * + * Example pipeline + * |[ + * gst-launch-1.0 avtpsrc ! avtprvfdepay ! videoconvert ! autovideosink + * ]| This example pipeline will de-payload raw video from the AVTPDUs + * and play them. Refer to the avtprvfpay example to payload raw video and send the + * AVTP stream. + * + */ + +#include +#include + +#include +#include "gstavtprvfdepay.h" + +GST_DEBUG_CATEGORY_STATIC (avtprvfdepay_debug); +#define GST_CAT_DEFAULT avtprvfdepay_debug + +/* prototypes */ + +static GstFlowReturn gst_avtp_rvf_depay_chain (GstPad * pad, GstObject * parent, + GstBuffer * buffer); + +static gboolean gst_avtp_rvf_depay_push_caps (GstAvtpVfDepayBase * avtpvfdepay); + +gboolean is_first_fragment (GstAvtpRvfDepay * avtprvfdepay, GstMapInfo * map); +gboolean is_last_fragment (GstAvtpRvfDepay * avtprvfdepay, GstMapInfo * map); + +#define AVTP_RVF_HEADER_SIZE (sizeof(struct avtp_stream_pdu) + sizeof(uint64_t)) + +/* pad templates */ + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{GRAY16_LE}")) + ); + +#define gst_avtp_rvf_depay_parent_class parent_class +G_DEFINE_TYPE (GstAvtpRvfDepay, gst_avtp_rvf_depay, + GST_TYPE_AVTP_VF_DEPAY_BASE); +GST_ELEMENT_REGISTER_DEFINE (avtprvfdepay, "avtprvfdepay", GST_RANK_NONE, + GST_TYPE_AVTP_RVF_DEPAY); + +static void +gst_avtp_rvf_depay_class_init (GstAvtpRvfDepayClass * klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstAvtpBaseDepayloadClass *avtpbasedepayload_class = + GST_AVTP_BASE_DEPAYLOAD_CLASS (klass); + GstAvtpVfDepayBaseClass *avtpvfdepaybase_class = + GST_AVTP_VF_DEPAY_BASE_CLASS (klass); + + gst_element_class_add_static_pad_template (element_class, &src_template); + + gst_element_class_set_static_metadata (element_class, + "AVTP Raw Video Format (RVF) depayloader", + "Codec/Depayloader/Network/AVTP", + "Extracts raw video from RVF AVTPDUs", + "Adrian Fiergolski "); + + avtpbasedepayload_class->chain = GST_DEBUG_FUNCPTR (gst_avtp_rvf_depay_chain); + + avtpvfdepaybase_class->depay_push_caps = + GST_DEBUG_FUNCPTR (gst_avtp_rvf_depay_push_caps); + + GST_DEBUG_CATEGORY_INIT (avtprvfdepay_debug, "avtprvfdepay", + 0, "debug category for avtprvfdepay element"); +} + +static void +gst_avtp_rvf_depay_init (GstAvtpRvfDepay * avtprvfdepay) +{ + avtprvfdepay->seqnum = 0; + avtprvfdepay->format_fixed = FALSE; +} + +static guint +translate_pixel_depth (guint8 pixel_depth) +{ + switch (pixel_depth) { + case AVTP_RVF_PIXEL_DEPTH_8: + return 8; + case AVTP_RVF_PIXEL_DEPTH_10: + return 10; + case AVTP_RVF_PIXEL_DEPTH_12: + return 12; + case AVTP_RVF_PIXEL_DEPTH_16: + return 16; + default: + return 0; + } +} + +static guint +translate_frame_rate (guint8 frame_rate) +{ + switch (frame_rate) { + case AVTP_RVF_FRAME_RATE_1: + return 1; + case AVTP_RVF_FRAME_RATE_2: + return 2; + case AVTP_RVF_FRAME_RATE_5: + return 5; + case AVTP_RVF_FRAME_RATE_10: + return 10; + case AVTP_RVF_FRAME_RATE_15: + return 15; + case AVTP_RVF_FRAME_RATE_20: + return 20; + case AVTP_RVF_FRAME_RATE_24: + return 24; + case AVTP_RVF_FRAME_RATE_25: + return 25; + case AVTP_RVF_FRAME_RATE_30: + return 30; + case AVTP_RVF_FRAME_RATE_48: + return 48; + case AVTP_RVF_FRAME_RATE_50: + return 50; + case AVTP_RVF_FRAME_RATE_60: + return 60; + case AVTP_RVF_FRAME_RATE_72: + return 72; + case AVTP_RVF_FRAME_RATE_85: + return 85; + case AVTP_RVF_FRAME_RATE_100: + return 100; + case AVTP_RVF_FRAME_RATE_120: + return 120; + case AVTP_RVF_FRAME_RATE_150: + return 150; + case AVTP_RVF_FRAME_RATE_200: + return 200; + case AVTP_RVF_FRAME_RATE_240: + return 240; + case AVTP_RVF_FRAME_RATE_300: + return 300; + default: + return 0; + } +} + +static gboolean +gst_avtp_rvf_depay_push_caps (GstAvtpVfDepayBase * avtpvfdepay) +{ + GstAvtpBaseDepayload *avtpbasedepayload = + GST_AVTP_BASE_DEPAYLOAD (avtpvfdepay); + GstAvtpRvfDepay *avtprvfdepay = GST_AVTP_RVF_DEPAY (avtpvfdepay); + GstEvent *event; + GstCaps *caps; + GstVideoInfo info; + GstVideoFormat format; + + GST_DEBUG_OBJECT (avtprvfdepay, "Setting src pad caps"); + + format = GST_VIDEO_FORMAT_UNKNOWN; + if (avtprvfdepay->pixel_depth == AVTP_RVF_PIXEL_DEPTH_16 && + avtprvfdepay->pixel_format == AVTP_RVF_PIXEL_FORMAT_MONO && + avtprvfdepay->colorspace == AVTP_RVF_COLORSPACE_GRAY) { + format = GST_VIDEO_FORMAT_GRAY16_LE; + } + + /* Unsupported format */ + if (format == GST_VIDEO_FORMAT_UNKNOWN) { + GST_ERROR_OBJECT (avtprvfdepay, "Unsupported raw video format"); + return FALSE; + } + + GST_DEBUG_OBJECT (avtprvfdepay, + "Selected source format: %s", gst_video_format_to_string (format)); + + gst_video_info_set_interlaced_format (&info, format, + GST_VIDEO_INTERLACE_MODE_PROGRESSIVE, avtprvfdepay->active_pixels, + avtprvfdepay->total_lines); + + info.fps_n = translate_frame_rate (avtprvfdepay->frame_rate); + info.fps_d = 1; + if (avtprvfdepay->pd) { + info.fps_n *= 1000; + info.fps_d = 1001; + } + + caps = gst_video_info_to_caps (&info); + event = gst_event_new_caps (caps); + + gst_caps_unref (caps); + + return gst_pad_push_event (avtpbasedepayload->srcpad, event); +} + +static GstFlowReturn +gst_avtp_rvf_depay_discard (GstAvtpRvfDepay * avtprvfdepay) +{ + GstAvtpVfDepayBase *avtpvfdepaybase = GST_AVTP_VF_DEPAY_BASE (avtprvfdepay); + GstFlowReturn ret = GST_FLOW_OK; + + /* Discard any incomplete frame */ + if (avtpvfdepaybase->out_buffer != NULL) { + GST_DEBUG_OBJECT (avtprvfdepay, "Discarding incomplete frame"); + gst_buffer_unref (avtpvfdepaybase->out_buffer); + avtpvfdepaybase->out_buffer = NULL; + } + + return ret; +} + +static gboolean +gst_avtp_rvf_depay_validate_avtpdu (GstAvtpRvfDepay * avtprvfdepay, + GstMapInfo * map, gboolean * lost_packet) +{ + GstAvtpBaseDepayload *avtpbasedepayload = + GST_AVTP_BASE_DEPAYLOAD (avtprvfdepay); + struct avtp_stream_pdu *pdu; + gboolean result = FALSE; + guint64 val; + guint val32; + gint r; + + if (G_UNLIKELY (map->size < AVTP_RVF_HEADER_SIZE)) { + GST_DEBUG_OBJECT (avtprvfdepay, + "Incomplete AVTP header, expected it to have size of %zd, got %zd", + AVTP_RVF_HEADER_SIZE, map->size); + goto end; + } + + pdu = (struct avtp_stream_pdu *) map->data; + + r = avtp_pdu_get ((struct avtp_common_pdu *) pdu, AVTP_FIELD_SUBTYPE, &val32); + g_assert (r == 0); + if (val32 != AVTP_SUBTYPE_RVF) { + GST_DEBUG_OBJECT (avtprvfdepay, + "Unexpected AVTP header subtype %d, expected %d", val32, + AVTP_SUBTYPE_RVF); + goto end; + } + + r = avtp_pdu_get ((struct avtp_common_pdu *) pdu, AVTP_FIELD_VERSION, &val32); + g_assert (r == 0); + if (G_UNLIKELY (val32 != 0)) { + GST_DEBUG_OBJECT (avtprvfdepay, + "Unexpected AVTP header version %d, expected %d", val32, 0); + goto end; + } + + r = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_SV, &val); + g_assert (r == 0); + if (G_UNLIKELY (val != 1)) { + GST_DEBUG_OBJECT (avtprvfdepay, + "Unexpected AVTP header stream valid %" G_GUINT64_FORMAT + ", expected %d", val, 1); + goto end; + } + + r = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_STREAM_ID, &val); + g_assert (r == 0); + if (val != avtpbasedepayload->streamid) { + GST_DEBUG_OBJECT (avtprvfdepay, + "Unexpected AVTP header stream id 0x%" G_GINT64_MODIFIER + "x, expected 0x%" G_GINT64_MODIFIER "x", val, + avtpbasedepayload->streamid); + goto end; + } + + r = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_STREAM_DATA_LEN, &val); + g_assert (r == 0); + if (G_UNLIKELY (val != avtprvfdepay->stream_data_length)) { + if (!avtprvfdepay->format_fixed) { + if (G_UNLIKELY (map->size < sizeof (*pdu) + val)) { + GST_DEBUG_OBJECT (avtprvfdepay, + "AVTP packet size %" G_GSIZE_FORMAT + " too small, expected at least %" G_GUINT64_FORMAT, + map->size - AVTP_RVF_HEADER_SIZE, sizeof (*pdu) + val); + goto end; + } + + GST_DEBUG_OBJECT (avtprvfdepay, + "Data length of the video format %" G_GSIZE_FORMAT, val); + avtprvfdepay->stream_data_length = (guint) val; + } else { + GST_DEBUG_OBJECT (avtprvfdepay, + "Unexpected AVTP header data_length %" G_GSIZE_FORMAT + ", should be fixed for a given stream (expected %d)", val, + avtprvfdepay->stream_data_length); + goto end; + } + } + + r = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_AP, &val); + g_assert (r == 0); + if (G_UNLIKELY (val != 1)) { + GST_DEBUG_OBJECT (avtprvfdepay, + "Unexpected AVTP header AP field %" G_GUINT64_FORMAT ", expected %d", + val, 1); + goto end; + } + + r = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_F, &val); + g_assert (r == 0); + if (G_UNLIKELY (val != 0)) { + GST_DEBUG_OBJECT (avtprvfdepay, + "Unexpected AVTP header F field %" G_GUINT64_FORMAT ", expected %d", + val, 0); + goto end; + } + + r = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_ACTIVE_PIXELS, &val); + g_assert (r == 0); + if (G_UNLIKELY (val != avtprvfdepay->active_pixels)) { + if (!avtprvfdepay->format_fixed) { + GST_DEBUG_OBJECT (avtprvfdepay, + "Active pixels of the AVTP raw video stream %" G_GUINT64_FORMAT, val); + avtprvfdepay->active_pixels = (guint) val; + } else { + GST_DEBUG_OBJECT (avtprvfdepay, + "Unexpected AVTP header active_pixels %" G_GUINT64_FORMAT + ", expected %u", val, avtprvfdepay->active_pixels); + goto end; + } + } + + r = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_TOTAL_LINES, &val); + g_assert (r == 0); + if (G_UNLIKELY (val != avtprvfdepay->total_lines)) { + if (!avtprvfdepay->format_fixed) { + GST_DEBUG_OBJECT (avtprvfdepay, + "Total lines of the AVTP raw video stream %" G_GUINT64_FORMAT, val); + avtprvfdepay->total_lines = (guint) val; + } else { + GST_DEBUG_OBJECT (avtprvfdepay, + "Unexpected AVTP header total_lines %" G_GUINT64_FORMAT + ", expected %d", val, avtprvfdepay->active_pixels); + goto end; + } + } + + r = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_PD, &val); + g_assert (r == 0); + if (G_UNLIKELY (val != avtprvfdepay->pd)) { + if (!avtprvfdepay->format_fixed) { + GST_DEBUG_OBJECT (avtprvfdepay, + "Pull-down (PD) filed of the of the AVTP raw video stream %" + G_GUINT64_FORMAT, val); + avtprvfdepay->pd = (guint) val; + } else { + GST_DEBUG_OBJECT (avtprvfdepay, + "Unexpected AVTP header PD filed %" G_GUINT64_FORMAT ", expected %d", + val, avtprvfdepay->pd); + goto end; + } + } + + r = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_RAW_PIXEL_DEPTH, &val); + g_assert (r == 0); + if (G_UNLIKELY (val != avtprvfdepay->pixel_depth)) { + if (!avtprvfdepay->format_fixed) { + GST_DEBUG_OBJECT (avtprvfdepay, + "Pixel depth of the AVTP raw video stream %" G_GUINT64_FORMAT, val); + avtprvfdepay->pixel_depth = (guint) val; + } else { + GST_DEBUG_OBJECT (avtprvfdepay, + "Unexpected AVTP header pixel_depth %" G_GUINT64_FORMAT + ", expected %d", val, avtprvfdepay->pixel_depth); + goto end; + } + } + + r = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_RAW_PIXEL_FORMAT, &val); + g_assert (r == 0); + if (G_UNLIKELY (val != avtprvfdepay->pixel_format)) { + if (!avtprvfdepay->format_fixed) { + GST_DEBUG_OBJECT (avtprvfdepay, + "Pixel format of the of the AVTP raw video stream 0x%" + G_GINT64_MODIFIER "x", val); + avtprvfdepay->pixel_format = (guint) val; + } else { + GST_DEBUG_OBJECT (avtprvfdepay, + "Unexpected AVTP header pixel_format filed 0x%" G_GINT64_MODIFIER + "x, expected %x", val, avtprvfdepay->pixel_format); + goto end; + } + } + + r = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE, &val); + g_assert (r == 0); + if (G_UNLIKELY (val != avtprvfdepay->frame_rate)) { + if (!avtprvfdepay->format_fixed) { + GST_DEBUG_OBJECT (avtprvfdepay, + "Frame_rate of the AVTP raw video stream 0x%" G_GINT64_MODIFIER "x", + val); + avtprvfdepay->frame_rate = (guint) val; + } else { + GST_DEBUG_OBJECT (avtprvfdepay, + "Unexpected AVTP header frame_rate filed 0x%" G_GINT64_MODIFIER + "x, expected %x", val, avtprvfdepay->frame_rate); + goto end; + } + } + + r = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_RAW_COLORSPACE, &val); + g_assert (r == 0); + if (G_UNLIKELY (val != avtprvfdepay->colorspace)) { + if (!avtprvfdepay->format_fixed) { + GST_DEBUG_OBJECT (avtprvfdepay, + "Colorspace of the AVTP raw video stream 0x%" G_GINT64_MODIFIER "x", + val); + avtprvfdepay->colorspace = (guint) val; + } else { + GST_DEBUG_OBJECT (avtprvfdepay, + "Unexpected AVTP header colorspace filed 0x%" G_GINT64_MODIFIER + "x, expected %x", val, avtprvfdepay->colorspace); + goto end; + } + } + + if (G_UNLIKELY (!avtprvfdepay->format_fixed)) { + guint8 pixelDepth; + guint8 samplesPerPixels; + const guint8 samplesPerPixelsFactor = 4; //multiplied by 4 to avoid floating numbers + + pixelDepth = translate_pixel_depth (avtprvfdepay->pixel_depth); + if (!pixelDepth) { + GST_DEBUG_OBJECT (avtprvfdepay, "Unsupported pixel depth"); + goto end; + } + + switch (avtprvfdepay->pixel_format) { + case AVTP_RVF_PIXEL_FORMAT_MONO: + samplesPerPixels = 1 * samplesPerPixelsFactor; + break; + case AVTP_RVF_PIXEL_FORMAT_411: + samplesPerPixels = 1.5 * samplesPerPixelsFactor; + break; + case AVTP_RVF_PIXEL_FORMAT_420: + samplesPerPixels = 1.5 * samplesPerPixelsFactor; + break; + case AVTP_RVF_PIXEL_FORMAT_422: + samplesPerPixels = 2 * samplesPerPixelsFactor; + break; + case AVTP_RVF_PIXEL_FORMAT_444: + samplesPerPixels = 3 * samplesPerPixelsFactor; + break; + case AVTP_RVF_PIXEL_FORMAT_4224: + samplesPerPixels = 2.25 * samplesPerPixelsFactor; + break; + case AVTP_RVF_PIXEL_FORMAT_4444: + samplesPerPixels = 4 * samplesPerPixelsFactor; + break; + case AVTP_RVF_PIXEL_FORMAT_BAYER_GRBG: + case AVTP_RVF_PIXEL_FORMAT_BAYER_RGGB: + case AVTP_RVF_PIXEL_FORMAT_BAYER_BGGR: + case AVTP_RVF_PIXEL_FORMAT_BAYER_GBRG: + samplesPerPixels = 2 * samplesPerPixelsFactor; + break; + default: + GST_DEBUG_OBJECT (avtprvfdepay, "Unsupported colorspace"); + goto end; + } + + avtprvfdepay->line_size = + (avtprvfdepay->active_pixels * samplesPerPixels * pixelDepth + + (samplesPerPixelsFactor * 8 - 1)) / (samplesPerPixelsFactor * 8); + + /* Take into account AVTP raw header which is considered to be part of the payload too */ + avtprvfdepay->fragment_size = + avtprvfdepay->stream_data_length - sizeof (uint64_t); + + r = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_RAW_NUM_LINES, &val); + g_assert (r == 0); + + if (val == 0) { //large raster + avtprvfdepay->fragment_eol_size = + avtprvfdepay->line_size % avtprvfdepay->fragment_size; + avtprvfdepay->i_seq_max = + avtprvfdepay->line_size / avtprvfdepay->fragment_size; + } + + /* Video format paramaters fixed */ + avtprvfdepay->format_fixed = TRUE; + } + + *lost_packet = FALSE; + r = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_SEQ_NUM, &val); + g_assert (r == 0); + if (G_UNLIKELY (val != avtprvfdepay->seqnum)) { + GST_INFO_OBJECT (avtprvfdepay, + "Unexpected AVTP header seq num %" G_GUINT64_FORMAT ", expected %u", + val, avtprvfdepay->seqnum); + + avtprvfdepay->seqnum = val; + + *lost_packet = TRUE; + } + avtprvfdepay->seqnum++; + + result = TRUE; + +end: + return result; +} + +static void +gst_avtp_rvf_depay_get_avtp_timestamp (GstAvtpRvfDepay * avtprvfdepay, + GstMapInfo * map, GstClockTime * ts) +{ + struct avtp_stream_pdu *pdu; + guint64 avtp_time, tv; + gint res; + + pdu = (struct avtp_stream_pdu *) map->data; + + res = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_TV, &tv); + g_assert (res == 0); + + if (tv == 1) { + res = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_TIMESTAMP, &avtp_time); + g_assert (res == 0); + + *ts = avtp_time; + } else { + *ts = GST_CLOCK_TIME_NONE; + } +} + +gboolean +is_first_fragment (GstAvtpRvfDepay * avtprvfdepay, GstMapInfo * map) +{ + struct avtp_stream_pdu *pdu; + guint64 num_lines, line_number, i_seq_num; + gint res; + + pdu = (struct avtp_stream_pdu *) map->data; + + res = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_RAW_NUM_LINES, &num_lines); + g_assert (res == 0); + + res = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_RAW_LINE_NUMBER, &line_number); + g_assert (res == 0); + + + if (line_number == 1) { + if (num_lines == 0) { //large raster + res = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_RAW_I_SEQ_NUM, &i_seq_num); + g_assert (res == 0); + + if (i_seq_num == 0) + return TRUE; + } else + return TRUE; + } + return FALSE; +} + +gboolean +is_last_fragment (GstAvtpRvfDepay * avtprvfdepay, GstMapInfo * map) +{ + struct avtp_stream_pdu *pdu; + guint64 val; + gint res; + + pdu = (struct avtp_stream_pdu *) map->data; + + res = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_EF, &val); + g_assert (res == 0); + + return (gboolean) val; +} + +static void +gst_avtp_rvf_depay_get_fragment_size (GstAvtpRvfDepay * avtprvfdepay, + GstMapInfo * map, guint16 * fragment_size) +{ + struct avtp_stream_pdu *pdu; + guint64 val; + gint res; + + pdu = (struct avtp_stream_pdu *) map->data; + + res = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_RAW_NUM_LINES, &val); + g_assert (res == 0); + + if (val == 0) { //large raster + res = avtp_rvf_pdu_get (pdu, AVTP_RVF_FIELD_RAW_I_SEQ_NUM, &val); + g_assert (res == 0); + + if (val == avtprvfdepay->i_seq_max) + *fragment_size = avtprvfdepay->fragment_eol_size; + else + *fragment_size = avtprvfdepay->fragment_size; + } else { //small raster + *fragment_size = val * avtprvfdepay->line_size; + } +} + +static GstFlowReturn +gst_avtp_rvf_depay_internal_push (GstAvtpRvfDepay * avtprvfdepay, + GstBuffer * buffer, GstMapInfo * map) +{ + GstAvtpVfDepayBase *avtpvfdepaybase = GST_AVTP_VF_DEPAY_BASE (avtprvfdepay); + GstFlowReturn ret = GST_FLOW_OK; + + GST_LOG_OBJECT (avtprvfdepay, + "Adding buffer of size %" G_GSIZE_FORMAT " to out_buffer", + gst_buffer_get_size (buffer)); + + if (avtpvfdepaybase->out_buffer) { + avtpvfdepaybase->out_buffer = + gst_buffer_append (avtpvfdepaybase->out_buffer, buffer); + } else { + + /* Store only if it's a first fragment of the video frame + * drop the packet otherwise */ + if (is_first_fragment (avtprvfdepay, map)) + avtpvfdepaybase->out_buffer = buffer; + } + + /* We only truly push downstream when we get the last video buffer */ + if (is_last_fragment (avtprvfdepay, map) && avtpvfdepaybase->out_buffer) { + ret = gst_avtp_vf_depay_base_push (GST_AVTP_VF_DEPAY_BASE (avtprvfdepay)); + } + + return ret; +} + +static GstFlowReturn +gst_avtp_rvf_depay_handle_single_fragment (GstAvtpRvfDepay * avtprvfdepay, + GstBuffer * buffer, GstMapInfo * map) +{ + GstClockTime ts; + GstBuffer *fragment; + guint16 fragment_size; + + GST_DEBUG_OBJECT (avtprvfdepay, "Handling single fragment unit"); + + fragment = gst_buffer_new (); + if (G_UNLIKELY (fragment == NULL)) { + GST_ERROR_OBJECT (avtprvfdepay, "Could not allocate buffer"); + return GST_FLOW_ERROR; + } + + gst_avtp_rvf_depay_get_avtp_timestamp (avtprvfdepay, map, &ts); + gst_avtp_rvf_depay_get_fragment_size (avtprvfdepay, map, &fragment_size); + gst_buffer_copy_into (fragment, buffer, GST_BUFFER_COPY_MEMORY, + AVTP_RVF_HEADER_SIZE, fragment_size); + + GST_BUFFER_PTS (fragment) = ts; + GST_BUFFER_DTS (fragment) = ts; + + return gst_avtp_rvf_depay_internal_push (avtprvfdepay, fragment, map); +} + +static GstFlowReturn +gst_avtp_rvf_depay_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) +{ + GstAvtpRvfDepay *avtprvfdepay = GST_AVTP_RVF_DEPAY (parent); + GstFlowReturn ret = GST_FLOW_OK; + gboolean lost_packet; + GstMapInfo map; + + gst_buffer_map (buffer, &map, GST_MAP_READ); + + if (!gst_avtp_rvf_depay_validate_avtpdu (avtprvfdepay, &map, &lost_packet)) { + GST_DEBUG_OBJECT (avtprvfdepay, "Invalid AVTPDU buffer, dropping it"); + goto end; + } + if (lost_packet) { + ret = gst_avtp_rvf_depay_discard (avtprvfdepay); + if (ret != GST_FLOW_OK) + goto end; + } + + gst_avtp_rvf_depay_handle_single_fragment (avtprvfdepay, buffer, &map); + +end: + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + + return ret; +} diff --git a/subprojects/gst-plugins-bad/ext/avtp/gstavtprvfdepay.h b/subprojects/gst-plugins-bad/ext/avtp/gstavtprvfdepay.h new file mode 100644 index 0000000000..cb7fa6893d --- /dev/null +++ b/subprojects/gst-plugins-bad/ext/avtp/gstavtprvfdepay.h @@ -0,0 +1,76 @@ +/* + * GStreamer AVTP Plugin + * Copyright (c) 2021, Fastree3D + * Adrian Fiergolski + * + * This library 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 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef __GST_AVTP_RVF_DEPAY_H__ +#define __GST_AVTP_RVF_DEPAY_H__ + +#include + +#include "gstavtpvfdepaybase.h" + +G_BEGIN_DECLS +#define GST_TYPE_AVTP_RVF_DEPAY (gst_avtp_rvf_depay_get_type()) +#define GST_AVTP_RVF_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AVTP_RVF_DEPAY,GstAvtpRvfDepay)) +#define GST_AVTP_RVF_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AVTP_RVF_DEPAY,GstAvtpRvfDepayClass)) +#define GST_IS_AVTP_RVF_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AVTP_RVF_DEPAY)) +#define GST_IS_AVTP_RVF_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AVTP_RVF_DEPAY)) +typedef struct _GstAvtpRvfDepay GstAvtpRvfDepay; +typedef struct _GstAvtpRvfDepayClass GstAvtpRvfDepayClass; + +struct _GstAvtpRvfDepay +{ + GstAvtpVfDepayBaseClass vfdepayload; + + guint8 seqnum; + + gboolean format_fixed; + + guint16 active_pixels; + guint16 total_lines; + guint16 stream_data_length; + gboolean pd; + guint8 pixel_depth; + guint8 pixel_format; + guint8 frame_rate; + guint8 colorspace; + + gsize line_size; + gsize fragment_size; + gsize fragment_eol_size; + guint8 i_seq_max; +}; + +struct _GstAvtpRvfDepayClass +{ + GstAvtpVfDepayBaseClass parent_class; +}; + +GType gst_avtp_rvf_depay_get_type (void); + +GST_ELEMENT_REGISTER_DECLARE (avtprvfdepay); + +G_END_DECLS +#endif /* __GST_AVTP_RVF_DEPAY_H__ */ diff --git a/subprojects/gst-plugins-bad/ext/avtp/meson.build b/subprojects/gst-plugins-bad/ext/avtp/meson.build index 1a2fdea088..0b0da47ded 100644 --- a/subprojects/gst-plugins-bad/ext/avtp/meson.build +++ b/subprojects/gst-plugins-bad/ext/avtp/meson.build @@ -3,6 +3,7 @@ avtp_sources = [ 'gstavtpaafdepay.c', 'gstavtpaafpay.c', 'gstavtpcvfdepay.c', + 'gstavtprvfdepay.c', 'gstavtpvfdepaybase.c', 'gstavtpcvfpay.c', 'gstavtprvfpay.c',