mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-18 14:26:43 +00:00
12838af353
TSN streams are expected to send packets to the network in a well defined "pace", which is arbitrarily defined for each stream. This pace is defined by the "measurement interval" property of a stream. When the AVTP CVF payloader element - avtpcvfpay - fragments a video frame that is too big to be sent to the network, it currently defines that all fragments should be transmitted at the same time (via DTS property of GstBuffers generated, as sink will use those to time the transmission of the AVTPDU). This doesn't comply with stream definition, which also has a limit on how many packets can be sent on a given measurement interval. This patch solves that by spreading in time the DTS of the GstBuffers containing the AVTPDUs. Two new properties, "measurement-interval" and "max-interval-frames", added to avptcvfpay element so that it knows stream measurement interval and how many AVTPDUs it can send on any of them. More details on the method used to proper spread DTS/PTS according to measurement interval can be found in a code commentary inside this patch. Tests also added for the new property and behaviour. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1004>
773 lines
26 KiB
C
773 lines
26 KiB
C
/*
|
|
* 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
|
|
*/
|
|
|
|
#include <gst/check/gstcheck.h>
|
|
#include <gst/check/gstharness.h>
|
|
|
|
#include <avtp.h>
|
|
#include <avtp_cvf.h>
|
|
|
|
#define AVTP_CVF_H264_HEADER_SIZE (sizeof(struct avtp_stream_pdu) + sizeof(guint32))
|
|
#define STREAM_ID 0xAABBCCDDEEFF0001
|
|
|
|
/* Simple codec data, with only the NAL size len, no SPS/PPS. */
|
|
static GstCaps *
|
|
generate_caps (guint8 nal_size_len)
|
|
{
|
|
GstBuffer *codec_data;
|
|
GstMapInfo map;
|
|
GstCaps *caps;
|
|
|
|
/* 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] = (nal_size_len - 1) | 0xfc; /* Other 6 bits are 1 */
|
|
map.data[5] = 0xe0; /* first 3 bits are 1 */
|
|
|
|
gst_buffer_unmap (codec_data, &map);
|
|
|
|
caps = gst_caps_new_simple ("video/x-h264",
|
|
"stream-format", G_TYPE_STRING, "avc",
|
|
"alignment", G_TYPE_STRING, "au",
|
|
"codec_data", GST_TYPE_BUFFER, codec_data, NULL);
|
|
gst_buffer_unref (codec_data);
|
|
|
|
return caps;
|
|
}
|
|
|
|
static void
|
|
fill (guint8 * buf, gsize size)
|
|
{
|
|
guint8 i = 0;
|
|
|
|
while (size--)
|
|
*buf++ = i++;
|
|
}
|
|
|
|
static gboolean
|
|
check_nal_filling (GstBuffer * buffer, guint8 first)
|
|
{
|
|
GstMapInfo map;
|
|
gint i;
|
|
gsize offset = AVTP_CVF_H264_HEADER_SIZE + 1;
|
|
gboolean result = TRUE;
|
|
|
|
gst_buffer_map (buffer, &map, GST_MAP_READ);
|
|
if ((map.data[AVTP_CVF_H264_HEADER_SIZE] & 0x1f) == 28)
|
|
offset++; /* Fragmented NALs have 2 bytes header */
|
|
|
|
for (i = offset; i < map.size; i++) {
|
|
if (map.data[i] != first++) {
|
|
result = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
gst_buffer_unmap (buffer, &map);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
add_nal (GstBuffer * buffer, gsize size, guint type, gsize offset)
|
|
{
|
|
GstMapInfo map;
|
|
|
|
gst_buffer_map (buffer, &map, GST_MAP_READWRITE);
|
|
|
|
map.data[offset] = map.data[offset + 1] = 0;
|
|
map.data[offset + 2] = size >> 8;
|
|
map.data[offset + 3] = size & 0xff;
|
|
map.data[offset + 4] = type & 0x1f;
|
|
fill (&map.data[offset + 5], size - 1);
|
|
|
|
gst_buffer_unmap (buffer, &map);
|
|
}
|
|
|
|
/* This function assumes that NAL size len is 2 */
|
|
static void
|
|
add_nal_2 (GstBuffer * buffer, gsize size, guint type, gsize offset)
|
|
{
|
|
GstMapInfo map;
|
|
|
|
gst_buffer_map (buffer, &map, GST_MAP_READWRITE);
|
|
|
|
map.data[offset] = size >> 8;
|
|
map.data[offset + 1] = size & 0xff;
|
|
map.data[offset + 2] = type & 0x1f;
|
|
fill (&map.data[offset + 3], size - 1);
|
|
|
|
gst_buffer_unmap (buffer, &map);
|
|
}
|
|
|
|
static gboolean
|
|
compare_h264_avtpdu (struct avtp_stream_pdu *pdu, GstBuffer * buffer)
|
|
{
|
|
GstMapInfo map;
|
|
gboolean result;
|
|
|
|
gst_buffer_map (buffer, &map, GST_MAP_READ);
|
|
/* buffer must have at least the header size */
|
|
if (map.size < AVTP_CVF_H264_HEADER_SIZE)
|
|
return FALSE;
|
|
|
|
result = memcmp (map.data, pdu, AVTP_CVF_H264_HEADER_SIZE) == 0;
|
|
|
|
gst_buffer_unmap (buffer, &map);
|
|
|
|
return result;
|
|
}
|
|
|
|
GST_START_TEST (test_payloader_spread_ts)
|
|
{
|
|
GstHarness *h;
|
|
GstBuffer *in, *out;
|
|
gint i, total_fragments, max_interval_frames;
|
|
guint64 first_tx_time, final_dts, measurement_interval = 250000;
|
|
|
|
/* Create the harness for the avtpcvfpay */
|
|
h = gst_harness_new_parse
|
|
("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=2000000 tu=125000 processing-deadline=0 mtu=128 measurement-interval=250000 max-interval-frames=3");
|
|
gst_harness_set_src_caps (h, generate_caps (4));
|
|
|
|
/* A 980 bytes NAL with mtu=128 should generate 10 fragments */
|
|
in = gst_harness_create_buffer (h, 980 + 4);
|
|
add_nal (in, 980, 7, 0);
|
|
GST_BUFFER_DTS (in) = final_dts = 1000000;
|
|
GST_BUFFER_PTS (in) = 2000000;
|
|
|
|
/* We now push the buffer, and check if we got ten from the avtpcvfpay */
|
|
gst_harness_push (h, in);
|
|
fail_unless_equals_int (gst_harness_buffers_received (h), 10);
|
|
|
|
/* Using max-interval-frames=3, we'll need 4 measurement intervals to send
|
|
* all fragments, with last one just about current DTS, and others
|
|
* progressively before that.
|
|
*
|
|
* So we should have something like:
|
|
*
|
|
* | 1st | 2nd | 3rd | 4th | Intervals
|
|
* | 1 2 3 | 4 5 6 | 7 8 9 | 10 | AVTPDUs in each interval (sharing same DTS/PTS)
|
|
*
|
|
* And PTS/DTS should increment by a
|
|
* measurement-interval / max-interval-frames for each AVTPDU.
|
|
*/
|
|
i = 0;
|
|
total_fragments = 10;
|
|
max_interval_frames = 3;
|
|
first_tx_time =
|
|
final_dts -
|
|
(measurement_interval / max_interval_frames) * (total_fragments - 1);
|
|
for (i = 0; i < 10; i++) {
|
|
out = gst_harness_pull (h);
|
|
fail_unless_equals_uint64 (GST_BUFFER_DTS (out), first_tx_time);
|
|
|
|
first_tx_time += measurement_interval / max_interval_frames;
|
|
}
|
|
|
|
|
|
gst_harness_teardown (h);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
GST_START_TEST (test_payloader_downstream_eos)
|
|
{
|
|
GstHarness *h;
|
|
GstBuffer *in;
|
|
|
|
/* Create the harness for the avtpcvfpay */
|
|
h = gst_harness_new_parse
|
|
("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=1000000 processing-deadline=0 ! fakesink num-buffers=1");
|
|
gst_harness_set_src_caps (h, generate_caps (4));
|
|
|
|
/* Buffer must have the nal len (4 bytes) and the nal (4 bytes) */
|
|
in = gst_harness_create_buffer (h, 8);
|
|
add_nal (in, 4, 1, 0);
|
|
GST_BUFFER_DTS (in) = 1000000;
|
|
GST_BUFFER_PTS (in) = 2000000;
|
|
|
|
fail_unless_equals_int (gst_harness_push (h, in), GST_FLOW_EOS);
|
|
|
|
gst_harness_teardown (h);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
GST_START_TEST (test_payloader_zero_sized_nal)
|
|
{
|
|
GstHarness *h;
|
|
GstBuffer *in;
|
|
GstMapInfo map;
|
|
|
|
/* Create the harness for the avtpcvfpay */
|
|
h = gst_harness_new_parse
|
|
("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=1000000 processing-deadline=0");
|
|
gst_harness_set_src_caps (h, generate_caps (4));
|
|
|
|
/* We have the buffer with the nal size (4 bytes) and the nal (4 bytes), but
|
|
* nal size will be zero */
|
|
in = gst_harness_create_buffer (h, 8);
|
|
GST_BUFFER_DTS (in) = 1000000;
|
|
GST_BUFFER_PTS (in) = 2000000;
|
|
|
|
gst_buffer_map (in, &map, GST_MAP_READWRITE);
|
|
map.data[0] = map.data[1] = map.data[2] = map.data[3] = 0; /* Set NAL size to 0 */
|
|
map.data[4] = 1; /* Some dummy vcl NAL type */
|
|
gst_buffer_unmap (in, &map);
|
|
|
|
gst_harness_push (h, in);
|
|
|
|
/* No buffer shuld come out */
|
|
fail_unless_equals_int (gst_harness_buffers_received (h), 0);
|
|
|
|
gst_harness_teardown (h);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
GST_START_TEST (test_payloader_no_codec_data)
|
|
{
|
|
GstHarness *h;
|
|
GstCaps *caps;
|
|
GstBuffer *in;
|
|
|
|
/* Caps without codec_data */
|
|
caps = gst_caps_new_simple ("video/x-h264",
|
|
"stream-format", G_TYPE_STRING, "avc",
|
|
"alignment", G_TYPE_STRING, "au", NULL);
|
|
|
|
/* Create the harness for the avtpcvfpay */
|
|
h = gst_harness_new_parse
|
|
("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=1000000 processing-deadline=0");
|
|
gst_harness_set_src_caps (h, caps);
|
|
|
|
/* No buffer should come out when we send input */
|
|
in = gst_harness_create_buffer (h, 8);
|
|
add_nal (in, 4, 1, 0);
|
|
GST_BUFFER_DTS (in) = 1000000;
|
|
GST_BUFFER_PTS (in) = 2000000;
|
|
|
|
gst_harness_push (h, in);
|
|
fail_unless_equals_int (gst_harness_buffers_received (h), 0);
|
|
|
|
gst_harness_teardown (h);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
GST_START_TEST (test_payloader_invalid_caps)
|
|
{
|
|
GstBuffer *codec_data;
|
|
GstElement *element;
|
|
GstPad *sinkpad;
|
|
GstMapInfo map;
|
|
GstCaps *caps;
|
|
GstHarness *h;
|
|
|
|
/* 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] = 0; /* version */
|
|
map.data[4] = 0x03 | 0xfc; /* Other 6 bits are 1 */
|
|
map.data[5] = 0xe0; /* first 3 bits are 1 */
|
|
|
|
gst_buffer_unmap (codec_data, &map);
|
|
|
|
caps = gst_caps_new_simple ("video/x-h264",
|
|
"stream-format", G_TYPE_STRING, "avc",
|
|
"alignment", G_TYPE_STRING, "au",
|
|
"codec_data", GST_TYPE_BUFFER, codec_data, NULL);
|
|
gst_buffer_unref (codec_data);
|
|
|
|
/* Create the harness for the avtpcvfpay */
|
|
h = gst_harness_new_parse
|
|
("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=1000000");
|
|
element = gst_harness_find_element (h, "avtpcvfpay");
|
|
sinkpad = gst_element_get_static_pad (element, "sink");
|
|
|
|
/* 'codec_data' caps has invalid version */
|
|
gst_harness_push_event (h, gst_event_new_caps (caps));
|
|
fail_unless (gst_pad_get_current_caps (sinkpad) == NULL);
|
|
gst_caps_unref (caps);
|
|
|
|
/* Send a 'codec_data' too small */
|
|
codec_data = gst_buffer_new_allocate (NULL, 6, NULL);
|
|
caps = gst_caps_new_simple ("video/x-h264",
|
|
"stream-format", G_TYPE_STRING, "avc",
|
|
"alignment", G_TYPE_STRING, "au",
|
|
"codec_data", GST_TYPE_BUFFER, codec_data, NULL);
|
|
gst_buffer_unref (codec_data);
|
|
|
|
gst_harness_push_event (h, gst_event_new_caps (caps));
|
|
fail_unless (gst_pad_get_current_caps (sinkpad) == NULL);
|
|
gst_caps_unref (caps);
|
|
|
|
gst_object_unref (sinkpad);
|
|
gst_object_unref (element);
|
|
gst_harness_teardown (h);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
GST_START_TEST (test_payloader_incomplete_nal)
|
|
{
|
|
GstHarness *h;
|
|
GstBuffer *in, *out;
|
|
GstMapInfo map;
|
|
struct avtp_stream_pdu *pdu = alloca (AVTP_CVF_H264_HEADER_SIZE);
|
|
const gint DATA_LEN = sizeof (guint32) + 3;
|
|
|
|
/* Create the 'expected' header */
|
|
avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_H264);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 3000000);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 4000000);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN);
|
|
|
|
/* Create the harness for the avtpcvfpay */
|
|
h = gst_harness_new_parse
|
|
("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=1000000 processing-deadline=0");
|
|
gst_harness_set_src_caps (h, generate_caps (4));
|
|
|
|
/* Buffer must have the nal len (4 bytes) and the nal (3 bytes) */
|
|
in = gst_harness_create_buffer (h, 7);
|
|
GST_BUFFER_DTS (in) = 1000000;
|
|
GST_BUFFER_PTS (in) = 2000000;
|
|
|
|
gst_buffer_map (in, &map, GST_MAP_READWRITE);
|
|
map.data[0] = map.data[1] = map.data[2] = 0;
|
|
map.data[3] = 8; /* Lie that NAL size is 8, when buffer is only 7 (so NAL is 3) */
|
|
map.data[4] = 1; /* Some dummy vcl NAL type */
|
|
map.data[5] = 0x0;
|
|
map.data[6] = 0x1;
|
|
gst_buffer_unmap (in, &map);
|
|
|
|
out = gst_harness_push_and_pull (h, in);
|
|
|
|
/* avtpcvfpay will happily payload the three byte nal. Now, we check it */
|
|
fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
|
|
fail_unless (check_nal_filling (out, 0) == TRUE);
|
|
|
|
gst_buffer_unref (out);
|
|
gst_harness_teardown (h);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
GST_START_TEST (test_payloader_properties)
|
|
{
|
|
GstHarness *h;
|
|
GstElement *element;
|
|
guint mtu, mtt, tu, max_interval_frames;
|
|
guint64 streamid, processing_deadline, measurement_interval;
|
|
|
|
/* Create the harness for the avtpcvfpay */
|
|
h = gst_harness_new_parse
|
|
("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=2000000 mtu=100 processing-deadline=5000 measurement-interval=125000 max-interval-frames=3");
|
|
|
|
/* Check if all properties were properly set up */
|
|
element = gst_harness_find_element (h, "avtpcvfpay");
|
|
g_object_get (G_OBJECT (element), "mtt", &mtt, NULL);
|
|
fail_unless_equals_uint64 (mtt, 1000000);
|
|
|
|
g_object_get (G_OBJECT (element), "mtu", &mtu, NULL);
|
|
fail_unless_equals_uint64 (mtu, 100);
|
|
|
|
g_object_get (G_OBJECT (element), "tu", &tu, NULL);
|
|
fail_unless_equals_uint64 (tu, 2000000);
|
|
|
|
g_object_get (G_OBJECT (element), "streamid", &streamid, NULL);
|
|
fail_unless_equals_uint64 (streamid, 0xAABBCCDDEEFF0001);
|
|
|
|
g_object_get (G_OBJECT (element), "processing-deadline", &processing_deadline,
|
|
NULL);
|
|
fail_unless_equals_uint64 (processing_deadline, 5000);
|
|
|
|
g_object_get (G_OBJECT (element), "measurement-interval",
|
|
&measurement_interval, NULL);
|
|
fail_unless_equals_uint64 (measurement_interval, 125000);
|
|
|
|
g_object_get (G_OBJECT (element), "max-interval-frames",
|
|
&max_interval_frames, NULL);
|
|
fail_unless_equals_uint64 (max_interval_frames, 3);
|
|
|
|
gst_object_unref (element);
|
|
gst_harness_teardown (h);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
GST_START_TEST (test_payloader_single_and_fragment_edge)
|
|
{
|
|
GstHarness *h;
|
|
GstBuffer *in, *out;
|
|
struct avtp_stream_pdu *pdu = alloca (AVTP_CVF_H264_HEADER_SIZE);
|
|
const gint DATA_LEN_1 = sizeof (guint32) + 100;
|
|
const gint DATA_LEN_2 = sizeof (guint32) + 100;
|
|
const gint DATA_LEN_3 = sizeof (guint32) + 4;
|
|
|
|
/* Create the 'expected' header */
|
|
avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_H264);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 3000000);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 4000000);
|
|
|
|
/* Create the harness for the avtpcvfpay. Setting mtu=128 ensures that
|
|
* NAL units will be broken roughly at 100 bytes. More details below. */
|
|
h = gst_harness_new_parse
|
|
("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=1000000 mtu=128 processing-deadline=0");
|
|
gst_harness_set_src_caps (h, generate_caps (4));
|
|
|
|
/* Create a buffer to contain the multiple NAL units. This buffer
|
|
* will hold two NAL units, with 100 and 101 bytes, each preceded
|
|
* by a 4 bytes header */
|
|
in = gst_harness_create_buffer (h, 100 + 101 + 2 * 4);
|
|
add_nal (in, 100, 7, 0);
|
|
add_nal (in, 101, 1, 104);
|
|
GST_BUFFER_DTS (in) = 1000000;
|
|
GST_BUFFER_PTS (in) = 2000000;
|
|
|
|
/* We now push the buffer, and check if we get three from the avtpcvfpay */
|
|
gst_harness_push (h, in);
|
|
fail_unless (gst_harness_buffers_received (h) == 3);
|
|
|
|
out = gst_harness_pull (h);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_1);
|
|
fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
|
|
gst_buffer_unref (out);
|
|
|
|
out = gst_harness_pull (h);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_2);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 0);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 0);
|
|
fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
|
|
gst_buffer_unref (out);
|
|
|
|
/* DATA_LEN_3 is 4 because only 98 bytes from the original NAL unit are
|
|
* sent on the first buffer (due 2 bytes header), and the two remaining
|
|
* bytes are preceded by the 2 bytes header. Note that the first byte of
|
|
* the NAL is stripped before the fragmentation (see comment on
|
|
* test_payloader_single_and_fragment below for more details). */
|
|
out = gst_harness_pull (h);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_3);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 2);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 3000000);
|
|
fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
|
|
gst_buffer_unref (out);
|
|
gst_harness_teardown (h);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
GST_START_TEST (test_payloader_single_and_fragment)
|
|
{
|
|
GstHarness *h;
|
|
GstBuffer *in, *out;
|
|
struct avtp_stream_pdu *pdu = alloca (AVTP_CVF_H264_HEADER_SIZE);
|
|
const gint DATA_LEN_1 = sizeof (guint32) + 4;
|
|
const gint DATA_LEN_2 = sizeof (guint32) + 100;
|
|
const gint DATA_LEN_3 = sizeof (guint32) + 100;
|
|
const gint DATA_LEN_4 = sizeof (guint32) + 55;
|
|
|
|
/* Create the 'expected' header */
|
|
avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_H264);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 4000000);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 5000000);
|
|
|
|
/* Create the harness for the avtpcvfpay. Setting mtu=128 ensures that
|
|
* NAL units will be broken roughly at 100 bytes. More details below. */
|
|
h = gst_harness_new_parse
|
|
("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=1000000 processing-deadline=1000000 mtu=128");
|
|
gst_harness_set_src_caps (h, generate_caps (4));
|
|
|
|
/* Create a buffer to contain the multiple NAL units. This buffer
|
|
* will hold two NAL units, with 4 and 250 bytes, each preceded
|
|
* by a 4 bytes header */
|
|
in = gst_harness_create_buffer (h, 4 + 250 + 2 * 4);
|
|
add_nal (in, 4, 7, 0);
|
|
add_nal (in, 250, 1, 8);
|
|
GST_BUFFER_DTS (in) = 1000000;
|
|
GST_BUFFER_PTS (in) = 2000000;
|
|
|
|
/* We now push the buffer, and check if we get four from the avtpcvfpay */
|
|
gst_harness_push (h, in);
|
|
fail_unless (gst_harness_buffers_received (h) == 4);
|
|
|
|
out = gst_harness_pull (h);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_1);
|
|
fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
|
|
fail_unless (check_nal_filling (out, 0) == TRUE);
|
|
gst_buffer_unref (out);
|
|
|
|
out = gst_harness_pull (h);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_2);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 0);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 0);
|
|
fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
|
|
fail_unless (check_nal_filling (out, 0) == TRUE);
|
|
gst_buffer_unref (out);
|
|
|
|
out = gst_harness_pull (h);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_3);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 2);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 0);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 0);
|
|
fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
|
|
fail_unless (check_nal_filling (out, 98) == TRUE);
|
|
gst_buffer_unref (out);
|
|
|
|
/* For those wondering why DATA_LEN_4 is 55 and not 50 - or why
|
|
* comment above states that NAL units are broken "roughly" at 100 bytes:
|
|
* With mtu=128, there are only 100 bytes left for NAL units, so anything
|
|
* bigger will be broken. But AVTP NAL units fragments have a header with
|
|
* two bytes, so NAL units will use only 98 bytes. This leaves the last
|
|
* fragment with 54 bytes. However, instead of being 56 (54 bytes plus
|
|
* 2 bytes header), it is 55 (53 bytes plus 2 bytes header) due to the
|
|
* fact that the first byte of the NAL unit (the NAL unit header) is
|
|
* in fact stripped from the NAL unit before the fragmentation. */
|
|
out = gst_harness_pull (h);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_4);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 3);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 4000000);
|
|
fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
|
|
fail_unless (check_nal_filling (out, 196) == TRUE);
|
|
gst_buffer_unref (out);
|
|
gst_harness_teardown (h);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
GST_START_TEST (test_payloader_multiple_single_2)
|
|
{
|
|
GstHarness *h;
|
|
GstBuffer *in, *out;
|
|
struct avtp_stream_pdu *pdu = alloca (AVTP_CVF_H264_HEADER_SIZE);
|
|
const gint DATA_LEN_1 = sizeof (guint32) + 32;
|
|
const gint DATA_LEN_2 = sizeof (guint32) + 16;
|
|
const gint DATA_LEN_3 = sizeof (guint32) + 8;
|
|
|
|
/* Create the 'expected' header */
|
|
avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_H264);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 3000000);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 4000000);
|
|
|
|
/* Create the harness for the avtpcvfpay */
|
|
h = gst_harness_new_parse
|
|
("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=1000000 processing-deadline=0");
|
|
gst_harness_set_src_caps (h, generate_caps (2));
|
|
|
|
/* Create a buffer to contain the multiple NAL units. This buffer
|
|
* will hold three NAL units, with 32, 16 and 8 bytes, each preceded
|
|
* by a 2 bytes header */
|
|
in = gst_harness_create_buffer (h, 32 + 16 + 8 + 4 * 2);
|
|
add_nal_2 (in, 32, 7, 0);
|
|
add_nal_2 (in, 16, 7, 34);
|
|
add_nal_2 (in, 8, 1, 52);
|
|
GST_BUFFER_DTS (in) = 1000000;
|
|
GST_BUFFER_PTS (in) = 2000000;
|
|
|
|
/* We now push the buffer, and check if we get three from the avtpcvfpay */
|
|
gst_harness_push (h, in);
|
|
fail_unless (gst_harness_buffers_received (h) == 3);
|
|
|
|
out = gst_harness_pull (h);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_1);
|
|
fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
|
|
fail_unless (check_nal_filling (out, 0) == TRUE);
|
|
gst_buffer_unref (out);
|
|
|
|
out = gst_harness_pull (h);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_2);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 1);
|
|
fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
|
|
fail_unless (check_nal_filling (out, 0) == TRUE);
|
|
gst_buffer_unref (out);
|
|
|
|
out = gst_harness_pull (h);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_3);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 2);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1);
|
|
fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
|
|
fail_unless (check_nal_filling (out, 0) == TRUE);
|
|
gst_buffer_unref (out);
|
|
gst_harness_teardown (h);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
GST_START_TEST (test_payloader_multiple_single)
|
|
{
|
|
GstHarness *h;
|
|
GstBuffer *in, *out;
|
|
struct avtp_stream_pdu *pdu = alloca (AVTP_CVF_H264_HEADER_SIZE);
|
|
const gint DATA_LEN_1 = sizeof (guint32) + 32;
|
|
const gint DATA_LEN_2 = sizeof (guint32) + 16;
|
|
const gint DATA_LEN_3 = sizeof (guint32) + 8;
|
|
|
|
/* Create the 'expected' header */
|
|
avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_H264);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 3000000);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 4000000);
|
|
|
|
/* Create the harness for the avtpcvfpay */
|
|
h = gst_harness_new_parse
|
|
("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=1000000 processing-deadline=0");
|
|
gst_harness_set_src_caps (h, generate_caps (4));
|
|
|
|
/* Create a buffer to contain the multiple NAL units. This buffer
|
|
* will hold three NAL units, with 32, 16 and 8 bytes, each preceded
|
|
* by a 4 bytes header */
|
|
in = gst_harness_create_buffer (h, 32 + 16 + 8 + 4 * 4);
|
|
add_nal (in, 32, 7, 0);
|
|
add_nal (in, 16, 7, 36);
|
|
add_nal (in, 8, 1, 56);
|
|
GST_BUFFER_DTS (in) = 1000000;
|
|
GST_BUFFER_PTS (in) = 2000000;
|
|
|
|
/* We now push the buffer, and check if we get three from the avtpcvfpay */
|
|
gst_harness_push (h, in);
|
|
fail_unless (gst_harness_buffers_received (h) == 3);
|
|
|
|
out = gst_harness_pull (h);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_1);
|
|
fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
|
|
fail_unless (check_nal_filling (out, 0) == TRUE);
|
|
gst_buffer_unref (out);
|
|
|
|
out = gst_harness_pull (h);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_2);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 1);
|
|
fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
|
|
fail_unless (check_nal_filling (out, 0) == TRUE);
|
|
gst_buffer_unref (out);
|
|
|
|
out = gst_harness_pull (h);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_3);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 2);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1);
|
|
fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
|
|
fail_unless (check_nal_filling (out, 0) == TRUE);
|
|
gst_buffer_unref (out);
|
|
gst_harness_teardown (h);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
GST_START_TEST (test_payloader_single)
|
|
{
|
|
GstHarness *h;
|
|
GstBuffer *in, *out;
|
|
struct avtp_stream_pdu *pdu = alloca (AVTP_CVF_H264_HEADER_SIZE);
|
|
const gint DATA_LEN = sizeof (guint32) + 4;
|
|
|
|
/* Create the 'expected' header */
|
|
avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_H264);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 3000000);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 4000000);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN);
|
|
|
|
/* Create the harness for the avtpcvfpay */
|
|
h = gst_harness_new_parse
|
|
("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=1000000 processing-deadline=0");
|
|
gst_harness_set_src_caps (h, generate_caps (4));
|
|
|
|
/* Buffer must have the nal len (4 bytes) and the nal (4 bytes) */
|
|
in = gst_harness_create_buffer (h, 8);
|
|
add_nal (in, 4, 1, 0);
|
|
GST_BUFFER_DTS (in) = 1000000;
|
|
GST_BUFFER_PTS (in) = 2000000;
|
|
|
|
out = gst_harness_push_and_pull (h, in);
|
|
fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
|
|
gst_buffer_unref (out);
|
|
|
|
/* Now test if, when nal_type is not vcl (not between 1 and 5), M is not set.
|
|
* Also, as we're using the same element, seqnum should increase by one */
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 0);
|
|
avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 1);
|
|
|
|
in = gst_harness_create_buffer (h, 8);
|
|
add_nal (in, 4, 6, 0);
|
|
GST_BUFFER_DTS (in) = 1000000;
|
|
GST_BUFFER_PTS (in) = 2000000;
|
|
|
|
out = gst_harness_push_and_pull (h, in);
|
|
fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
|
|
fail_unless (check_nal_filling (out, 0) == TRUE);
|
|
gst_buffer_unref (out);
|
|
gst_harness_teardown (h);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
static Suite *
|
|
avtpcvfpay_suite (void)
|
|
{
|
|
Suite *s = suite_create ("avtpcvfpay");
|
|
TCase *tc_chain = tcase_create ("general");
|
|
|
|
suite_add_tcase (s, tc_chain);
|
|
tcase_add_test (tc_chain, test_payloader_single);
|
|
tcase_add_test (tc_chain, test_payloader_multiple_single);
|
|
tcase_add_test (tc_chain, test_payloader_multiple_single_2);
|
|
tcase_add_test (tc_chain, test_payloader_single_and_fragment);
|
|
tcase_add_test (tc_chain, test_payloader_single_and_fragment_edge);
|
|
tcase_add_test (tc_chain, test_payloader_incomplete_nal);
|
|
tcase_add_test (tc_chain, test_payloader_invalid_caps);
|
|
tcase_add_test (tc_chain, test_payloader_properties);
|
|
tcase_add_test (tc_chain, test_payloader_no_codec_data);
|
|
tcase_add_test (tc_chain, test_payloader_zero_sized_nal);
|
|
tcase_add_test (tc_chain, test_payloader_downstream_eos);
|
|
tcase_add_test (tc_chain, test_payloader_spread_ts);
|
|
|
|
return s;
|
|
}
|
|
|
|
GST_CHECK_MAIN (avtpcvfpay);
|