2021-11-02 11:49:31 +00:00
|
|
|
/*
|
|
|
|
* 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>
|
2023-02-02 17:48:41 +00:00
|
|
|
* Since: 1.24
|
2021-11-02 11:49:31 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#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;
|
|
|
|
}
|