gstrtpjpegdepay.c 24.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* 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
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
16
17
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
18
19
20
21
22
23
24
25
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include <gst/rtp/gstrtpbuffer.h>

26
#include <math.h>
27
#include <stdio.h>
28
#include <stdlib.h>
29
30
31
32
33
34
35
36
37
38
39
#include <string.h>
#include "gstrtpjpegdepay.h"

GST_DEBUG_CATEGORY_STATIC (rtpjpegdepay_debug);
#define GST_CAT_DEFAULT (rtpjpegdepay_debug)

static GstStaticPadTemplate gst_rtp_jpeg_depay_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("image/jpeg")
40
41
42
43
        /* optional SDP attributes */
        /*
         * "width = (int) 0, "
         * "height = (int) 0, "
44
45
         * "framerate = (fraction) 0/1, "
         * "x-dimensions = (string) "0\,0", "
46
         */
47
48
49
50
51
52
53
54
    );

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\", "
55
56
        "clock-rate = (int) 90000, "
        "encoding-name = (string) \"JPEG\"; "
57
58
        /* optional SDP attributes */
        /*
59
60
         * "width = (int) 0, "
         * "height = (int) 0, "
61
         * "framerate = (fraction) 0/1, "
62
63
         * "a-framerate = (string) 0.00, "
         * "x-framerate = (string) 0.00, "
64
         * "x-dimensions = (string) "0\,0", "
65
         */
66
67
68
        "application/x-rtp, "
        "media = (string) \"video\", "
        "payload = (int) " GST_RTP_PAYLOAD_JPEG_STRING ", "
69
70
71
        "clock-rate = (int) 90000"
        /* optional SDP attributes */
        /*
72
73
         * "width = (int) 0, "
         * "height = (int) 0, "
74
         * "framerate = (fraction) 0/1, "
75
76
         * "a-framerate = (string) 0.00, "
         * "x-framerate = (string) 0.00, "
77
         * "x-dimensions = (string) "0\,0", "
78
79
         */
    )
80
81
    );

82
83
#define gst_rtp_jpeg_depay_parent_class parent_class
G_DEFINE_TYPE (GstRtpJPEGDepay, gst_rtp_jpeg_depay,
Wim Taymans's avatar
Wim Taymans committed
84
    GST_TYPE_RTP_BASE_DEPAYLOAD);
85
86
87

static void gst_rtp_jpeg_depay_finalize (GObject * object);

88
89
90
static GstStateChangeReturn gst_rtp_jpeg_depay_change_state (GstElement *
    element, GstStateChange transition);

Wim Taymans's avatar
Wim Taymans committed
91
static gboolean gst_rtp_jpeg_depay_setcaps (GstRTPBaseDepayload * depayload,
92
    GstCaps * caps);
Wim Taymans's avatar
Wim Taymans committed
93
static GstBuffer *gst_rtp_jpeg_depay_process (GstRTPBaseDepayload * depayload,
94
95
96
97
98
99
    GstBuffer * buf);

static void
gst_rtp_jpeg_depay_class_init (GstRtpJPEGDepayClass * klass)
{
  GObjectClass *gobject_class;
100
  GstElementClass *gstelement_class;
Wim Taymans's avatar
Wim Taymans committed
101
  GstRTPBaseDepayloadClass *gstrtpbasedepayload_class;
102
103

  gobject_class = (GObjectClass *) klass;
104
  gstelement_class = (GstElementClass *) klass;
Wim Taymans's avatar
Wim Taymans committed
105
  gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass;
106
107
108

  gobject_class->finalize = gst_rtp_jpeg_depay_finalize;

109
110
111
112
113
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&gst_rtp_jpeg_depay_src_template));
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&gst_rtp_jpeg_depay_sink_template));

114
  gst_element_class_set_static_metadata (gstelement_class,
115
116
117
118
      "RTP JPEG depayloader", "Codec/Depayloader/Network/RTP",
      "Extracts JPEG video from RTP packets (RFC 2435)",
      "Wim Taymans <wim.taymans@gmail.com>");

119
120
  gstelement_class->change_state = gst_rtp_jpeg_depay_change_state;

