gstreamer/gst/rtp/gstrtpjpegdepay.c
Luc Deschenaux 654ca56d85 jpegdepay: use attributes for extra properties
Use some of the SDP attributes when they are present to specify the output
dimension and framerate. This allows us to receive jpeg frames larger than
2040 width/height.

Fixes #564437
2009-08-03 18:02:31 +02:00

689 lines
21 KiB
C

/* GStreamer
* Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <gst/rtp/gstrtpbuffer.h>
#include <string.h>
#include "gstrtpjpegdepay.h"
GST_DEBUG_CATEGORY_STATIC (rtpjpegdepay_debug);
#define GST_CAT_DEFAULT (rtpjpegdepay_debug)
/* elementfactory information */
static const GstElementDetails gst_rtp_jpegdepay_details =
GST_ELEMENT_DETAILS ("RTP JPEG depayloader",
"Codec/Depayloader/Network",
"Extracts JPEG video from RTP packets (RFC 2435)",
"Wim Taymans <wim.taymans@gmail.com>");
static GstStaticPadTemplate gst_rtp_jpeg_depay_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("image/jpeg")
);
static GstStaticPadTemplate gst_rtp_jpeg_depay_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/x-rtp, "
"media = (string) \"video\", "
"payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
"clock-rate = (int) 90000, " "encoding-name = (string) \"JPEG\"; "
/* optional SDP attributes */
/*
* "a-framerate = (string) 0.00, "
* "x-framerate = (string) 0.00, "
* "x-dimensions = (string) \"1234,1234\", "
*/
"application/x-rtp, "
"media = (string) \"video\", "
"payload = (int) " GST_RTP_PAYLOAD_JPEG_STRING ", "
"clock-rate = (int) 90000"
/* optional SDP attributes */
/*
* "a-framerate = (string) 0.00, "
* "x-framerate = (string) 0.00, "
* "x-dimensions = (string) \"1234,1234\""
*/
)
);
GST_BOILERPLATE (GstRtpJPEGDepay, gst_rtp_jpeg_depay, GstBaseRTPDepayload,
GST_TYPE_BASE_RTP_DEPAYLOAD);
static void gst_rtp_jpeg_depay_finalize (GObject * object);
static gboolean gst_rtp_jpeg_depay_setcaps (GstBaseRTPDepayload * depayload,
GstCaps * caps);
static GstBuffer *gst_rtp_jpeg_depay_process (GstBaseRTPDepayload * depayload,
GstBuffer * buf);
static void
gst_rtp_jpeg_depay_base_init (gpointer klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_rtp_jpeg_depay_src_template));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_rtp_jpeg_depay_sink_template));
gst_element_class_set_details (element_class, &gst_rtp_jpegdepay_details);
}
static void
gst_rtp_jpeg_depay_class_init (GstRtpJPEGDepayClass * klass)
{
GObjectClass *gobject_class;
GstBaseRTPDepayloadClass *gstbasertpdepayload_class;
gobject_class = (GObjectClass *) klass;
gstbasertpdepayload_class = (GstBaseRTPDepayloadClass *) klass;
gobject_class->finalize = gst_rtp_jpeg_depay_finalize;
parent_class = g_type_class_peek_parent (klass);
gstbasertpdepayload_class->set_caps = gst_rtp_jpeg_depay_setcaps;
gstbasertpdepayload_class->process = gst_rtp_jpeg_depay_process;
GST_DEBUG_CATEGORY_INIT (rtpjpegdepay_debug, "rtpjpegdepay", 0,
"JPEG Video RTP Depayloader");
}
static void
gst_rtp_jpeg_depay_init (GstRtpJPEGDepay * rtpjpegdepay,
GstRtpJPEGDepayClass * klass)
{
rtpjpegdepay->adapter = gst_adapter_new ();
}
static void
gst_rtp_jpeg_depay_finalize (GObject * object)
{
GstRtpJPEGDepay *rtpjpegdepay;
rtpjpegdepay = GST_RTP_JPEG_DEPAY (object);
g_object_unref (rtpjpegdepay->adapter);
rtpjpegdepay->adapter = NULL;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
/*
* Table K.1 from JPEG spec.
*/
static const int jpeg_luma_quantizer[64] = {
16, 11, 10, 16, 24, 40, 51, 61,
12, 12, 14, 19, 26, 58, 60, 55,
14, 13, 16, 24, 40, 57, 69, 56,
14, 17, 22, 29, 51, 87, 80, 62,
18, 22, 37, 56, 68, 109, 103, 77,
24, 35, 55, 64, 81, 104, 113, 92,
49, 64, 78, 87, 103, 121, 120, 101,
72, 92, 95, 98, 112, 100, 103, 99
};
/*
* Table K.2 from JPEG spec.
*/
static const int jpeg_chroma_quantizer[64] = {
17, 18, 24, 47, 99, 99, 99, 99,
18, 21, 26, 66, 99, 99, 99, 99,
24, 26, 56, 99, 99, 99, 99, 99,
47, 66, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99
};
/* Call MakeTables with the Q factor and a guint8[128] return array
*/
static void
MakeTables (GstRtpJPEGDepay * rtpjpegdepay, gint Q, guint8 qtable[128])
{
gint i;
guint factor;
factor = CLAMP (Q, 1, 99);
if (Q < 50)
Q = 5000 / factor;
else
Q = 200 - factor * 2;
for (i = 0; i < 64; i++) {
gint lq = (jpeg_luma_quantizer[i] * Q + 50) / 100;
gint cq = (jpeg_chroma_quantizer[i] * Q + 50) / 100;
/* Limit the quantizers to 1 <= q <= 255 */
qtable[i] = CLAMP (lq, 1, 255);
qtable[i + 64] = CLAMP (cq, 1, 255);
}
}
static const guint8 lum_dc_codelens[] = {
0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0
};
static const guint8 lum_dc_symbols[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
};
static const guint8 lum_ac_codelens[] = {
0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d
};
static const guint8 lum_ac_symbols[] = {
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
};
static const guint8 chm_dc_codelens[] = {
0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
};
static const guint8 chm_dc_symbols[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
};
static const guint8 chm_ac_codelens[] = {
0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77
};
static const guint8 chm_ac_symbols[] = {
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
};
static guint8 *
MakeQuantHeader (guint8 * p, guint8 * qt, gint size, gint tableNo)
{
*p++ = 0xff;
*p++ = 0xdb; /* DQT */
*p++ = 0; /* length msb */
*p++ = size + 3; /* length lsb */
*p++ = tableNo;
memcpy (p, qt, size);
return (p + size);
}
static guint8 *
MakeHuffmanHeader (guint8 * p, const guint8 * codelens, int ncodes,
const guint8 * symbols, int nsymbols, int tableNo, int tableClass)
{
*p++ = 0xff;
*p++ = 0xc4; /* DHT */
*p++ = 0; /* length msb */
*p++ = 3 + ncodes + nsymbols; /* length lsb */
*p++ = (tableClass << 4) | tableNo;
memcpy (p, codelens, ncodes);
p += ncodes;
memcpy (p, symbols, nsymbols);
p += nsymbols;
return (p);
}
static guint8 *
MakeDRIHeader (guint8 * p, guint16 dri)
{
*p++ = 0xff;
*p++ = 0xdd; /* DRI */
*p++ = 0x0; /* length msb */
*p++ = 4; /* length lsb */
*p++ = dri >> 8; /* dri msb */
*p++ = dri & 0xff; /* dri lsb */
return (p);
}
/*
* Arguments:
* type, width, height: as supplied in RTP/JPEG header
* qt: quantization tables as either derived from
* the Q field using MakeTables() or as specified
* in section 4.2.
* dri: restart interval in MCUs, or 0 if no restarts.
*
* p: pointer to return area
*
* Return value:
* The length of the generated headers.
*
* Generate a frame and scan headers that can be prepended to the
* RTP/JPEG data payload to produce a JPEG compressed image in
* interchange format (except for possible trailing garbage and
* absence of an EOI marker to terminate the scan).
*/
static guint
MakeHeaders (guint8 * p, int type, int width, int height, guint8 * qt,
guint precision, guint16 dri)
{
guint8 *start = p;
gint size;
*p++ = 0xff;
*p++ = 0xd8; /* SOI */
size = ((precision & 1) ? 128 : 64);
p = MakeQuantHeader (p, qt, size, 0);
qt += size;
size = ((precision & 2) ? 128 : 64);
p = MakeQuantHeader (p, qt, size, 1);
qt += size;
if (dri != 0)
p = MakeDRIHeader (p, dri);
*p++ = 0xff;
*p++ = 0xc0; /* SOF */
*p++ = 0; /* length msb */
*p++ = 17; /* length lsb */
*p++ = 8; /* 8-bit precision */
*p++ = height >> 8; /* height msb */
*p++ = height; /* height lsb */
*p++ = width >> 8; /* width msb */
*p++ = width; /* width lsb */
*p++ = 3; /* number of components */
*p++ = 0; /* comp 0 */
if ((type & 0x3f) == 0)
*p++ = 0x21; /* hsamp = 2, vsamp = 1 */
else
*p++ = 0x22; /* hsamp = 2, vsamp = 2 */
*p++ = 0; /* quant table 0 */
*p++ = 1; /* comp 1 */
*p++ = 0x11; /* hsamp = 1, vsamp = 1 */
*p++ = 1; /* quant table 1 */
*p++ = 2; /* comp 2 */
*p++ = 0x11; /* hsamp = 1, vsamp = 1 */
*p++ = 1; /* quant table 1 */
p = MakeHuffmanHeader (p, lum_dc_codelens,
sizeof (lum_dc_codelens), lum_dc_symbols, sizeof (lum_dc_symbols), 0, 0);
p = MakeHuffmanHeader (p, lum_ac_codelens,
sizeof (lum_ac_codelens), lum_ac_symbols, sizeof (lum_ac_symbols), 0, 1);
p = MakeHuffmanHeader (p, chm_dc_codelens,
sizeof (chm_dc_codelens), chm_dc_symbols, sizeof (chm_dc_symbols), 1, 0);
p = MakeHuffmanHeader (p, chm_ac_codelens,
sizeof (chm_ac_codelens), chm_ac_symbols, sizeof (chm_ac_symbols), 1, 1);
*p++ = 0xff;
*p++ = 0xda; /* SOS */
*p++ = 0; /* length msb */
*p++ = 12; /* length lsb */
*p++ = 3; /* 3 components */
*p++ = 0; /* comp 0 */
*p++ = 0; /* huffman table 0 */
*p++ = 1; /* comp 1 */
*p++ = 0x11; /* huffman table 1 */
*p++ = 2; /* comp 2 */
*p++ = 0x11; /* huffman table 1 */
*p++ = 0; /* first DCT coeff */
*p++ = 63; /* last DCT coeff */
*p++ = 0; /* sucessive approx. */
return (p - start);
};
static gboolean
gst_rtp_jpeg_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps)
{
GstRtpJPEGDepay *rtpjpegdepay;
GstStructure *structure;
gint clock_rate;
const gchar *media_attr;
rtpjpegdepay = GST_RTP_JPEG_DEPAY (depayload);
structure = gst_caps_get_structure (caps, 0);
GST_DEBUG_OBJECT (rtpjpegdepay, "Caps set: %" GST_PTR_FORMAT, caps);
if (!gst_structure_get_int (structure, "clock-rate", &clock_rate))
clock_rate = 90000;
depayload->clock_rate = clock_rate;
/* reset defaults */
rtpjpegdepay->width = 0;
rtpjpegdepay->height = 0;
rtpjpegdepay->media_width = 0;
rtpjpegdepay->media_height = 0;
rtpjpegdepay->frate_num = 0;
rtpjpegdepay->frate_denom = 1;
/* check for optional SDP attributes */
if ((media_attr = gst_structure_get_string (structure, "x-dimensions"))) {
gint w, h;
if (sscanf (media_attr, "%d,%d", &w, &h) == 2) {
rtpjpegdepay->media_width = w;
rtpjpegdepay->media_height = h;
}
}
/* try to get a framerate */
media_attr = gst_structure_get_string (structure, "a-framerate");
if (!media_attr)
media_attr = gst_structure_get_string (structure, "x-framerate");
if (media_attr) {
GValue src = { 0 };
GValue dest = { 0 };
/* convert the float to a fraction */
g_value_init (&src, G_TYPE_DOUBLE);
g_value_set_double (&src, atof (media_attr));
g_value_init (&dest, GST_TYPE_FRACTION);
g_value_transform (&src, &dest);
rtpjpegdepay->frate_num = gst_value_get_fraction_numerator (&dest);
rtpjpegdepay->frate_denom = gst_value_get_fraction_denominator (&dest);
}
return TRUE;
}
static GstBuffer *
gst_rtp_jpeg_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
{
GstRtpJPEGDepay *rtpjpegdepay;
GstBuffer *outbuf;
gint payload_len, header_len;
guint8 *payload;
guint frag_offset;
gint Q;
guint type, width, height;
guint16 dri, precision, length;
guint8 *qtable;
rtpjpegdepay = GST_RTP_JPEG_DEPAY (depayload);
if (GST_BUFFER_IS_DISCONT (buf)) {
gst_adapter_clear (rtpjpegdepay->adapter);
}
payload_len = gst_rtp_buffer_get_payload_len (buf);
if (payload_len < 8)
goto empty_packet;
payload = gst_rtp_buffer_get_payload (buf);
header_len = 0;
/* 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 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
frag_offset = (payload[1] << 16) | (payload[2] << 8) | payload[3];
type = payload[4];
Q = payload[5];
width = payload[6] * 8;
height = payload[7] * 8;
/* allow frame dimensions > 2040, passed in SDP session or media attributes
* from gstrtspsrc.c (gst_rtspsrc_sdp_attributes_to_caps), or in caps */
if (!width)
width = rtpjpegdepay->media_width;
if (!height)
height = rtpjpegdepay->media_height;
if (width == 0 || height == 0)
goto invalid_dimension;
GST_DEBUG_OBJECT (rtpjpegdepay, "frag %u, type %u, Q %d, width %u, height %u",
frag_offset, type, Q, width, height);
header_len += 8;
payload += 8;
payload_len -= 8;
dri = 0;
if (type > 63) {
if (payload_len < 4)
goto empty_packet;
/* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Restart Interval |F|L| Restart Count |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
dri = (payload[0] << 8) | payload[1];
GST_DEBUG_OBJECT (rtpjpegdepay, "DRI %" G_GUINT16_FORMAT, dri);
payload += 4;
header_len += 4;
payload_len -= 4;
}
if (Q >= 128 && frag_offset == 0) {
if (payload_len < 4)
goto empty_packet;
/* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | MBZ | Precision | Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Quantization Table Data |
* | ... |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
precision = payload[1];
length = (payload[2] << 8) | payload[3];
GST_DEBUG_OBJECT (rtpjpegdepay, "precision %04x, length %" G_GUINT16_FORMAT,
precision, length);
if (Q == 255 && length == 0)
goto empty_packet;
payload += 4;
header_len += 4;
payload_len -= 4;
if (length > payload_len)
goto empty_packet;
if (length > 0)
qtable = payload;
else
qtable = rtpjpegdepay->qtables[Q];
payload += length;
header_len += length;
payload_len -= length;
} else {
length = 0;
qtable = NULL;
precision = 0;
}
if (frag_offset == 0) {
guint size;
if (rtpjpegdepay->width != width || rtpjpegdepay->height != height) {
GstCaps *outcaps;
outcaps =
gst_caps_new_simple ("image/jpeg", "framerate", GST_TYPE_FRACTION,
rtpjpegdepay->frate_num, rtpjpegdepay->frate_denom, "width",
G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL);
gst_pad_set_caps (depayload->srcpad, outcaps);
gst_caps_unref (outcaps);
rtpjpegdepay->width = width;
rtpjpegdepay->height = height;
}
GST_LOG_OBJECT (rtpjpegdepay, "first packet, length %" G_GUINT16_FORMAT,
length);
/* first packet */
if (length == 0) {
if (Q < 128) {
/* no quant table, see if we have one cached */
qtable = rtpjpegdepay->qtables[Q];
if (!qtable) {
GST_DEBUG_OBJECT (rtpjpegdepay, "making Q %d table", Q);
/* make and cache the table */
qtable = g_new (guint8, 128);
MakeTables (rtpjpegdepay, Q, qtable);
rtpjpegdepay->qtables[Q] = qtable;
} else {
GST_DEBUG_OBJECT (rtpjpegdepay, "using cached table for Q %d", Q);
}
/* all 8 bit quantizers */
precision = 0;
} else {
if (!qtable)
goto no_qtable;
}
}
/* max header length, should be big enough */
outbuf = gst_buffer_new_and_alloc (1000);
size = MakeHeaders (GST_BUFFER_DATA (outbuf), type,
width, height, qtable, precision, dri);
GST_DEBUG_OBJECT (rtpjpegdepay, "pushing %u bytes of header", size);
GST_BUFFER_SIZE (outbuf) = size;
gst_adapter_push (rtpjpegdepay->adapter, outbuf);
}
/* take JPEG data, push in the adapter */
GST_DEBUG_OBJECT (rtpjpegdepay, "pushing data at offset %d", header_len);
outbuf = gst_rtp_buffer_get_payload_subbuffer (buf, header_len, -1);
gst_adapter_push (rtpjpegdepay->adapter, outbuf);
outbuf = NULL;
if (gst_rtp_buffer_get_marker (buf)) {
guint avail;
guint8 end[2];
guint8 *data;
/* last buffer take all data out of the adapter */
avail = gst_adapter_available (rtpjpegdepay->adapter);
GST_DEBUG_OBJECT (rtpjpegdepay, "marker set, last buffer");
/* take the last bytes of the jpeg data to see if there is an EOI
* marker */
gst_adapter_copy (rtpjpegdepay->adapter, end, avail - 2, 2);
if (end[0] != 0xff && end[1] != 0xd9) {
GST_DEBUG_OBJECT (rtpjpegdepay, "no EOI marker, adding one");
/* no EOI marker, add one */
outbuf = gst_buffer_new_and_alloc (2);
data = GST_BUFFER_DATA (outbuf);
data[0] = 0xff;
data[1] = 0xd9;
gst_adapter_push (rtpjpegdepay->adapter, outbuf);
avail += 2;
}
outbuf = gst_adapter_take_buffer (rtpjpegdepay->adapter, avail);
GST_DEBUG_OBJECT (rtpjpegdepay, "returning %u bytes", avail);
}
return outbuf;
/* ERRORS */
empty_packet:
{
GST_ELEMENT_WARNING (rtpjpegdepay, STREAM, DECODE,
("Empty Payload."), (NULL));
return NULL;
}
invalid_dimension:
{
GST_ELEMENT_WARNING (rtpjpegdepay, STREAM, FORMAT,
("Invalid Dimension %dx%d.", width, height), (NULL));
return NULL;
}
no_qtable:
{
GST_WARNING_OBJECT (rtpjpegdepay, "no qtable");
return NULL;
}
}
gboolean
gst_rtp_jpeg_depay_plugin_init (GstPlugin * plugin)
{
return gst_element_register (plugin, "rtpjpegdepay",
GST_RANK_MARGINAL, GST_TYPE_RTP_JPEG_DEPAY);
}