mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-26 18:20:44 +00:00
774 lines
26 KiB
C
774 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);
|
|
gst_buffer_unref (out);
|
|
|
|
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);
|