Wim Taymans's avatar
Wim Taymans committed
121
122
  gstrtpbasedepayload_class->set_caps = gst_rtp_jpeg_depay_setcaps;
  gstrtpbasedepayload_class->process = gst_rtp_jpeg_depay_process;
123
124
125
126
127
128

  GST_DEBUG_CATEGORY_INIT (rtpjpegdepay_debug, "rtpjpegdepay", 0,
      "JPEG Video RTP Depayloader");
}

static void
129
gst_rtp_jpeg_depay_init (GstRtpJPEGDepay * rtpjpegdepay)
130
131
132
133
{
  rtpjpegdepay->adapter = gst_adapter_new ();
}

134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
static void
gst_rtp_jpeg_depay_reset (GstRtpJPEGDepay * depay)
{
  gint i;

  depay->width = 0;
  depay->height = 0;
  depay->media_width = 0;
  depay->media_height = 0;
  depay->frate_num = 0;
  depay->frate_denom = 1;
  depay->discont = TRUE;

  for (i = 0; i < 255; i++) {
    g_free (depay->qtables[i]);
    depay->qtables[i] = NULL;
  }

  gst_adapter_clear (depay->adapter);
}

155
156
157
158
159
160
161
static void
gst_rtp_jpeg_depay_finalize (GObject * object)
{
  GstRtpJPEGDepay *rtpjpegdepay;

  rtpjpegdepay = GST_RTP_JPEG_DEPAY (object);

162
163
  gst_rtp_jpeg_depay_reset (rtpjpegdepay);

164
165
166
167
168
169
  g_object_unref (rtpjpegdepay->adapter);
  rtpjpegdepay->adapter = NULL;

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

170
171
172
173
174
175
176
177
178
179
180
static const int zigzag[] = {
  0, 1, 8, 16, 9, 2, 3, 10,
  17, 24, 32, 25, 18, 11, 4, 5,
  12, 19, 26, 33, 40, 48, 41, 34,
  27, 20, 13, 6, 7, 14, 21, 28,
  35, 42, 49, 56, 57, 50, 43, 36,
  29, 22, 15, 23, 30, 37, 44, 51,
  58, 59, 52, 45, 38, 31, 39, 46,
  53, 60, 61, 54, 47, 55, 62, 63
};

181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/*
 * 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++) {
225
226
    gint lq = (jpeg_luma_quantizer[zigzag[i]] * Q + 50) / 100;
    gint cq = (jpeg_chroma_quantizer[zigzag[i]] * Q + 50) / 100;
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398

    /* 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 */
399
  if ((type & 0x3f) == 0)
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
    *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
Wim Taymans's avatar
Wim Taymans committed
439
gst_rtp_jpeg_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
440
{
441
  GstRtpJPEGDepay *rtpjpegdepay;
442
443
  GstStructure *structure;
  gint clock_rate;
444
  const gchar *media_attr;
445
  gint width = 0, height = 0;
446
  gint num = 0, denom = 1;
447
448

  rtpjpegdepay = GST_RTP_JPEG_DEPAY (depayload);
449
450

  structure = gst_caps_get_structure (caps, 0);
451
  GST_DEBUG_OBJECT (rtpjpegdepay, "Caps set: %" GST_PTR_FORMAT, caps);
452
453
454
455
456

  if (!gst_structure_get_int (structure, "clock-rate", &clock_rate))
    clock_rate = 90000;
  depayload->clock_rate = clock_rate;

457
458
  /* check for optional SDP attributes */
  if ((media_attr = gst_structure_get_string (structure, "x-dimensions"))) {
459
460
461
    if (sscanf (media_attr, "%d,%d", &width, &height) != 2 || width <= 0 ||
        height <= 0) {
      goto invalid_dimension;
462
463
464
    }
  }

465
466
467
468
469
  if (gst_structure_get_int (structure, "width", &width) && width <= 0) {
    goto invalid_dimension;
  }
  if (gst_structure_get_int (structure, "height", &height) && height <= 0) {
    goto invalid_dimension;
470
471
  }

472
473
474
475
476
477
  /* 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) {
478
    gchar *s;
479
    gdouble rate;
480
481
482
483
484

    /* canonicalise floating point string so we can handle framerate strings
     * in the form "24.930" or "24,930" irrespective of the current locale */
    s = g_strdup (media_attr);
    g_strdelimit (s, ",", '.');
485
486

    /* convert the float to a fraction */
487
488
    rate = g_ascii_strtod (s, NULL);
    gst_util_double_to_fraction (rate, &num, &denom);
489
    g_free (s);
490
491
492
493
494
495
496
497
    if (num < 0 || denom <= 0) {
      goto invalid_framerate;
    }
  }

  if (gst_structure_get_fraction (structure, "framerate", &num, &denom) &&
      (num < 0 || denom <= 0)) {
    goto invalid_framerate;
498
  }
499

500
501
  rtpjpegdepay->width = 0;
  rtpjpegdepay->height = 0;
502
503
  rtpjpegdepay->media_width = width;
  rtpjpegdepay->media_height = height;
504
505
  rtpjpegdepay->frate_num = num;
  rtpjpegdepay->frate_denom = denom;
506

507
  return TRUE;
508
509
510
511
512
513

invalid_dimension:
  {
    GST_ERROR_OBJECT (rtpjpegdepay, "invalid width/height from caps");
    return FALSE;
  }
514
515
516
517
518
invalid_framerate:
  {
    GST_ERROR_OBJECT (rtpjpegdepay, "invalid framerate from caps");
    return FALSE;
  }
519
520
521
}

