diff --git a/gst/rtp/gstrtpjpegpay.c b/gst/rtp/gstrtpjpegpay.c index 1b5aecffd0..31f8efa384 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 RtpJpegMarker -gst_rtp_jpeg_pay_scan_marker (const guint8 * data, guint size, guint * offset) +static void +gst_rtp_jpeg_pay_skipping_marker (GstBufferMemoryMap * memory) { - while ((data[(*offset)++] != JPEG_MARKER) && ((*offset) < size)); + guint skip; - if (G_UNLIKELY ((*offset) >= size)) { + 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 (GstBufferMemoryMap * memory) +{ + 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 ((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 0000000000..a12f7ab1c9 --- /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 aa17106232..04f9668e66 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']],