mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-22 16:26:39 +00:00
7c59f39bfe
Use the width and the height from the payload headers and set them on the output caps for added awesomeness. Fix quant parsing, we need to check the type in the lower 6 bits. Add first bits of caching quantization tables.
630 lines
19 KiB
C
630 lines
19 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\";"
|
|
"application/x-rtp, "
|
|
"media = (string) \"video\", "
|
|
"payload = (int) " GST_RTP_PAYLOAD_JPEG_STRING ", "
|
|
"clock-rate = (int) 90000")
|
|
);
|
|
|
|
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;
|
|
|
|
rtpjpegdepay = GST_RTP_JPEG_DEPAY (depayload);
|
|
|
|
structure = gst_caps_get_structure (caps, 0);
|
|
|
|
if (!gst_structure_get_int (structure, "clock-rate", &clock_rate))
|
|
clock_rate = 90000;
|
|
depayload->clock_rate = clock_rate;
|
|
|
|
rtpjpegdepay->width = 0;
|
|
rtpjpegdepay->height = 0;
|
|
|
|
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;
|
|
|
|
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, 0,
|
|
1, "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);
|
|
}
|