diff --git a/gst/rtp/gstbuffermemory.c b/gst/rtp/gstbuffermemory.c new file mode 100644 index 0000000000..3b28417ba4 --- /dev/null +++ b/gst/rtp/gstbuffermemory.c @@ -0,0 +1,116 @@ +/* GStreamer + * Copyright (C) 2020 Ognyan Tonchev + * + * 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 "gstbuffermemory.h" + +gboolean +gst_buffer_memory_map (GstBuffer * buffer, GstBufferMemoryMap * map) +{ + GstMemory *mem; + + g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE); + g_return_val_if_fail (map != NULL, FALSE); + + if (gst_buffer_n_memory (buffer) == 0) { + GST_DEBUG ("no memory blocks in buffer"); + return FALSE; + } + + mem = gst_buffer_get_memory (buffer, 0); + + if (!gst_memory_map (mem, &map->map, GST_MAP_READ)) { + GST_ERROR ("failed to map memory"); + gst_memory_unref (mem); + return FALSE; + } + + map->buf = buffer; + map->mem = mem; + map->data = map->map.data; + map->size = map->map.size; + map->index = 0; + + return TRUE; +} + +static gboolean +buffer_memory_map_next (GstBufferMemoryMap * map) +{ + if (!map->mem) + return FALSE; + + gst_memory_unmap (map->mem, &map->map); + gst_memory_unref (map->mem); + map->mem = NULL; + map->data = NULL; + map->size = 0; + + map->index++; + + if (map->index >= gst_buffer_n_memory (map->buf)) { + GST_DEBUG ("no more memory blocks in buffer"); + return FALSE; + } + + map->mem = gst_buffer_get_memory (map->buf, map->index); + + if (!gst_memory_map (map->mem, &map->map, GST_MAP_READ)) { + GST_ERROR ("failed to map memory"); + gst_memory_unref (map->mem); + map->mem = NULL; + return FALSE; + } + + map->data = map->map.data; + map->size = map->map.size; + + return TRUE; +} + +gboolean +gst_buffer_memory_advance_bytes (GstBufferMemoryMap * map, gsize size) +{ + gsize offset = size; + + g_return_val_if_fail (map != NULL, FALSE); + + while (offset >= map->size) { + offset -= map->size; + GST_DEBUG ("switching memory"); + if (!buffer_memory_map_next (map)) + return FALSE; + } + + map->data += offset; + map->size -= offset; + + return TRUE; +} + +void +gst_buffer_memory_unmap (GstBufferMemoryMap * map) +{ + g_return_if_fail (map != NULL); + + if (map->mem) { + gst_memory_unmap (map->mem, &map->map); + gst_memory_unref (map->mem); + map->mem = NULL; + } +} diff --git a/gst/rtp/gstbuffermemory.h b/gst/rtp/gstbuffermemory.h new file mode 100644 index 0000000000..cc00d4c1b1 --- /dev/null +++ b/gst/rtp/gstbuffermemory.h @@ -0,0 +1,64 @@ +/* GStreamer + * Copyright (C) 2020 Ognyan Tonchev + * + * 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. + */ + +#ifndef __GST_BUFFER_MEMORY_H__ +#define __GST_BUFFER_MEMORY_H__ + +#include + +G_BEGIN_DECLS + +struct _GstBufferMemoryMap +{ + /* private datas */ + + GstBuffer *buf; + GstMemory *mem; + GstMapInfo map; + guint index; + + /* public datas */ + + /* data of the currently mapped memory */ + const guint8 *data; + + /* size of the currently mapped memory */ + gsize size; + + /* When advancing through the data with gst_buffer_memory_advance_bytes () + * the data field is also advanced and the size field decreased with the + * corresponding number of bytes. If all the bytes from the currently mapped + * GstMemory have been consumed then a new GstMemory will be mapped and data + * and size fileds will be updated. + * */ +}; +typedef struct _GstBufferMemoryMap GstBufferMemoryMap; + +G_GNUC_INTERNAL +gboolean gst_buffer_memory_map (GstBuffer * buffer, GstBufferMemoryMap * map); + +G_GNUC_INTERNAL +gboolean gst_buffer_memory_advance_bytes (GstBufferMemoryMap * map, gsize size); + +G_GNUC_INTERNAL +void gst_buffer_memory_unmap (GstBufferMemoryMap * map); + +G_END_DECLS + +#endif /* __GST_BUFFER_MEMORY_H__ */ diff --git a/gst/rtp/gstrtph264pay.c b/gst/rtp/gstrtph264pay.c index b5d3e6ab71..623b68dac2 100644 --- a/gst/rtp/gstrtph264pay.c +++ b/gst/rtp/gstrtph264pay.c @@ -33,6 +33,7 @@ #include "gstrtph264pay.h" #include "gstrtputils.h" +#include "gstbuffermemory.h" #define IDR_TYPE_ID 5 @@ -1342,7 +1343,6 @@ gst_rtp_h264_pay_handle_buffer (GstRTPBasePayload * basepayload, GstFlowReturn ret; gsize size; guint nal_len, i; - GstMapInfo map; const guint8 *data; GstClockTime dts, pts; GArray *nal_queue; @@ -1364,16 +1364,6 @@ gst_rtp_h264_pay_handle_buffer (GstRTPBasePayload * basepayload, /* In AVC mode, there is no adapter, so nothing to drain */ if (draining) return GST_FLOW_OK; - gst_buffer_map (buffer, &map, GST_MAP_READ); - data = map.data; - size = map.size; - pts = GST_BUFFER_PTS (buffer); - dts = GST_BUFFER_DTS (buffer); - rtph264pay->delta_unit = GST_BUFFER_FLAG_IS_SET (buffer, - GST_BUFFER_FLAG_DELTA_UNIT); - rtph264pay->discont = GST_BUFFER_IS_DISCONT (buffer); - marker = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_MARKER); - GST_DEBUG_OBJECT (basepayload, "got %" G_GSIZE_FORMAT " bytes", size); } else { if (buffer) { if (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) { @@ -1417,29 +1407,43 @@ gst_rtp_h264_pay_handle_buffer (GstRTPBasePayload * basepayload, /* now loop over all NAL units and put them in a packet */ if (avc) { + GstBufferMemoryMap memory; + gsize remaining_buffer_size; guint nal_length_size; gsize offset = 0; + gst_buffer_memory_map (buffer, &memory); + remaining_buffer_size = gst_buffer_get_size (buffer); + + pts = GST_BUFFER_PTS (buffer); + dts = GST_BUFFER_DTS (buffer); + rtph264pay->delta_unit = GST_BUFFER_FLAG_IS_SET (buffer, + GST_BUFFER_FLAG_DELTA_UNIT); + rtph264pay->discont = GST_BUFFER_IS_DISCONT (buffer); + marker = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_MARKER); + GST_DEBUG_OBJECT (basepayload, "got %" G_GSIZE_FORMAT " bytes", + remaining_buffer_size); + nal_length_size = rtph264pay->nal_length_size; - while (size > nal_length_size) { + while (remaining_buffer_size > nal_length_size) { gint i; gboolean end_of_au = FALSE; nal_len = 0; for (i = 0; i < nal_length_size; i++) { - nal_len = ((nal_len << 8) + data[i]); + nal_len = (nal_len << 8) + *memory.data; + if (!gst_buffer_memory_advance_bytes (&memory, 1)) + break; } - /* skip the length bytes, make sure we don't run past the buffer size */ - data += nal_length_size; offset += nal_length_size; - size -= nal_length_size; + remaining_buffer_size -= nal_length_size; - if (size >= nal_len) { + if (remaining_buffer_size >= nal_len) { GST_DEBUG_OBJECT (basepayload, "got NAL of size %u", nal_len); } else { - nal_len = size; + nal_len = remaining_buffer_size; GST_DEBUG_OBJECT (basepayload, "got incomplete NAL of size %u", nal_len); } @@ -1447,7 +1451,7 @@ gst_rtp_h264_pay_handle_buffer (GstRTPBasePayload * basepayload, /* If we're at the end of the buffer, then we're at the end of the * access unit */ - if (size - nal_len <= nal_length_size) { + if (remaining_buffer_size - nal_len <= nal_length_size) { if (rtph264pay->alignment == GST_H264_ALIGNMENT_AU || marker) end_of_au = TRUE; } @@ -1469,10 +1473,19 @@ gst_rtp_h264_pay_handle_buffer (GstRTPBasePayload * basepayload, if (ret != GST_FLOW_OK) break; - data += nal_len; + /* Skip current nal. If it is split over multiple GstMemory + * advance_bytes () will switch to the correct GstMemory. The payloader + * does not access those bytes directly but uses gst_buffer_copy_region () + * to create a sub-buffer referencing the nal instead */ + if (!gst_buffer_memory_advance_bytes (&memory, nal_len)) + break; + offset += nal_len; - size -= nal_len; + remaining_buffer_size -= nal_len; } + + gst_buffer_memory_unmap (&memory); + gst_buffer_unref (buffer); } else { guint next; gboolean update = FALSE; @@ -1638,10 +1651,7 @@ gst_rtp_h264_pay_handle_buffer (GstRTPBasePayload * basepayload, done: - if (avc) { - gst_buffer_unmap (buffer, &map); - gst_buffer_unref (buffer); - } else { + if (!avc) { gst_adapter_unmap (rtph264pay->adapter); } diff --git a/gst/rtp/gstrtph265pay.c b/gst/rtp/gstrtph265pay.c index d5285f0510..420f164b88 100644 --- a/gst/rtp/gstrtph265pay.c +++ b/gst/rtp/gstrtph265pay.c @@ -34,6 +34,7 @@ #include "gstrtph265pay.h" #include "gstrtputils.h" +#include "gstbuffermemory.h" #define AP_TYPE_ID 48 #define FU_TYPE_ID 49 @@ -1424,7 +1425,6 @@ gst_rtp_h265_pay_handle_buffer (GstRTPBasePayload * basepayload, GstFlowReturn ret; gsize size; guint nal_len, i; - GstMapInfo map; const guint8 *data; GstClockTime dts, pts; GArray *nal_queue; @@ -1446,13 +1446,6 @@ gst_rtp_h265_pay_handle_buffer (GstRTPBasePayload * basepayload, /* In hevc mode, there is no adapter, so nothing to drain */ if (draining) return GST_FLOW_OK; - gst_buffer_map (buffer, &map, GST_MAP_READ); - data = map.data; - size = map.size; - pts = GST_BUFFER_PTS (buffer); - dts = GST_BUFFER_DTS (buffer); - marker = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_MARKER); - GST_DEBUG_OBJECT (basepayload, "got %" G_GSIZE_FORMAT " bytes", size); } else { if (buffer) { if (gst_adapter_available (rtph265pay->adapter) == 0) @@ -1478,6 +1471,8 @@ gst_rtp_h265_pay_handle_buffer (GstRTPBasePayload * basepayload, /* now loop over all NAL units and put them in a packet */ if (hevc) { + GstBufferMemoryMap memory; + gsize remaining_buffer_size; guint nal_length_size; gsize offset = 0; GPtrArray *paybufs; @@ -1485,23 +1480,32 @@ gst_rtp_h265_pay_handle_buffer (GstRTPBasePayload * basepayload, paybufs = g_ptr_array_new (); nal_length_size = rtph265pay->nal_length_size; - while (size > nal_length_size) { + gst_buffer_memory_map (buffer, &memory); + remaining_buffer_size = gst_buffer_get_size (buffer); + + pts = GST_BUFFER_PTS (buffer); + dts = GST_BUFFER_DTS (buffer); + marker = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_MARKER); + GST_DEBUG_OBJECT (basepayload, "got %" G_GSIZE_FORMAT " bytes", + remaining_buffer_size); + + while (remaining_buffer_size > nal_length_size) { gint i; nal_len = 0; for (i = 0; i < nal_length_size; i++) { - nal_len = ((nal_len << 8) + data[i]); + nal_len = (nal_len << 8) + *memory.data; + if (!gst_buffer_memory_advance_bytes (&memory, 1)) + break; } - /* skip the length bytes, make sure we don't run past the buffer size */ - data += nal_length_size; offset += nal_length_size; - size -= nal_length_size; + remaining_buffer_size -= nal_length_size; - if (size >= nal_len) { + if (remaining_buffer_size >= nal_len) { GST_DEBUG_OBJECT (basepayload, "got NAL of size %u", nal_len); } else { - nal_len = size; + nal_len = remaining_buffer_size; GST_DEBUG_OBJECT (basepayload, "got incomplete NAL of size %u", nal_len); } @@ -1514,7 +1518,7 @@ gst_rtp_h265_pay_handle_buffer (GstRTPBasePayload * basepayload, * access unit */ GST_BUFFER_FLAG_UNSET (paybuf, GST_BUFFER_FLAG_MARKER); - if (size - nal_len <= nal_length_size) { + if (remaining_buffer_size - nal_len <= nal_length_size) { if (rtph265pay->alignment == GST_H265_ALIGNMENT_AU || marker) GST_BUFFER_FLAG_SET (paybuf, GST_BUFFER_FLAG_MARKER); } @@ -1525,11 +1529,19 @@ gst_rtp_h265_pay_handle_buffer (GstRTPBasePayload * basepayload, discont = FALSE; } - data += nal_len; + /* Skip current nal. If it is split over multiple GstMemory + * advance_bytes () will switch to the correct GstMemory. The payloader + * does not access those bytes directly but uses gst_buffer_copy_region () + * to create a sub-buffer referencing the nal instead */ + if (!gst_buffer_memory_advance_bytes (&memory, nal_len)) + break; offset += nal_len; - size -= nal_len; + remaining_buffer_size -= nal_len; } ret = gst_rtp_h265_pay_payload_nal (basepayload, paybufs, dts, pts); + + gst_buffer_memory_unmap (&memory); + gst_buffer_unref (buffer); } else { guint next; gboolean update = FALSE; @@ -1658,10 +1670,7 @@ gst_rtp_h265_pay_handle_buffer (GstRTPBasePayload * basepayload, } done: - if (hevc) { - gst_buffer_unmap (buffer, &map); - gst_buffer_unref (buffer); - } else { + if (!hevc) { gst_adapter_unmap (rtph265pay->adapter); } diff --git a/gst/rtp/meson.build b/gst/rtp/meson.build index 5bfdc3be7c..d57a195aeb 100644 --- a/gst/rtp/meson.build +++ b/gst/rtp/meson.build @@ -1,6 +1,7 @@ rtp_sources = [ 'dboolhuff.c', 'fnv1hash.c', + 'gstbuffermemory.c', 'gstrtp.c', 'gstrtpchannels.c', 'gstrtpac3depay.c', diff --git a/tests/check/elements/rtph264.c b/tests/check/elements/rtph264.c index 51bd041807..2d7e877156 100644 --- a/tests/check/elements/rtph264.c +++ b/tests/check/elements/rtph264.c @@ -199,6 +199,53 @@ c_mem_app_sink_class_init (CMemAppSinkClass * klass) #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; @@ -298,21 +345,36 @@ GST_END_TEST; static GstBuffer * -wrap_static_buffer_with_pts (guint8 * buf, gsize size, GstClockTime pts) +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, NULL, NULL); + 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_with_pts (buf, size, GST_CLOCK_TIME_NONE); + return wrap_static_buffer_full (buf, size, NULL, NULL); } /* This was generated using pipeline: @@ -527,6 +589,40 @@ static guint8 h264_idr_slice_2[] = { 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) @@ -1092,6 +1188,144 @@ GST_START_TEST (test_rtph264pay_aggregate_until_vcl) GST_END_TEST; +GST_START_TEST (test_rtph264pay_avc) +{ + GstHarness *h = gst_harness_new_parse ("rtph264pay timestamp-offset=123"); + 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"); + 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) { @@ -1118,6 +1352,10 @@ rtph264_suite (void) 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; }