upload tizen1.0 source
[framework/multimedia/gst-plugins-good0.10.git] / gst / rtp / gstrtpj2kpay.c
1 /* GStreamer
2  * Copyright (C) 2009 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 /**
21  * SECTION:element-rtpj2kpay
22  *
23  * Payload encode JPEG 2000 pictures into RTP packets according to RFC 5371.
24  * For detailed information see: http://www.rfc-editor.org/rfc/rfc5371.txt
25  *
26  * The payloader takes a JPEG 2000 picture, scans the header for packetization
27  * units and constructs the RTP packet header followed by the actual JPEG 2000
28  * codestream.
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #  include "config.h"
33 #endif
34
35 #include <string.h>
36 #include <gst/rtp/gstrtpbuffer.h>
37
38 #include "gstrtpj2kpay.h"
39
40 static GstStaticPadTemplate gst_rtp_j2k_pay_sink_template =
41 GST_STATIC_PAD_TEMPLATE ("sink",
42     GST_PAD_SINK,
43     GST_PAD_ALWAYS,
44     GST_STATIC_CAPS ("image/x-jpc")
45     );
46
47 static GstStaticPadTemplate gst_rtp_j2k_pay_src_template =
48 GST_STATIC_PAD_TEMPLATE ("src",
49     GST_PAD_SRC,
50     GST_PAD_ALWAYS,
51     GST_STATIC_CAPS ("application/x-rtp, "
52         "  media = (string) \"video\", "
53         "  payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
54         "  clock-rate = (int) 90000, "
55         "  encoding-name = (string) \"JPEG2000\"")
56     );
57
58 GST_DEBUG_CATEGORY_STATIC (rtpj2kpay_debug);
59 #define GST_CAT_DEFAULT (rtpj2kpay_debug)
60
61 /*
62  * RtpJ2KMarker:
63  * @J2K_MARKER: Prefix for JPEG 2000 marker
64  * @J2K_MARKER_SOC: Start of Codestream
65  * @J2K_MARKER_SOT: Start of tile
66  * @J2K_MARKER_EOC: End of Codestream
67  *
68  * Identifers for markers in JPEG 2000 codestreams
69  */
70 typedef enum
71 {
72   J2K_MARKER = 0xFF,
73   J2K_MARKER_SOC = 0x4F,
74   J2K_MARKER_SOT = 0x90,
75   J2K_MARKER_SOP = 0x91,
76   J2K_MARKER_EPH = 0x92,
77   J2K_MARKER_SOD = 0x93,
78   J2K_MARKER_EOC = 0xD9
79 } RtpJ2KMarker;
80
81 #define DEFAULT_BUFFER_LIST             TRUE
82
83 enum
84 {
85   PROP_0,
86   PROP_BUFFER_LIST,
87   PROP_LAST
88 };
89
90 typedef struct
91 {
92   guint tp:2;
93   guint MHF:2;
94   guint mh_id:3;
95   guint T:1;
96   guint priority:8;
97   guint tile:16;
98   guint offset:24;
99 } RtpJ2KHeader;
100
101 #define HEADER_SIZE 8
102
103 static void gst_rtp_j2k_pay_set_property (GObject * object, guint prop_id,
104     const GValue * value, GParamSpec * pspec);
105 static void gst_rtp_j2k_pay_get_property (GObject * object, guint prop_id,
106     GValue * value, GParamSpec * pspec);
107
108 static gboolean gst_rtp_j2k_pay_setcaps (GstBaseRTPPayload * basepayload,
109     GstCaps * caps);
110
111 static GstFlowReturn gst_rtp_j2k_pay_handle_buffer (GstBaseRTPPayload * pad,
112     GstBuffer * buffer);
113
114 GST_BOILERPLATE (GstRtpJ2KPay, gst_rtp_j2k_pay, GstBaseRTPPayload,
115     GST_TYPE_BASE_RTP_PAYLOAD);
116
117 static void
118 gst_rtp_j2k_pay_base_init (gpointer klass)
119 {
120   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
121
122   gst_element_class_add_pad_template (element_class,
123       gst_static_pad_template_get (&gst_rtp_j2k_pay_src_template));
124   gst_element_class_add_pad_template (element_class,
125       gst_static_pad_template_get (&gst_rtp_j2k_pay_sink_template));
126
127   gst_element_class_set_details_simple (element_class,
128       "RTP JPEG 2000 payloader", "Codec/Payloader/Network/RTP",
129       "Payload-encodes JPEG 2000 pictures into RTP packets (RFC 5371)",
130       "Wim Taymans <wim.taymans@gmail.com>");
131 }
132
133 static void
134 gst_rtp_j2k_pay_class_init (GstRtpJ2KPayClass * klass)
135 {
136   GObjectClass *gobject_class;
137   GstBaseRTPPayloadClass *gstbasertppayload_class;
138
139   gobject_class = (GObjectClass *) klass;
140   gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass;
141
142   gobject_class->set_property = gst_rtp_j2k_pay_set_property;
143   gobject_class->get_property = gst_rtp_j2k_pay_get_property;
144
145   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BUFFER_LIST,
146       g_param_spec_boolean ("buffer-list", "Buffer List",
147           "Use Buffer Lists",
148           DEFAULT_BUFFER_LIST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
149
150   gstbasertppayload_class->set_caps = gst_rtp_j2k_pay_setcaps;
151   gstbasertppayload_class->handle_buffer = gst_rtp_j2k_pay_handle_buffer;
152
153   GST_DEBUG_CATEGORY_INIT (rtpj2kpay_debug, "rtpj2kpay", 0,
154       "JPEG 2000 RTP Payloader");
155 }
156
157 static void
158 gst_rtp_j2k_pay_init (GstRtpJ2KPay * pay, GstRtpJ2KPayClass * klass)
159 {
160   pay->buffer_list = DEFAULT_BUFFER_LIST;
161 }
162
163 static gboolean
164 gst_rtp_j2k_pay_setcaps (GstBaseRTPPayload * basepayload, GstCaps * caps)
165 {
166   GstStructure *caps_structure = gst_caps_get_structure (caps, 0);
167   GstRtpJ2KPay *pay;
168   gint width = 0, height = 0;
169   gboolean res;
170
171   pay = GST_RTP_J2K_PAY (basepayload);
172
173   /* these properties are not mandatory, we can get them from the stream */
174   if (gst_structure_get_int (caps_structure, "height", &height)) {
175     pay->height = height;
176   }
177   if (gst_structure_get_int (caps_structure, "width", &width)) {
178     pay->width = width;
179   }
180
181   gst_basertppayload_set_options (basepayload, "video", TRUE, "JPEG2000",
182       90000);
183   res = gst_basertppayload_set_outcaps (basepayload, NULL);
184
185   return res;
186 }
187
188
189 static guint
190 gst_rtp_j2k_pay_header_size (const guint8 * data, guint offset)
191 {
192   return data[offset] << 8 | data[offset + 1];
193 }
194
195 static RtpJ2KMarker
196 gst_rtp_j2k_pay_scan_marker (const guint8 * data, guint size, guint * offset)
197 {
198   while ((data[(*offset)++] != J2K_MARKER) && ((*offset) < size));
199
200   if (G_UNLIKELY ((*offset) >= size)) {
201     return J2K_MARKER_EOC;
202   } else {
203     guint8 marker = data[(*offset)++];
204     return marker;
205   }
206 }
207
208 typedef struct
209 {
210   RtpJ2KHeader header;
211   gboolean bitstream;
212   guint n_tiles;
213   guint next_sot;
214   gboolean force_packet;
215 } RtpJ2KState;
216
217 static guint
218 find_pu_end (GstRtpJ2KPay * pay, const guint8 * data, guint size,
219     guint offset, RtpJ2KState * state)
220 {
221   gboolean cut_sop = FALSE;
222   RtpJ2KMarker marker;
223
224   /* parse the j2k header for 'start of codestream' */
225   GST_LOG_OBJECT (pay, "checking from offset %u", offset);
226   while (offset < size) {
227     marker = gst_rtp_j2k_pay_scan_marker (data, size, &offset);
228
229     if (state->bitstream) {
230       /* parsing bitstream, only look for SOP */
231       switch (marker) {
232         case J2K_MARKER_SOP:
233           GST_LOG_OBJECT (pay, "found SOP at %u", offset);
234           if (cut_sop)
235             return offset - 2;
236           cut_sop = TRUE;
237           break;
238         case J2K_MARKER_EPH:
239           /* just skip over EPH */
240           GST_LOG_OBJECT (pay, "found EPH at %u", offset);
241           break;
242         default:
243           if (offset >= state->next_sot) {
244             GST_LOG_OBJECT (pay, "reached next SOT at %u", offset);
245             state->bitstream = FALSE;
246             state->force_packet = TRUE;
247             if (marker == J2K_MARKER_EOC && state->next_sot + 2 <= size)
248               /* include EOC but never go past the max size */
249               return state->next_sot + 2;
250             else
251               return state->next_sot;
252           }
253           break;
254       }
255     } else {
256       switch (marker) {
257         case J2K_MARKER_SOC:
258           GST_LOG_OBJECT (pay, "found SOC at %u", offset);
259           state->header.MHF = 1;
260           break;
261         case J2K_MARKER_SOT:
262         {
263           guint len, Psot;
264
265           GST_LOG_OBJECT (pay, "found SOT at %u", offset);
266           /* we found SOT but also had a header first */
267           if (state->header.MHF) {
268             state->force_packet = TRUE;
269             return offset - 2;
270           }
271
272           /* parse SOT but do some sanity checks first */
273           len = gst_rtp_j2k_pay_header_size (data, offset);
274           GST_LOG_OBJECT (pay, "SOT length %u", len);
275           if (len < 8)
276             return size;
277           if (offset + len >= size)
278             return size;
279
280           if (state->n_tiles == 0)
281             /* first tile, T is valid */
282             state->header.T = 0;
283           else
284             /* more tiles, T becomes invalid */
285             state->header.T = 1;
286           state->header.tile = GST_READ_UINT16_BE (&data[offset + 2]);
287           state->n_tiles++;
288
289           /* get offset of next tile, if it's 0, it goes all the way to the end of
290            * the data */
291           Psot = GST_READ_UINT32_BE (&data[offset + 4]);
292           if (Psot == 0)
293             state->next_sot = size;
294           else
295             state->next_sot = offset - 2 + Psot;
296
297           offset += len;
298           GST_LOG_OBJECT (pay, "Isot %u, Psot %u, next %u", state->header.tile,
299               Psot, state->next_sot);
300           break;
301         }
302         case J2K_MARKER_SOD:
303           GST_LOG_OBJECT (pay, "found SOD at %u", offset);
304           /* can't have more tiles now */
305           state->n_tiles = 0;
306           /* go to bitstream parsing */
307           state->bitstream = TRUE;
308           /* cut at the next SOP or else include all data */
309           cut_sop = TRUE;
310           /* force a new packet when we see SOP, this can be optional but the
311            * spec recommends packing headers separately */
312           state->force_packet = TRUE;
313           break;
314         case J2K_MARKER_EOC:
315           GST_LOG_OBJECT (pay, "found EOC at %u", offset);
316           return offset;
317         default:
318         {
319           guint len = gst_rtp_j2k_pay_header_size (data, offset);
320           GST_LOG_OBJECT (pay, "skip 0x%02x len %u", marker, len);
321           offset += len;
322           break;
323         }
324       }
325     }
326   }
327   GST_DEBUG_OBJECT (pay, "reached end of data");
328   return size;
329 }
330
331 static GstFlowReturn
332 gst_rtp_j2k_pay_handle_buffer (GstBaseRTPPayload * basepayload,
333     GstBuffer * buffer)
334 {
335   GstRtpJ2KPay *pay;
336   GstClockTime timestamp;
337   GstFlowReturn ret = GST_FLOW_ERROR;
338   RtpJ2KState state;
339   GstBufferList *list = NULL;
340   GstBufferListIterator *it = NULL;
341   guint8 *data;
342   guint size;
343   guint mtu, max_size;
344   guint offset;
345   guint end, pos;
346
347   pay = GST_RTP_J2K_PAY (basepayload);
348   mtu = GST_BASE_RTP_PAYLOAD_MTU (pay);
349
350   size = GST_BUFFER_SIZE (buffer);
351   data = GST_BUFFER_DATA (buffer);
352   timestamp = GST_BUFFER_TIMESTAMP (buffer);
353   offset = pos = end = 0;
354
355   GST_LOG_OBJECT (pay, "got buffer size %u, timestamp %" GST_TIME_FORMAT, size,
356       GST_TIME_ARGS (timestamp));
357
358   /* do some header defaults first */
359   state.header.tp = 0;          /* only progressive scan */
360   state.header.MHF = 0;         /* no header */
361   state.header.mh_id = 0;       /* always 0 for now */
362   state.header.T = 1;           /* invalid tile */
363   state.header.priority = 255;  /* always 255 for now */
364   state.header.tile = 0;        /* no tile number */
365   state.header.offset = 0;      /* offset of 0 */
366   state.bitstream = FALSE;
367   state.n_tiles = 0;
368   state.next_sot = 0;
369   state.force_packet = FALSE;
370
371   if (pay->buffer_list) {
372     list = gst_buffer_list_new ();
373     it = gst_buffer_list_iterate (list);
374   }
375
376   /* get max packet length */
377   max_size = gst_rtp_buffer_calc_payload_len (mtu - HEADER_SIZE, 0, 0);
378
379   do {
380     GstBuffer *outbuf;
381     guint8 *header;
382     guint payload_size;
383     guint pu_size;
384
385     /* try to pack as much as we can */
386     do {
387       /* see how much we have scanned already */
388       pu_size = end - offset;
389       GST_DEBUG_OBJECT (pay, "scanned pu size %u", pu_size);
390
391       /* we need to make a new packet */
392       if (state.force_packet) {
393         GST_DEBUG_OBJECT (pay, "need to force a new packet");
394         state.force_packet = FALSE;
395         pos = end;
396         break;
397       }
398
399       /* else see if we have enough */
400       if (pu_size > max_size) {
401         if (pos != offset)
402           /* the packet became too large, use previous scanpos */
403           pu_size = pos - offset;
404         else
405           /* the already scanned data was already too big, make sure we start
406            * scanning from the last searched position */
407           pos = end;
408
409         GST_DEBUG_OBJECT (pay, "max size exceeded pu_size %u", pu_size);
410         break;
411       }
412
413       pos = end;
414
415       /* exit when finished */
416       if (pos == size)
417         break;
418
419       /* scan next packetization unit and fill in the header */
420       end = find_pu_end (pay, data, size, pos, &state);
421     } while (TRUE);
422
423     while (pu_size > 0) {
424       guint packet_size, data_size;
425
426       /* calculate the packet size */
427       packet_size =
428           gst_rtp_buffer_calc_packet_len (pu_size + HEADER_SIZE, 0, 0);
429
430       if (packet_size > mtu) {
431         GST_DEBUG_OBJECT (pay, "needed packet size %u clamped to MTU %u",
432             packet_size, mtu);
433         packet_size = mtu;
434       } else {
435         GST_DEBUG_OBJECT (pay, "needed packet size %u fits in MTU %u",
436             packet_size, mtu);
437       }
438
439       /* get total payload size and data size */
440       payload_size = gst_rtp_buffer_calc_payload_len (packet_size, 0, 0);
441       data_size = payload_size - HEADER_SIZE;
442
443       if (pay->buffer_list) {
444         /* make buffer for header */
445         outbuf = gst_rtp_buffer_new_allocate (HEADER_SIZE, 0, 0);
446       } else {
447         /* make buffer for header and data */
448         outbuf = gst_rtp_buffer_new_allocate (payload_size, 0, 0);
449       }
450       GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
451
452       /* get pointer to header */
453       header = gst_rtp_buffer_get_payload (outbuf);
454
455       pu_size -= data_size;
456       if (pu_size == 0) {
457         /* reached the end of a packetization unit */
458         if (state.header.MHF) {
459           /* we were doing a header, see if all fit in one packet or if
460            * we had to fragment it */
461           if (offset == 0)
462             state.header.MHF = 3;
463           else
464             state.header.MHF = 2;
465         }
466         if (end >= size)
467           gst_rtp_buffer_set_marker (outbuf, TRUE);
468       }
469
470       /*
471        * RtpJ2KHeader:
472        * @tp: type (0 progressive, 1 odd field, 2 even field)
473        * @MHF: Main Header Flag
474        * @mh_id: Main Header Identification
475        * @T: Tile field invalidation flag
476        * @priority: priority
477        * @tile number: the tile number of the payload
478        * @reserved: set to 0
479        * @fragment offset: the byte offset of the current payload
480        *
481        *  0                   1                   2                   3
482        *  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
483        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
484        * |tp |MHF|mh_id|T|     priority  |           tile number         |
485        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
486        * |reserved       |             fragment offset                   |
487        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
488        */
489       header[0] = (state.header.tp << 6) | (state.header.MHF << 4) |
490           (state.header.mh_id << 1) | state.header.T;
491       header[1] = state.header.priority;
492       header[2] = state.header.tile >> 8;
493       header[3] = state.header.tile & 0xff;
494       header[4] = 0;
495       header[5] = state.header.offset >> 16;
496       header[6] = (state.header.offset >> 8) & 0xff;
497       header[7] = state.header.offset & 0xff;
498
499       if (pay->buffer_list) {
500         GstBuffer *paybuf;
501
502         /* make subbuffer of j2k data */
503         paybuf = gst_buffer_create_sub (buffer, offset, data_size);
504
505         /* create a new group to hold the header and the payload */
506         gst_buffer_list_iterator_add_group (it);
507
508         /* add both buffers to the buffer list */
509         gst_buffer_list_iterator_add (it, outbuf);
510         gst_buffer_list_iterator_add (it, paybuf);
511       } else {
512         /* copy payload */
513         memcpy (header + HEADER_SIZE, &data[offset], data_size);
514
515         ret = gst_basertppayload_push (basepayload, outbuf);
516         if (ret != GST_FLOW_OK)
517           goto done;
518       }
519
520       /* reset header for next round */
521       state.header.MHF = 0;
522       state.header.T = 1;
523       state.header.tile = 0;
524
525       offset += data_size;
526     }
527     offset = pos;
528   } while (offset < size);
529
530 done:
531   gst_buffer_unref (buffer);
532
533   if (pay->buffer_list) {
534     /* free iterator and push the whole buffer list at once */
535     gst_buffer_list_iterator_free (it);
536     ret = gst_basertppayload_push_list (basepayload, list);
537   }
538
539   return ret;
540 }
541
542 static void
543 gst_rtp_j2k_pay_set_property (GObject * object, guint prop_id,
544     const GValue * value, GParamSpec * pspec)
545 {
546   GstRtpJ2KPay *rtpj2kpay;
547
548   rtpj2kpay = GST_RTP_J2K_PAY (object);
549
550   switch (prop_id) {
551     case PROP_BUFFER_LIST:
552       rtpj2kpay->buffer_list = g_value_get_boolean (value);
553       break;
554     default:
555       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
556       break;
557   }
558 }
559
560 static void
561 gst_rtp_j2k_pay_get_property (GObject * object, guint prop_id,
562     GValue * value, GParamSpec * pspec)
563 {
564   GstRtpJ2KPay *rtpj2kpay;
565
566   rtpj2kpay = GST_RTP_J2K_PAY (object);
567
568   switch (prop_id) {
569     case PROP_BUFFER_LIST:
570       g_value_set_boolean (value, rtpj2kpay->buffer_list);
571       break;
572     default:
573       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
574       break;
575   }
576 }
577
578 gboolean
579 gst_rtp_j2k_pay_plugin_init (GstPlugin * plugin)
580 {
581   return gst_element_register (plugin, "rtpj2kpay", GST_RANK_SECONDARY,
582       GST_TYPE_RTP_J2K_PAY);
583 }