#include "gstrtpjpegpay.h"
#include "gstrtputils.h"
+#include "gstbuffermemory.h"
static GstStaticPadTemplate gst_rtp_jpeg_pay_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
}
}
+/*
+ * 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
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:
{
}
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);
}
/* 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 */
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:
}
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;
}
}
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;
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;
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!");
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:
(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;
GST_LOG_OBJECT (pay, "header size %u", jpeg_header_size);
- size -= jpeg_header_size;
- data += jpeg_header_size;
offset = 0;
if (dri_found)
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;
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);
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;
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;
}
--- /dev/null
+/* GStreamer RTP jpeg unit test
+ *
+ * Copyright (C) 2020 Kristofer Bjorkstrom <at axis dot 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>
+
+
+/* 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);