static GstBuffer *
Wim Taymans's avatar
Wim Taymans committed
522
gst_rtp_jpeg_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf)
523
524
525
526
527
528
529
530
531
532
{
  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;
533
  GstRTPBuffer rtp = { NULL };
534
535
536
537
538

  rtpjpegdepay = GST_RTP_JPEG_DEPAY (depayload);

  if (GST_BUFFER_IS_DISCONT (buf)) {
    gst_adapter_clear (rtpjpegdepay->adapter);
539
    rtpjpegdepay->discont = TRUE;
540
541
  }

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
542
543
  gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp);
  payload_len = gst_rtp_buffer_get_payload_len (&rtp);
544
545
546
547

  if (payload_len < 8)
    goto empty_packet;

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
548
  payload = gst_rtp_buffer_get_payload (&rtp);
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
  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;

565
566
567
568
569
570
571
572
  /* 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;

573
574
575
  if (width == 0 || height == 0)
    goto invalid_dimension;

576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
  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;

632
633
634
635
    if (length > 0)
      qtable = payload;
    else
      qtable = rtpjpegdepay->qtables[Q];
636
637
638
639
640
641
642
643
644
645
646

    payload += length;
    header_len += length;
    payload_len -= length;
  } else {
    length = 0;
    qtable = NULL;
    precision = 0;
  }

  if (frag_offset == 0) {
Wim Taymans's avatar
Wim Taymans committed
647
    GstMapInfo map;
648
649
    guint size;

650
651
652
    if (rtpjpegdepay->width != width || rtpjpegdepay->height != height) {
      GstCaps *outcaps;

653
      outcaps = gst_caps_new_empty_simple ("image/jpeg");
654
655
656
657
658
659

      if (width > 0 && height > 0) {
        gst_caps_set_simple (outcaps, "width", G_TYPE_INT, width, "height",
            G_TYPE_INT, height, NULL);
      }

660
661
662
663
664
      if (rtpjpegdepay->frate_num > 0) {
        gst_caps_set_simple (outcaps, "framerate", GST_TYPE_FRACTION,
            rtpjpegdepay->frate_num, rtpjpegdepay->frate_denom, NULL);
      }

665
666
667
668
669
670
671
672
673
674
      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);

675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
    /* 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;
691
692
693
      } else {
        if (!qtable)
          goto no_qtable;
694
695
696
697
      }
    }
    /* max header length, should be big enough */
    outbuf = gst_buffer_new_and_alloc (1000);
Wim Taymans's avatar
Wim Taymans committed
698
699
700
701
    gst_buffer_map (outbuf, &map, GST_MAP_WRITE);
    size = MakeHeaders (map.data, type, width, height, qtable, precision, dri);
    gst_buffer_unmap (outbuf, &map);
    gst_buffer_resize (outbuf, 0, size);
702

