mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-04-26 06:54:49 +00:00
avtp: rvf: add AVTP RVF payload support
Add AVTP Raw Video Format payload support. The element supports only GRAY16_LE input format, so: - active pixels (no vertical blanking), - progressive mode, - 8 and 16-bit pixel depth, - mono pixel format, - grayscale colorspace. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1335>
This commit is contained in:
parent
8702a1fa67
commit
4f2fde0163
6 changed files with 667 additions and 13 deletions
|
@ -7357,6 +7357,8 @@
|
||||||
"GstAvtpCvfDepay!src",
|
"GstAvtpCvfDepay!src",
|
||||||
"GstAvtpCvfPay",
|
"GstAvtpCvfPay",
|
||||||
"GstAvtpCvfPay!sink",
|
"GstAvtpCvfPay!sink",
|
||||||
|
"GstAvtpRvfPay",
|
||||||
|
"GstAvtpRvfPay!sink",
|
||||||
"GstAvtpSink",
|
"GstAvtpSink",
|
||||||
"GstAvtpSink!sink",
|
"GstAvtpSink!sink",
|
||||||
"GstAvtpSink:address",
|
"GstAvtpSink:address",
|
||||||
|
@ -34938,6 +34940,7 @@
|
||||||
"element-avtpcrfsync",
|
"element-avtpcrfsync",
|
||||||
"element-avtpcvfdepay",
|
"element-avtpcvfdepay",
|
||||||
"element-avtpcvfpay",
|
"element-avtpcvfpay",
|
||||||
|
"element-avtprvfpay",
|
||||||
"element-avtpsink",
|
"element-avtpsink",
|
||||||
"element-avtpsrc",
|
"element-avtpsrc",
|
||||||
"element-avwait",
|
"element-avwait",
|
||||||
|
|
|
@ -3600,6 +3600,29 @@
|
||||||
},
|
},
|
||||||
"rank": "none"
|
"rank": "none"
|
||||||
},
|
},
|
||||||
|
"avtprvfpay": {
|
||||||
|
"author": "Adrian Fiergolski <Adrian.Fiergolski@fastree3d.com>",
|
||||||
|
"description": "Payload-encode raw video into RVF AVTPDU (IEEE 1722)",
|
||||||
|
"hierarchy": [
|
||||||
|
"GstAvtpRvfPay",
|
||||||
|
"GstAvtpVfPayBase",
|
||||||
|
"GstAvtpBasePayload",
|
||||||
|
"GstElement",
|
||||||
|
"GstObject",
|
||||||
|
"GInitiallyUnowned",
|
||||||
|
"GObject"
|
||||||
|
],
|
||||||
|
"klass": "Codec/Payloader/Network/AVTP",
|
||||||
|
"long-name": "AVTP Raw Video Format (RVF) payloader",
|
||||||
|
"pad-templates": {
|
||||||
|
"sink": {
|
||||||
|
"caps": "video/x-raw:\n format: { GRAY16_LE }\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n framerate: [ 0/1, 2147483647/1 ]\n",
|
||||||
|
"direction": "sink",
|
||||||
|
"presence": "always"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rank": "none"
|
||||||
|
},
|
||||||
"avtpsink": {
|
"avtpsink": {
|
||||||
"author": "Andre Guedes <andre.guedes@intel.com>",
|
"author": "Andre Guedes <andre.guedes@intel.com>",
|
||||||
"description": "Send AVTPDUs over the network",
|
"description": "Send AVTPDUs over the network",
|
||||||
|
|
|
@ -149,8 +149,9 @@
|
||||||
* Each element has its own configuration properties, with some being common
|
* Each element has its own configuration properties, with some being common
|
||||||
* to several elements. Basic properties are:
|
* to several elements. Basic properties are:
|
||||||
*
|
*
|
||||||
* * streamid (avtpaafpay, avtpcvfpay, avtpaafdepay, avtpcvfdepay,
|
* * streamid (avtpaafpay, avtprvfpay, avtpcvfpay, avtpcvfdepay,
|
||||||
* avtpcrfsync, avtpcrfcheck): Stream ID associated with the stream.
|
* avtprvfdepay, avtpcrfsync, avtpcrfcheck): Stream ID associated with the
|
||||||
|
* stream.
|
||||||
*
|
*
|
||||||
* * ifname (avtpsink, avtpsrc, avtpcrfsync, avtpcrfcheck): Network interface
|
* * ifname (avtpsink, avtpsrc, avtpcrfsync, avtpcrfcheck): Network interface
|
||||||
* used to send/receive AVTP packets.
|
* used to send/receive AVTP packets.
|
||||||
|
@ -160,23 +161,23 @@
|
||||||
* * priority (avtpsink): Priority used by the plugin to transmit AVTP
|
* * priority (avtpsink): Priority used by the plugin to transmit AVTP
|
||||||
* traffic.
|
* traffic.
|
||||||
*
|
*
|
||||||
* * mtt (avtpaafpay, avtpcvfpay): Maximum Transit Time, in nanoseconds, as
|
* * mtt (avtpaafpay, avtprvfpay, avtpcvfpay): Maximum Transit Time, in
|
||||||
* defined in AVTP spec.
|
* nanoseconds, as defined in AVTP spec.
|
||||||
*
|
*
|
||||||
* * tu (avtpaafpay, avtpcvfpay): Maximum Time Uncertainty, in nanoseconds, as
|
* * tu (avtpaafpay, avtprvfpay, avtpcvfpay): Maximum Time Uncertainty, in
|
||||||
* defined in AVTP spec.
|
* nanoseconds, as defined in AVTP spec.
|
||||||
*
|
*
|
||||||
* * processing-deadline (avtpaafpay, avtpcvfpay, avtpsink): Maximum amount of
|
* * processing-deadline (avtpaafpay, avtprvfpay, avtpcvfpay, avtpsink):
|
||||||
* time, in nanoseconds, that the pipeline is expected to process any
|
* Maximum amount of time, in nanoseconds, that the pipeline is expected to
|
||||||
* buffer. This value should be in sync between the one used on the
|
* process any buffer. This value should be in sync between the one used on
|
||||||
* payloader and the sink, as this time is also taken into consideration to
|
* the payloader and the sink, as this time is also taken into consideration
|
||||||
* define the correct presentation time of the packets on the AVTP listener
|
* to define the correct presentation time of the packets on the AVTP listener
|
||||||
* side. It should be as low as possible (zero if possible).
|
* side. It should be as low as possible (zero if possible).
|
||||||
*
|
*
|
||||||
* * timestamp-mode (avtpaafpay): AAF timestamping mode, as defined in AVTP spec.
|
* * timestamp-mode (avtpaafpay): AAF timestamping mode, as defined in AVTP spec.
|
||||||
*
|
*
|
||||||
* * mtu (avtpcvfpay): Maximum Transmit Unit of the underlying network, used
|
* * mtu (avtprvfpay, avtpcvfpay): Maximum Transmit Unit of the underlying network,
|
||||||
* to determine when to fragment a CVF packet and how big it should be.
|
* used to determine when to fragment a RVF/CVF packet and how big it should be.
|
||||||
*
|
*
|
||||||
* Check each element documentation for more details.
|
* Check each element documentation for more details.
|
||||||
*
|
*
|
||||||
|
@ -241,6 +242,7 @@
|
||||||
#include "gstavtpaafpay.h"
|
#include "gstavtpaafpay.h"
|
||||||
#include "gstavtpcvfdepay.h"
|
#include "gstavtpcvfdepay.h"
|
||||||
#include "gstavtpcvfpay.h"
|
#include "gstavtpcvfpay.h"
|
||||||
|
#include "gstavtprvfpay.h"
|
||||||
#include "gstavtpsink.h"
|
#include "gstavtpsink.h"
|
||||||
#include "gstavtpsrc.h"
|
#include "gstavtpsrc.h"
|
||||||
#include "gstavtpcrfsync.h"
|
#include "gstavtpcrfsync.h"
|
||||||
|
@ -255,6 +257,7 @@ plugin_init (GstPlugin * plugin)
|
||||||
ret |= GST_ELEMENT_REGISTER (avtpaafdepay, plugin);
|
ret |= GST_ELEMENT_REGISTER (avtpaafdepay, plugin);
|
||||||
ret |= GST_ELEMENT_REGISTER (avtpsink, plugin);
|
ret |= GST_ELEMENT_REGISTER (avtpsink, plugin);
|
||||||
ret |= GST_ELEMENT_REGISTER (avtpsrc, plugin);
|
ret |= GST_ELEMENT_REGISTER (avtpsrc, plugin);
|
||||||
|
ret |= GST_ELEMENT_REGISTER (avtprvfpay, plugin);
|
||||||
ret |= GST_ELEMENT_REGISTER (avtpcvfpay, plugin);
|
ret |= GST_ELEMENT_REGISTER (avtpcvfpay, plugin);
|
||||||
ret |= GST_ELEMENT_REGISTER (avtpcvfdepay, plugin);
|
ret |= GST_ELEMENT_REGISTER (avtpcvfdepay, plugin);
|
||||||
ret |= GST_ELEMENT_REGISTER (avtpcrfsync, plugin);
|
ret |= GST_ELEMENT_REGISTER (avtpcrfsync, plugin);
|
||||||
|
|
552
subprojects/gst-plugins-bad/ext/avtp/gstavtprvfpay.c
Normal file
552
subprojects/gst-plugins-bad/ext/avtp/gstavtprvfpay.c
Normal file
|
@ -0,0 +1,552 @@
|
||||||
|
/*
|
||||||
|
* GStreamer AVTP Plugin
|
||||||
|
* Copyright (c) 2021, Fastree3D
|
||||||
|
* Adrian Fiergolski <Adrian.Fiergolski@fastree3d.com>
|
||||||
|
*
|
||||||
|
* 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-avtprvfpay
|
||||||
|
* @see_also: avtprvfdepay
|
||||||
|
*
|
||||||
|
* Payload raw video into AVTPDUs according
|
||||||
|
* to IEEE 1722-2016. For detailed information see
|
||||||
|
* https://standards.ieee.org/standard/1722-2016.html.
|
||||||
|
*
|
||||||
|
* <refsect2>
|
||||||
|
* <title>Example pipeline</title>
|
||||||
|
* |[
|
||||||
|
* gst-launch-1.0 videotestsrc ! avtprvfpay ! avtpsink
|
||||||
|
* ]| This example pipeline will payload raw video. Refer to the avtprvfdepay
|
||||||
|
* example to depayload and play the AVTP stream.
|
||||||
|
* </refsect2>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <avtp.h>
|
||||||
|
#include <avtp_rvf.h>
|
||||||
|
|
||||||
|
#include <gst/video/video.h>
|
||||||
|
#include "gstavtprvfpay.h"
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_STATIC (avtprvfpay_debug);
|
||||||
|
#define GST_CAT_DEFAULT avtprvfpay_debug
|
||||||
|
|
||||||
|
/* prototypes */
|
||||||
|
static GstStateChangeReturn gst_avtp_rvf_change_state (GstElement *
|
||||||
|
element, GstStateChange transition);
|
||||||
|
|
||||||
|
static gboolean gst_avtp_rvf_pay_new_caps (GstAvtpVfPayBase * avtpvfpaybase,
|
||||||
|
GstCaps * caps);
|
||||||
|
static gboolean gst_avtp_rvf_pay_prepare_avtp_packets (GstAvtpVfPayBase *
|
||||||
|
avtpvfpaybase, GstBuffer * buffer, GPtrArray * avtp_packets);
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_0,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define AVTP_RVF_HEADER_SIZE (sizeof(struct avtp_stream_pdu) + sizeof(uint64_t))
|
||||||
|
|
||||||
|
/* pad templates */
|
||||||
|
|
||||||
|
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||||
|
GST_PAD_SINK,
|
||||||
|
GST_PAD_ALWAYS,
|
||||||
|
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{GRAY16_LE}"))
|
||||||
|
);
|
||||||
|
|
||||||
|
/* class initialization */
|
||||||
|
|
||||||
|
#define gst_avtp_rvf_pay_parent_class parent_class
|
||||||
|
G_DEFINE_TYPE (GstAvtpRvfPay, gst_avtp_rvf_pay, GST_TYPE_AVTP_VF_PAY_BASE);
|
||||||
|
GST_ELEMENT_REGISTER_DEFINE (avtprvfpay, "avtprvfpay", GST_RANK_NONE,
|
||||||
|
GST_TYPE_AVTP_RVF_PAY);
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_avtp_rvf_pay_class_init (GstAvtpRvfPayClass * klass)
|
||||||
|
{
|
||||||
|
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
|
||||||
|
GstAvtpVfPayBaseClass *avtpvfpaybase_class =
|
||||||
|
GST_AVTP_VF_PAY_BASE_CLASS (klass);
|
||||||
|
|
||||||
|
gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
|
||||||
|
|
||||||
|
gst_element_class_set_static_metadata (gstelement_class,
|
||||||
|
"AVTP Raw Video Format (RVF) payloader",
|
||||||
|
"Codec/Payloader/Network/AVTP",
|
||||||
|
"Payload-encode raw video into RVF AVTPDU (IEEE 1722)",
|
||||||
|
"Adrian Fiergolski <Adrian.Fiergolski@fastree3d.com>");
|
||||||
|
|
||||||
|
gstelement_class->change_state =
|
||||||
|
GST_DEBUG_FUNCPTR (gst_avtp_rvf_change_state);
|
||||||
|
|
||||||
|
avtpvfpaybase_class->new_caps = GST_DEBUG_FUNCPTR (gst_avtp_rvf_pay_new_caps);
|
||||||
|
avtpvfpaybase_class->prepare_avtp_packets =
|
||||||
|
GST_DEBUG_FUNCPTR (gst_avtp_rvf_pay_prepare_avtp_packets);
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_INIT (avtprvfpay_debug, "avtprvfpay",
|
||||||
|
0, "debug category for avtprvfpay element");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_avtp_rvf_pay_init (GstAvtpRvfPay * avtprvfpay)
|
||||||
|
{
|
||||||
|
avtprvfpay->header = NULL;
|
||||||
|
/* size of the payload */
|
||||||
|
avtprvfpay->fragment_size = 0;
|
||||||
|
/* large raster: number of data bytes for a fragment at the end of line */
|
||||||
|
avtprvfpay->fragment_eol_size = 0;
|
||||||
|
avtprvfpay->fragment_padding = NULL;
|
||||||
|
avtprvfpay->num_lines = 0;
|
||||||
|
/* size of the line in bytes */
|
||||||
|
avtprvfpay->line_size = 0;
|
||||||
|
/* large raster: maximum i_seq_num */
|
||||||
|
avtprvfpay->i_seq_max = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_avtp_rvf_pay_new_caps (GstAvtpVfPayBase * avtpvfpaybase, GstCaps * caps)
|
||||||
|
{
|
||||||
|
GstAvtpRvfPay *avtprvfpay = GST_AVTP_RVF_PAY (avtpvfpaybase);
|
||||||
|
GstMapInfo map;
|
||||||
|
struct avtp_stream_pdu *pdu;
|
||||||
|
|
||||||
|
GstVideoInfo info;
|
||||||
|
unsigned int fps_up, fps_down;
|
||||||
|
|
||||||
|
gboolean ret = FALSE;
|
||||||
|
|
||||||
|
gsize fragment_padding_size;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (avtprvfpay, "gst_avtp_rvf_pay_new_caps");
|
||||||
|
|
||||||
|
gst_buffer_map (avtprvfpay->header, &map, GST_MAP_WRITE);
|
||||||
|
pdu = (struct avtp_stream_pdu *) map.data;
|
||||||
|
|
||||||
|
if (!gst_video_info_from_caps (&info, caps)) {
|
||||||
|
GST_ERROR_OBJECT (avtprvfpay,
|
||||||
|
"Can't retrieve the video information from caps");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_ACTIVE_PIXELS, info.width);
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_TOTAL_LINES, info.height);
|
||||||
|
|
||||||
|
switch (info.interlace_mode) {
|
||||||
|
case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_I, 0);
|
||||||
|
break;
|
||||||
|
/* to-do: support for interleaved modes */
|
||||||
|
case GST_VIDEO_INTERLACE_MODE_INTERLEAVED:
|
||||||
|
case GST_VIDEO_INTERLACE_MODE_FIELDS:
|
||||||
|
case GST_VIDEO_INTERLACE_MODE_ALTERNATE:
|
||||||
|
default:
|
||||||
|
GST_ERROR_OBJECT (avtprvfpay, "Unsupported interlace mode");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_F, 0);
|
||||||
|
|
||||||
|
switch (*info.finfo->depth) {
|
||||||
|
case 8:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_PIXEL_DEPTH,
|
||||||
|
AVTP_RVF_PIXEL_DEPTH_8);
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_PIXEL_DEPTH,
|
||||||
|
AVTP_RVF_PIXEL_DEPTH_16);
|
||||||
|
break;
|
||||||
|
/* to-do: add support for 10 and 12 bit pixel depth
|
||||||
|
* it requires shifting of the buffer data */
|
||||||
|
case 10:
|
||||||
|
case 12:
|
||||||
|
default:
|
||||||
|
GST_ERROR_OBJECT (avtprvfpay, "Unsupported pixel depth");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.finfo->n_planes != 1) {
|
||||||
|
GST_ERROR_OBJECT (avtprvfpay, "Planar formats are not supported");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
//All pixels are active
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_AP, 1);
|
||||||
|
|
||||||
|
switch (info.finfo->format) {
|
||||||
|
case GST_VIDEO_FORMAT_GRAY16_LE:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_PIXEL_FORMAT,
|
||||||
|
AVTP_RVF_PIXEL_FORMAT_MONO);
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_COLORSPACE,
|
||||||
|
AVTP_RVF_COLORSPACE_GRAY);
|
||||||
|
break;
|
||||||
|
/* to-do: support more formats */
|
||||||
|
default:
|
||||||
|
GST_ERROR_OBJECT (avtprvfpay, "Unsupported video format");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
avtprvfpay->line_size =
|
||||||
|
(info.finfo->n_components * (*info.finfo->depth) * info.width) / 8;
|
||||||
|
//video_data_payload field for large rasters
|
||||||
|
if (avtprvfpay->line_size > avtpvfpaybase->mtu - AVTP_RVF_HEADER_SIZE) {
|
||||||
|
avtprvfpay->num_lines = 0;
|
||||||
|
|
||||||
|
avtprvfpay->fragment_size = avtpvfpaybase->mtu - AVTP_RVF_HEADER_SIZE;
|
||||||
|
avtprvfpay->fragment_eol_size =
|
||||||
|
avtprvfpay->line_size % avtprvfpay->fragment_size;
|
||||||
|
avtprvfpay->i_seq_max = avtprvfpay->line_size / avtprvfpay->fragment_size;
|
||||||
|
|
||||||
|
fragment_padding_size =
|
||||||
|
avtprvfpay->fragment_size - avtprvfpay->fragment_eol_size;
|
||||||
|
}
|
||||||
|
//video_data_payload field for small rasters
|
||||||
|
else {
|
||||||
|
//only full lines
|
||||||
|
avtprvfpay->num_lines =
|
||||||
|
(avtpvfpaybase->mtu - AVTP_RVF_HEADER_SIZE) / avtprvfpay->line_size;
|
||||||
|
|
||||||
|
//Full video frame is smaller than MTU
|
||||||
|
if (avtprvfpay->num_lines > info.height)
|
||||||
|
avtprvfpay->num_lines = info.height;
|
||||||
|
|
||||||
|
//num_lines field is 4 bit only
|
||||||
|
if (avtprvfpay->num_lines > 15)
|
||||||
|
avtprvfpay->num_lines = 15;
|
||||||
|
|
||||||
|
avtprvfpay->fragment_size = avtprvfpay->num_lines * avtprvfpay->line_size;
|
||||||
|
avtprvfpay->fragment_eol_size = 0;
|
||||||
|
avtprvfpay->i_seq_max = 0;
|
||||||
|
|
||||||
|
fragment_padding_size =
|
||||||
|
avtprvfpay->fragment_size -
|
||||||
|
((info.height % avtprvfpay->num_lines) * avtprvfpay->line_size);
|
||||||
|
}
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_NUM_LINES, avtprvfpay->num_lines);
|
||||||
|
|
||||||
|
//FPS
|
||||||
|
fps_down = info.fps_n / info.fps_d; //Round down
|
||||||
|
fps_up = (info.fps_n + (info.fps_d - 1)) / info.fps_d; //Round up
|
||||||
|
|
||||||
|
if (fps_down == fps_up) {
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_PD, 0);
|
||||||
|
} else {
|
||||||
|
if ((info.fps_n * 1001) == info.fps_d * 1000 * fps_up)
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_PD, 1);
|
||||||
|
else
|
||||||
|
GST_ERROR_OBJECT (avtprvfpay, "Unsupported frame rate");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (fps_up) {
|
||||||
|
case 1:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_1);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_2);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_5);
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_10);
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_15);
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_20);
|
||||||
|
break;
|
||||||
|
case 24:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_24);
|
||||||
|
break;
|
||||||
|
case 25:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_25);
|
||||||
|
break;
|
||||||
|
case 30:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_30);
|
||||||
|
break;
|
||||||
|
case 48:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_48);
|
||||||
|
break;
|
||||||
|
case 50:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_50);
|
||||||
|
break;
|
||||||
|
case 60:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_60);
|
||||||
|
break;
|
||||||
|
case 72:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_72);
|
||||||
|
break;
|
||||||
|
case 85:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_85);
|
||||||
|
break;
|
||||||
|
case 100:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_100);
|
||||||
|
break;
|
||||||
|
case 120:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_120);
|
||||||
|
break;
|
||||||
|
case 150:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_150);
|
||||||
|
break;
|
||||||
|
case 200:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_200);
|
||||||
|
break;
|
||||||
|
case 240:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_240);
|
||||||
|
break;
|
||||||
|
case 300:
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
|
||||||
|
AVTP_RVF_FRAME_RATE_300);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
GST_ERROR_OBJECT (avtprvfpay, "Unsupported frame rate");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
//padding bytes
|
||||||
|
avtprvfpay->fragment_padding = gst_buffer_new_allocate (NULL,
|
||||||
|
fragment_padding_size, NULL);
|
||||||
|
if (G_UNLIKELY (avtprvfpay->fragment_padding == NULL)) {
|
||||||
|
GST_ERROR_OBJECT (avtprvfpay,
|
||||||
|
"Could not allocate memory for padding bytes");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
gst_buffer_memset (avtprvfpay->fragment_padding, 0, 0U,
|
||||||
|
fragment_padding_size);
|
||||||
|
|
||||||
|
ret = TRUE;
|
||||||
|
|
||||||
|
error:
|
||||||
|
gst_buffer_unmap (avtprvfpay->header, &map);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstStateChangeReturn
|
||||||
|
gst_avtp_rvf_change_state (GstElement * element, GstStateChange transition)
|
||||||
|
{
|
||||||
|
GstAvtpRvfPay *avtprvfpay = GST_AVTP_RVF_PAY (element);
|
||||||
|
GstAvtpBasePayload *avtpbasepayload = GST_AVTP_BASE_PAYLOAD (avtprvfpay);
|
||||||
|
GstStateChangeReturn ret;
|
||||||
|
|
||||||
|
if (transition == GST_STATE_CHANGE_NULL_TO_READY) {
|
||||||
|
GstMapInfo map;
|
||||||
|
struct avtp_stream_pdu *pdu;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
avtprvfpay->header = gst_buffer_new_allocate (NULL,
|
||||||
|
AVTP_RVF_HEADER_SIZE, NULL);
|
||||||
|
if (avtprvfpay->header == NULL) {
|
||||||
|
GST_ERROR_OBJECT (avtprvfpay, "Could not allocate buffer");
|
||||||
|
return GST_STATE_CHANGE_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_buffer_map (avtprvfpay->header, &map, GST_MAP_WRITE);
|
||||||
|
pdu = (struct avtp_stream_pdu *) map.data;
|
||||||
|
|
||||||
|
res = avtp_rvf_pdu_init (pdu);
|
||||||
|
g_assert (res == 0);
|
||||||
|
|
||||||
|
res =
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_STREAM_ID,
|
||||||
|
avtpbasepayload->streamid);
|
||||||
|
g_assert (res == 0);
|
||||||
|
|
||||||
|
gst_buffer_unmap (avtprvfpay->header, &map);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||||
|
if (ret == GST_STATE_CHANGE_FAILURE) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
|
||||||
|
gst_buffer_unref (avtprvfpay->header);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checks if stream is large raster video (see: 12.2.9) */
|
||||||
|
static gboolean
|
||||||
|
is_large_raster (GstAvtpRvfPay * avtprvfpay)
|
||||||
|
{
|
||||||
|
return avtprvfpay->num_lines == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_avtp_rvf_pay_prepare_avtp_packets (GstAvtpVfPayBase * avtpvfpaybase,
|
||||||
|
GstBuffer * buffer, GPtrArray * avtp_packets)
|
||||||
|
{
|
||||||
|
GstAvtpBasePayload *avtpbasepayload = GST_AVTP_BASE_PAYLOAD (avtpvfpaybase);
|
||||||
|
GstAvtpRvfPay *avtprvfpay = GST_AVTP_RVF_PAY (avtpvfpaybase);
|
||||||
|
GstBuffer *header;
|
||||||
|
GstMapInfo map;
|
||||||
|
guint64 avtp_time;
|
||||||
|
gsize offset, buffer_size;
|
||||||
|
gsize i_seq_num, line_number;
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (avtprvfpay,
|
||||||
|
"Preparing AVTP packets for video frame whose size is %" G_GSIZE_FORMAT,
|
||||||
|
gst_buffer_get_size (buffer));
|
||||||
|
|
||||||
|
/* Calculate timestamps using PTS as base
|
||||||
|
* - code inherited from avtpbasepayload.
|
||||||
|
* Also worth noting: `avtpbasepayload->latency` is updated after
|
||||||
|
* first call to gst_avtp_base_payload_calc_ptime, so we MUST call
|
||||||
|
* it before using the latency value */
|
||||||
|
avtp_time = gst_avtp_base_payload_calc_ptime (avtpbasepayload, buffer);
|
||||||
|
|
||||||
|
offset = 0;
|
||||||
|
buffer_size = gst_buffer_get_size (buffer);
|
||||||
|
i_seq_num = 0;
|
||||||
|
line_number = 1;
|
||||||
|
while (offset != buffer_size) {
|
||||||
|
GstBuffer *packet;
|
||||||
|
struct avtp_stream_pdu *pdu;
|
||||||
|
gint res;
|
||||||
|
GstBuffer *fragment;
|
||||||
|
gsize fragment_size;
|
||||||
|
|
||||||
|
/* Copy header to reuse common fields and change what is needed */
|
||||||
|
header = gst_buffer_copy (avtprvfpay->header);
|
||||||
|
gst_buffer_map (header, &map, GST_MAP_WRITE);
|
||||||
|
pdu = (struct avtp_stream_pdu *) map.data;
|
||||||
|
|
||||||
|
/* Prepare the fragment */
|
||||||
|
if (is_large_raster (avtprvfpay)) {
|
||||||
|
if (i_seq_num == avtprvfpay->i_seq_max)
|
||||||
|
fragment_size = avtprvfpay->fragment_eol_size;
|
||||||
|
else
|
||||||
|
fragment_size = avtprvfpay->fragment_size;
|
||||||
|
} else {
|
||||||
|
guint reamaining_size = buffer_size - offset;
|
||||||
|
if (reamaining_size < avtprvfpay->fragment_size)
|
||||||
|
fragment_size = reamaining_size;
|
||||||
|
else
|
||||||
|
fragment_size = avtprvfpay->fragment_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment = gst_buffer_copy_region (buffer,
|
||||||
|
GST_BUFFER_COPY_MEMORY, offset, fragment_size);
|
||||||
|
|
||||||
|
offset += fragment_size;
|
||||||
|
|
||||||
|
/* video_data_payload is always the same size
|
||||||
|
* so add padding bytes if needed */
|
||||||
|
if (fragment_size != avtprvfpay->fragment_size) {
|
||||||
|
fragment = gst_buffer_append (fragment, avtprvfpay->fragment_padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (avtprvfpay,
|
||||||
|
"Generated fragment with size %" G_GSIZE_FORMAT,
|
||||||
|
avtprvfpay->fragment_size);
|
||||||
|
|
||||||
|
/* Stream data len includes AVTP raw header len as this is part of
|
||||||
|
* the payload too. It's just the uint64_t */
|
||||||
|
res =
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_STREAM_DATA_LEN,
|
||||||
|
avtprvfpay->fragment_size + sizeof (uint64_t));
|
||||||
|
g_assert (res == 0);
|
||||||
|
|
||||||
|
res =
|
||||||
|
avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_SEQ_NUM,
|
||||||
|
avtpbasepayload->seqnum++);
|
||||||
|
g_assert (res == 0);
|
||||||
|
|
||||||
|
/* AVTP_TS fields */
|
||||||
|
if ((is_large_raster (avtprvfpay) && i_seq_num == 0)
|
||||||
|
|| (!is_large_raster (avtprvfpay)
|
||||||
|
&& line_number == 1)) {
|
||||||
|
res = avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_TV, 1);
|
||||||
|
g_assert (res == 0);
|
||||||
|
|
||||||
|
res = avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_TIMESTAMP, avtp_time);
|
||||||
|
g_assert (res == 0);
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (avtprvfpay, "TV packet sent, PTS: %" GST_TIME_FORMAT
|
||||||
|
" DTS: %" GST_TIME_FORMAT " AVTP_TS: %" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (avtp_time),
|
||||||
|
GST_TIME_ARGS (GST_BUFFER_DTS (buffer)),
|
||||||
|
GST_TIME_ARGS (avtp_time & 0xffffffff));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set ef */
|
||||||
|
if (offset == buffer_size) { //last fragment
|
||||||
|
res = avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_EF, 1);
|
||||||
|
g_assert (res == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set line_number */
|
||||||
|
res = avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_LINE_NUMBER, line_number);
|
||||||
|
g_assert (res == 0);
|
||||||
|
|
||||||
|
if (is_large_raster (avtprvfpay)) {
|
||||||
|
if (i_seq_num == avtprvfpay->i_seq_max)
|
||||||
|
line_number++;
|
||||||
|
} else {
|
||||||
|
line_number += avtprvfpay->num_lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle i_seq_num only for large raster */
|
||||||
|
if (is_large_raster (avtprvfpay)) {
|
||||||
|
res = avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_I_SEQ_NUM, i_seq_num);
|
||||||
|
g_assert (res == 0);
|
||||||
|
|
||||||
|
if (i_seq_num < avtprvfpay->i_seq_max)
|
||||||
|
i_seq_num++;
|
||||||
|
else
|
||||||
|
i_seq_num = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
packet = gst_buffer_append (header, fragment);
|
||||||
|
|
||||||
|
/* Keep original timestamps */
|
||||||
|
GST_BUFFER_PTS (packet) = GST_BUFFER_PTS (buffer);
|
||||||
|
GST_BUFFER_DTS (packet) = GST_BUFFER_DTS (buffer);
|
||||||
|
|
||||||
|
g_ptr_array_add (avtp_packets, packet);
|
||||||
|
|
||||||
|
gst_buffer_unmap (header, &map);
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (avtprvfpay, "Prepared %u AVTP packets", avtp_packets->len);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
72
subprojects/gst-plugins-bad/ext/avtp/gstavtprvfpay.h
Normal file
72
subprojects/gst-plugins-bad/ext/avtp/gstavtprvfpay.h
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* GStreamer AVTP Plugin
|
||||||
|
* Copyright (c) 2021, Fastree3D
|
||||||
|
* Adrian Fiergolski <Adrian.Fiergolski@fastree3d.com>
|
||||||
|
*
|
||||||
|
* 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_PAY_H__
|
||||||
|
#define __GST_AVTP_RVF_PAY_H__
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
|
||||||
|
#include "gstavtpvfpaybase.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
#define GST_TYPE_AVTP_RVF_PAY (gst_avtp_rvf_pay_get_type())
|
||||||
|
#define GST_AVTP_RVF_PAY(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AVTP_RVF_PAY,GstAvtpRvfPay))
|
||||||
|
#define GST_AVTP_RVF_PAY_CLASS(klass) \
|
||||||
|
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AVTP_RVF_PAY,GstAvtpRvfPayClass))
|
||||||
|
#define GST_IS_AVTP_RVF_PAY(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AVTP_RVF_PAY))
|
||||||
|
#define GST_IS_AVTP_RVF_PAY_CLASS(klass) \
|
||||||
|
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AVTP_RVF_PAY))
|
||||||
|
typedef struct _GstAvtpRvfPay GstAvtpRvfPay;
|
||||||
|
typedef struct _GstAvtpRvfPayClass GstAvtpRvfPayClass;
|
||||||
|
|
||||||
|
struct _GstAvtpRvfPay
|
||||||
|
{
|
||||||
|
GstAvtpVfPayBase vfbase;
|
||||||
|
|
||||||
|
GstBuffer *header;
|
||||||
|
//size of the buffer fragment
|
||||||
|
gsize fragment_size;
|
||||||
|
//large raster: size of the end of line fragment
|
||||||
|
gsize fragment_eol_size;
|
||||||
|
//padding bytes appended to the last fragment of the frame
|
||||||
|
GstBuffer *fragment_padding;
|
||||||
|
//number of lines per fragment
|
||||||
|
guint num_lines;
|
||||||
|
//size of the single line
|
||||||
|
gsize line_size;
|
||||||
|
//maximum value of i_seq_num
|
||||||
|
guint8 i_seq_max;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GstAvtpRvfPayClass
|
||||||
|
{
|
||||||
|
GstAvtpVfPayBaseClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
GType gst_avtp_rvf_pay_get_type (void);
|
||||||
|
|
||||||
|
GST_ELEMENT_REGISTER_DECLARE (avtprvfpay);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
#endif /* __GST_AVTP_RVF_PAY_H__ */
|
|
@ -4,6 +4,7 @@ avtp_sources = [
|
||||||
'gstavtpaafpay.c',
|
'gstavtpaafpay.c',
|
||||||
'gstavtpcvfdepay.c',
|
'gstavtpcvfdepay.c',
|
||||||
'gstavtpcvfpay.c',
|
'gstavtpcvfpay.c',
|
||||||
|
'gstavtprvfpay.c',
|
||||||
'gstavtpvfpaybase.c',
|
'gstavtpvfpaybase.c',
|
||||||
'gstavtpbasedepayload.c',
|
'gstavtpbasedepayload.c',
|
||||||
'gstavtpbasepayload.c',
|
'gstavtpbasepayload.c',
|
||||||
|
|
Loading…
Reference in a new issue