From 586fc57e55796da55ceb4cf3599affe0f3a354de Mon Sep 17 00:00:00 2001 From: =?utf8?q?Kristofer=20Bj=C3=B6rkstr=C3=B6m?= Date: Wed, 1 Apr 2020 13:19:46 +0200 Subject: [PATCH] rtpjpeg: Use gst_memory_map() instead of gst_buffer_map() gst_buffer_map () results in memcopying when a GstBuffer contains more than one GstMemory. This has quite an impact on performance on systems with limited amount of resources. With this patch the whole GstBuffer will not be mapped at once, instead each individual GstMemory will be iterated and mapped separately. --- gst/rtp/gstrtpjpegpay.c | 225 +++++++++++++++----------- tests/check/elements/rtpjpeg.c | 353 +++++++++++++++++++++++++++++++++++++++++ tests/check/meson.build | 1 + 3 files changed, 490 insertions(+), 89 deletions(-) create mode 100644 tests/check/elements/rtpjpeg.c diff --git a/gst/rtp/gstrtpjpegpay.c b/gst/rtp/gstrtpjpegpay.c index 1b5aecff..31f8efa 100644 --- a/gst/rtp/gstrtpjpegpay.c +++ b/gst/rtp/gstrtpjpegpay.c @@ -43,6 +43,7 @@ #include "gstrtpjpegpay.h" #include "gstrtputils.h" +#include "gstbuffermemory.h" static GstStaticPadTemplate gst_rtp_jpeg_pay_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", @@ -383,45 +384,76 @@ invalid_framerate: } } +/* + * get uint16 value from current position in mapped memory. + * the memory offset will be increased with 2. + */ static guint -gst_rtp_jpeg_pay_header_size (const guint8 * data, guint offset) +parse_mem_inc_offset_guint16 (GstBufferMemoryMap * memory) { - return data[offset] << 8 | data[offset + 1]; + guint data; + + g_return_val_if_fail (memory->total_size > (memory->offset + 1), 0); + + data = ((guint) * memory->data) << 8; + gst_buffer_memory_advance_bytes (memory, 1); + data = data | (*memory->data); + gst_buffer_memory_advance_bytes (memory, 1); + + return data; } +/* + * get uint8 value from current position in mapped memory. + * the memory offset will be increased with 1. + */ static guint -gst_rtp_jpeg_pay_read_quant_table (const guint8 * data, guint size, - guint offset, RtpQuantTable tables[]) +parse_mem_inc_offset_guint8 (GstBufferMemoryMap * memory) +{ + guint data; + + g_return_val_if_fail (memory->total_size > memory->offset, 0); + + data = (*memory->data); + gst_buffer_memory_advance_bytes (memory, 1); + + return data; +} + +static void +gst_rtp_jpeg_pay_read_quant_table (GstBufferMemoryMap * memory, + RtpQuantTable tables[]) { guint quant_size, tab_size; guint8 prec; guint8 id; - if (offset + 2 > size) + if (memory->total_size <= (memory->offset + 1)) goto too_small; - quant_size = gst_rtp_jpeg_pay_header_size (data, offset); + quant_size = parse_mem_inc_offset_guint16 (memory); if (quant_size < 2) goto small_quant_size; /* clamp to available data */ - if (offset + quant_size > size) - quant_size = size - offset; + if (memory->offset + quant_size > memory->total_size) + quant_size = memory->total_size - memory->offset; - offset += 2; quant_size -= 2; while (quant_size > 0) { + guint8 data; /* not enough to read the id */ - if (offset + 1 > size) + if (memory->offset + 1 > memory->total_size) break; - id = data[offset] & 0x0f; + data = parse_mem_inc_offset_guint8 (memory); + id = data & 0x0f; if (id == 15) /* invalid id received - corrupt data */ goto invalid_id; - prec = (data[offset] & 0xf0) >> 4; + prec = (data & 0xf0) >> 4; if (prec) tab_size = 128; else @@ -434,25 +466,26 @@ gst_rtp_jpeg_pay_read_quant_table (const guint8 * data, guint size, GST_LOG ("read quant table %d, tab_size %d, prec %02x", id, tab_size, prec); tables[id].size = tab_size; - tables[id].data = &data[offset + 1]; + tables[id].data = memory->data; - tab_size += 1; - quant_size -= tab_size; - offset += tab_size; + quant_size -= (tab_size + 1); + if (!gst_buffer_memory_advance_bytes (memory, tab_size)) { + goto too_small; + } } done: - return offset + quant_size; + return; /* ERRORS */ too_small: { GST_WARNING ("not enough data"); - return size; + return; } small_quant_size: { GST_WARNING ("quant_size too small (%u < 2)", quant_size); - return size; + return; } invalid_id: { @@ -468,38 +501,31 @@ no_table: } static gboolean -gst_rtp_jpeg_pay_read_sof (GstRtpJPEGPay * pay, const guint8 * data, - guint size, guint * offset, CompInfo info[], RtpQuantTable tables[], - gulong tables_elements) +gst_rtp_jpeg_pay_read_sof (GstRtpJPEGPay * pay, GstBufferMemoryMap * memory, + CompInfo info[], RtpQuantTable tables[], gulong tables_elements) { guint sof_size, off; guint width, height, infolen; CompInfo elem; gint i, j; - off = *offset; + off = memory->offset; /* we need at least 17 bytes for the SOF */ - if (off + 17 > size) + if (off + 17 > memory->total_size) goto wrong_size; - sof_size = gst_rtp_jpeg_pay_header_size (data, off); + sof_size = parse_mem_inc_offset_guint16 (memory); if (sof_size < 17) goto wrong_length; - *offset += sof_size; - - /* skip size */ - off += 2; - /* precision should be 8 */ - if (data[off++] != 8) + if (parse_mem_inc_offset_guint8 (memory) != 8) goto bad_precision; /* read dimensions */ - height = data[off] << 8 | data[off + 1]; - width = data[off + 2] << 8 | data[off + 3]; - off += 4; + height = parse_mem_inc_offset_guint16 (memory); + width = parse_mem_inc_offset_guint16 (memory); GST_LOG_OBJECT (pay, "got dimensions %ux%u", height, width); @@ -525,14 +551,14 @@ gst_rtp_jpeg_pay_read_sof (GstRtpJPEGPay * pay, const guint8 * data, } /* we only support 3 components */ - if (data[off++] != 3) + if (parse_mem_inc_offset_guint8 (memory) != 3) goto bad_components; infolen = 0; for (i = 0; i < 3; i++) { - elem.id = data[off++]; - elem.samp = data[off++]; - elem.qt = data[off++]; + elem.id = parse_mem_inc_offset_guint8 (memory); + elem.samp = parse_mem_inc_offset_guint8 (memory); + elem.qt = parse_mem_inc_offset_guint8 (memory); GST_LOG_OBJECT (pay, "got comp %d, samp %02x, qt %d", elem.id, elem.samp, elem.qt); /* insertion sort from the last element to the first */ @@ -565,7 +591,8 @@ gst_rtp_jpeg_pay_read_sof (GstRtpJPEGPay * pay, const guint8 * data, wrong_size: { GST_ELEMENT_WARNING (pay, STREAM, FORMAT, - ("Wrong size %u (needed %u).", size, off + 17), (NULL)); + ("Wrong size %u (needed %u).", (guint) memory->total_size, off + 17), + (NULL)); return FALSE; } wrong_length: @@ -600,57 +627,81 @@ invalid_comp: } static gboolean -gst_rtp_jpeg_pay_read_dri (GstRtpJPEGPay * pay, const guint8 * data, - guint size, guint * offset, RtpRestartMarkerHeader * dri) +gst_rtp_jpeg_pay_read_dri (GstRtpJPEGPay * pay, GstBufferMemoryMap * memory, + RtpRestartMarkerHeader * dri) { - guint dri_size, off; - - off = *offset; + guint dri_size, restart_interval; /* we need at least 4 bytes for the DRI */ - if (off + 4 > size) + if (memory->offset + 4 > memory->total_size) goto wrong_size; - dri_size = gst_rtp_jpeg_pay_header_size (data, off); + dri_size = parse_mem_inc_offset_guint16 (memory); if (dri_size < 4) goto wrong_length; - *offset += dri_size; - off += 2; - - dri->restart_interval = g_htons ((data[off] << 8) | (data[off + 1])); + restart_interval = parse_mem_inc_offset_guint16 (memory); + dri->restart_interval = g_htons (restart_interval); dri->restart_count = g_htons (0xFFFF); + if (!gst_buffer_memory_advance_bytes (memory, dri_size - 4)) { + goto wrong_size; + } return dri->restart_interval > 0; wrong_size: { GST_WARNING ("not enough data for DRI"); - *offset = size; return FALSE; } wrong_length: { GST_WARNING ("DRI size too small (%u)", dri_size); - *offset += dri_size; + /* offset got incremented by two when dri_size was parsed. */ + if (dri_size > 2) + gst_buffer_memory_advance_bytes (memory, dri_size - 2); return FALSE; } } +static void +gst_rtp_jpeg_pay_skipping_marker (GstBufferMemoryMap * memory) +{ + guint skip; + + if (G_UNLIKELY (((memory->offset + 1) >= memory->total_size))) { + goto wrong_size; + } + skip = parse_mem_inc_offset_guint16 (memory); + + if (G_UNLIKELY (((skip - 2 + memory->offset) > memory->total_size))) { + goto wrong_size; + } + if (skip > 2) { + gst_buffer_memory_advance_bytes (memory, skip - 2); + } + return; + +wrong_size: + { + GST_WARNING ("not enough data"); + } +} + static RtpJpegMarker -gst_rtp_jpeg_pay_scan_marker (const guint8 * data, guint size, guint * offset) +gst_rtp_jpeg_pay_scan_marker (GstBufferMemoryMap * memory) { - while ((data[(*offset)++] != JPEG_MARKER) && ((*offset) < size)); + guint8 marker = parse_mem_inc_offset_guint8 (memory); + + while (marker != JPEG_MARKER && ((memory->offset) < memory->total_size)) { + marker = parse_mem_inc_offset_guint8 (memory); + } - if (G_UNLIKELY ((*offset) >= size)) { + if (G_UNLIKELY ((memory->offset) >= memory->total_size)) { GST_LOG ("found EOI marker"); return JPEG_MARKER_EOI; } else { - guint8 marker; - - marker = data[*offset]; - GST_LOG ("found 0x%02x marker at offset %u", marker, *offset); - (*offset)++; + marker = parse_mem_inc_offset_guint8 (memory); return marker; } } @@ -670,9 +721,6 @@ gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload, RtpQuantTable tables[15] = { {0, NULL}, }; CompInfo info[3] = { {0,}, }; guint quant_data_size; - GstMapInfo map; - guint8 *data; - gsize size; guint mtu, max_payload_size; guint bytes_left; guint jpeg_header_size = 0; @@ -682,19 +730,19 @@ gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload, gint i; GstBufferList *list = NULL; gboolean discont; + GstBufferMemoryMap memory; pay = GST_RTP_JPEG_PAY (basepayload); mtu = GST_RTP_BASE_PAYLOAD_MTU (pay); - gst_buffer_map (buffer, &map, GST_MAP_READ); - data = map.data; - size = map.size; + gst_buffer_memory_map (buffer, &memory); + timestamp = GST_BUFFER_PTS (buffer); - offset = 0; discont = GST_BUFFER_IS_DISCONT (buffer); GST_LOG_OBJECT (pay, "got buffer size %" G_GSIZE_FORMAT - " , timestamp %" GST_TIME_FORMAT, size, GST_TIME_ARGS (timestamp)); + " , timestamp %" GST_TIME_FORMAT, memory.total_size, + GST_TIME_ARGS (timestamp)); /* parse the jpeg header for 'start of scan' and read quant tables if needed */ sos_found = FALSE; @@ -702,33 +750,35 @@ gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload, sof_found = FALSE; dri_found = FALSE; - while (!sos_found && (offset < size)) { + while (!sos_found && (memory.offset < memory.total_size)) { gint marker; - GST_LOG_OBJECT (pay, "checking from offset %u", offset); - switch ((marker = gst_rtp_jpeg_pay_scan_marker (data, size, &offset))) { + GST_LOG_OBJECT (pay, "checking from offset %u", memory.offset); + marker = gst_rtp_jpeg_pay_scan_marker (&memory); + switch (marker) { case JPEG_MARKER_JFIF: case JPEG_MARKER_CMT: case JPEG_MARKER_DHT: case JPEG_MARKER_H264: GST_LOG_OBJECT (pay, "skipping marker"); - offset += gst_rtp_jpeg_pay_header_size (data, offset); + gst_rtp_jpeg_pay_skipping_marker (&memory); break; case JPEG_MARKER_SOF: - if (!gst_rtp_jpeg_pay_read_sof (pay, data, size, &offset, info, tables, + if (!gst_rtp_jpeg_pay_read_sof (pay, &memory, info, tables, G_N_ELEMENTS (tables))) goto invalid_format; sof_found = TRUE; break; case JPEG_MARKER_DQT: GST_LOG ("DQT found"); - offset = gst_rtp_jpeg_pay_read_quant_table (data, size, offset, tables); + gst_rtp_jpeg_pay_read_quant_table (&memory, tables); dqt_found = TRUE; break; case JPEG_MARKER_SOS: sos_found = TRUE; GST_LOG_OBJECT (pay, "SOS found"); - jpeg_header_size = offset + gst_rtp_jpeg_pay_header_size (data, offset); + jpeg_header_size = + memory.offset + parse_mem_inc_offset_guint16 (&memory); break; case JPEG_MARKER_EOI: GST_WARNING_OBJECT (pay, "EOI reached before SOS!"); @@ -738,8 +788,7 @@ gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload, break; case JPEG_MARKER_DRI: GST_LOG_OBJECT (pay, "DRI found"); - if (gst_rtp_jpeg_pay_read_dri (pay, data, size, &offset, - &restart_marker_header)) + if (gst_rtp_jpeg_pay_read_dri (pay, &memory, &restart_marker_header)) dri_found = TRUE; break; default: @@ -747,8 +796,9 @@ gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload, (marker >= JPEG_MARKER_JPG0 && marker <= JPEG_MARKER_JPG13) || (marker >= JPEG_MARKER_APP0 && marker <= JPEG_MARKER_APP15)) { GST_LOG_OBJECT (pay, "skipping marker"); - offset += gst_rtp_jpeg_pay_header_size (data, offset); + gst_rtp_jpeg_pay_skipping_marker (&memory); } else { + /* no need to do anything, gst_rtp_jpeg_pay_scan_marker will go on */ GST_FIXME_OBJECT (pay, "unhandled marker 0x%02x", marker); } break; @@ -765,8 +815,6 @@ gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload, GST_LOG_OBJECT (pay, "header size %u", jpeg_header_size); - size -= jpeg_header_size; - data += jpeg_header_size; offset = 0; if (dri_found) @@ -778,7 +826,6 @@ gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload, jpeg_header.q = pay->quant; jpeg_header.width = pay->width; jpeg_header.height = pay->height; - /* collect the quant headers sizes */ quant_header.mbz = 0; quant_header.precision = 0; @@ -809,7 +856,9 @@ gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload, GST_LOG_OBJECT (pay, "quant_data size %u", quant_data_size); - bytes_left = sizeof (jpeg_header) + quant_data_size + size; + bytes_left = + sizeof (jpeg_header) + quant_data_size + memory.total_size - + jpeg_header_size; if (dri_found) bytes_left += sizeof (restart_marker_header); @@ -912,14 +961,12 @@ gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload, bytes_left -= payload_size; offset += payload_size; - data += payload_size; } while (!frame_done); - /* push the whole buffer list at once */ ret = gst_rtp_base_payload_push_list (basepayload, list); - gst_buffer_unmap (buffer, &map); + gst_buffer_memory_unmap (&memory); gst_buffer_unref (buffer); return ret; @@ -928,28 +975,28 @@ gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload, unsupported_jpeg: { GST_ELEMENT_WARNING (pay, STREAM, FORMAT, ("Unsupported JPEG"), (NULL)); - gst_buffer_unmap (buffer, &map); + gst_buffer_memory_unmap (&memory); gst_buffer_unref (buffer); return GST_FLOW_OK; } no_dimension: { GST_ELEMENT_WARNING (pay, STREAM, FORMAT, ("No size given"), (NULL)); - gst_buffer_unmap (buffer, &map); + gst_buffer_memory_unmap (&memory); gst_buffer_unref (buffer); return GST_FLOW_OK; } invalid_format: { /* error was posted */ - gst_buffer_unmap (buffer, &map); + gst_buffer_memory_unmap (&memory); gst_buffer_unref (buffer); return GST_FLOW_OK; } invalid_quant: { GST_ELEMENT_WARNING (pay, STREAM, FORMAT, ("Invalid quant tables"), (NULL)); - gst_buffer_unmap (buffer, &map); + gst_buffer_memory_unmap (&memory); gst_buffer_unref (buffer); return GST_FLOW_OK; } diff --git a/tests/check/elements/rtpjpeg.c b/tests/check/elements/rtpjpeg.c new file mode 100644 index 0000000..a12f7ab --- /dev/null +++ b/tests/check/elements/rtpjpeg.c @@ -0,0 +1,353 @@ +/* GStreamer RTP jpeg unit test + * + * Copyright (C) 2020 Kristofer Bjorkstrom + * + * 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 +#include +#include + + +/* one complete blank jpeg 1x1 */ +static const guint8 rtp_jpeg_frame_data[] = + { /* SOI */ 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x60, + 0x00, 0x60, 0x00, 0x00, /* DQT */ 0xff, 0xdb, 0x00, 0x43, 0x00, 0x08, 0x06, + 0x06, 0x07, 0x06, 0x05, 0x08, + 0x07, 0x07, 0x07, 0x09, 0x09, 0x08, 0x0a, 0x0c, 0x14, 0x0d, 0x0c, 0x0b, 0x0b, + 0x0c, 0x19, 0x12, + 0x13, 0x0f, 0x14, 0x1d, 0x1a, 0x1f, 0x1e, 0x1d, 0x1a, 0x1c, 0x1c, 0x20, 0x24, + 0x2e, 0x27, 0x20, + 0x22, 0x2c, 0x23, 0x1c, 0x1c, 0x28, 0x37, 0x29, 0x2c, 0x30, 0x31, 0x34, 0x34, + 0x34, 0x1f, 0x27, + 0x39, 0x3d, 0x38, 0x32, 0x3c, 0x2e, 0x33, 0x34, 0x32, 0xff, 0xdb, 0x00, 0x43, + 0x01, 0x09, 0x09, + 0x09, 0x0c, 0x0b, 0x0c, 0x18, 0x0d, 0x0d, 0x18, 0x32, 0x21, 0x1c, 0x21, 0x32, + 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, /* SOF */ 0xff, 0xc0, + 0x00, 0x11, 0x08, 0x00, 0x01, 0x00, 0x01, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, + 0x01, 0x03, 0x11, + 0x01, 0xff, 0xc4, 0x00, 0x1f, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, + 0x04, 0x03, 0x05, + 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, + 0x05, 0x12, 0x21, + 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, + 0xa1, 0x08, 0x23, + 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, + 0x0a, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, + 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, + 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, + 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xf1, + 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc4, 0x00, 0x1f, + 0x01, 0x00, 0x03, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xff, 0xc4, 0x00, + 0xb5, 0x11, 0x00, + 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, + 0x02, 0x77, 0x00, + 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, + 0x61, 0x71, 0x13, + 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, + 0x52, 0xf0, 0x15, + 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, + 0x1a, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, + 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, + 0x86, 0x87, 0x88, + 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, + 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, + 0xc2, 0xc3, 0xc4, + 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, + 0xd9, 0xda, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, + 0xf7, 0xf8, 0xf9, + 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, + 0x3f, 0x00, 0xf7, + 0xfa, 0x28, 0xa2, 0x80, 0x3f, 0xff, 0xd9 +}; + +/* first slice of one complete blank jpeg 1x1 */ +static const guint8 rtp_jpeg_frame_data_s1[] = { +/* SOI */ 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x60, + 0x00, 0x60, 0x00 +}; + +/* second slice of one complete blank jpeg 1x1 */ +static const guint8 rtp_jpeg_frame_data_s2[] = { + 0x00, /* DQT */ 0xff, 0xdb, 0x00, 0x43, 0x00, 0x08, 0x06, 0x06, 0x07, 0x06, + 0x05, 0x08, + 0x07, 0x07, 0x07, 0x09, 0x09, 0x08, 0x0a, 0x0c, 0x14, 0x0d, 0x0c, 0x0b, 0x0b, + 0x0c, 0x19, 0x12, + 0x13, 0x0f, 0x14, 0x1d, 0x1a, 0x1f, 0x1e, 0x1d, 0x1a, 0x1c, 0x1c, 0x20, 0x24, + 0x2e, 0x27, 0x20, + 0x22, 0x2c, 0x23, 0x1c, 0x1c, 0x28, 0x37, 0x29, 0x2c, 0x30, 0x31, 0x34, 0x34, + 0x34, 0x1f, 0x27, + 0x39, 0x3d, 0x38, 0x32, 0x3c, 0x2e, 0x33, 0x34, 0x32, 0xff, 0xdb, 0x00, 0x43, + 0x01, 0x09, 0x09, + 0x09, 0x0c, 0x0b, 0x0c, 0x18, 0x0d, 0x0d, 0x18, 0x32, 0x21, 0x1c, 0x21, 0x32, + 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32 +}; + +/* third slice of one complete blank jpeg 1x1 */ +static const guint8 rtp_jpeg_frame_data_s3[] = { + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, /* SOF */ 0xff, 0xc0, + 0x00, 0x11, 0x08, 0x00, 0x01, 0x00, 0x01, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, + 0x01, 0x03, 0x11, + 0x01, 0xff, 0xc4, 0x00, 0x1f, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, + 0x0a, 0x0b +}; + +/* fourth slice of one complete blank jpeg 1x1 */ +static const guint8 rtp_jpeg_frame_data_s4[] = { + 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x05, + 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, + 0x05, 0x12, 0x21, + 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, + 0xa1, 0x08, 0x23, + 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, + 0x0a, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, + 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, + 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, + 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xf1, + 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff +}; + +/* fifth and last slice of one complete blank jpeg 1x1 */ +static const guint8 rtp_jpeg_frame_data_s5[] = { + 0xc4, 0x00, 0x1f, 0x01, 0x00, 0x03, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xff, 0xc4, 0x00, + 0xb5, 0x11, 0x00, + 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, + 0x02, 0x77, 0x00, + 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, + 0x61, 0x71, 0x13, + 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, + 0x52, 0xf0, 0x15, + 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, + 0x1a, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, + 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, + 0x86, 0x87, 0x88, + 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, + 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, + 0xc2, 0xc3, 0xc4, + 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, + 0xd9, 0xda, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, + 0xf7, 0xf8, 0xf9, + 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, + 0x3f, 0x00, 0xf7, + 0xfa, 0x28, 0xa2, 0x80, 0x3f, 0xff, 0xd9 +}; + + +/* + * rfc2435 3.1. JPEG header + * + * Each packet contains a special JPEG header which immediately follows + * the RTP header. The first 8 bytes of this header, called the "main + * JPEG header", are as follows: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type-specific | Fragment Offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Q | Width | Height | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + */ + +GST_START_TEST (test_rtpjpegpay_1_slice) +{ + GstFlowReturn ret; + GstBuffer *buffer; + GstRTPBuffer rtp = GST_RTP_BUFFER_INIT; + GstCaps *caps = gst_caps_from_string ("video/x-jpeg,height=1,width=1"); + gchar *s = g_strdup_printf ("rtpjpegpay"); + GstHarness *h = gst_harness_new_parse (s); + guint8 *payload; + + gst_harness_set_src_caps (h, caps); + g_free (s); + + buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, + (guint8 *) rtp_jpeg_frame_data, sizeof (rtp_jpeg_frame_data), 0, + sizeof (rtp_jpeg_frame_data), NULL, NULL); + + 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 (payload = gst_rtp_buffer_get_payload (&rtp)); + + /* verify JPEG header */ + fail_unless (GST_READ_UINT24_BE (&payload[1]) == 0); /* offset */ + fail_unless (payload[4] == 1); /* type */ + fail_unless (payload[6] == 1); /* Width */ + fail_unless (payload[7] == 1); /* Height */ + + 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_rtpjpegpay_5_slices) +{ + GstFlowReturn ret; + GstBuffer *buffer; + GstBuffer *buffer_s1; + GstBuffer *buffer_s2; + GstBuffer *buffer_s3; + GstBuffer *buffer_s4; + GstBuffer *buffer_s5; + GstRTPBuffer rtp = GST_RTP_BUFFER_INIT; + GstCaps *caps = gst_caps_from_string ("video/x-jpeg,height=1,width=1"); + gchar *s = g_strdup_printf ("rtpjpegpay"); + GstHarness *h = gst_harness_new_parse (s); + guint8 *payload; + + gst_harness_set_src_caps (h, caps); + g_free (s); + + buffer_s1 = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, + (guint8 *) rtp_jpeg_frame_data_s1, sizeof (rtp_jpeg_frame_data_s1), 0, + sizeof (rtp_jpeg_frame_data_s1), NULL, NULL); + buffer_s2 = + gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, + (guint8 *) rtp_jpeg_frame_data_s2, sizeof (rtp_jpeg_frame_data_s2), 0, + sizeof (rtp_jpeg_frame_data_s2), NULL, NULL); + buffer_s3 = + gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, + (guint8 *) rtp_jpeg_frame_data_s3, sizeof (rtp_jpeg_frame_data_s3), 0, + sizeof (rtp_jpeg_frame_data_s3), NULL, NULL); + buffer_s4 = + gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, + (guint8 *) rtp_jpeg_frame_data_s4, sizeof (rtp_jpeg_frame_data_s4), 0, + sizeof (rtp_jpeg_frame_data_s4), NULL, NULL); + buffer_s5 = + gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, + (guint8 *) rtp_jpeg_frame_data_s5, sizeof (rtp_jpeg_frame_data_s5), 0, + sizeof (rtp_jpeg_frame_data_s5), NULL, NULL); + + + buffer = gst_buffer_append (buffer_s1, buffer_s2); + buffer = gst_buffer_append (buffer, buffer_s3); + buffer = gst_buffer_append (buffer, buffer_s4); + buffer = gst_buffer_append (buffer, buffer_s5); + + 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 (payload = gst_rtp_buffer_get_payload (&rtp)); + + /* verify JPEG header */ + fail_unless (GST_READ_UINT24_BE (&payload[1]) == 0); /* offset */ + fail_unless (payload[4] == 1); /* type */ + fail_unless (payload[6] == 1); /* Width */ + fail_unless (payload[7] == 1); /* Height */ + + fail_unless (gst_rtp_buffer_get_marker (&rtp)); + gst_rtp_buffer_unmap (&rtp); + gst_buffer_unref (buffer); + + gst_harness_teardown (h); +} + +GST_END_TEST; + + +static Suite * +rtpjpeg_suite (void) +{ + Suite *s = suite_create ("rtpjpeg"); + TCase *tc_chain; + + tc_chain = tcase_create ("rtpjpegpay_memory_slices"); + suite_add_tcase (s, tc_chain); + + tcase_add_test (tc_chain, test_rtpjpegpay_1_slice); + tcase_add_test (tc_chain, test_rtpjpegpay_5_slices); + + return s; +} + +GST_CHECK_MAIN (rtpjpeg); diff --git a/tests/check/meson.build b/tests/check/meson.build index aa17106..04f9668 100644 --- a/tests/check/meson.build +++ b/tests/check/meson.build @@ -71,6 +71,7 @@ good_tests = [ [ 'elements/rtpcollision' ], [ 'elements/rtpfunnel' ], [ 'elements/rtpjitterbuffer' ], + [ 'elements/rtpjpeg' ], [ 'elements/rtptimerqueue', false, [gstrtp_dep], ['../../gst/rtpmanager/rtptimerqueue.c']], -- 2.7.4