703
    GST_DEBUG_OBJECT (rtpjpegdepay, "pushing %u bytes of header", size);
704
705
706
707
708
709

    gst_adapter_push (rtpjpegdepay->adapter, outbuf);
  }

  /* take JPEG data, push in the adapter */
  GST_DEBUG_OBJECT (rtpjpegdepay, "pushing data at offset %d", header_len);
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
710
  outbuf = gst_rtp_buffer_get_payload_subbuffer (&rtp, header_len, -1);
711
712
713
  gst_adapter_push (rtpjpegdepay->adapter, outbuf);
  outbuf = NULL;

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
714
  if (gst_rtp_buffer_get_marker (&rtp)) {
715
    guint avail;
716
    guint8 end[2];
Wim Taymans's avatar
Wim Taymans committed
717
    GstMapInfo map;
718
719
720

    /* last buffer take all data out of the adapter */
    avail = gst_adapter_available (rtpjpegdepay->adapter);
721
722
    GST_DEBUG_OBJECT (rtpjpegdepay, "marker set, last buffer");

723
724
725
    if (avail < 2)
      goto invalid_packet;

726
727
728
729
730
731
732
733
734
    /* 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);
Wim Taymans's avatar
Wim Taymans committed
735
736
737
738
      gst_buffer_map (outbuf, &map, GST_MAP_WRITE);
      map.data[0] = 0xff;
      map.data[1] = 0xd9;
      gst_buffer_unmap (outbuf, &map);
739
740
741
742

      gst_adapter_push (rtpjpegdepay->adapter, outbuf);
      avail += 2;
    }
743
744
    outbuf = gst_adapter_take_buffer (rtpjpegdepay->adapter, avail);

745
746
747
748
749
    if (rtpjpegdepay->discont) {
      GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
      rtpjpegdepay->discont = FALSE;
    }

750
    GST_DEBUG_OBJECT (rtpjpegdepay, "returning %u bytes", avail);
751
752
  }

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
753
754
  gst_rtp_buffer_unmap (&rtp);

755
756
757
758
759
760
761
  return outbuf;

  /* ERRORS */
empty_packet:
  {
    GST_ELEMENT_WARNING (rtpjpegdepay, STREAM, DECODE,
        ("Empty Payload."), (NULL));
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
762
    gst_rtp_buffer_unmap (&rtp);
763
764
    return NULL;
  }
765
766
767
768
invalid_dimension:
  {
    GST_ELEMENT_WARNING (rtpjpegdepay, STREAM, FORMAT,
        ("Invalid Dimension %dx%d.", width, height), (NULL));
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
769
    gst_rtp_buffer_unmap (&rtp);
770
771
772
773
774
    return NULL;
  }
no_qtable:
  {
    GST_WARNING_OBJECT (rtpjpegdepay, "no qtable");
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
775
    gst_rtp_buffer_unmap (&rtp);
776
777
    return NULL;
  }
778
779
780
781
782
783
784
785
invalid_packet:
  {
    GST_WARNING_OBJECT (rtpjpegdepay, "invalid packet");
    gst_adapter_flush (rtpjpegdepay->adapter,
        gst_adapter_available (rtpjpegdepay->adapter));
    gst_rtp_buffer_unmap (&rtp);
    return NULL;
  }
786
787
}

788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817

static GstStateChangeReturn
gst_rtp_jpeg_depay_change_state (GstElement * element,
    GstStateChange transition)
{
  GstRtpJPEGDepay *rtpjpegdepay;
  GstStateChangeReturn ret;

  rtpjpegdepay = GST_RTP_JPEG_DEPAY (element);

  switch (transition) {
    case GST_STATE_CHANGE_READY_TO_PAUSED:
      gst_rtp_jpeg_depay_reset (rtpjpegdepay);
      break;
    default:
      break;
  }

  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);

  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:
      break;
    default:
      break;
  }
  return ret;
}


818
819
820
821
gboolean
gst_rtp_jpeg_depay_plugin_init (GstPlugin * plugin)
{
  return gst_element_register (plugin, "rtpjpegdepay",
822
      GST_RANK_SECONDARY, GST_TYPE_RTP_JPEG_DEPAY);
823
}