gstreamer/tests/check/elements/rtph264.c
Matthew Waters ea61714c70 rtph26*depay: drop FU's without a corresponding start bit
If we have not received a FU with a start bit set, any subsequent FU
data is not useful at all and would result in an invalid stream.

This case is constructed from multiple requirements in
RFC 3984 Section 5.8 and RFC 7798 Section 4.4.3.  Following are excerpts
from RFC 3984 but RFC 7798 contains similar language.

The FU in a single FU case is forbidden:

   A fragmented NAL unit MUST NOT be transmitted in one FU; i.e., the
   Start bit and End bit MUST NOT both be set to one in the same FU
   header.

and dropping is possible:

   If a fragmentation unit is lost, the receiver SHOULD discard all
   following fragmentation units in transmission order corresponding to
   the same fragmented NAL unit.

The jump in seqnum case is supported by this from the specification
instead of implementing the forbidden_zero_bit mangling:

   If a fragmentation unit is lost, the receiver SHOULD discard all
   following fragmentation units in transmission order corresponding to
   the same fragmented NAL unit.

   A receiver in an endpoint or in a MANE MAY aggregate the first n-1
   fragments of a NAL unit to an (incomplete) NAL unit, even if fragment
   n of that NAL unit is not received.  In this case, the
   forbidden_zero_bit of the NAL unit MUST be set to one to indicate a
   syntax violation.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/730>
2020-09-21 08:08:38 +00:00

1491 lines
47 KiB
C

