From 45d2f5a779c572b94d9dae842bca6369f92d78b6 Mon Sep 17 00:00:00 2001 From: Ederson de Souza Date: Tue, 12 Mar 2019 15:46:16 -0700 Subject: [PATCH] avtp: Introduce AVTP CVF depayloader element This patch introduces the AVTP Compressed Video Format (CVF) depayloader specified in IEEE 1722-2016 section 8. Currently, this depayloader only supports H.264 encapsulation described in section 8.5. Is also worth noting that only single NAL units are handled: aggregated and fragmented payloads are not handled. As stated in AVTP CVF payloader patch, AVTP timestamp is used to define outgoing buffer DTS, while the H264_TIMESTAMP defines outgoing buffer PTS. When an AVTP packet is received, the extracted H.264 NAL unit is added to a "stash" (the out_buffer) of H.264 NAL units. This "stash" is pushed downstream as single buffer (with NAL units aggregated according to format used on GStreamer, based on ISO/IEC 14496-15) as soon as we get the AVTP packet with M bit set. This patch groups NAL units using a fixed NAL size lenght, sent downstream on the `codec_data` capability. The "stash" of NAL units can be prematurely sent downstream if a discontinuity (a missing SEQNUM) happens. This patch reuses the infra provided by gstavtpbasedepayload.c. --- ext/avtp/Makefile.am | 2 + ext/avtp/gstavtp.c | 3 + ext/avtp/gstavtpcvfdepay.c | 539 +++++++++++++++++++++++++++++++++++++++++++++ ext/avtp/gstavtpcvfdepay.h | 63 ++++++ ext/avtp/meson.build | 1 + 5 files changed, 608 insertions(+) create mode 100644 ext/avtp/gstavtpcvfdepay.c create mode 100644 ext/avtp/gstavtpcvfdepay.h diff --git a/ext/avtp/Makefile.am b/ext/avtp/Makefile.am index 1d3eb5b..a9a248b 100644 --- a/ext/avtp/Makefile.am +++ b/ext/avtp/Makefile.am @@ -6,6 +6,7 @@ libgstavtp_la_SOURCES = \ gstavtpaafpay.c \ gstavtpbasedepayload.c \ gstavtpbasepayload.c \ + gstavtpcvfdepay.c \ gstavtpcvfpay.c \ gstavtpsink.c \ gstavtpsrc.c @@ -29,6 +30,7 @@ noinst_HEADERS = \ gstavtpaafpay.h \ gstavtpbasedepayload.h \ gstavtpbasepayload.h \ + gstavtpcvfdepay.h \ gstavtpcvfpay.h \ gstavtpsink.h \ gstavtpsrc.h diff --git a/ext/avtp/gstavtp.c b/ext/avtp/gstavtp.c index c077eb4..e7c85a0 100644 --- a/ext/avtp/gstavtp.c +++ b/ext/avtp/gstavtp.c @@ -52,6 +52,7 @@ #include "gstavtpaafdepay.h" #include "gstavtpaafpay.h" +#include "gstavtpcvfdepay.h" #include "gstavtpcvfpay.h" #include "gstavtpsink.h" #include "gstavtpsrc.h" @@ -69,6 +70,8 @@ plugin_init (GstPlugin * plugin) return FALSE; if (!gst_avtp_cvf_pay_plugin_init (plugin)) return FALSE; + if (!gst_avtp_cvf_depay_plugin_init (plugin)) + return FALSE; return TRUE; } diff --git a/ext/avtp/gstavtpcvfdepay.c b/ext/avtp/gstavtpcvfdepay.c new file mode 100644 index 0000000..05a6df0 --- /dev/null +++ b/ext/avtp/gstavtpcvfdepay.c @@ -0,0 +1,539 @@ +/* + * GStreamer AVTP Plugin + * Copyright (C) 2019 Intel Corporation + * + * 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-avtpcvfdepay + * @see_also: avtpcvfpay + * + * De-payload CVF AVTPDUs into compressed video (currently, only H.264) according + * to IEEE 1722-2016. For detailed information see + * https://standards.ieee.org/standard/1722-2016.html. + * + * + * Example pipeline + * |[ + * gst-launch-1.0 avtpsrc ! avtpcvfdepay ! decodebin ! videoconvert ! autovideosink + * ]| This example pipeline will de-payload H.264 video from the AVTPDUs, decode + * and play them. Refer to the avtpcvfpay example to payload H.264 and send the + * AVTP stream. + * + */ + +#include +#include +#include +#include + +#include "gstavtpcvfdepay.h" + +GST_DEBUG_CATEGORY_STATIC (avtpcvfdepay_debug); +#define GST_CAT_DEFAULT avtpcvfdepay_debug + +/* prototypes */ + +static GstFlowReturn gst_avtp_cvf_depay_chain (GstPad * pad, GstObject * parent, + GstBuffer * buffer); + +#define AVTP_CVF_H264_HEADER_SIZE (sizeof(struct avtp_stream_pdu) + sizeof(guint32)) +#define FU_A_HEADER_SIZE (sizeof(guint16)) +#define STAP_A_TYPE 24 +#define STAP_B_TYPE 25 +#define MTAP16_TYPE 26 +#define MTAP24_TYPE 27 +#define FU_A_TYPE 28 +#define FU_B_TYPE 29 + +#define NAL_TYPE_MASK 0x1f + +/* pad templates */ + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-h264," + " stream-format = (string) avc, alignment = (string) au") + ); + +G_DEFINE_TYPE (GstAvtpCvfDepay, gst_avtp_cvf_depay, + GST_TYPE_AVTP_BASE_DEPAYLOAD); + +static void +gst_avtp_cvf_depay_class_init (GstAvtpCvfDepayClass * klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstAvtpBaseDepayloadClass *avtpbasedepayload_class = + GST_AVTP_BASE_DEPAYLOAD_CLASS (klass); + + gst_element_class_add_static_pad_template (element_class, &src_template); + + gst_element_class_set_static_metadata (element_class, + "AVTP Compressed Video Format (CVF) depayloader", + "Codec/Depayloader/Network/AVTP", + "Extracts compressed video from CVF AVTPDUs", + "Ederson de Souza "); + + avtpbasedepayload_class->chain = GST_DEBUG_FUNCPTR (gst_avtp_cvf_depay_chain); + + GST_DEBUG_CATEGORY_INIT (avtpcvfdepay_debug, "avtpcvfdepay", + 0, "debug category for avtpcvfdepay element"); +} + +static void +gst_avtp_cvf_depay_init (GstAvtpCvfDepay * avtpcvfdepay) +{ + avtpcvfdepay->out_buffer = NULL; + avtpcvfdepay->seqnum = 0; +} + +static gboolean +gst_avtp_cvf_depay_push_caps (GstAvtpCvfDepay * avtpcvfdepay) +{ + GstAvtpBaseDepayload *avtpbasedepayload = + GST_AVTP_BASE_DEPAYLOAD (avtpcvfdepay); + GstBuffer *codec_data; + GstEvent *event; + GstMapInfo map; + GstCaps *caps; + + GST_DEBUG_OBJECT (avtpcvfdepay, "Setting src pad caps"); + + /* Send simple codec data, with only the NAL size len, no SPS/PPS. + * Below, 7 is the minimal codec_data size, when no SPS/PPS is sent */ + codec_data = gst_buffer_new_allocate (NULL, 7, NULL); + gst_buffer_map (codec_data, &map, GST_MAP_READWRITE); + + memset (map.data, 0, map.size); + map.data[0] = 1; /* version */ + map.data[4] = 0x03 | 0xfc; /* nal len size (4) - 1. Other 6 bits are 1 */ + map.data[5] = 0xe0; /* first 3 bits are 1 */ + gst_buffer_unmap (codec_data, &map); + + caps = gst_pad_get_pad_template_caps (avtpbasedepayload->srcpad); + caps = gst_caps_make_writable (caps); + gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, codec_data, NULL); + + event = gst_event_new_caps (caps); + + gst_buffer_unref (codec_data); + gst_caps_unref (caps); + + return gst_pad_push_event (avtpbasedepayload->srcpad, event); +} + +static void +gst_avtp_cvf_depay_push (GstAvtpCvfDepay * avtpcvfdepay) +{ + GstAvtpBaseDepayload *avtpbasedepayload = + GST_AVTP_BASE_DEPAYLOAD (avtpcvfdepay); + + if (G_UNLIKELY (!gst_pad_has_current_caps (avtpbasedepayload->srcpad))) { + guint64 pts_m; + guint32 dts, pts; + + if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_DEBUG) { + GstClock *clock = gst_element_get_clock (GST_ELEMENT_CAST (avtpcvfdepay)); + if (clock == NULL) { + GST_DEBUG_OBJECT (avtpcvfdepay, + "Sending initial CAPS and SEGMENT, no pipeline time."); + } else { + GST_DEBUG_OBJECT (avtpcvfdepay, + "Sending initial CAPS and SEGMENT, pipeline time: %" + GST_TIME_FORMAT, GST_TIME_ARGS (gst_clock_get_time (clock))); + } + } + + if (!gst_avtp_cvf_depay_push_caps (avtpcvfdepay)) { + GST_ELEMENT_ERROR (avtpcvfdepay, CORE, CAPS, (NULL), (NULL)); + return; + } + + if (!gst_avtp_base_depayload_push_segment_event (avtpbasedepayload, + GST_BUFFER_PTS (avtpcvfdepay->out_buffer))) { + GST_ELEMENT_ERROR (avtpcvfdepay, CORE, EVENT, + ("Could not send SEGMENT event"), (NULL)); + } + + /* Now that we sent our segment starting on the first Presentation + * time available, `avtpbasedepayload->prev_ptime` saves that value, + * to be a reference for calculating future buffer timestamps from + * the AVTP timestamps (avtp_ts and h264_ts). + * + * However, decode timestamps can be smaller than presentation + * timestamps. So we can't use `avtpbasedepayload->prev_time` as + * reference to calculate them. Instead, here, we calculate the + * first decode timestamp and save it on `avtpcvfdepay->prev_ptime`. + * + * The method used to calculate the "absolute" decode timestamp (DTS) + * from presentation timestamp is as follows: + * + * DTS = dts > pts ? (PTSm - 1) | dts : PTSm | dts + * + * Where: + * dts: 32 bits gPTP decode timestamp + * pts: 32 bits gPTP presentation timestamp + * PTSm: 32 most signifactive bits of the "absolute" presentation + * timestamp + * + * This allow us to handle cases where the pts ends up being smaller + * than dts due pts falling after an AVTP timestamp wrapping. + */ + + pts = GST_BUFFER_PTS (avtpcvfdepay->out_buffer); + dts = GST_BUFFER_DTS (avtpcvfdepay->out_buffer); + pts_m = avtpbasedepayload->prev_ptime & 0xFFFFFFFF00000000ULL; + + avtpbasedepayload->prev_ptime = dts > pts ? (pts_m -= + (1ULL << 32)) | dts : pts_m | dts; + GST_DEBUG_OBJECT (avtpcvfdepay, "prev_ptime set to %" GST_TIME_FORMAT, + GST_TIME_ARGS (avtpbasedepayload->prev_ptime)); + } + + /* At this point, we're sure segment was sent, so we can properly calc + * buffer timestamps */ + GST_DEBUG_OBJECT (avtpcvfdepay, "Converting %" GST_TIME_FORMAT " to PTS", + GST_TIME_ARGS (GST_BUFFER_PTS (avtpcvfdepay->out_buffer))); + GST_BUFFER_PTS (avtpcvfdepay->out_buffer) = + gst_avtp_base_depayload_tstamp_to_ptime (avtpbasedepayload, GST_BUFFER_PTS + (avtpcvfdepay->out_buffer), avtpbasedepayload->prev_ptime); + + GST_DEBUG_OBJECT (avtpcvfdepay, "Converting %" GST_TIME_FORMAT " to DTS", + GST_TIME_ARGS (GST_BUFFER_DTS (avtpcvfdepay->out_buffer))); + GST_BUFFER_DTS (avtpcvfdepay->out_buffer) = + gst_avtp_base_depayload_tstamp_to_ptime (avtpbasedepayload, GST_BUFFER_DTS + (avtpcvfdepay->out_buffer), avtpbasedepayload->prev_ptime); + + /* Use DTS as prev_ptime as it is smaller or equal to PTS, so that + * next calculations of PTS/DTS won't wrap too early */ + avtpbasedepayload->prev_ptime = GST_BUFFER_DTS (avtpcvfdepay->out_buffer); + + gst_pad_push (GST_AVTP_BASE_DEPAYLOAD (avtpcvfdepay)->srcpad, + avtpcvfdepay->out_buffer); + avtpcvfdepay->out_buffer = NULL; +} + +static void +gst_avtp_cvf_depay_push_and_discard (GstAvtpCvfDepay * avtpcvfdepay) +{ + /* Push everything we have, hopefully decoder can handle it */ + if (avtpcvfdepay->out_buffer != NULL) { + GST_DEBUG_OBJECT (avtpcvfdepay, "Pushing incomplete buffers"); + + gst_avtp_cvf_depay_push (avtpcvfdepay); + } +} + +static gboolean +gst_avtp_cvf_depay_validate_avtpdu (GstAvtpCvfDepay * avtpcvfdepay, + GstMapInfo * map) +{ + GstAvtpBaseDepayload *avtpbasedepayload = + GST_AVTP_BASE_DEPAYLOAD (avtpcvfdepay); + struct avtp_stream_pdu *pdu; + gboolean result = FALSE; + guint64 val; + guint val32; + gint r; + + if (G_UNLIKELY (map->size < AVTP_CVF_H264_HEADER_SIZE)) { + GST_WARNING_OBJECT (avtpcvfdepay, + "Incomplete AVTP header, expected it to have size of %zd, got %zd", + AVTP_CVF_H264_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 (G_UNLIKELY (val32 != AVTP_SUBTYPE_CVF)) { + GST_WARNING_OBJECT (avtpcvfdepay, + "Unexpected AVTP header subtype %d, expected %d", val32, + AVTP_SUBTYPE_CVF); + 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_WARNING_OBJECT (avtpcvfdepay, + "Unexpected AVTP header version %d, expected %d", val32, 0); + goto end; + } + + r = avtp_cvf_pdu_get (pdu, AVTP_CVF_FIELD_SV, &val); + g_assert (r == 0); + if (G_UNLIKELY (val != 1)) { + GST_WARNING_OBJECT (avtpcvfdepay, + "Unexpected AVTP header stream valid %ld, expected %d", val, 1); + goto end; + } + + r = avtp_cvf_pdu_get (pdu, AVTP_CVF_FIELD_STREAM_ID, &val); + g_assert (r == 0); + if (G_UNLIKELY (val != avtpbasedepayload->streamid)) { + GST_WARNING_OBJECT (avtpcvfdepay, + "Unexpected AVTP header stream id 0x%lx, expected 0x%lx", val, + avtpbasedepayload->streamid); + goto end; + } + + r = avtp_cvf_pdu_get (pdu, AVTP_CVF_FIELD_FORMAT, &val); + g_assert (r == 0); + if (G_UNLIKELY (val != AVTP_CVF_FORMAT_RFC)) { + GST_WARNING_OBJECT (avtpcvfdepay, + "Unexpected AVTP header format %ld, expected %d", val, + AVTP_CVF_FORMAT_RFC); + goto end; + } + + r = avtp_cvf_pdu_get (pdu, AVTP_CVF_FIELD_FORMAT_SUBTYPE, &val); + g_assert (r == 0); + if (G_UNLIKELY (val != AVTP_CVF_FORMAT_SUBTYPE_H264)) { + GST_WARNING_OBJECT (avtpcvfdepay, + "Unsupported AVTP header format subtype %ld", val); + goto end; + } + + r = avtp_cvf_pdu_get (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, &val); + g_assert (r == 0); + if (G_UNLIKELY (map->size < sizeof (*pdu) + val)) { + GST_WARNING_OBJECT (avtpcvfdepay, + "AVTP packet size %ld too small, expected at least %lu", + map->size - AVTP_CVF_H264_HEADER_SIZE, sizeof (*pdu) + val); + goto end; + } + + r = avtp_cvf_pdu_get (pdu, AVTP_CVF_FIELD_SEQ_NUM, &val); + g_assert (r == 0); + if (G_UNLIKELY (val != avtpcvfdepay->seqnum)) { + GST_INFO_OBJECT (avtpcvfdepay, + "Unexpected AVTP header seq num %lu, expected %u", val, + avtpcvfdepay->seqnum); + + avtpcvfdepay->seqnum = val; + /* This is not a reason to drop the packet, but it may be a good moment + * to push everything we have - maybe we lost the M packet? */ + gst_avtp_cvf_depay_push_and_discard (avtpcvfdepay); + } + avtpcvfdepay->seqnum++; + + result = TRUE; + +end: + return result; +} + +static guint8 +gst_avtp_cvf_depay_get_nal_type (GstMapInfo * map) +{ + struct avtp_stream_pdu *pdu; + struct avtp_cvf_h264_payload *pay; + guint8 nal_header, nal_type; + + pdu = (struct avtp_stream_pdu *) map->data; + pay = (struct avtp_cvf_h264_payload *) pdu->avtp_payload; + nal_header = pay->h264_data[0]; + nal_type = nal_header & NAL_TYPE_MASK; + + return nal_type; +} + +static void +gst_avtp_cvf_depay_get_avtp_timestamps (GstAvtpCvfDepay * avtpcvfdepay, + GstMapInfo * map, GstClockTime * pts, GstClockTime * dts) +{ + struct avtp_stream_pdu *pdu; + guint64 avtp_time, h264_time, tv, ptv; + gint res; + + *pts = GST_CLOCK_TIME_NONE; + *dts = GST_CLOCK_TIME_NONE; + + pdu = (struct avtp_stream_pdu *) map->data; + + res = avtp_cvf_pdu_get (pdu, AVTP_CVF_FIELD_TV, &tv); + g_assert (res == 0); + + if (tv == 1) { + res = avtp_cvf_pdu_get (pdu, AVTP_CVF_FIELD_TIMESTAMP, &avtp_time); + g_assert (res == 0); + + *dts = avtp_time; + } + + res = avtp_cvf_pdu_get (pdu, AVTP_CVF_FIELD_H264_PTV, &ptv); + g_assert (res == 0); + + if (ptv == 1) { + res = avtp_cvf_pdu_get (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, &h264_time); + g_assert (res == 0); + + *pts = h264_time; + } +} + +static void +gst_avtp_cvf_depay_internal_push (GstAvtpCvfDepay * avtpcvfdepay, + GstBuffer * buffer, gboolean M) +{ + GST_LOG_OBJECT (avtpcvfdepay, + "Adding buffer of size %lu (nalu size %lu) to out_buffer", + gst_buffer_get_size (buffer), + gst_buffer_get_size (buffer) - sizeof (guint32)); + + if (avtpcvfdepay->out_buffer) { + avtpcvfdepay->out_buffer = + gst_buffer_append (avtpcvfdepay->out_buffer, buffer); + } else { + avtpcvfdepay->out_buffer = buffer; + } + + /* We only truly push to decoder when we get the last video buffer */ + if (M) { + gst_avtp_cvf_depay_push (avtpcvfdepay); + } +} + +static void +gst_avtp_cvf_depay_get_M (GstAvtpCvfDepay * avtpcvfdepay, GstMapInfo * map, + gboolean * M) +{ + struct avtp_stream_pdu *pdu; + guint64 val; + gint res; + + pdu = (struct avtp_stream_pdu *) map->data; + + res = avtp_cvf_pdu_get (pdu, AVTP_CVF_FIELD_M, &val); + g_assert (res == 0); + + *M = val; +} + +static void +gst_avtp_cvf_depay_get_nalu_size (GstAvtpCvfDepay * avtpcvfdepay, + GstMapInfo * map, guint16 * nalu_size) +{ + struct avtp_stream_pdu *pdu; + guint64 val; + gint res; + + pdu = (struct avtp_stream_pdu *) map->data; + + res = avtp_cvf_pdu_get (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, &val); + g_assert (res == 0); + + /* We need to discount the H.264 header field */ + *nalu_size = val - sizeof (guint32); +} + +static GstFlowReturn +gst_avtp_cvf_depay_handle_fragment_a (GstAvtpCvfDepay * avtpcvfdepay, + GstBuffer * avtpdu, GstMapInfo * map) +{ + GstFlowReturn ret = GST_FLOW_OK; + + return ret; +} + +static GstFlowReturn +gst_avtp_cvf_depay_handle_single_nal (GstAvtpCvfDepay * avtpcvfdepay, + GstBuffer * avtpdu, GstMapInfo * map) +{ + GstClockTime pts, dts; + GstMapInfo map_nal; + guint16 nalu_size; + GstBuffer *nal; + gboolean M; + + GST_DEBUG_OBJECT (avtpcvfdepay, "Handling single NAL unit"); + + gst_avtp_cvf_depay_get_avtp_timestamps (avtpcvfdepay, map, &pts, &dts); + gst_avtp_cvf_depay_get_nalu_size (avtpcvfdepay, map, &nalu_size); + gst_avtp_cvf_depay_get_M (avtpcvfdepay, map, &M); + + /* Four is the number of bytes containing NALu size just before the NALu */ + nal = gst_buffer_new_allocate (NULL, 4, NULL); + gst_buffer_map (nal, &map_nal, GST_MAP_READWRITE); + + /* Add NAL size just before the NAL itself (4 bytes before it) */ + map_nal.data[0] = map_nal.data[1] = 0; + map_nal.data[2] = nalu_size >> 8; + map_nal.data[3] = nalu_size & 0xff; + gst_buffer_unmap (nal, &map_nal); + + gst_buffer_copy_into (nal, avtpdu, GST_BUFFER_COPY_MEMORY, + AVTP_CVF_H264_HEADER_SIZE, nalu_size); + GST_BUFFER_PTS (nal) = pts; + GST_BUFFER_DTS (nal) = dts; + + gst_avtp_cvf_depay_internal_push (avtpcvfdepay, nal, M); + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_avtp_cvf_depay_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) +{ + GstAvtpCvfDepay *avtpcvfdepay = GST_AVTP_CVF_DEPAY (parent); + GstFlowReturn ret = GST_FLOW_OK; + GstMapInfo map; + + gst_buffer_map (buffer, &map, GST_MAP_READ); + + if (!gst_avtp_cvf_depay_validate_avtpdu (avtpcvfdepay, &map)) { + GST_WARNING_OBJECT (avtpcvfdepay, "Invalid AVTPDU buffer, dropping it"); + goto end; + } + + switch (gst_avtp_cvf_depay_get_nal_type (&map)) { + case STAP_A_TYPE: + case STAP_B_TYPE: + case MTAP16_TYPE: + case MTAP24_TYPE: + GST_WARNING_OBJECT (avtpcvfdepay, + "AVTP aggregation packets not supported, dropping it"); + break; + case FU_A_TYPE: + ret = gst_avtp_cvf_depay_handle_fragment_a (avtpcvfdepay, buffer, &map); + break; + case FU_B_TYPE: + GST_WARNING_OBJECT (avtpcvfdepay, + "AVTP fragmentation FU-B packets not supported, dropping it"); + break; + default: + ret = gst_avtp_cvf_depay_handle_single_nal (avtpcvfdepay, buffer, &map); + break; + } + +end: + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + + return ret; +} + +gboolean +gst_avtp_cvf_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "avtpcvfdepay", GST_RANK_NONE, + GST_TYPE_AVTP_CVF_DEPAY); +} diff --git a/ext/avtp/gstavtpcvfdepay.h b/ext/avtp/gstavtpcvfdepay.h new file mode 100644 index 0000000..682e188 --- /dev/null +++ b/ext/avtp/gstavtpcvfdepay.h @@ -0,0 +1,63 @@ +/* + * GStreamer AVTP Plugin + * Copyright (C) 2019 Intel Corporation + * + * 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_CVF_DEPAY_H__ +#define __GST_AVTP_CVF_DEPAY_H__ + +#include + +#include "gstavtpbasedepayload.h" + +G_BEGIN_DECLS + +#define GST_TYPE_AVTP_CVF_DEPAY (gst_avtp_cvf_depay_get_type()) +#define GST_AVTP_CVF_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AVTP_CVF_DEPAY,GstAvtpCvfDepay)) +#define GST_AVTP_CVF_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AVTP_CVF_DEPAY,GstAvtpCvfDepayClass)) +#define GST_IS_AVTP_CVF_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AVTP_CVF_DEPAY)) +#define GST_IS_AVTP_CVF_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AVTP_CVF_DEPAY)) + +typedef struct _GstAvtpCvfDepay GstAvtpCvfDepay; +typedef struct _GstAvtpCvfDepayClass GstAvtpCvfDepayClass; + +struct _GstAvtpCvfDepay +{ + GstAvtpBaseDepayload depayload; + + guint8 seqnum; + GstBuffer *out_buffer; +}; + +struct _GstAvtpCvfDepayClass +{ + GstAvtpBaseDepayloadClass parent_class; +}; + +GType gst_avtp_cvf_depay_get_type (void); + +gboolean gst_avtp_cvf_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_AVTP_CVF_DEPAY_H__ */ diff --git a/ext/avtp/meson.build b/ext/avtp/meson.build index 2eb3347..33a9153 100644 --- a/ext/avtp/meson.build +++ b/ext/avtp/meson.build @@ -2,6 +2,7 @@ avtp_sources = [ 'gstavtp.c', 'gstavtpaafdepay.c', 'gstavtpaafpay.c', + 'gstavtpcvfdepay.c', 'gstavtpcvfpay.c', 'gstavtpbasedepayload.c', 'gstavtpbasepayload.c', -- 2.7.4