mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-19 14:56:36 +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",
|
||||
"GstAvtpCvfPay",
|
||||
"GstAvtpCvfPay!sink",
|
||||
"GstAvtpRvfPay",
|
||||
"GstAvtpRvfPay!sink",
|
||||
"GstAvtpSink",
|
||||
"GstAvtpSink!sink",
|
||||
"GstAvtpSink:address",
|
||||
|
@ -34938,6 +34940,7 @@
|
|||
"element-avtpcrfsync",
|
||||
"element-avtpcvfdepay",
|
||||
"element-avtpcvfpay",
|
||||
"element-avtprvfpay",
|
||||
"element-avtpsink",
|
||||
"element-avtpsrc",
|
||||
"element-avwait",
|
||||
|
|
|
@ -3600,6 +3600,29 @@
|
|||
},
|
||||
"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": {
|
||||
"author": "Andre Guedes <andre.guedes@intel.com>",
|
||||
"description": "Send AVTPDUs over the network",
|
||||
|
|
|
@ -149,8 +149,9 @@
|
|||
* Each element has its own configuration properties, with some being common
|
||||
* to several elements. Basic properties are:
|
||||
*
|
||||
* * streamid (avtpaafpay, avtpcvfpay, avtpaafdepay, avtpcvfdepay,
|
||||
* avtpcrfsync, avtpcrfcheck): Stream ID associated with the stream.
|
||||
* * streamid (avtpaafpay, avtprvfpay, avtpcvfpay, avtpcvfdepay,
|
||||
* avtprvfdepay, avtpcrfsync, avtpcrfcheck): Stream ID associated with the
|
||||
* stream.
|
||||
*
|
||||
* * ifname (avtpsink, avtpsrc, avtpcrfsync, avtpcrfcheck): Network interface
|
||||
* used to send/receive AVTP packets.
|
||||
|
@ -160,23 +161,23 @@
|
|||
* * priority (avtpsink): Priority used by the plugin to transmit AVTP
|
||||
* traffic.
|
||||
*
|
||||
* * mtt (avtpaafpay, avtpcvfpay): Maximum Transit Time, in nanoseconds, as
|
||||
* defined in AVTP spec.
|
||||
* * mtt (avtpaafpay, avtprvfpay, avtpcvfpay): Maximum Transit Time, in
|
||||
* nanoseconds, as defined in AVTP spec.
|
||||
*
|
||||
* * tu (avtpaafpay, avtpcvfpay): Maximum Time Uncertainty, in nanoseconds, as
|
||||
* defined in AVTP spec.
|
||||
* * tu (avtpaafpay, avtprvfpay, avtpcvfpay): Maximum Time Uncertainty, in
|
||||
* nanoseconds, as defined in AVTP spec.
|
||||
*
|
||||
* * processing-deadline (avtpaafpay, avtpcvfpay, avtpsink): Maximum amount of
|
||||
* time, in nanoseconds, that the pipeline is expected to process any
|
||||
* buffer. This value should be in sync between the one used on the
|
||||
* payloader and the sink, as this time is also taken into consideration to
|
||||
* define the correct presentation time of the packets on the AVTP listener
|
||||
* * processing-deadline (avtpaafpay, avtprvfpay, avtpcvfpay, avtpsink):
|
||||
* Maximum amount of time, in nanoseconds, that the pipeline is expected to
|
||||
* process any buffer. This value should be in sync between the one used on
|
||||
* the payloader and the sink, as this time is also taken into consideration
|
||||
* to define the correct presentation time of the packets on the AVTP listener
|
||||
* side. It should be as low as possible (zero if possible).
|
||||
*
|
||||
* * timestamp-mode (avtpaafpay): AAF timestamping mode, as defined in AVTP spec.
|
||||
*
|
||||
* * mtu (avtpcvfpay): Maximum Transmit Unit of the underlying network, used
|
||||
* to determine when to fragment a CVF packet and how big it should be.
|
||||
* * mtu (avtprvfpay, avtpcvfpay): Maximum Transmit Unit of the underlying network,
|
||||
* used to determine when to fragment a RVF/CVF packet and how big it should be.
|
||||
*
|
||||
* Check each element documentation for more details.
|
||||
*
|
||||
|
@ -241,6 +242,7 @@
|
|||
#include "gstavtpaafpay.h"
|
||||
#include "gstavtpcvfdepay.h"
|
||||
#include "gstavtpcvfpay.h"
|
||||
#include "gstavtprvfpay.h"
|
||||
#include "gstavtpsink.h"
|
||||
#include "gstavtpsrc.h"
|
||||
#include "gstavtpcrfsync.h"
|
||||
|
@ -255,6 +257,7 @@ plugin_init (GstPlugin * plugin)
|
|||
ret |= GST_ELEMENT_REGISTER (avtpaafdepay, plugin);
|
||||
ret |= GST_ELEMENT_REGISTER (avtpsink, plugin);
|
||||
ret |= GST_ELEMENT_REGISTER (avtpsrc, plugin);
|
||||
ret |= GST_ELEMENT_REGISTER (avtprvfpay, plugin);
|
||||
ret |= GST_ELEMENT_REGISTER (avtpcvfpay, plugin);
|
||||
ret |= GST_ELEMENT_REGISTER (avtpcvfdepay, 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',
|
||||
'gstavtpcvfdepay.c',
|
||||
'gstavtpcvfpay.c',
|
||||
'gstavtprvfpay.c',
|
||||
'gstavtpvfpaybase.c',
|
||||
'gstavtpbasedepayload.c',
|
||||
'gstavtpbasepayload.c',
|
||||
|
|
Loading…
Reference in a new issue