/* GStreamer RTP H.264 unit test
*
* Copyright (C) 2017 Centricular Ltd
* @author: Tim-Philipp Müller <tim@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <gst/check/check.h>
#include <gst/app/app.h>
#include <gst/rtp/gstrtpbuffer.h>
#define ALLOCATOR_CUSTOM_SYSMEM "CustomSysMem"
static GstAllocator *custom_sysmem_allocator; /* NULL */
/* Custom memory */
typedef struct
{
GstMemory mem;
guint8 *data;
guint8 *allocdata;
} CustomSysmem;
static CustomSysmem *
custom_sysmem_new (GstMemoryFlags flags, gsize maxsize, gsize align,
gsize offset, gsize size)
{
gsize aoffset, padding;
CustomSysmem *mem;
/* ensure configured alignment */
align |= gst_memory_alignment;
/* allocate more to compensate for alignment */
maxsize += align;
mem = g_new0 (CustomSysmem, 1);
mem->allocdata = g_malloc (maxsize);
mem->data = mem->allocdata;
/* do alignment */
if ((aoffset = ((guintptr) mem->data & align))) {
aoffset = (align + 1) - aoffset;
mem->data += aoffset;
maxsize -= aoffset;
}
if (offset && (flags & GST_MEMORY_FLAG_ZERO_PREFIXED))
memset (mem->data, 0, offset);
padding = maxsize - (offset + size);
if (padding && (flags & GST_MEMORY_FLAG_ZERO_PADDED))
memset (mem->data + offset + size, 0, padding);
gst_memory_init (GST_MEMORY_CAST (mem), flags, custom_sysmem_allocator,
NULL, maxsize, align, offset, size);
return mem;
}
static gpointer
custom_sysmem_map (CustomSysmem * mem, gsize maxsize, GstMapFlags flags)
{
return mem->data;
}
static gboolean
custom_sysmem_unmap (CustomSysmem * mem)
{
return TRUE;
}
static CustomSysmem *
custom_sysmem_copy (CustomSysmem * mem, gssize offset, gsize size)
{
g_return_val_if_reached (NULL);
}
static CustomSysmem *
custom_sysmem_share (CustomSysmem * mem, gssize offset, gsize size)
{
g_return_val_if_reached (NULL);
}
static gboolean
custom_sysmem_is_span (CustomSysmem * mem1, CustomSysmem * mem2, gsize * offset)
{
g_return_val_if_reached (FALSE);
}
/* Custom allocator */
typedef struct
{
GstAllocator allocator;
} CustomSysmemAllocator;
typedef struct
{
GstAllocatorClass allocator_class;
} CustomSysmemAllocatorClass;
GType custom_sysmem_allocator_get_type (void);
G_DEFINE_TYPE (CustomSysmemAllocator, custom_sysmem_allocator,
GST_TYPE_ALLOCATOR);
static GstMemory *
custom_sysmem_allocator_alloc (GstAllocator * allocator, gsize size,
GstAllocationParams * params)
{
gsize maxsize = size + params->prefix + params->padding;
return (GstMemory *) custom_sysmem_new (params->flags,
maxsize, params->align, params->prefix, size);
}
static void
custom_sysmem_allocator_free (GstAllocator * allocator, GstMemory * mem)
{
CustomSysmem *csmem = (CustomSysmem *) mem;
g_free (csmem->allocdata);
g_free (csmem);
}
static void
custom_sysmem_allocator_class_init (CustomSysmemAllocatorClass * klass)
{
GstAllocatorClass *allocator_class = (GstAllocatorClass *) klass;
allocator_class->alloc = custom_sysmem_allocator_alloc;
allocator_class->free = custom_sysmem_allocator_free;
}
static void
custom_sysmem_allocator_init (CustomSysmemAllocator * allocator)
{
GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
alloc->mem_type = ALLOCATOR_CUSTOM_SYSMEM;
alloc->mem_map = (GstMemoryMapFunction) custom_sysmem_map;
alloc->mem_unmap = (GstMemoryUnmapFunction) custom_sysmem_unmap;
alloc->mem_copy = (GstMemoryCopyFunction) custom_sysmem_copy;
alloc->mem_share = (GstMemoryShareFunction) custom_sysmem_share;
alloc->mem_is_span = (GstMemoryIsSpanFunction) custom_sysmem_is_span;
}
/* AppSink subclass proposing our custom allocator to upstream */
typedef struct
{
GstAppSink appsink;
} CMemAppSink;
typedef struct
{
GstAppSinkClass appsink;
} CMemAppSinkClass;
GType c_mem_app_sink_get_type (void);
G_DEFINE_TYPE (CMemAppSink, c_mem_app_sink, GST_TYPE_APP_SINK);
static void
c_mem_app_sink_init (CMemAppSink * cmemsink)
{
}
static gboolean
c_mem_app_sink_propose_allocation (GstBaseSink * sink, GstQuery * query)
{
gst_query_add_allocation_param (query, custom_sysmem_allocator, NULL);
return TRUE;
}
static void
c_mem_app_sink_class_init (CMemAppSinkClass * klass)
{
GstBaseSinkClass *basesink_class = (GstBaseSinkClass *) klass;
basesink_class->propose_allocation = c_mem_app_sink_propose_allocation;
}
#define RTP_H264_FILE GST_TEST_FILES_PATH G_DIR_SEPARATOR_S "h264.rtp"
static GstBuffer *
create_codec_data (guint8 * sps, gsize sps_size, guint8 * pps, gsize pps_size)
{
unsigned int offset = 0;
GstBuffer *codec_data_buffer;
GstMemory *mem;
GstMapInfo map_info;
guint8 *codec_data;
codec_data_buffer =
gst_buffer_new_allocate (NULL, sps_size + pps_size + 11, NULL);
mem = gst_buffer_peek_memory (codec_data_buffer, 0);
gst_memory_map (mem, &map_info, GST_MAP_WRITE);
codec_data = map_info.data;
codec_data[offset++] = 0x01; /* Configuration Version */
codec_data[offset++] = sps[1]; /* AVCProfileIndication */
codec_data[offset++] = sps[2]; /* profile_compatibility */
codec_data[offset++] = sps[3]; /* AVCLevelIndication */
codec_data[offset++] = 0xff; /* lengthSizeMinusOne == 3 -> length == 4 byte */
/* SPS */
codec_data[offset++] = 0xe1; /* numOfSequenceParameterSets | b11100000 -> numSPS == 1 */
g_assert (sps_size <= 0xffff);
codec_data[offset++] = (sps_size >> 8) & 0xff; /* numOfSequenceParameterSets high 8bit */
codec_data[offset++] = sps_size & 0xff; /* numOfSequenceParameterSets low 8bit */
memcpy (codec_data + offset, sps, sps_size);
offset += sps_size;
/* PPS */
codec_data[offset++] = 0x1; /* numOfPictureParameterSets == 1 */
g_assert (pps_size <= 0xffff);
codec_data[offset++] = (pps_size >> 8) & 0xff;
codec_data[offset++] = pps_size & 0xff;
memcpy (codec_data + offset, pps, pps_size);
offset += pps_size;
gst_memory_unmap (mem, &map_info);
g_assert (offset == gst_buffer_get_size (codec_data_buffer));
return codec_data_buffer;
}
GST_START_TEST (test_rtph264depay_with_downstream_allocator)
{
GstElement *pipeline, *src, *depay, *sink;
GstMemory *mem;
GstSample *sample;
GstBuffer *buf;
GstCaps *caps;
custom_sysmem_allocator =
g_object_new (custom_sysmem_allocator_get_type (), NULL);
pipeline = gst_pipeline_new ("pipeline");
src = gst_element_factory_make ("appsrc", NULL);
caps = gst_caps_new_simple ("application/x-rtp",
"media", G_TYPE_STRING, "video",
"payload", G_TYPE_INT, 96,
"clock-rate", G_TYPE_INT, 90000,
"encoding-name", G_TYPE_STRING, "H264",
"ssrc", G_TYPE_UINT, 1990683810,
"timestamp-offset", G_TYPE_UINT, 3697583446UL,
"seqnum-offset", G_TYPE_UINT, 15568,
"a-framerate", G_TYPE_STRING, "30", NULL);
g_object_set (src, "format", GST_FORMAT_TIME, "caps", caps, NULL);
gst_bin_add (GST_BIN (pipeline), src);
gst_caps_unref (caps);
depay = gst_element_factory_make ("rtph264depay", NULL);
gst_bin_add (GST_BIN (pipeline), depay);
sink = g_object_new (c_mem_app_sink_get_type (), NULL);
gst_bin_add (GST_BIN (pipeline), sink);
gst_element_link_many (src, depay, sink, NULL);
gst_element_set_state (pipeline, GST_STATE_PAUSED);
{
gchar *data, *pdata;
gsize len;
fail_unless (g_file_get_contents (RTP_H264_FILE, &data, &len, NULL));
fail_unless (len > 2);
pdata = data;
while (len > 2) {
GstFlowReturn flow;
guint16 packet_len;
packet_len = GST_READ_UINT16_BE (pdata);
GST_INFO ("rtp packet length: %u (bytes left: %u)", packet_len,
(guint) len);
fail_unless (len >= 2 + packet_len);
flow = gst_app_src_push_buffer (GST_APP_SRC (src),
gst_buffer_new_wrapped (g_memdup (pdata + 2, packet_len),
packet_len));
fail_unless_equals_int (flow, GST_FLOW_OK);
pdata += 2 + packet_len;
len -= 2 + packet_len;
}
g_free (data);
}
gst_app_src_end_of_stream (GST_APP_SRC (src));
sample = gst_app_sink_pull_preroll (GST_APP_SINK (sink));
fail_unless (sample != NULL);
buf = gst_sample_get_buffer (sample);
GST_LOG ("buffer has %u memories", gst_buffer_n_memory (buf));
GST_LOG ("buffer size: %u", (guint) gst_buffer_get_size (buf));
fail_unless (gst_buffer_n_memory (buf) > 0);
mem = gst_buffer_peek_memory (buf, 0);
fail_unless (mem != NULL);
GST_LOG ("buffer memory type: %s", mem->allocator->mem_type);
fail_unless (gst_memory_is_type (mem, ALLOCATOR_CUSTOM_SYSMEM));
gst_sample_unref (sample);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (pipeline);
g_object_unref (custom_sysmem_allocator);
custom_sysmem_allocator = NULL;
}
GST_END_TEST;
static GstBuffer *
wrap_static_buffer_with_pts_full (guint8 * buf, gsize size, GstClockTime pts,
gpointer user_data, GDestroyNotify notify)
{
GstBuffer *buffer;
buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
buf, size, 0, size, user_data, notify);
GST_BUFFER_PTS (buffer) = pts;
return buffer;
}
static GstBuffer *
wrap_static_buffer_full (guint8 * buf, gsize size, gpointer user_data,
GDestroyNotify notify)
{
return wrap_static_buffer_with_pts_full (buf, size, GST_CLOCK_TIME_NONE,
user_data, notify);
}
static GstBuffer *
wrap_static_buffer_with_pts (guint8 * buf, gsize size, GstClockTime pts)
{
return wrap_static_buffer_with_pts_full (buf, size, pts, NULL, NULL);
}
static GstBuffer *
wrap_static_buffer (guint8 * buf, gsize size)
{
return wrap_static_buffer_full (buf, size, NULL, NULL);
}
/* This was generated using pipeline:
* gst-launch-1.0 videotestsrc num-buffers=1 pattern=green \
* ! video/x-raw,width=16,height=16 \
* ! openh264enc ! rtph264pay ! fakesink dump=1
*/
/* RTP h264_idr + marker */
static guint8 rtp_h264_idr[] = {
0x80, 0xe0, 0x3e, 0xf0, 0xd9, 0xbe, 0x80, 0x28,
0xf4, 0x2d, 0xe1, 0x70, 0x65, 0xb8, 0x00, 0x04,
0x00, 0x00, 0x09, 0xff, 0xff, 0xf8, 0x22, 0x8a,
0x00, 0x1f, 0x1c, 0x00, 0x04, 0x1c, 0xe3, 0x80,
0x00, 0x84, 0xde
};
GST_START_TEST (test_rtph264depay_eos)
{
GstHarness *h = gst_harness_new ("rtph264depay");
GstBuffer *buffer;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
GstFlowReturn ret;
gst_harness_set_caps_str (h,
"application/x-rtp,media=video,clock-rate=90000,encoding-name=H264",
"video/x-h264,alignment=au,stream-format=byte-stream");
buffer = wrap_static_buffer (rtp_h264_idr, sizeof (rtp_h264_idr));
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_WRITE, &rtp));
gst_rtp_buffer_set_marker (&rtp, FALSE);
gst_rtp_buffer_unmap (&rtp);
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 0);
fail_unless (gst_harness_push_event (h, gst_event_new_eos ()));
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1);
gst_harness_teardown (h);
}
GST_END_TEST;
GST_START_TEST (test_rtph264depay_marker_to_flag)
{
GstHarness *h = gst_harness_new ("rtph264depay");
GstBuffer *buffer;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
GstFlowReturn ret;
guint16 seq;
gst_harness_set_caps_str (h,
"application/x-rtp,media=video,clock-rate=90000,encoding-name=H264",
"video/x-h264,alignment=au,stream-format=byte-stream");
buffer = wrap_static_buffer (rtp_h264_idr, sizeof (rtp_h264_idr));
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless (gst_rtp_buffer_get_marker (&rtp));
seq = gst_rtp_buffer_get_seq (&rtp);
gst_rtp_buffer_unmap (&rtp);
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1);
buffer = wrap_static_buffer (rtp_h264_idr, sizeof (rtp_h264_idr));
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_WRITE, &rtp));
gst_rtp_buffer_set_marker (&rtp, FALSE);
gst_rtp_buffer_set_seq (&rtp, ++seq);
gst_rtp_buffer_unmap (&rtp);
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
/* the second NAL is blocked as there is no marker to let the payloader
* know it's a complete AU, we'll use an EOS to unblock it */
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1);
fail_unless (gst_harness_push_event (h, gst_event_new_eos ()));
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2);
buffer = gst_harness_pull (h);
fail_unless (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_MARKER));
gst_buffer_unref (buffer);
buffer = gst_harness_pull (h);
fail_if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_MARKER));
gst_buffer_unref (buffer);
gst_harness_teardown (h);
}
GST_END_TEST;
/* This was generated using pipeline:
* gst-launch-1.0 videotestsrc num-buffers=1 pattern=green \
* ! video/x-raw,width=24,height=16 \
* ! openh264enc ! rtph264pay mtu=32 ! fakesink dump=1
*/
/* RTP h264_idr FU-A */
static guint8 rtp_h264_idr_fu_start[] = {
0x80, 0x60, 0x5f, 0xd2, 0x20, 0x3b, 0x6e, 0xcf,
0x6c, 0x54, 0x21, 0x8d, 0x7c, 0x85, 0xb8, 0x00,
0x04, 0x00, 0x00, 0x09, 0xff, 0xff, 0xf8, 0x22,
0x8a, 0x00, 0x1f, 0x1c, 0x00, 0x04, 0x1c, 0xe3,
};
static guint8 rtp_h264_idr_fu_middle[] = {
0x80, 0x60, 0x5f, 0xd3, 0x20, 0x3b, 0x6e, 0xcf,
0x6c, 0x54, 0x21, 0x8d, 0x7c, 0x05, 0x80, 0x00,
0x84, 0xdf, 0xf8, 0x7f, 0xe0, 0x8e, 0x28, 0x00,
0x08, 0x37, 0xf8, 0x80, 0x00, 0x20, 0x52, 0x00,
};
static guint8 rtp_h264_idr_fu_end[] = {
0x80, 0xe0, 0x5f, 0xd4, 0x20, 0x3b, 0x6e, 0xcf,
0x6c, 0x54, 0x21, 0x8d, 0x7c, 0x45, 0x02, 0x01,
0x91, 0x00, 0x00, 0x40, 0xf4, 0x00, 0x04, 0x08,
0x30,
};
GST_START_TEST (test_rtph264depay_fu_a)
{
GstHarness *h = gst_harness_new ("rtph264depay");
GstBuffer *buffer;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
GstFlowReturn ret;
gst_harness_set_caps_str (h,
"application/x-rtp,media=video,clock-rate=90000,encoding-name=H264",
"video/x-h264,alignment=au,stream-format=byte-stream");
buffer =
wrap_static_buffer (rtp_h264_idr_fu_start,
sizeof (rtp_h264_idr_fu_start));
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
gst_rtp_buffer_unmap (&rtp);
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 0);
buffer =
wrap_static_buffer (rtp_h264_idr_fu_middle,
sizeof (rtp_h264_idr_fu_middle));
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
buffer =
wrap_static_buffer (rtp_h264_idr_fu_end, sizeof (rtp_h264_idr_fu_end));
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1);
buffer = gst_harness_pull (h);
fail_unless (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_MARKER));
gst_buffer_unref (buffer);
gst_harness_teardown (h);
}
GST_END_TEST;
GST_START_TEST (test_rtph264depay_fu_a_missing_start)
{
GstHarness *h = gst_harness_new ("rtph264depay");
GstBuffer *buffer;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
GstFlowReturn ret;
guint16 seq;
gst_harness_set_caps_str (h,
"application/x-rtp,media=video,clock-rate=90000,encoding-name=H264",
"video/x-h264,alignment=au,stream-format=byte-stream");
buffer =
wrap_static_buffer (rtp_h264_idr_fu_start,
sizeof (rtp_h264_idr_fu_start));
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
buffer =
wrap_static_buffer (rtp_h264_idr_fu_middle,
sizeof (rtp_h264_idr_fu_middle));
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
buffer =
wrap_static_buffer (rtp_h264_idr_fu_end, sizeof (rtp_h264_idr_fu_end));
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
seq = gst_rtp_buffer_get_seq (&rtp);
gst_rtp_buffer_unmap (&rtp);
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1);
/* A broken sender case seen in the wild where the seqnums are continuous
* but only contain a FU with an end-bit, no start-bit */
buffer =
wrap_static_buffer (rtp_h264_idr_fu_end, sizeof (rtp_h264_idr_fu_end));
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_WRITE, &rtp));
gst_rtp_buffer_set_seq (&rtp, ++seq);
gst_rtp_buffer_unmap (&rtp);
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1);
gst_harness_teardown (h);
}
GST_END_TEST;
/* As GStreamer does not have STAP-A yet, this was extracted from
* issue #557 provided sample */
static guint8 rtp_stapa_pps_sps[] = {
0x80, 0x60, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0xd8, 0xd9, 0x06, 0x7f, 0x38, 0x00, 0x1c, 0x27,
0x64, 0x00, 0x29, 0xac, 0x13, 0x16, 0x50, 0x28,
0x0f, 0x6c, 0x00, 0x30, 0x30, 0x00, 0x5d, 0xc0,
0x00, 0x17, 0x70, 0x5e, 0xf7, 0xc1, 0xda, 0x08,
0x84, 0x65, 0x80, 0x00, 0x04, 0x28, 0xef, 0x1f,
0x2c
};
static guint8 rtp_stapa_slices_marker[] = {
0x80, 0xe0, 0x00, 0x0c, 0x00, 0x00, 0x57, 0xe4,
0xd8, 0xd9, 0x06, 0x7f, 0x38, 0x00, 0x28, 0x21,
0xe0, 0x02, 0x00, 0x10, 0x73, 0x84, 0x12, 0xff,
0x00, 0x1f, 0x0c, 0xbf, 0x0c, 0xb7, 0xb8, 0x73,
0xd3, 0xf2, 0xfc, 0xba, 0x7b, 0xce, 0x3c, 0xaf,
0x90, 0x11, 0xb3, 0x15, 0xa2, 0x53, 0x96, 0xc1,
0xa8, 0x27, 0x01, 0xdb, 0xde, 0xc8, 0x80, 0x00,
0x35, 0x21, 0x00, 0xa0, 0xe0, 0x02, 0x00, 0x10,
0x73, 0x84, 0x12, 0xff, 0x79, 0x9d, 0x3b, 0x38,
0xc6, 0x26, 0x47, 0x01, 0x02, 0x6b, 0xd6, 0x40,
0x17, 0x06, 0x0b, 0x8c, 0x22, 0xd0, 0x04, 0xe4,
0xea, 0x5e, 0x3f, 0xb8, 0x6a, 0x03, 0x4e, 0x15,
0x1f, 0x74, 0x62, 0x51, 0x78, 0xef, 0x5a, 0xd1,
0xf0, 0x3e, 0xb8, 0x56, 0xbb, 0x08, 0x00, 0x2e,
0x21, 0x00, 0x50, 0x38, 0x00, 0x80, 0x04, 0x1c,
0xe1, 0x04, 0xbf, 0x00, 0xa8, 0x40, 0x67, 0x09,
0xa8, 0x4d, 0x95, 0x5b, 0x0e, 0x2b, 0xba, 0x34,
0xc7, 0xa6, 0x78, 0x27, 0xe4, 0x5c, 0x74, 0xa0,
0xa2, 0xce, 0x30, 0x51, 0x78, 0x30, 0x56, 0xd0,
0x7a, 0xcd, 0x12, 0xbc, 0xba, 0xe0, 0x00, 0x1e,
0x21, 0x00, 0x78, 0x38, 0x00, 0x80, 0x04, 0x1c,
0xe1, 0x04, 0xbf, 0x67, 0x37, 0xc0, 0x86, 0x26,
0x7d, 0x4c, 0x52, 0x4b, 0x80, 0x9a, 0x3c, 0xa3,
0x02, 0xcd, 0x8d, 0xcd, 0x18, 0xd8
};
GST_START_TEST (test_rtph264depay_stap_a_marker)
{
GstHarness *h = gst_harness_new ("rtph264depay");
GstBuffer *buffer;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
GstFlowReturn ret;
gst_harness_set_caps_str (h,
"application/x-rtp,media=video,clock-rate=90000,encoding-name=H264",
"video/x-h264,alignment=au,stream-format=byte-stream");
ret = gst_harness_push (h,
wrap_static_buffer (rtp_stapa_pps_sps, sizeof (rtp_stapa_pps_sps)));
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 0);
buffer = wrap_static_buffer (rtp_stapa_slices_marker,
sizeof (rtp_stapa_slices_marker));
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_WRITE, &rtp));
gst_rtp_buffer_set_seq (&rtp, 2);
gst_rtp_buffer_unmap (&rtp);
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
/* Only one AU should have been pushed */
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1);
gst_harness_teardown (h);
}
GST_END_TEST;
/* AUD */
static guint8 h264_aud[] = {
0x00, 0x00, 0x00, 0x01, 0x09, 0xf0
};
/* These were generated using pipeline:
* gst-launch-1.0 videotestsrc num-buffers=1 pattern=green \
* ! video/x-raw,width=128,height=128 \
* ! openh264enc num-slices=2 \
* ! fakesink dump=1
*/
/* SPS */
static guint8 h264_sps[] = {
0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x29,
0x8c, 0x8d, 0x41, 0x02, 0x24, 0x03, 0xc2, 0x21,
0x1a, 0x80
};
/* PPS */
static guint8 h264_pps[] = {
0x00, 0x00, 0x00, 0x01, 0x68, 0xce, 0x3c, 0x80
};
/* IDR Slice 1 */
static guint8 h264_idr_slice_1[] = {
0x00, 0x00, 0x00, 0x01, 0x65, 0xb8, 0x00, 0x04,
0x00, 0x00, 0x11, 0xff, 0xff, 0xf8, 0x22, 0x8a,
0x1f, 0x1c, 0x00, 0x04, 0x0a, 0x63, 0x80, 0x00,
0x81, 0xec, 0x9a, 0x93, 0x93, 0x93, 0x93, 0x93,
0x93, 0xad, 0x57, 0x5d, 0x75, 0xd7, 0x5d, 0x75,
0xd7, 0x5d, 0x75, 0xd7, 0x5d, 0x75, 0xd7, 0x5d,
0x75, 0xd7, 0x5d, 0x78
};
/* IDR Slice 2 */
static guint8 h264_idr_slice_2[] = {
0x00, 0x00, 0x00, 0x01, 0x65, 0x04, 0x2e, 0x00,
0x01, 0x00, 0x00, 0x04, 0x7f, 0xff, 0xfe, 0x08,
0xa2, 0x87, 0xc7, 0x00, 0x01, 0x02, 0x98, 0xe0,
0x00, 0x20, 0x7b, 0x26, 0xa4, 0xe4, 0xe4, 0xe4,
0xe4, 0xe4, 0xeb, 0x55, 0xd7, 0x5d, 0x75, 0xd7,
0x5d, 0x75, 0xd7, 0x5d, 0x75, 0xd7, 0x5d, 0x75,
0xd7, 0x5d, 0x75, 0xd7, 0x5e
};
/* SPS */
static guint8 h264_sps_avc[] = {
0x00, 0x00, 0x00, 0x0E, 0x67, 0x42, 0xc0, 0x29,
0x8c, 0x8d, 0x41, 0x02, 0x24, 0x03, 0xc2, 0x21,
0x1a, 0x80
};
/* PPS */
static guint8 h264_pps_avc[] = {
0x00, 0x00, 0x00, 0x04, 0x68, 0xce, 0x3c, 0x80
};
/* IDR Slice 1 */
static guint8 h264_idr_slice_1_avc[] = {
0x00, 0x00, 0x00, 0x30, 0x65, 0xb8, 0x00, 0x04,
0x00, 0x00, 0x11, 0xff, 0xff, 0xf8, 0x22, 0x8a,
0x1f, 0x1c, 0x00, 0x04, 0x0a, 0x63, 0x80, 0x00,
0x81, 0xec, 0x9a, 0x93, 0x93, 0x93, 0x93, 0x93,
0x93, 0xad, 0x57, 0x5d, 0x75, 0xd7, 0x5d, 0x75,
0xd7, 0x5d, 0x75, 0xd7, 0x5d, 0x75, 0xd7, 0x5d,
0x75, 0xd7, 0x5d, 0x78
};
/* IDR Slice 2 */
static guint8 h264_idr_slice_2_avc[] = {
0x00, 0x00, 0x00, 0x31, 0x65, 0x04, 0x2e, 0x00,
0x01, 0x00, 0x00, 0x04, 0x7f, 0xff, 0xfe, 0x08,
0xa2, 0x87, 0xc7, 0x00, 0x01, 0x02, 0x98, 0xe0,
0x00, 0x20, 0x7b, 0x26, 0xa4, 0xe4, 0xe4, 0xe4,
0xe4, 0xe4, 0xeb, 0x55, 0xd7, 0x5d, 0x75, 0xd7,
0x5d, 0x75, 0xd7, 0x5d, 0x75, 0xd7, 0x5d, 0x75,
0xd7, 0x5d, 0x75, 0xd7, 0x5e
};
/* The RFC makes special use of NAL type 24 to 27, this test makes sure that
* such a NAL from the outside gets ignored properly. */
GST_START_TEST (test_rtph264pay_reserved_nals)
{
GstHarness *h = gst_harness_new_parse ("rtph264pay aggregate-mode=none");
/* we simply hack an AUD with the reserved nal types */
guint8 nal_24[sizeof (h264_aud)];
guint8 nal_25[sizeof (h264_aud)];
guint8 nal_26[sizeof (h264_aud)];
guint8 nal_27[sizeof (h264_aud)];
GstFlowReturn ret;
gst_harness_set_src_caps_str (h,
"video/x-h264,alignment=nal,stream-format=byte-stream");
ret = gst_harness_push (h, wrap_static_buffer (h264_sps, sizeof (h264_sps)));
fail_unless_equals_int (ret, GST_FLOW_OK);
ret = gst_harness_push (h, wrap_static_buffer (h264_pps, sizeof (h264_pps)));
fail_unless_equals_int (ret, GST_FLOW_OK);
memcpy (nal_24, h264_aud, sizeof (h264_aud));
nal_24[4] = 24;
ret = gst_harness_push (h, wrap_static_buffer (nal_24, sizeof (nal_24)));
fail_unless_equals_int (ret, GST_FLOW_OK);
memcpy (nal_25, h264_aud, sizeof (h264_aud));
nal_25[4] = 25;
ret = gst_harness_push (h, wrap_static_buffer (nal_25, sizeof (nal_25)));
fail_unless_equals_int (ret, GST_FLOW_OK);
memcpy (nal_26, h264_aud, sizeof (h264_aud));
nal_26[4] = 26;
ret = gst_harness_push (h, wrap_static_buffer (nal_26, sizeof (nal_26)));
fail_unless_equals_int (ret, GST_FLOW_OK);
memcpy (nal_27, h264_aud, sizeof (h264_aud));
nal_27[4] = 27;
ret = gst_harness_push (h, wrap_static_buffer (nal_27, sizeof (nal_27)));
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2);
gst_harness_teardown (h);
}
GST_END_TEST;
GST_START_TEST (test_rtph264pay_two_slices_timestamp)
{
GstHarness *h = gst_harness_new_parse ("rtph264pay timestamp-offset=123"
" aggregate-mode=zero-latency");
GstFlowReturn ret;
GstBuffer *buffer;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
gst_harness_set_src_caps_str (h,
"video/x-h264,alignment=nal,stream-format=byte-stream");
ret = gst_harness_push (h, wrap_static_buffer_with_pts (h264_idr_slice_1,
sizeof (h264_idr_slice_1), 0));
fail_unless_equals_int (ret, GST_FLOW_OK);
ret = gst_harness_push (h, wrap_static_buffer_with_pts (h264_idr_slice_2,
sizeof (h264_idr_slice_2), 0));
fail_unless_equals_int (ret, GST_FLOW_OK);
ret = gst_harness_push (h, wrap_static_buffer_with_pts (h264_idr_slice_1,
sizeof (h264_idr_slice_1), GST_SECOND));
fail_unless_equals_int (ret, GST_FLOW_OK);
ret = gst_harness_push (h, wrap_static_buffer_with_pts (h264_idr_slice_2,
sizeof (h264_idr_slice_2), GST_SECOND));
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 4);
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), 0);
fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123);
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), 0);
fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123);
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), GST_SECOND);
fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123 + 90000);
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), GST_SECOND);
fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123 + 90000);
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
gst_harness_teardown (h);
}
GST_END_TEST;
GST_START_TEST (test_rtph264pay_marker_for_flag)
{
GstHarness *h = gst_harness_new_parse ("rtph264pay timestamp-offset=123"
" aggregate-mode=zero-latency");
GstFlowReturn ret;
GstBuffer *buffer;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
gst_harness_set_src_caps_str (h,
"video/x-h264,alignment=nal,stream-format=byte-stream");
ret = gst_harness_push (h, wrap_static_buffer (h264_idr_slice_1,
sizeof (h264_idr_slice_1)));
fail_unless_equals_int (ret, GST_FLOW_OK);
buffer = wrap_static_buffer (h264_idr_slice_2, sizeof (h264_idr_slice_2));
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_MARKER);
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2);
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_if (gst_rtp_buffer_get_marker (&rtp));
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless (gst_rtp_buffer_get_marker (&rtp));
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
gst_harness_teardown (h);
}
GST_END_TEST;
GST_START_TEST (test_rtph264pay_marker_for_au)
{
GstHarness *h =
gst_harness_new_parse
("rtph264pay timestamp-offset=123 aggregate-mode=none");
GstFlowReturn ret;
GstBuffer *slice1, *slice2, *buffer;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
gst_harness_set_src_caps_str (h,
"video/x-h264,alignment=au,stream-format=byte-stream");
slice1 = wrap_static_buffer (h264_idr_slice_1, sizeof (h264_idr_slice_1));
slice2 = wrap_static_buffer (h264_idr_slice_2, sizeof (h264_idr_slice_2));
buffer = gst_buffer_append (slice1, slice2);
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2);
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_if (gst_rtp_buffer_get_marker (&rtp));
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless (gst_rtp_buffer_get_marker (&rtp));
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
gst_harness_teardown (h);
}
GST_END_TEST;
GST_START_TEST (test_rtph264pay_marker_for_fragmented_au)
{
GstHarness *h =
gst_harness_new_parse ("rtph264pay timestamp-offset=123 mtu=40"
" aggregate-mode=zero-latency");
GstFlowReturn ret;
GstBuffer *slice1, *slice2, *buffer;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
gint i;
gst_harness_set_src_caps_str (h,
"video/x-h264,alignment=au,stream-format=byte-stream");
slice1 = wrap_static_buffer (h264_idr_slice_1, sizeof (h264_idr_slice_1));
slice2 = wrap_static_buffer (h264_idr_slice_2, sizeof (h264_idr_slice_2));
buffer = gst_buffer_append (slice1, slice2);
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 4);
for (i = 0; i < 3; i++) {
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_if (gst_rtp_buffer_get_marker (&rtp));
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
}
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless (gst_rtp_buffer_get_marker (&rtp));
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
gst_harness_teardown (h);
}
GST_END_TEST;
GST_START_TEST (test_rtph264pay_aggregate_two_slices_per_buffer)
{
GstHarness *h = gst_harness_new_parse ("rtph264pay timestamp-offset=123"
" name=p");
GstFlowReturn ret;
GstBuffer *buffer;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
GstElement *e = gst_bin_get_by_name (GST_BIN (h->element), "p");
gint i;
gst_harness_set_src_caps_str (h,
"video/x-h264,alignment=nal,stream-format=byte-stream");
/* No aggregation mode */
g_object_set (e, "aggregate-mode", 0, NULL);
buffer = wrap_static_buffer_with_pts (h264_idr_slice_1,
sizeof (h264_idr_slice_1), 0);
buffer = gst_buffer_append (buffer,
wrap_static_buffer_with_pts (h264_idr_slice_2, sizeof (h264_idr_slice_2),
0));
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
buffer = wrap_static_buffer_with_pts (h264_idr_slice_1,
sizeof (h264_idr_slice_1), 0);
buffer = gst_buffer_append (buffer,
wrap_static_buffer_with_pts (h264_idr_slice_2, sizeof (h264_idr_slice_2),
0));
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 4);
for (i = 0; i < 4; i++) {
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), 0);
fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123);
fail_unless_equals_int (gst_buffer_get_size (buffer), 12 +
((i % 2) ? sizeof (h264_idr_slice_2) : sizeof (h264_idr_slice_1)) - 4);
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
}
/* Zero latency mode */
g_object_set (e, "aggregate-mode", 1, NULL);
buffer = wrap_static_buffer_with_pts (h264_idr_slice_1,
sizeof (h264_idr_slice_1), 0);
buffer = gst_buffer_append (buffer,
wrap_static_buffer_with_pts (h264_idr_slice_2, sizeof (h264_idr_slice_2),
0));
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
buffer = wrap_static_buffer_with_pts (h264_idr_slice_1,
sizeof (h264_idr_slice_1), 0);
buffer = gst_buffer_append (buffer,
wrap_static_buffer_with_pts (h264_idr_slice_2, sizeof (h264_idr_slice_2),
0));
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2);
for (i = 0; i < 2; i++) {
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), 0);
fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123);
/* RTP header = 12, STAP header = 1, 2 bytes length per NAL */
fail_unless_equals_int (gst_buffer_get_size (buffer), 12 + 1 +
(2 + sizeof (h264_idr_slice_2) - 4) +
(2 + sizeof (h264_idr_slice_1) - 4));
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
}
/* Max aggregation */
g_object_set (e, "aggregate-mode", 2, NULL);
buffer = wrap_static_buffer_with_pts (h264_idr_slice_1,
sizeof (h264_idr_slice_1), 0);
buffer = gst_buffer_append (buffer,
wrap_static_buffer_with_pts (h264_idr_slice_2, sizeof (h264_idr_slice_2),
0));
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
buffer = wrap_static_buffer_with_pts (h264_idr_slice_1,
sizeof (h264_idr_slice_1), 0);
buffer = gst_buffer_append (buffer,
wrap_static_buffer_with_pts (h264_idr_slice_2, sizeof (h264_idr_slice_2),
0));
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 0);
/* Push EOS to send it out */
gst_harness_push_event (h, gst_event_new_eos ());
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1);
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), 0);
fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123);
/* RTP header = 12, STAP header = 1, 2 bytes length per NAL */
fail_unless_equals_int (gst_buffer_get_size (buffer), 12 + 1 +
2 * (2 + sizeof (h264_idr_slice_2) - 4) +
2 * (2 + sizeof (h264_idr_slice_1) - 4));
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
g_object_unref (e);
gst_harness_teardown (h);
}
GST_END_TEST;
GST_START_TEST (test_rtph264pay_aggregate_with_aud)
{
GstHarness *h = gst_harness_new_parse ("rtph264pay timestamp-offset=123"
" aggregate-mode=zero-latency");
GstFlowReturn ret;
GstBuffer *buffer;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
gst_harness_set_src_caps_str (h,
"video/x-h264,alignment=nal,stream-format=byte-stream");
buffer = wrap_static_buffer_with_pts (h264_idr_slice_1,
sizeof (h264_idr_slice_1), 0);
buffer = gst_buffer_append (buffer,
wrap_static_buffer_with_pts (h264_aud, sizeof (h264_aud), 0));
buffer = gst_buffer_append (buffer,
wrap_static_buffer_with_pts (h264_idr_slice_1, sizeof (h264_idr_slice_1),
0));
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2);
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), 0);
fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123);
/* RTP header = 12 */
fail_unless_equals_int (gst_buffer_get_size (buffer), 12 +
(sizeof (h264_idr_slice_1) - 4));
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), 0);
fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123);
/* RTP header = 12, STAP header = 1, 2 bytes length per NAL */
fail_unless_equals_int (gst_buffer_get_size (buffer), 12 + 1 +
(2 + sizeof (h264_aud) - 4) + (2 + sizeof (h264_idr_slice_1) - 4));
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
gst_harness_teardown (h);
}
GST_END_TEST;
GST_START_TEST (test_rtph264pay_aggregate_with_ts_change)
{
GstHarness *h = gst_harness_new_parse ("rtph264pay timestamp-offset=123 "
"aggregate-mode=max-stap");
GstFlowReturn ret;
GstBuffer *buffer;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
gst_harness_set_src_caps_str (h,
"video/x-h264,alignment=nal,stream-format=byte-stream");
buffer = wrap_static_buffer_with_pts (h264_idr_slice_1,
sizeof (h264_idr_slice_1), 0);
buffer = gst_buffer_append (buffer,
wrap_static_buffer_with_pts (h264_idr_slice_2, sizeof (h264_idr_slice_2),
0));
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
buffer = wrap_static_buffer_with_pts (h264_idr_slice_1,
sizeof (h264_idr_slice_1), GST_SECOND);
buffer = gst_buffer_append (buffer,
wrap_static_buffer_with_pts (h264_idr_slice_2, sizeof (h264_idr_slice_2),
GST_SECOND));
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1);
/* Push EOS to send the second one out */
gst_harness_push_event (h, gst_event_new_eos ());
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2);
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), 0);
fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123);
/* RTP header = 12, STAP header = 1, 2 bytes length per NAL */
fail_unless_equals_int (gst_buffer_get_size (buffer), 12 + 1 +
(2 + sizeof (h264_idr_slice_1) - 4) +
(2 + sizeof (h264_idr_slice_2) - 4));
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), GST_SECOND);
fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123 + 90000);
/* RTP header = 12, STAP header = 1, 2 bytes length per NAL */
fail_unless_equals_int (gst_buffer_get_size (buffer), 12 + 1 +
(2 + sizeof (h264_idr_slice_1) - 4) +
(2 + sizeof (h264_idr_slice_2) - 4));
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
gst_harness_teardown (h);
}
GST_END_TEST;
GST_START_TEST (test_rtph264pay_aggregate_with_discont)
{
GstHarness *h = gst_harness_new_parse ("rtph264pay timestamp-offset=123"
" aggregate-mode=zero-latency");
GstFlowReturn ret;
GstBuffer *buffer;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
gst_harness_set_src_caps_str (h,
"video/x-h264,alignment=nal,stream-format=byte-stream");
buffer = wrap_static_buffer_with_pts (h264_idr_slice_1,
sizeof (h264_idr_slice_1), 0);
buffer = gst_buffer_append (buffer,
wrap_static_buffer_with_pts (h264_idr_slice_2, sizeof (h264_idr_slice_2),
0));
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
buffer = wrap_static_buffer_with_pts (h264_idr_slice_1,
sizeof (h264_idr_slice_1), 0);
buffer = gst_buffer_append (buffer,
wrap_static_buffer_with_pts (h264_idr_slice_2, sizeof (h264_idr_slice_2),
0));
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2);
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), 0);
fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123);
/* RTP header = 12, STAP header = 1, 2 bytes length per NAL */
fail_unless_equals_int (gst_buffer_get_size (buffer), 12 + 1 +
(2 + sizeof (h264_idr_slice_1) - 4) +
(2 + sizeof (h264_idr_slice_2) - 4));
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), 0);
fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123 + 0);
/* RTP header = 12, STAP header = 1, 2 bytes length per NAL */
fail_unless_equals_int (gst_buffer_get_size (buffer), 12 + 1 +
(2 + sizeof (h264_idr_slice_1) - 4) +
(2 + sizeof (h264_idr_slice_2) - 4));
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
gst_harness_teardown (h);
}
GST_END_TEST;
GST_START_TEST (test_rtph264pay_aggregate_until_vcl)
{
GstHarness *h = gst_harness_new_parse ("rtph264pay timestamp-offset=123"
" name=p aggregate-mode=zero-latency");
GstFlowReturn ret;
GstBuffer *buffer;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
gst_harness_set_src_caps_str (h,
"video/x-h264,alignment=nal,stream-format=byte-stream");
buffer = wrap_static_buffer_with_pts (h264_sps, sizeof (h264_sps), 0);
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
buffer = wrap_static_buffer_with_pts (h264_pps, sizeof (h264_pps), 0);
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
buffer = wrap_static_buffer_with_pts (h264_idr_slice_1,
sizeof (h264_idr_slice_1), 0);
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1);
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), 0);
fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123);
/* RTP header = 12, STAP header = 1, 2 bytes length per NAL */
fail_unless_equals_int (gst_buffer_get_size (buffer), 12 + 1 +
(2 + sizeof (h264_sps) - 4) +
(2 + sizeof (h264_pps) - 4) + (2 + sizeof (h264_idr_slice_1) - 4));
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
gst_harness_teardown (h);
}
GST_END_TEST;
GST_START_TEST (test_rtph264pay_avc)
{
GstHarness *h = gst_harness_new_parse ("rtph264pay timestamp-offset=123"
" aggregate-mode=zero-latency");
GstFlowReturn ret;
GstBuffer *buffer;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
GstCaps *caps;
GstBuffer *codec_data;
codec_data = create_codec_data (h264_sps_avc, sizeof (h264_sps_avc) - 4,
h264_pps_avc, sizeof (h264_pps_avc) - 4);
caps = gst_caps_from_string ("video/x-h264,alignment=au,stream-format=avc");
gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, codec_data, NULL);
gst_buffer_unref (codec_data);
GST_DEBUG ("caps are %" GST_PTR_FORMAT, caps);
gst_harness_set_src_caps (h, caps);
ret = gst_harness_push (h, wrap_static_buffer (h264_idr_slice_1_avc,
sizeof (h264_idr_slice_1_avc)));
fail_unless_equals_int (ret, GST_FLOW_OK);
buffer =
wrap_static_buffer (h264_idr_slice_2_avc, sizeof (h264_idr_slice_2_avc));
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_MARKER);
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2);
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless (gst_rtp_buffer_get_marker (&rtp));
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
fail_unless (gst_rtp_buffer_get_marker (&rtp));
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
gst_harness_teardown (h);
}
GST_END_TEST;
/*
* +------------------------------------------------+
* | GstBuffer |
* +------------------------------------------------+
* | GstMemory 1 | GstMemory 2 |
* +------------------------------------------------+
* | Slice 1 Part 1 | Slice 1 Part2, Slice 2 |
* +------------------------------------------------+
*
* "Slice 1 Part 1" is of size @memory1_len
*
*/
static void
test_rtph264pay_avc_two_slices (gsize memory1_len, guint num_slices)
{
GstHarness *h = gst_harness_new_parse ("rtph264pay timestamp-offset=123"
" aggregate-mode=zero-latency");
GstFlowReturn ret;
GstBuffer *slice1;
GstBuffer *slice2;
GstBuffer *buffer;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
GstCaps *caps;
GstBuffer *codec_data;
guint8 *rest_of_image;
gsize rest_of_slice_1_size;
gsize rest_of_image_size;
fail_unless (num_slices <= 2);
codec_data = create_codec_data (h264_sps_avc, sizeof (h264_sps_avc) - 4,
h264_pps_avc, sizeof (h264_pps_avc) - 4);
caps = gst_caps_from_string ("video/x-h264,alignment=au,stream-format=avc");
gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, codec_data, NULL);
gst_buffer_unref (codec_data);
GST_DEBUG ("caps are %" GST_PTR_FORMAT, caps);
gst_harness_set_src_caps (h, caps);
slice1 = wrap_static_buffer (h264_idr_slice_1_avc, memory1_len);
rest_of_slice_1_size = sizeof (h264_idr_slice_1_avc) - memory1_len;
if (num_slices == 2) {
rest_of_image_size = rest_of_slice_1_size + sizeof (h264_idr_slice_2_avc);
rest_of_image = g_malloc (rest_of_image_size);
memcpy (rest_of_image, h264_idr_slice_1_avc + memory1_len,
rest_of_slice_1_size);
memcpy (rest_of_image + rest_of_slice_1_size, h264_idr_slice_2_avc,
sizeof (h264_idr_slice_2_avc));
slice2 =
wrap_static_buffer_full (rest_of_image, rest_of_image_size,
rest_of_image, g_free);
buffer = gst_buffer_append (slice1, slice2);
} else
buffer = slice1;
GST_DEBUG ("number of memories: %d", gst_buffer_n_memory (buffer));
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1);
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (buffer);
gst_harness_teardown (h);
}
GST_START_TEST (test_rtph264pay_avc_two_slices_per_buffer)
{
test_rtph264pay_avc_two_slices (1, 2);
test_rtph264pay_avc_two_slices (2, 2);
test_rtph264pay_avc_two_slices (sizeof (h264_idr_slice_1_avc) - 10, 2);
}
GST_END_TEST;
GST_START_TEST (test_rtph264pay_avc_incomplete_nal)
{
test_rtph264pay_avc_two_slices (sizeof (h264_idr_slice_1_avc) - 10, 1);
}
GST_END_TEST;
static Suite *
rtph264_suite (void)
{
Suite *s = suite_create ("rtph264");
TCase *tc_chain;
tc_chain = tcase_create ("rtph264depay");
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_rtph264depay_with_downstream_allocator);
tcase_add_test (tc_chain, test_rtph264depay_eos);
tcase_add_test (tc_chain, test_rtph264depay_marker_to_flag);
tcase_add_test (tc_chain, test_rtph264depay_stap_a_marker);
tcase_add_test (tc_chain, test_rtph264depay_fu_a);
tcase_add_test (tc_chain, test_rtph264depay_fu_a_missing_start);
tc_chain = tcase_create ("rtph264pay");
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_rtph264pay_reserved_nals);
tcase_add_test (tc_chain, test_rtph264pay_two_slices_timestamp);
tcase_add_test (tc_chain, test_rtph264pay_marker_for_flag);
tcase_add_test (tc_chain, test_rtph264pay_marker_for_au);
tcase_add_test (tc_chain, test_rtph264pay_marker_for_fragmented_au);
tcase_add_test (tc_chain, test_rtph264pay_aggregate_two_slices_per_buffer);
tcase_add_test (tc_chain, test_rtph264pay_aggregate_with_aud);
tcase_add_test (tc_chain, test_rtph264pay_aggregate_with_ts_change);
tcase_add_test (tc_chain, test_rtph264pay_aggregate_with_discont);
tcase_add_test (tc_chain, test_rtph264pay_aggregate_until_vcl);
tcase_add_test (tc_chain, test_rtph264pay_avc);
tcase_add_test (tc_chain, test_rtph264pay_avc_two_slices_per_buffer);
tcase_add_test (tc_chain, test_rtph264pay_avc_incomplete_nal);
return s;
}
GST_CHECK_MAIN (rtph264);