rtp: fix rank of payloaders and depayloaders
[platform/upstream/gst-plugins-good.git] / gst / rtp / gstrtpvrawpay.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 <string.h>
25
26 #include <gst/rtp/gstrtpbuffer.h>
27
28 #include "gstrtpvrawpay.h"
29
30 GST_DEBUG_CATEGORY_STATIC (rtpvrawpay_debug);
31 #define GST_CAT_DEFAULT (rtpvrawpay_debug)
32
33 static GstStaticPadTemplate gst_rtp_vraw_pay_sink_template =
34     GST_STATIC_PAD_TEMPLATE ("sink",
35     GST_PAD_SINK,
36     GST_PAD_ALWAYS,
37     GST_STATIC_CAPS ("video/x-raw-rgb, "
38         "bpp = (int) 24, "
39         "depth = (int) 24, "
40         "endianness = (int) BIG_ENDIAN, "
41         "red_mask = (int) 0xFF000000, "
42         "green_mask = (int) 0x00FF0000, "
43         "blue_mask = (int) 0x0000FF00, "
44         "width = (int) [ 1, 32767 ], "
45         "height = (int) [ 1, 32767 ]; "
46         "video/x-raw-rgb, "
47         "bpp = (int) 32, "
48         "depth = (int) 32, "
49         "endianness = (int) BIG_ENDIAN, "
50         "red_mask = (int) 0xFF000000, "
51         "green_mask = (int) 0x00FF0000, "
52         "blue_mask = (int) 0x0000FF00, "
53         "alpha_mask = (int) 0x000000FF, "
54         "width = (int) [ 1, 32767 ], "
55         "height = (int) [ 1, 32767 ]; "
56         "video/x-raw-rgb, "
57         "bpp = (int) 24, "
58         "depth = (int) 24, "
59         "endianness = (int) BIG_ENDIAN, "
60         "red_mask = (int) 0x0000FF00, "
61         "green_mask = (int) 0x00FF0000, "
62         "blue_mask = (int) 0xFF000000, "
63         "width = (int) [ 1, 32767 ], "
64         "height = (int) [ 1, 32767 ]; "
65         "video/x-raw-rgb, "
66         "bpp = (int) 32, "
67         "depth = (int) 32, "
68         "endianness = (int) BIG_ENDIAN, "
69         "red_mask = (int) 0x0000FF00, "
70         "green_mask = (int) 0x00FF0000, "
71         "blue_mask = (int) 0xFF000000, "
72         "alpha_mask = (int) 0x000000FF, "
73         "width = (int) [ 1, 32767 ], "
74         "height = (int) [ 1, 32767 ]; "
75         "video/x-raw-yuv, "
76         "format = (fourcc) { AYUV, UYVY, I420, Y41B }, "
77         "width = (int) [ 1, 32767 ], " "height = (int) [ 1, 32767 ]; ")
78     );
79
80 static GstStaticPadTemplate gst_rtp_vraw_pay_src_template =
81 GST_STATIC_PAD_TEMPLATE ("src",
82     GST_PAD_SRC,
83     GST_PAD_ALWAYS,
84     GST_STATIC_CAPS ("application/x-rtp, "
85         "media = (string) \"video\", "
86         "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
87         "clock-rate = (int) 90000, "
88         "encoding-name = (string) \"RAW\","
89         "sampling = (string) { \"RGB\", \"RGBA\", \"BGR\", \"BGRA\", "
90         "\"YCbCr-4:4:4\", \"YCbCr-4:2:2\", \"YCbCr-4:2:0\", "
91         "\"YCbCr-4:1:1\" },"
92         /* we cannot express these as strings 
93          * "width = (string) [1 32767],"
94          * "height = (string) [1 32767],"
95          */
96         "depth = (string) { \"8\", \"10\", \"12\", \"16\" },"
97         "colorimetry = (string) { \"BT601-5\", \"BT709-2\", \"SMPTE240M\" }"
98         /* optional 
99          * interlace = 
100          * top-field-first = 
101          * chroma-position = (string) 
102          * gamma = (float)
103          */
104     )
105     );
106
107 static gboolean gst_rtp_vraw_pay_setcaps (GstBaseRTPPayload * payload,
108     GstCaps * caps);
109 static GstFlowReturn gst_rtp_vraw_pay_handle_buffer (GstBaseRTPPayload *
110     payload, GstBuffer * buffer);
111
112 GST_BOILERPLATE (GstRtpVRawPay, gst_rtp_vraw_pay, GstBaseRTPPayload,
113     GST_TYPE_BASE_RTP_PAYLOAD)
114
115      static void gst_rtp_vraw_pay_base_init (gpointer klass)
116 {
117   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
118
119   gst_element_class_add_pad_template (element_class,
120       gst_static_pad_template_get (&gst_rtp_vraw_pay_src_template));
121   gst_element_class_add_pad_template (element_class,
122       gst_static_pad_template_get (&gst_rtp_vraw_pay_sink_template));
123
124   gst_element_class_set_details_simple (element_class,
125       "RTP Raw Video payloader", "Codec/Payloader/Network",
126       "Payload raw video as RTP packets (RFC 4175)",
127       "Wim Taymans <wim.taymans@gmail.com>");
128 }
129
130 static void
131 gst_rtp_vraw_pay_class_init (GstRtpVRawPayClass * klass)
132 {
133   GstBaseRTPPayloadClass *gstbasertppayload_class;
134
135   gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass;
136
137   gstbasertppayload_class->set_caps = gst_rtp_vraw_pay_setcaps;
138   gstbasertppayload_class->handle_buffer = gst_rtp_vraw_pay_handle_buffer;
139
140   GST_DEBUG_CATEGORY_INIT (rtpvrawpay_debug, "rtpvrawpay", 0,
141       "Raw video RTP Payloader");
142 }
143
144 static void
145 gst_rtp_vraw_pay_init (GstRtpVRawPay * rtpvrawpay, GstRtpVRawPayClass * klass)
146 {
147 }
148
149 static gboolean
150 gst_rtp_vraw_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps)
151 {
152   GstRtpVRawPay *rtpvrawpay;
153   GstStructure *s;
154   gboolean res;
155   const gchar *name;
156   gint width, height;
157   gint yp, up, vp;
158   gint pgroup, ystride, uvstride = 0, xinc, yinc;
159   GstVideoFormat sampling;
160   const gchar *depthstr, *samplingstr, *colorimetrystr;
161   gchar *wstr, *hstr;
162   gboolean interlaced;
163
164   rtpvrawpay = GST_RTP_VRAW_PAY (payload);
165
166   s = gst_caps_get_structure (caps, 0);
167
168   /* start parsing the format */
169   name = gst_structure_get_name (s);
170
171   /* these values are the only thing we can do */
172   depthstr = "8";
173   colorimetrystr = "SMPTE240M";
174
175   /* parse common width/height */
176   res = gst_structure_get_int (s, "width", &width);
177   res &= gst_structure_get_int (s, "height", &height);
178   if (!res)
179     goto missing_dimension;
180
181   /* fail on interlaced video for now */
182   if (!gst_structure_get_boolean (s, "interlaced", &interlaced))
183     interlaced = FALSE;
184
185   if (interlaced)
186     goto interlaced;
187
188   yp = up = vp = 0;
189   xinc = yinc = 1;
190
191   if (!strcmp (name, "video/x-raw-rgb")) {
192     gint amask, rmask;
193     gboolean has_alpha;
194
195     has_alpha = gst_structure_get_int (s, "alpha_mask", &amask);
196
197     if (!gst_structure_get_int (s, "red_mask", &rmask))
198       goto unknown_mask;
199
200     if (has_alpha) {
201       pgroup = 4;
202       ystride = width * 4;
203       if (rmask == 0xFF000000) {
204         sampling = GST_VIDEO_FORMAT_RGBA;
205         samplingstr = "RGBA";
206       } else {
207         sampling = GST_VIDEO_FORMAT_BGRA;
208         samplingstr = "BGRA";
209       }
210     } else {
211       pgroup = 3;
212       ystride = GST_ROUND_UP_4 (width * 3);
213       if (rmask == 0xFF000000) {
214         sampling = GST_VIDEO_FORMAT_RGB;
215         samplingstr = "RGB";
216       } else {
217         sampling = GST_VIDEO_FORMAT_BGR;
218         samplingstr = "BGR";
219       }
220     }
221   } else if (!strcmp (name, "video/x-raw-yuv")) {
222     guint32 fourcc;
223
224     if (!gst_structure_get_fourcc (s, "format", &fourcc))
225       goto unknown_fourcc;
226
227     GST_LOG_OBJECT (payload, "have fourcc %" GST_FOURCC_FORMAT,
228         GST_FOURCC_ARGS (fourcc));
229
230     switch (fourcc) {
231       case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
232         sampling = GST_VIDEO_FORMAT_AYUV;
233         samplingstr = "YCbCr-4:4:4";
234         pgroup = 3;
235         ystride = width * 4;
236         break;
237       case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
238         sampling = GST_VIDEO_FORMAT_UYVY;
239         samplingstr = "YCbCr-4:2:2";
240         pgroup = 4;
241         xinc = 2;
242         ystride = GST_ROUND_UP_2 (width) * 2;
243         break;
244       case GST_MAKE_FOURCC ('Y', '4', '1', 'B'):
245         sampling = GST_VIDEO_FORMAT_Y41B;
246         samplingstr = "YCbCr-4:1:1";
247         pgroup = 6;
248         xinc = 4;
249         ystride = GST_ROUND_UP_4 (width);
250         uvstride = GST_ROUND_UP_8 (width) / 4;
251         up = ystride * height;
252         vp = up + uvstride * height;
253         break;
254       case GST_MAKE_FOURCC ('I', '4', '2', '0'):
255         sampling = GST_VIDEO_FORMAT_I420;
256         samplingstr = "YCbCr-4:2:0";
257         pgroup = 6;
258         xinc = yinc = 2;
259         ystride = GST_ROUND_UP_4 (width);
260         uvstride = GST_ROUND_UP_8 (width) / 2;
261         up = ystride * GST_ROUND_UP_2 (height);
262         vp = up + uvstride * GST_ROUND_UP_2 (height) / 2;
263         break;
264       default:
265         goto unknown_fourcc;
266     }
267   } else
268     goto unknown_format;
269
270   rtpvrawpay->width = width;
271   rtpvrawpay->height = height;
272   rtpvrawpay->sampling = sampling;
273   rtpvrawpay->pgroup = pgroup;
274   rtpvrawpay->xinc = xinc;
275   rtpvrawpay->yinc = yinc;
276   rtpvrawpay->yp = yp;
277   rtpvrawpay->up = up;
278   rtpvrawpay->vp = vp;
279   rtpvrawpay->ystride = ystride;
280   rtpvrawpay->uvstride = uvstride;
281
282   GST_DEBUG_OBJECT (payload, "width %d, height %d, sampling %d", width, height,
283       sampling);
284   GST_DEBUG_OBJECT (payload, "yp %d, up %d, vp %d", yp, up, vp);
285   GST_DEBUG_OBJECT (payload, "pgroup %d, ystride %d, uvstride %d", pgroup,
286       ystride, uvstride);
287
288   wstr = g_strdup_printf ("%d", rtpvrawpay->width);
289   hstr = g_strdup_printf ("%d", rtpvrawpay->height);
290
291   gst_basertppayload_set_options (payload, "video", TRUE, "RAW", 90000);
292   res = gst_basertppayload_set_outcaps (payload, "sampling", G_TYPE_STRING,
293       samplingstr, "depth", G_TYPE_STRING, depthstr, "width", G_TYPE_STRING,
294       wstr, "height", G_TYPE_STRING, hstr, "colorimetry", G_TYPE_STRING,
295       colorimetrystr, NULL);
296   g_free (wstr);
297   g_free (hstr);
298
299   return res;
300
301   /* ERRORS */
302 unknown_mask:
303   {
304     GST_ERROR_OBJECT (payload, "unknown red mask specified");
305     return FALSE;
306   }
307 unknown_format:
308   {
309     GST_ERROR_OBJECT (payload, "unknown caps format");
310     return FALSE;
311   }
312 unknown_fourcc:
313   {
314     GST_ERROR_OBJECT (payload, "invalid or missing fourcc");
315     return FALSE;
316   }
317 interlaced:
318   {
319     GST_ERROR_OBJECT (payload, "interlaced video not supported yet");
320     return FALSE;
321   }
322 missing_dimension:
323   {
324     GST_ERROR_OBJECT (payload, "missing width or height property");
325     return FALSE;
326   }
327 }
328
329 static GstFlowReturn
330 gst_rtp_vraw_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buffer)
331 {
332   GstRtpVRawPay *rtpvrawpay;
333   GstFlowReturn ret = GST_FLOW_OK;
334   guint line, offset;
335   guint8 *data, *yp, *up, *vp;
336   guint ystride, uvstride;
337   guint size, pgroup;
338   guint mtu;
339   guint width, height;
340
341   rtpvrawpay = GST_RTP_VRAW_PAY (payload);
342
343   data = GST_BUFFER_DATA (buffer);
344   size = GST_BUFFER_SIZE (buffer);
345
346   GST_LOG_OBJECT (rtpvrawpay, "new frame of %u bytes", size);
347
348   /* get pointer and strides of the planes */
349   yp = data + rtpvrawpay->yp;
350   up = data + rtpvrawpay->up;
351   vp = data + rtpvrawpay->vp;
352
353   ystride = rtpvrawpay->ystride;
354   uvstride = rtpvrawpay->uvstride;
355
356   mtu = GST_BASE_RTP_PAYLOAD_MTU (payload);
357
358   /* amount of bytes for one pixel */
359   pgroup = rtpvrawpay->pgroup;
360   width = rtpvrawpay->width;
361   height = rtpvrawpay->height;
362
363   /* start with line 0, offset 0 */
364   line = 0;
365   offset = 0;
366
367   /* write all lines */
368   while (line < height) {
369     guint left;
370     GstBuffer *out;
371     guint8 *outdata, *headers;
372     gboolean next_line;
373     guint length, cont, pixels, fieldid;
374
375     /* get the max allowed payload length size, we try to fill the complete MTU */
376     left = gst_rtp_buffer_calc_payload_len (mtu, 0, 0);
377     out = gst_rtp_buffer_new_allocate (left, 0, 0);
378
379     GST_BUFFER_TIMESTAMP (out) = GST_BUFFER_TIMESTAMP (buffer);
380
381     outdata = gst_rtp_buffer_get_payload (out);
382
383     GST_LOG_OBJECT (rtpvrawpay, "created buffer of size %u for MTU %u", left,
384         mtu);
385
386     /*
387      *   0                   1                   2                   3
388      *   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
389      *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
390      *  |   Extended Sequence Number    |            Length             |
391      *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
392      *  |F|          Line No            |C|           Offset            |
393      *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
394      *  |            Length             |F|          Line No            |
395      *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
396      *  |C|           Offset            |                               .
397      *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               .
398      *  .                                                               .
399      *  .                 Two (partial) lines of video data             .
400      *  .                                                               .
401      *  +---------------------------------------------------------------+
402      */
403
404     /* need 2 bytes for the extended sequence number */
405     *outdata++ = 0;
406     *outdata++ = 0;
407     left -= 2;
408
409     /* the headers start here */
410     headers = outdata;
411
412     /* while we can fit at least one header and one pixel */
413     while (left > (6 + pgroup)) {
414       /* we need a 6 bytes header */
415       left -= 6;
416
417       /* get how may bytes we need for the remaining pixels */
418       pixels = width - offset;
419       length = (pixels * pgroup) / rtpvrawpay->xinc;
420
421       if (left >= length) {
422         /* pixels and header fit completely, we will write them and skip to the
423          * next line. */
424         next_line = TRUE;
425       } else {
426         /* line does not fit completely, see how many pixels fit */
427         pixels = (left / pgroup) * rtpvrawpay->xinc;
428         length = (pixels * pgroup) / rtpvrawpay->xinc;
429         next_line = FALSE;
430       }
431       GST_LOG_OBJECT (rtpvrawpay, "filling %u bytes in %u pixels", length,
432           pixels);
433       left -= length;
434
435       /* write length */
436       *outdata++ = (length >> 8) & 0xff;
437       *outdata++ = length & 0xff;
438
439       /* always 0 for now */
440       fieldid = 0x00;
441
442       /* write line no */
443       *outdata++ = ((line >> 8) & 0x7f) | fieldid;
444       *outdata++ = line & 0xff;
445
446       if (next_line) {
447         /* go to next line we do this here to make the check below easier */
448         line += rtpvrawpay->yinc;
449       }
450
451       /* calculate continuation marker */
452       cont = (left > (6 + pgroup) && line < height) ? 0x80 : 0x00;
453
454       /* write offset and continuation marker */
455       *outdata++ = ((offset >> 8) & 0x7f) | cont;
456       *outdata++ = offset & 0xff;
457
458       if (next_line) {
459         /* reset offset */
460         offset = 0;
461         GST_LOG_OBJECT (rtpvrawpay, "go to next line %u", line);
462       } else {
463         offset += pixels;
464         GST_LOG_OBJECT (rtpvrawpay, "next offset %u", offset);
465       }
466
467       if (!cont)
468         break;
469     }
470     GST_LOG_OBJECT (rtpvrawpay, "consumed %u bytes",
471         (guint) (outdata - headers));
472
473     /* second pass, read headers and write the data */
474     while (TRUE) {
475       guint offs, lin;
476
477       /* read length and cont */
478       length = (headers[0] << 8) | headers[1];
479       lin = ((headers[2] & 0x7f) << 8) | headers[3];
480       offs = ((headers[4] & 0x7f) << 8) | headers[5];
481       cont = headers[4] & 0x80;
482       pixels = length / pgroup;
483       headers += 6;
484
485       GST_LOG_OBJECT (payload, "writing length %u, line %u, offset %u, cont %d",
486           length, lin, offs, cont);
487
488       switch (rtpvrawpay->sampling) {
489         case GST_VIDEO_FORMAT_RGB:
490         case GST_VIDEO_FORMAT_RGBA:
491         case GST_VIDEO_FORMAT_BGR:
492         case GST_VIDEO_FORMAT_BGRA:
493         case GST_VIDEO_FORMAT_UYVY:
494           offs /= rtpvrawpay->xinc;
495           memcpy (outdata, yp + (lin * ystride) + (offs * pgroup), length);
496           outdata += length;
497           break;
498         case GST_VIDEO_FORMAT_AYUV:
499         {
500           gint i;
501           guint8 *datap;
502
503           datap = yp + (lin * ystride) + (offs * 4);
504
505           for (i = 0; i < pixels; i++) {
506             *outdata++ = datap[2];
507             *outdata++ = datap[1];
508             *outdata++ = datap[3];
509             datap += 4;
510           }
511           break;
512         }
513         case GST_VIDEO_FORMAT_I420:
514         {
515           gint i;
516           guint uvoff;
517           guint8 *yd1p, *yd2p, *udp, *vdp;
518
519           yd1p = yp + (lin * ystride) + (offs);
520           yd2p = yd1p + ystride;
521           uvoff =
522               (lin / rtpvrawpay->yinc * uvstride) + (offs / rtpvrawpay->xinc);
523           udp = up + uvoff;
524           vdp = vp + uvoff;
525
526           for (i = 0; i < pixels; i++) {
527             *outdata++ = *yd1p++;
528             *outdata++ = *yd1p++;
529             *outdata++ = *yd2p++;
530             *outdata++ = *yd2p++;
531             *outdata++ = *udp++;
532             *outdata++ = *vdp++;
533           }
534           break;
535         }
536         case GST_VIDEO_FORMAT_Y41B:
537         {
538           gint i;
539           guint uvoff;
540           guint8 *ydp, *udp, *vdp;
541
542           ydp = yp + (lin * ystride) + offs;
543           uvoff =
544               (lin / rtpvrawpay->yinc * uvstride) + (offs / rtpvrawpay->xinc);
545           udp = up + uvoff;
546           vdp = vp + uvoff;
547
548           for (i = 0; i < pixels; i++) {
549             *outdata++ = *udp++;
550             *outdata++ = *ydp++;
551             *outdata++ = *ydp++;
552             *outdata++ = *vdp++;
553             *outdata++ = *ydp++;
554             *outdata++ = *ydp++;
555           }
556           break;
557         }
558         default:
559           gst_buffer_unref (out);
560           goto unknown_sampling;
561       }
562
563       if (!cont)
564         break;
565     }
566
567     if (line >= height) {
568       GST_LOG_OBJECT (rtpvrawpay, "frame complete, set marker");
569       gst_rtp_buffer_set_marker (out, TRUE);
570     }
571     if (left > 0) {
572       GST_LOG_OBJECT (rtpvrawpay, "we have %u bytes left", left);
573       GST_BUFFER_SIZE (out) -= left;
574     }
575
576     /* push buffer */
577     ret = gst_basertppayload_push (payload, out);
578   }
579   gst_buffer_unref (buffer);
580
581   return ret;
582
583   /* ERRORS */
584 unknown_sampling:
585   {
586     GST_ELEMENT_ERROR (payload, STREAM, FORMAT,
587         (NULL), ("unimplemented sampling"));
588     gst_buffer_unref (buffer);
589     return GST_FLOW_NOT_SUPPORTED;
590   }
591 }
592
593 gboolean
594 gst_rtp_vraw_pay_plugin_init (GstPlugin * plugin)
595 {
596   return gst_element_register (plugin, "rtpvrawpay",
597       GST_RANK_SECONDARY, GST_TYPE_RTP_VRAW_PAY);
598 }