vrawdepay: handle invalid payload better
[platform/upstream/gst-plugins-good.git] / gst / rtp / gstrtpvrawdepay.c
1 /* GStreamer
2  * Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include <gst/rtp/gstrtpbuffer.h>
25
26 #include <string.h>
27 #include <stdlib.h>
28 #include "gstrtpvrawdepay.h"
29
30 GST_DEBUG_CATEGORY_STATIC (rtpvrawdepay_debug);
31 #define GST_CAT_DEFAULT (rtpvrawdepay_debug)
32
33 static GstStaticPadTemplate gst_rtp_vraw_depay_src_template =
34     GST_STATIC_PAD_TEMPLATE ("src",
35     GST_PAD_SRC,
36     GST_PAD_ALWAYS,
37     GST_STATIC_CAPS ("video/x-raw-rgb; video/x-raw-yuv")
38     );
39
40 static GstStaticPadTemplate gst_rtp_vraw_depay_sink_template =
41 GST_STATIC_PAD_TEMPLATE ("sink",
42     GST_PAD_SINK,
43     GST_PAD_ALWAYS,
44     GST_STATIC_CAPS ("application/x-rtp, "
45         "media = (string) \"video\", "
46         "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
47         "clock-rate = (int) 90000, " "encoding-name = (string) \"RAW\"")
48     );
49
50 GST_BOILERPLATE (GstRtpVRawDepay, gst_rtp_vraw_depay, GstBaseRTPDepayload,
51     GST_TYPE_BASE_RTP_DEPAYLOAD);
52
53 static gboolean gst_rtp_vraw_depay_setcaps (GstBaseRTPDepayload * depayload,
54     GstCaps * caps);
55 static GstBuffer *gst_rtp_vraw_depay_process (GstBaseRTPDepayload * depayload,
56     GstBuffer * buf);
57
58 static GstStateChangeReturn gst_rtp_vraw_depay_change_state (GstElement *
59     element, GstStateChange transition);
60
61 static void
62 gst_rtp_vraw_depay_base_init (gpointer klass)
63 {
64   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
65
66   gst_element_class_add_pad_template (element_class,
67       gst_static_pad_template_get (&gst_rtp_vraw_depay_src_template));
68   gst_element_class_add_pad_template (element_class,
69       gst_static_pad_template_get (&gst_rtp_vraw_depay_sink_template));
70
71   gst_element_class_set_details_simple (element_class,
72       "RTP Raw Video depayloader", "Codec/Depayloader/Network",
73       "Extracts raw video from RTP packets (RFC 4175)",
74       "Wim Taymans <wim.taymans@gmail.com>");
75 }
76
77 static void
78 gst_rtp_vraw_depay_class_init (GstRtpVRawDepayClass * klass)
79 {
80   GstElementClass *gstelement_class;
81   GstBaseRTPDepayloadClass *gstbasertpdepayload_class;
82
83   gstelement_class = (GstElementClass *) klass;
84   gstbasertpdepayload_class = (GstBaseRTPDepayloadClass *) klass;
85
86   gstelement_class->change_state = gst_rtp_vraw_depay_change_state;
87
88   gstbasertpdepayload_class->set_caps = gst_rtp_vraw_depay_setcaps;
89   gstbasertpdepayload_class->process = gst_rtp_vraw_depay_process;
90
91   GST_DEBUG_CATEGORY_INIT (rtpvrawdepay_debug, "rtpvrawdepay", 0,
92       "raw video RTP Depayloader");
93 }
94
95 static void
96 gst_rtp_vraw_depay_init (GstRtpVRawDepay * rtpvrawdepay,
97     GstRtpVRawDepayClass * klass)
98 {
99   /* needed because of GST_BOILERPLATE */
100 }
101
102 static gboolean
103 gst_rtp_vraw_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps)
104 {
105   GstStructure *structure;
106   GstRtpVRawDepay *rtpvrawdepay;
107   gint clock_rate;
108   const gchar *str, *type;
109   gint format, width, height, pgroup, xinc, yinc;
110   guint ystride, uvstride, yp, up, vp, outsize;
111   GstCaps *srccaps;
112   guint32 fourcc = 0;
113   gboolean res;
114
115   rtpvrawdepay = GST_RTP_VRAW_DEPAY (depayload);
116
117   structure = gst_caps_get_structure (caps, 0);
118
119   yp = up = vp = uvstride = 0;
120   xinc = yinc = 1;
121
122   if (!gst_structure_get_int (structure, "clock-rate", &clock_rate))
123     clock_rate = 90000;         /* default */
124   depayload->clock_rate = clock_rate;
125
126   if (!(str = gst_structure_get_string (structure, "width")))
127     goto no_width;
128   width = atoi (str);
129
130   if (!(str = gst_structure_get_string (structure, "height")))
131     goto no_height;
132   height = atoi (str);
133
134   /* optional interlace value but we don't handle interlaced
135    * formats yet */
136   if (gst_structure_get_string (structure, "interlace"))
137     goto interlaced;
138
139   if (!(str = gst_structure_get_string (structure, "sampling")))
140     goto no_sampling;
141
142   if (!strcmp (str, "RGB")) {
143     format = GST_VIDEO_FORMAT_RGB;
144     pgroup = 3;
145     ystride = GST_ROUND_UP_4 (width * 3);
146     outsize = ystride * height;
147     type = "video/x-raw-rgb";
148   } else if (!strcmp (str, "RGBA")) {
149     format = GST_VIDEO_FORMAT_RGBA;
150     pgroup = 4;
151     ystride = width * 4;
152     outsize = ystride * height;
153     type = "video/x-raw-rgb";
154   } else if (!strcmp (str, "BGR")) {
155     format = GST_VIDEO_FORMAT_BGR;
156     pgroup = 3;
157     ystride = GST_ROUND_UP_4 (width * 3);
158     outsize = ystride * height;
159     type = "video/x-raw-rgb";
160   } else if (!strcmp (str, "BGRA")) {
161     format = GST_VIDEO_FORMAT_BGRA;
162     pgroup = 4;
163     ystride = width * 4;
164     outsize = ystride * height;
165     type = "video/x-raw-rgb";
166   } else if (!strcmp (str, "YCbCr-4:4:4")) {
167     format = GST_VIDEO_FORMAT_AYUV;
168     pgroup = 3;
169     ystride = width * 4;
170     outsize = ystride * height;
171     type = "video/x-raw-yuv";
172     fourcc = GST_MAKE_FOURCC ('A', 'Y', 'U', 'V');
173   } else if (!strcmp (str, "YCbCr-4:2:2")) {
174     format = GST_VIDEO_FORMAT_UYVY;
175     pgroup = 4;
176     ystride = GST_ROUND_UP_2 (width) * 2;
177     outsize = ystride * height;
178     type = "video/x-raw-yuv";
179     fourcc = GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y');
180     xinc = 2;
181   } else if (!strcmp (str, "YCbCr-4:2:0")) {
182     format = GST_VIDEO_FORMAT_I420;
183     pgroup = 6;
184     ystride = GST_ROUND_UP_4 (width);
185     uvstride = GST_ROUND_UP_8 (width) / 2;
186     up = ystride * GST_ROUND_UP_2 (height);
187     vp = up + uvstride * GST_ROUND_UP_2 (height) / 2;
188     outsize = vp + uvstride * GST_ROUND_UP_2 (height) / 2;
189     type = "video/x-raw-yuv";
190     fourcc = GST_MAKE_FOURCC ('I', '4', '2', '0');
191     xinc = yinc = 2;
192   } else if (!strcmp (str, "YCbCr-4:1:1")) {
193     format = GST_VIDEO_FORMAT_Y41B;
194     pgroup = 6;
195     ystride = GST_ROUND_UP_4 (width);
196     uvstride = GST_ROUND_UP_8 (width) / 4;
197     up = ystride * height;
198     vp = up + uvstride * height;
199     outsize = vp + uvstride * height;
200     type = "video/x-raw-yuv";
201     fourcc = GST_MAKE_FOURCC ('Y', '4', '1', 'B');
202     xinc = 4;
203   } else
204     goto unknown_format;
205
206   rtpvrawdepay->width = width;
207   rtpvrawdepay->height = height;
208   rtpvrawdepay->format = format;
209   rtpvrawdepay->yp = yp;
210   rtpvrawdepay->up = up;
211   rtpvrawdepay->vp = vp;
212   rtpvrawdepay->pgroup = pgroup;
213   rtpvrawdepay->xinc = xinc;
214   rtpvrawdepay->yinc = yinc;
215   rtpvrawdepay->ystride = ystride;
216   rtpvrawdepay->uvstride = uvstride;
217   rtpvrawdepay->outsize = outsize;
218
219   srccaps = gst_caps_new_simple (type,
220       "width", G_TYPE_INT, width,
221       "height", G_TYPE_INT, height,
222       "format", GST_TYPE_FOURCC, fourcc,
223       "framerate", GST_TYPE_FRACTION, 0, 1, NULL);
224
225   res = gst_pad_set_caps (GST_BASE_RTP_DEPAYLOAD_SRCPAD (depayload), srccaps);
226   gst_caps_unref (srccaps);
227
228   GST_DEBUG_OBJECT (depayload, "width %d, height %d, format %d", width, height,
229       format);
230   GST_DEBUG_OBJECT (depayload, "yp %d, up %d, vp %d", yp, up, vp);
231   GST_DEBUG_OBJECT (depayload, "pgroup %d, ystride %d, uvstride %d", pgroup,
232       ystride, uvstride);
233   GST_DEBUG_OBJECT (depayload, "outsize %u", outsize);
234
235   return res;
236
237   /* ERRORS */
238 no_width:
239   {
240     GST_ERROR_OBJECT (depayload, "no width specified");
241     return FALSE;
242   }
243 no_height:
244   {
245     GST_ERROR_OBJECT (depayload, "no height specified");
246     return FALSE;
247   }
248 interlaced:
249   {
250     GST_ERROR_OBJECT (depayload, "interlaced formats not supported yet");
251     return FALSE;
252   }
253 no_sampling:
254   {
255     GST_ERROR_OBJECT (depayload, "no sampling specified");
256     return FALSE;
257   }
258 unknown_format:
259   {
260     GST_ERROR_OBJECT (depayload, "unknown sampling format '%s'", str);
261     return FALSE;
262   }
263 }
264
265 static GstBuffer *
266 gst_rtp_vraw_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
267 {
268   GstRtpVRawDepay *rtpvrawdepay;
269   guint8 *payload, *data, *dataend, *yp, *up, *vp, *headers;
270   guint32 timestamp;
271   guint cont, ystride, uvstride, pgroup, payload_len, size;
272   gint width, height, xinc, yinc;
273
274   rtpvrawdepay = GST_RTP_VRAW_DEPAY (depayload);
275
276   timestamp = gst_rtp_buffer_get_timestamp (buf);
277
278   if (timestamp != rtpvrawdepay->timestamp || rtpvrawdepay->outbuf == NULL) {
279     GstBuffer *outbuf;
280     GstFlowReturn ret;
281
282     GST_LOG_OBJECT (depayload, "new frame with timestamp %u", timestamp);
283     /* new timestamp, flush old buffer and create new output buffer */
284     if (rtpvrawdepay->outbuf) {
285       gst_base_rtp_depayload_push_ts (depayload, rtpvrawdepay->timestamp,
286           rtpvrawdepay->outbuf);
287       rtpvrawdepay->outbuf = NULL;
288     }
289
290     ret = gst_pad_alloc_buffer (depayload->srcpad, -1, rtpvrawdepay->outsize,
291         GST_PAD_CAPS (depayload->srcpad), &outbuf);
292     if (ret != GST_FLOW_OK)
293       goto alloc_failed;
294
295     /* clear timestamp from alloc... */
296     GST_BUFFER_TIMESTAMP (outbuf) = -1;
297
298     rtpvrawdepay->outbuf = outbuf;
299     rtpvrawdepay->timestamp = timestamp;
300   }
301
302   data = GST_BUFFER_DATA (rtpvrawdepay->outbuf);
303   size = GST_BUFFER_SIZE (rtpvrawdepay->outbuf);
304   dataend = data + size;
305
306   /* get pointer and strides of the planes */
307   yp = data + rtpvrawdepay->yp;
308   up = data + rtpvrawdepay->up;
309   vp = data + rtpvrawdepay->vp;
310
311   ystride = rtpvrawdepay->ystride;
312   uvstride = rtpvrawdepay->uvstride;
313   pgroup = rtpvrawdepay->pgroup;
314   width = rtpvrawdepay->width;
315   height = rtpvrawdepay->height;
316   xinc = rtpvrawdepay->xinc;
317   yinc = rtpvrawdepay->yinc;
318
319   payload = gst_rtp_buffer_get_payload (buf);
320   payload_len = gst_rtp_buffer_get_payload_len (buf);
321
322   if (payload_len < 3)
323     goto short_packet;
324
325   /* skip extended seqnum */
326   payload += 2;
327   payload_len -= 2;
328
329   /* remember header position */
330   headers = payload;
331
332   /* find data start */
333   do {
334     if (payload_len < 6)
335       goto short_packet;
336
337     cont = payload[4] & 0x80;
338
339     payload += 6;
340     payload_len -= 6;
341   } while (cont);
342
343   while (TRUE) {
344     guint length, line, offs, plen;
345     guint8 *datap;
346
347     /* stop when we run out of data */
348     if (payload_len == 0)
349       break;
350
351     /* read length and cont. This should work because we iterated the headers
352      * above. */
353     length = (headers[0] << 8) | headers[1];
354     line = ((headers[2] & 0x7f) << 8) | headers[3];
355     offs = ((headers[4] & 0x7f) << 8) | headers[5];
356     cont = headers[4] & 0x80;
357     headers += 6;
358
359     /* length must be a multiple of pgroup */
360     if (length % pgroup != 0)
361       goto wrong_length;
362
363     if (length > payload_len)
364       length = payload_len;
365
366     /* sanity check */
367     if (line > (height - yinc)) {
368       GST_WARNING_OBJECT (depayload, "skipping line %d: out of range", line);
369       goto next;
370     }
371     if (offs > (width - xinc)) {
372       GST_WARNING_OBJECT (depayload, "skipping offset %d: out of range", offs);
373       goto next;
374     }
375
376     /* calculate the maximim amount of bytes we can use per line */
377     if (offs + ((length / pgroup) * xinc) > (width - xinc)) {
378       plen = ((width - offs) * pgroup) / xinc;
379       GST_WARNING_OBJECT (depayload, "clipping length %d, offset %d", length,
380           offs);
381     } else
382       plen = length;
383
384     GST_LOG_OBJECT (depayload,
385         "writing length %u/%u, line %u, offset %u, remaining %u", plen, length,
386         line, offs, payload_len);
387
388     switch (rtpvrawdepay->format) {
389       case GST_VIDEO_FORMAT_RGB:
390       case GST_VIDEO_FORMAT_RGBA:
391       case GST_VIDEO_FORMAT_BGR:
392       case GST_VIDEO_FORMAT_BGRA:
393       case GST_VIDEO_FORMAT_UYVY:
394         /* samples are packed just like gstreamer packs them */
395         offs /= xinc;
396         datap = yp + (line * ystride) + (offs * pgroup);
397
398         memcpy (datap, payload, plen);
399         break;
400       case GST_VIDEO_FORMAT_AYUV:
401       {
402         gint i;
403         guint8 *p;
404
405         datap = yp + (line * ystride) + (offs * 4);
406         p = payload;
407
408         /* samples are packed in order Cb-Y-Cr for both interlaced and
409          * progressive frames */
410         for (i = 0; i < plen; i += pgroup) {
411           *datap++ = 0;
412           *datap++ = p[1];
413           *datap++ = p[0];
414           *datap++ = p[2];
415           p += pgroup;
416         }
417         break;
418       }
419       case GST_VIDEO_FORMAT_I420:
420       {
421         gint i;
422         guint uvoff;
423         guint8 *yd1p, *yd2p, *udp, *vdp, *p;
424
425         yd1p = yp + (line * ystride) + (offs);
426         yd2p = yd1p + ystride;
427         uvoff = (line / yinc * uvstride) + (offs / xinc);
428
429         udp = up + uvoff;
430         vdp = vp + uvoff;
431         p = payload;
432
433         /* line 0/1: Y00-Y01-Y10-Y11-Cb00-Cr00 Y02-Y03-Y12-Y13-Cb01-Cr01 ...  */
434         for (i = 0; i < plen; i += pgroup) {
435           *yd1p++ = p[0];
436           *yd1p++ = p[1];
437           *yd2p++ = p[2];
438           *yd2p++ = p[3];
439           *udp++ = p[4];
440           *vdp++ = p[5];
441           p += pgroup;
442         }
443         break;
444       }
445       case GST_VIDEO_FORMAT_Y41B:
446       {
447         gint i;
448         guint uvoff;
449         guint8 *ydp, *udp, *vdp, *p;
450
451         ydp = yp + (line * ystride) + (offs);
452         uvoff = (line / yinc * uvstride) + (offs / xinc);
453
454         udp = up + uvoff;
455         vdp = vp + uvoff;
456         p = payload;
457
458         /* Samples are packed in order Cb0-Y0-Y1-Cr0-Y2-Y3 for both interlaced
459          * and progressive scan lines */
460         for (i = 0; i < plen; i += pgroup) {
461           *udp++ = p[0];
462           *ydp++ = p[1];
463           *ydp++ = p[2];
464           *vdp++ = p[3];
465           *ydp++ = p[4];
466           *ydp++ = p[5];
467           p += pgroup;
468         }
469         break;
470       }
471       default:
472         goto unknown_sampling;
473     }
474
475   next:
476     if (!cont)
477       break;
478
479     payload += length;
480     payload_len -= length;
481   }
482
483   if (gst_rtp_buffer_get_marker (buf)) {
484     GST_LOG_OBJECT (depayload, "marker, flushing frame");
485     if (rtpvrawdepay->outbuf) {
486       gst_base_rtp_depayload_push_ts (depayload, timestamp,
487           rtpvrawdepay->outbuf);
488       rtpvrawdepay->outbuf = NULL;
489     }
490     rtpvrawdepay->timestamp = -1;
491   }
492   return NULL;
493
494   /* ERRORS */
495 unknown_sampling:
496   {
497     GST_ELEMENT_ERROR (depayload, STREAM, FORMAT,
498         (NULL), ("unimplemented sampling"));
499     return NULL;
500   }
501 alloc_failed:
502   {
503     GST_WARNING_OBJECT (depayload, "failed to alloc output buffer");
504     return NULL;
505   }
506 wrong_length:
507   {
508     GST_WARNING_OBJECT (depayload, "length not multiple of pgroup");
509     return NULL;
510   }
511 short_packet:
512   {
513     GST_WARNING_OBJECT (depayload, "short packet");
514     return NULL;
515   }
516 }
517
518 static GstStateChangeReturn
519 gst_rtp_vraw_depay_change_state (GstElement * element,
520     GstStateChange transition)
521 {
522   GstRtpVRawDepay *rtpvrawdepay;
523   GstStateChangeReturn ret;
524
525   rtpvrawdepay = GST_RTP_VRAW_DEPAY (element);
526
527   switch (transition) {
528     case GST_STATE_CHANGE_NULL_TO_READY:
529       break;
530     case GST_STATE_CHANGE_READY_TO_PAUSED:
531       rtpvrawdepay->timestamp = -1;
532       break;
533     default:
534       break;
535   }
536
537   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
538
539   switch (transition) {
540     case GST_STATE_CHANGE_PAUSED_TO_READY:
541       if (rtpvrawdepay->outbuf) {
542         gst_buffer_unref (rtpvrawdepay->outbuf);
543         rtpvrawdepay->outbuf = NULL;
544       }
545       break;
546     case GST_STATE_CHANGE_READY_TO_NULL:
547       break;
548     default:
549       break;
550   }
551   return ret;
552 }
553
554 gboolean
555 gst_rtp_vraw_depay_plugin_init (GstPlugin * plugin)
556 {
557   return gst_element_register (plugin, "rtpvrawdepay",
558       GST_RANK_MARGINAL, GST_TYPE_RTP_VRAW_DEPAY);
559 }