rtpvorbisdepay: remove dead code
[platform/upstream/gst-plugins-good.git] / gst / rtp / gstrtptheorapay.c
1 /* GStreamer
2  * Copyright (C) <2006> 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., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, 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 #include <gst/video/video.h>
28
29 #include "fnv1hash.h"
30 #include "gstrtptheorapay.h"
31 #include "gstrtputils.h"
32
33 #define THEORA_ID_LEN   42
34
35 GST_DEBUG_CATEGORY_STATIC (rtptheorapay_debug);
36 #define GST_CAT_DEFAULT (rtptheorapay_debug)
37
38 /* references:
39  * http://svn.xiph.org/trunk/theora/doc/draft-ietf-avt-rtp-theora-01.txt
40  */
41
42 static GstStaticPadTemplate gst_rtp_theora_pay_src_template =
43 GST_STATIC_PAD_TEMPLATE ("src",
44     GST_PAD_SRC,
45     GST_PAD_ALWAYS,
46     GST_STATIC_CAPS ("application/x-rtp, "
47         "media = (string) \"video\", "
48         "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
49         "clock-rate = (int) 90000, " "encoding-name = (string) \"THEORA\""
50         /* All required parameters
51          *
52          * "sampling = (string) { "YCbCr-4:2:0", "YCbCr-4:2:2", "YCbCr-4:4:4" } "
53          * "width = (string) [1, 1048561] (multiples of 16) "
54          * "height = (string) [1, 1048561] (multiples of 16) "
55          * "configuration = (string) ANY"
56          */
57         /* All optional parameters
58          *
59          * "configuration-uri ="
60          * "delivery-method = (string) { inline, in_band, out_band/<specific_name> } "
61          */
62     )
63     );
64
65 static GstStaticPadTemplate gst_rtp_theora_pay_sink_template =
66 GST_STATIC_PAD_TEMPLATE ("sink",
67     GST_PAD_SINK,
68     GST_PAD_ALWAYS,
69     GST_STATIC_CAPS ("video/x-theora")
70     );
71
72 #define DEFAULT_CONFIG_INTERVAL 0
73
74 enum
75 {
76   PROP_0,
77   PROP_CONFIG_INTERVAL
78 };
79
80 #define gst_rtp_theora_pay_parent_class parent_class
81 G_DEFINE_TYPE (GstRtpTheoraPay, gst_rtp_theora_pay, GST_TYPE_RTP_BASE_PAYLOAD);
82
83 static gboolean gst_rtp_theora_pay_setcaps (GstRTPBasePayload * basepayload,
84     GstCaps * caps);
85 static GstStateChangeReturn gst_rtp_theora_pay_change_state (GstElement *
86     element, GstStateChange transition);
87 static GstFlowReturn gst_rtp_theora_pay_handle_buffer (GstRTPBasePayload * pad,
88     GstBuffer * buffer);
89 static gboolean gst_rtp_theora_pay_sink_event (GstRTPBasePayload * payload,
90     GstEvent * event);
91
92 static gboolean gst_rtp_theora_pay_parse_id (GstRTPBasePayload * basepayload,
93     guint8 * data, guint size);
94 static gboolean gst_rtp_theora_pay_finish_headers (GstRTPBasePayload *
95     basepayload);
96
97 static void gst_rtp_theora_pay_set_property (GObject * object, guint prop_id,
98     const GValue * value, GParamSpec * pspec);
99 static void gst_rtp_theora_pay_get_property (GObject * object, guint prop_id,
100     GValue * value, GParamSpec * pspec);
101
102 static void
103 gst_rtp_theora_pay_class_init (GstRtpTheoraPayClass * klass)
104 {
105   GObjectClass *gobject_class;
106   GstElementClass *gstelement_class;
107   GstRTPBasePayloadClass *gstrtpbasepayload_class;
108
109   gobject_class = (GObjectClass *) klass;
110   gstelement_class = (GstElementClass *) klass;
111   gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
112
113   gstelement_class->change_state = gst_rtp_theora_pay_change_state;
114
115   gstrtpbasepayload_class->set_caps = gst_rtp_theora_pay_setcaps;
116   gstrtpbasepayload_class->handle_buffer = gst_rtp_theora_pay_handle_buffer;
117   gstrtpbasepayload_class->sink_event = gst_rtp_theora_pay_sink_event;
118
119   gobject_class->set_property = gst_rtp_theora_pay_set_property;
120   gobject_class->get_property = gst_rtp_theora_pay_get_property;
121
122   gst_element_class_add_static_pad_template (gstelement_class,
123       &gst_rtp_theora_pay_src_template);
124   gst_element_class_add_static_pad_template (gstelement_class,
125       &gst_rtp_theora_pay_sink_template);
126
127   gst_element_class_set_static_metadata (gstelement_class,
128       "RTP Theora payloader", "Codec/Payloader/Network/RTP",
129       "Payload-encode Theora video into RTP packets (draft-01 RFC XXXX)",
130       "Wim Taymans <wim.taymans@gmail.com>");
131
132   GST_DEBUG_CATEGORY_INIT (rtptheorapay_debug, "rtptheorapay", 0,
133       "Theora RTP Payloader");
134
135   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CONFIG_INTERVAL,
136       g_param_spec_uint ("config-interval", "Config Send Interval",
137           "Send Config Insertion Interval in seconds (configuration headers "
138           "will be multiplexed in the data stream when detected.) (0 = disabled)",
139           0, 3600, DEFAULT_CONFIG_INTERVAL,
140           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
141       );
142 }
143
144 static void
145 gst_rtp_theora_pay_init (GstRtpTheoraPay * rtptheorapay)
146 {
147   rtptheorapay->last_config = GST_CLOCK_TIME_NONE;
148 }
149
150 static void
151 gst_rtp_theora_pay_clear_packet (GstRtpTheoraPay * rtptheorapay)
152 {
153   if (rtptheorapay->packet)
154     gst_buffer_unref (rtptheorapay->packet);
155   rtptheorapay->packet = NULL;
156   g_list_free_full (rtptheorapay->packet_buffers,
157       (GDestroyNotify) gst_buffer_unref);
158   rtptheorapay->packet_buffers = NULL;
159 }
160
161 static void
162 gst_rtp_theora_pay_cleanup (GstRtpTheoraPay * rtptheorapay)
163 {
164   gst_rtp_theora_pay_clear_packet (rtptheorapay);
165   g_list_free_full (rtptheorapay->headers, (GDestroyNotify) gst_buffer_unref);
166   rtptheorapay->headers = NULL;
167   g_free (rtptheorapay->config_data);
168   rtptheorapay->config_data = NULL;
169   rtptheorapay->last_config = GST_CLOCK_TIME_NONE;
170 }
171
172 static gboolean
173 gst_rtp_theora_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps)
174 {
175   GstRtpTheoraPay *rtptheorapay;
176   GstStructure *s;
177   const GValue *array;
178   gint asize, i;
179   GstBuffer *buf;
180   GstMapInfo map;
181
182   rtptheorapay = GST_RTP_THEORA_PAY (basepayload);
183
184   s = gst_caps_get_structure (caps, 0);
185
186   rtptheorapay->need_headers = TRUE;
187
188   if ((array = gst_structure_get_value (s, "streamheader")) == NULL)
189     goto done;
190
191   if (G_VALUE_TYPE (array) != GST_TYPE_ARRAY)
192     goto done;
193
194   if ((asize = gst_value_array_get_size (array)) < 3)
195     goto done;
196
197   for (i = 0; i < asize; i++) {
198     const GValue *value;
199
200     value = gst_value_array_get_value (array, i);
201     if ((buf = gst_value_get_buffer (value)) == NULL)
202       goto null_buffer;
203
204     gst_buffer_map (buf, &map, GST_MAP_READ);
205     /* no data packets allowed */
206     if (map.size < 1)
207       goto invalid_streamheader;
208
209     /* we need packets with id 0x80, 0x81, 0x82 */
210     if (map.data[0] != 0x80 + i)
211       goto invalid_streamheader;
212
213     if (i == 0) {
214       /* identification, we need to parse this in order to get the clock rate. */
215       if (G_UNLIKELY (!gst_rtp_theora_pay_parse_id (basepayload, map.data,
216                   map.size)))
217         goto parse_id_failed;
218     }
219     GST_DEBUG_OBJECT (rtptheorapay, "collecting header %d", i);
220     rtptheorapay->headers =
221         g_list_append (rtptheorapay->headers, gst_buffer_ref (buf));
222     gst_buffer_unmap (buf, &map);
223   }
224   if (!gst_rtp_theora_pay_finish_headers (basepayload))
225     goto finish_failed;
226
227 done:
228   return TRUE;
229
230   /* ERRORS */
231 null_buffer:
232   {
233     GST_WARNING_OBJECT (rtptheorapay, "streamheader with null buffer received");
234     return FALSE;
235   }
236 invalid_streamheader:
237   {
238     GST_WARNING_OBJECT (rtptheorapay, "unable to parse initial header");
239     gst_buffer_unmap (buf, &map);
240     return FALSE;
241   }
242 parse_id_failed:
243   {
244     GST_WARNING_OBJECT (rtptheorapay, "unable to parse initial header");
245     gst_buffer_unmap (buf, &map);
246     return FALSE;
247   }
248 finish_failed:
249   {
250     GST_WARNING_OBJECT (rtptheorapay, "unable to finish headers");
251     return FALSE;
252   }
253 }
254
255 static void
256 gst_rtp_theora_pay_reset_packet (GstRtpTheoraPay * rtptheorapay, guint8 TDT)
257 {
258   guint payload_len;
259   GstRTPBuffer rtp = { NULL };
260
261   GST_DEBUG_OBJECT (rtptheorapay, "reset packet");
262
263   rtptheorapay->payload_pos = 4;
264   gst_rtp_buffer_map (rtptheorapay->packet, GST_MAP_READ, &rtp);
265   payload_len = gst_rtp_buffer_get_payload_len (&rtp);
266   gst_rtp_buffer_unmap (&rtp);
267   rtptheorapay->payload_left = payload_len - 4;
268   rtptheorapay->payload_duration = 0;
269   rtptheorapay->payload_F = 0;
270   rtptheorapay->payload_TDT = TDT;
271   rtptheorapay->payload_pkts = 0;
272 }
273
274 static void
275 gst_rtp_theora_pay_init_packet (GstRtpTheoraPay * rtptheorapay, guint8 TDT,
276     GstClockTime timestamp)
277 {
278   GST_DEBUG_OBJECT (rtptheorapay, "starting new packet, TDT: %d", TDT);
279
280   gst_rtp_theora_pay_clear_packet (rtptheorapay);
281
282   /* new packet allocate max packet size */
283   rtptheorapay->packet =
284       gst_rtp_buffer_new_allocate_len (GST_RTP_BASE_PAYLOAD_MTU
285       (rtptheorapay), 0, 0);
286   gst_rtp_theora_pay_reset_packet (rtptheorapay, TDT);
287
288   GST_BUFFER_PTS (rtptheorapay->packet) = timestamp;
289 }
290
291 static GstFlowReturn
292 gst_rtp_theora_pay_flush_packet (GstRtpTheoraPay * rtptheorapay)
293 {
294   GstFlowReturn ret;
295   guint8 *payload;
296   guint hlen;
297   GstRTPBuffer rtp = { NULL };
298   GList *l;
299
300   /* check for empty packet */
301   if (!rtptheorapay->packet || rtptheorapay->payload_pos <= 4)
302     return GST_FLOW_OK;
303
304   GST_DEBUG_OBJECT (rtptheorapay, "flushing packet");
305
306   gst_rtp_buffer_map (rtptheorapay->packet, GST_MAP_WRITE, &rtp);
307
308   /* fix header */
309   payload = gst_rtp_buffer_get_payload (&rtp);
310   /*
311    *  0                   1                   2                   3
312    *  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
313    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
314    * |                     Ident                     | F |TDT|# pkts.|
315    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
316    *
317    * F: Fragment type (0=none, 1=start, 2=cont, 3=end)
318    * TDT: Theora data type (0=theora, 1=config, 2=comment, 3=reserved)
319    * pkts: number of packets.
320    */
321   payload[0] = (rtptheorapay->payload_ident >> 16) & 0xff;
322   payload[1] = (rtptheorapay->payload_ident >> 8) & 0xff;
323   payload[2] = (rtptheorapay->payload_ident) & 0xff;
324   payload[3] = (rtptheorapay->payload_F & 0x3) << 6 |
325       (rtptheorapay->payload_TDT & 0x3) << 4 |
326       (rtptheorapay->payload_pkts & 0xf);
327
328   gst_rtp_buffer_unmap (&rtp);
329
330   /* shrink the buffer size to the last written byte */
331   hlen = gst_rtp_buffer_calc_header_len (0);
332   gst_buffer_resize (rtptheorapay->packet, 0, hlen + rtptheorapay->payload_pos);
333
334   GST_BUFFER_DURATION (rtptheorapay->packet) = rtptheorapay->payload_duration;
335
336   for (l = g_list_last (rtptheorapay->packet_buffers); l; l = l->prev) {
337     GstBuffer *buf = GST_BUFFER_CAST (l->data);
338     gst_rtp_copy_meta (GST_ELEMENT_CAST (rtptheorapay), rtptheorapay->packet,
339         buf, g_quark_from_static_string (GST_META_TAG_VIDEO_STR));
340     gst_buffer_unref (buf);
341   }
342   g_list_free (rtptheorapay->packet_buffers);
343   rtptheorapay->packet_buffers = NULL;
344
345   /* push, this gives away our ref to the packet, so clear it. */
346   ret =
347       gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtptheorapay),
348       rtptheorapay->packet);
349   rtptheorapay->packet = NULL;
350
351   return ret;
352 }
353
354 static gboolean
355 gst_rtp_theora_pay_finish_headers (GstRTPBasePayload * basepayload)
356 {
357   GstRtpTheoraPay *rtptheorapay = GST_RTP_THEORA_PAY (basepayload);
358   GList *walk;
359   guint length, size, n_headers, configlen, extralen;
360   gchar *wstr, *hstr, *configuration;
361   guint8 *data, *config;
362   guint32 ident;
363   gboolean res;
364   const gchar *sampling = NULL;
365
366   GST_DEBUG_OBJECT (rtptheorapay, "finish headers");
367
368   if (!rtptheorapay->headers) {
369     GST_DEBUG_OBJECT (rtptheorapay, "We need 2 headers but have none");
370     goto no_headers;
371   }
372
373   /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
374    * |                     Number of packed headers                  |
375    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
376    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
377    * |                          Packed header                        |
378    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
379    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
380    * |                          Packed header                        |
381    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
382    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
383    * |                          ....                                 |
384    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
385    *
386    * We only construct a config containing 1 packed header like this:
387    *
388    *  0                   1                   2                   3
389    *  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
390    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
391    * |                   Ident                       | length       ..
392    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
393    * ..              | n. of headers |    length1    |    length2   ..
394    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
395    * ..              |             Identification Header            ..
396    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
397    * .................................................................
398    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
399    * ..              |         Comment Header                       ..
400    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
401    * .................................................................
402    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
403    * ..                        Comment Header                        |
404    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
405    * |                          Setup Header                        ..
406    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
407    * .................................................................
408    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
409    * ..                         Setup Header                         |
410    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
411    */
412
413   /* we need 4 bytes for the number of headers (which is always 1), 3 bytes for
414    * the ident, 2 bytes for length, 1 byte for n. of headers. */
415   size = 4 + 3 + 2 + 1;
416
417   /* count the size of the headers first and update the hash */
418   length = 0;
419   n_headers = 0;
420   ident = fnv1_hash_32_new ();
421   extralen = 1;
422   for (walk = rtptheorapay->headers; walk; walk = g_list_next (walk)) {
423     GstBuffer *buf = GST_BUFFER_CAST (walk->data);
424     GstMapInfo map;
425     guint bsize;
426
427     bsize = gst_buffer_get_size (buf);
428     length += bsize;
429     n_headers++;
430
431     /* count number of bytes needed for length fields, we don't need this for
432      * the last header. */
433     if (g_list_next (walk)) {
434       do {
435         size++;
436         extralen++;
437         bsize >>= 7;
438       } while (bsize);
439     }
440     /* update hash */
441     gst_buffer_map (buf, &map, GST_MAP_READ);
442     ident = fnv1_hash_32_update (ident, map.data, map.size);
443     gst_buffer_unmap (buf, &map);
444   }
445
446   /* packet length is header size + packet length */
447   configlen = size + length;
448   config = data = g_malloc (configlen);
449
450   /* number of packed headers, we only pack 1 header */
451   data[0] = 0;
452   data[1] = 0;
453   data[2] = 0;
454   data[3] = 1;
455
456   ident = fnv1_hash_32_to_24 (ident);
457   rtptheorapay->payload_ident = ident;
458   GST_DEBUG_OBJECT (rtptheorapay, "ident 0x%08x", ident);
459
460   /* take lower 3 bytes */
461   data[4] = (ident >> 16) & 0xff;
462   data[5] = (ident >> 8) & 0xff;
463   data[6] = ident & 0xff;
464
465   /* store length of all theora headers */
466   data[7] = ((length) >> 8) & 0xff;
467   data[8] = (length) & 0xff;
468
469   /* store number of headers minus one. */
470   data[9] = n_headers - 1;
471   data += 10;
472
473   /* store length for each header */
474   for (walk = rtptheorapay->headers; walk; walk = g_list_next (walk)) {
475     GstBuffer *buf = GST_BUFFER_CAST (walk->data);
476     guint bsize, size, temp;
477     guint flag;
478
479     /* only need to store the length when it's not the last header */
480     if (!g_list_next (walk))
481       break;
482
483     bsize = gst_buffer_get_size (buf);
484
485     /* calc size */
486     size = 0;
487     do {
488       size++;
489       bsize >>= 7;
490     } while (bsize);
491     temp = size;
492
493     bsize = gst_buffer_get_size (buf);
494     /* write the size backwards */
495     flag = 0;
496     while (size) {
497       size--;
498       data[size] = (bsize & 0x7f) | flag;
499       bsize >>= 7;
500       flag = 0x80;              /* Flag bit on all bytes of the length except the last */
501     }
502     data += temp;
503   }
504
505   /* copy header data */
506   for (walk = rtptheorapay->headers; walk; walk = g_list_next (walk)) {
507     GstBuffer *buf = GST_BUFFER_CAST (walk->data);
508
509     gst_buffer_extract (buf, 0, data, gst_buffer_get_size (buf));
510     data += gst_buffer_get_size (buf);
511   }
512   rtptheorapay->need_headers = FALSE;
513
514   /* serialize to base64 */
515   configuration = g_base64_encode (config, configlen);
516
517   /* store for later re-sending */
518   g_free (rtptheorapay->config_data);
519   rtptheorapay->config_size = configlen - 4 - 3 - 2;
520   rtptheorapay->config_data = g_malloc (rtptheorapay->config_size);
521   rtptheorapay->config_extra_len = extralen;
522   memcpy (rtptheorapay->config_data, config + 4 + 3 + 2,
523       rtptheorapay->config_size);
524
525   g_free (config);
526
527   /* configure payloader settings */
528   switch (rtptheorapay->pixel_format) {
529     case 2:
530       sampling = "YCbCr-4:2:2";
531       break;
532     case 3:
533       sampling = "YCbCr-4:4:4";
534       break;
535     case 0:
536     default:
537       sampling = "YCbCr-4:2:0";
538       break;
539   }
540
541
542   wstr = g_strdup_printf ("%d", rtptheorapay->width);
543   hstr = g_strdup_printf ("%d", rtptheorapay->height);
544   gst_rtp_base_payload_set_options (basepayload, "video", TRUE, "THEORA",
545       90000);
546   res =
547       gst_rtp_base_payload_set_outcaps (basepayload, "sampling", G_TYPE_STRING,
548       sampling, "width", G_TYPE_STRING, wstr, "height", G_TYPE_STRING,
549       hstr, "configuration", G_TYPE_STRING, configuration, "delivery-method",
550       G_TYPE_STRING, "inline",
551       /* don't set the other defaults 
552        */
553       NULL);
554   g_free (wstr);
555   g_free (hstr);
556   g_free (configuration);
557
558   return res;
559
560   /* ERRORS */
561 no_headers:
562   {
563     GST_DEBUG_OBJECT (rtptheorapay, "finish headers");
564     return FALSE;
565   }
566 }
567
568 static gboolean
569 gst_rtp_theora_pay_parse_id (GstRTPBasePayload * basepayload, guint8 * data,
570     guint size)
571 {
572   GstRtpTheoraPay *rtptheorapay;
573   gint width, height, pixel_format;
574
575   rtptheorapay = GST_RTP_THEORA_PAY (basepayload);
576
577   if (G_UNLIKELY (size < 42))
578     goto too_short;
579
580   if (G_UNLIKELY (memcmp (data, "\200theora", 7)))
581     goto invalid_start;
582   data += 7;
583
584   if (G_UNLIKELY (data[0] != 3))
585     goto invalid_version;
586   if (G_UNLIKELY (data[1] != 2))
587     goto invalid_version;
588   data += 3;
589
590   width = GST_READ_UINT16_BE (data) << 4;
591   data += 2;
592   height = GST_READ_UINT16_BE (data) << 4;
593   data += 29;
594
595   pixel_format = (GST_READ_UINT8 (data) >> 3) & 0x03;
596
597   /* store values */
598   rtptheorapay->pixel_format = pixel_format;
599   rtptheorapay->width = width;
600   rtptheorapay->height = height;
601
602   return TRUE;
603
604   /* ERRORS */
605 too_short:
606   {
607     GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
608         (NULL),
609         ("Identification packet is too short, need at least 42, got %d", size));
610     return FALSE;
611   }
612 invalid_start:
613   {
614     GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
615         (NULL), ("Invalid header start in identification packet"));
616     return FALSE;
617   }
618 invalid_version:
619   {
620     GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
621         (NULL), ("Invalid version"));
622     return FALSE;
623   }
624 }
625
626 static GstFlowReturn
627 gst_rtp_theora_pay_payload_buffer (GstRtpTheoraPay * rtptheorapay, guint8 TDT,
628     GstBuffer * buffer, guint8 * data, guint size, GstClockTime timestamp,
629     GstClockTime duration, guint not_in_length)
630 {
631   GstFlowReturn ret = GST_FLOW_OK;
632   guint newsize;
633   guint packet_len;
634   GstClockTime newduration;
635   gboolean flush;
636   guint plen;
637   guint8 *ppos, *payload;
638   gboolean fragmented;
639   GstRTPBuffer rtp = { NULL };
640
641   /* size increases with packet length and 2 bytes size eader. */
642   newduration = rtptheorapay->payload_duration;
643   if (duration != GST_CLOCK_TIME_NONE)
644     newduration += duration;
645
646   newsize = rtptheorapay->payload_pos + 2 + size;
647   packet_len = gst_rtp_buffer_calc_packet_len (newsize, 0, 0);
648
649   /* check buffer filled against length and max latency */
650   flush = gst_rtp_base_payload_is_filled (GST_RTP_BASE_PAYLOAD (rtptheorapay),
651       packet_len, newduration);
652   /* we can store up to 15 theora packets in one RTP packet. */
653   flush |= (rtptheorapay->payload_pkts == 15);
654   /* flush if we have a new TDT */
655   if (rtptheorapay->packet)
656     flush |= (rtptheorapay->payload_TDT != TDT);
657   if (flush)
658     ret = gst_rtp_theora_pay_flush_packet (rtptheorapay);
659
660   if (ret != GST_FLOW_OK)
661     goto done;
662
663   /* create new packet if we must */
664   if (!rtptheorapay->packet) {
665     gst_rtp_theora_pay_init_packet (rtptheorapay, TDT, timestamp);
666   }
667
668   gst_rtp_buffer_map (rtptheorapay->packet, GST_MAP_WRITE, &rtp);
669   payload = gst_rtp_buffer_get_payload (&rtp);
670   ppos = payload + rtptheorapay->payload_pos;
671   fragmented = FALSE;
672
673   /* put buffer in packet, it either fits completely or needs to be fragmented
674    * over multiple RTP packets. */
675   do {
676     plen = MIN (rtptheorapay->payload_left - 2, size);
677
678     GST_DEBUG_OBJECT (rtptheorapay, "append %u bytes", plen);
679
680     /* data is copied in the payload with a 2 byte length header */
681     ppos[0] = ((plen - not_in_length) >> 8) & 0xff;
682     ppos[1] = ((plen - not_in_length) & 0xff);
683     if (plen)
684       memcpy (&ppos[2], data, plen);
685
686     if (buffer) {
687       if (!rtptheorapay->packet_buffers
688           || rtptheorapay->packet_buffers->data != (gpointer) buffer)
689         rtptheorapay->packet_buffers =
690             g_list_prepend (rtptheorapay->packet_buffers,
691             gst_buffer_ref (buffer));
692     } else {
693       GList *l;
694
695       for (l = rtptheorapay->headers; l; l = l->next)
696         rtptheorapay->packet_buffers =
697             g_list_prepend (rtptheorapay->packet_buffers,
698             gst_buffer_ref (l->data));
699     }
700
701     /* only first (only) configuration cuts length field */
702     /* NOTE: spec (if any) is not clear on this ... */
703     not_in_length = 0;
704
705     size -= plen;
706     data += plen;
707
708     rtptheorapay->payload_pos += plen + 2;
709     rtptheorapay->payload_left -= plen + 2;
710
711     if (fragmented) {
712       if (size == 0)
713         /* last fragment, set F to 0x3. */
714         rtptheorapay->payload_F = 0x3;
715       else
716         /* fragment continues, set F to 0x2. */
717         rtptheorapay->payload_F = 0x2;
718     } else {
719       if (size > 0) {
720         /* fragmented packet starts, set F to 0x1, mark ourselves as
721          * fragmented. */
722         rtptheorapay->payload_F = 0x1;
723         fragmented = TRUE;
724       }
725     }
726     if (fragmented) {
727       gst_rtp_buffer_unmap (&rtp);
728       /* fragmented packets are always flushed and have ptks of 0 */
729       rtptheorapay->payload_pkts = 0;
730       ret = gst_rtp_theora_pay_flush_packet (rtptheorapay);
731
732       if (size > 0) {
733         /* start new packet and get pointers. TDT stays the same. */
734         gst_rtp_theora_pay_init_packet (rtptheorapay,
735             rtptheorapay->payload_TDT, timestamp);
736         gst_rtp_buffer_map (rtptheorapay->packet, GST_MAP_WRITE, &rtp);
737         payload = gst_rtp_buffer_get_payload (&rtp);
738         ppos = payload + rtptheorapay->payload_pos;
739       }
740     } else {
741       /* unfragmented packet, update stats for next packet, size == 0 and we
742        * exit the while loop */
743       rtptheorapay->payload_pkts++;
744       if (duration != GST_CLOCK_TIME_NONE)
745         rtptheorapay->payload_duration += duration;
746     }
747   } while (size && ret == GST_FLOW_OK);
748
749   if (rtp.buffer)
750     gst_rtp_buffer_unmap (&rtp);
751 done:
752
753   return ret;
754 }
755
756 static GstFlowReturn
757 gst_rtp_theora_pay_handle_buffer (GstRTPBasePayload * basepayload,
758     GstBuffer * buffer)
759 {
760   GstRtpTheoraPay *rtptheorapay;
761   GstFlowReturn ret;
762   GstMapInfo map;
763   gsize size;
764   guint8 *data;
765   GstClockTime duration, timestamp;
766   guint8 TDT;
767   gboolean keyframe = FALSE;
768
769   rtptheorapay = GST_RTP_THEORA_PAY (basepayload);
770
771   gst_buffer_map (buffer, &map, GST_MAP_READ);
772   data = map.data;
773   size = map.size;
774   duration = GST_BUFFER_DURATION (buffer);
775   timestamp = GST_BUFFER_PTS (buffer);
776
777   GST_DEBUG_OBJECT (rtptheorapay, "size %" G_GSIZE_FORMAT
778       ", duration %" GST_TIME_FORMAT, size, GST_TIME_ARGS (duration));
779
780   /* find packet type */
781   if (size == 0) {
782     TDT = 0;
783     keyframe = FALSE;
784   } else if (data[0] & 0x80) {
785     /* header */
786     if (data[0] == 0x80) {
787       /* identification, we need to parse this in order to get the clock rate.
788        */
789       if (G_UNLIKELY (!gst_rtp_theora_pay_parse_id (basepayload, data, size)))
790         goto parse_id_failed;
791       TDT = 1;
792     } else if (data[0] == 0x81) {
793       /* comment */
794       TDT = 2;
795     } else if (data[0] == 0x82) {
796       /* setup */
797       TDT = 1;
798     } else
799       goto unknown_header;
800   } else {
801     /* data */
802     TDT = 0;
803     keyframe = ((data[0] & 0x40) == 0);
804   }
805
806   /* we need to collect the headers and construct a config string from them */
807   if (TDT != 0) {
808     GST_DEBUG_OBJECT (rtptheorapay, "collecting header, buffer %p", buffer);
809     /* append header to the list of headers */
810     gst_buffer_unmap (buffer, &map);
811     rtptheorapay->headers = g_list_append (rtptheorapay->headers, buffer);
812     ret = GST_FLOW_OK;
813     goto done;
814   } else if (rtptheorapay->headers && rtptheorapay->need_headers) {
815     if (!gst_rtp_theora_pay_finish_headers (basepayload))
816       goto header_error;
817   }
818
819   /* there is a config request, see if we need to insert it */
820   if (keyframe && (rtptheorapay->config_interval > 0) &&
821       rtptheorapay->config_data) {
822     gboolean send_config = FALSE;
823
824     if (rtptheorapay->last_config != -1) {
825       guint64 diff;
826
827       GST_LOG_OBJECT (rtptheorapay,
828           "now %" GST_TIME_FORMAT ", last VOP-I %" GST_TIME_FORMAT,
829           GST_TIME_ARGS (timestamp), GST_TIME_ARGS (rtptheorapay->last_config));
830
831       /* calculate diff between last config in milliseconds */
832       if (timestamp > rtptheorapay->last_config) {
833         diff = timestamp - rtptheorapay->last_config;
834       } else {
835         diff = 0;
836       }
837
838       GST_DEBUG_OBJECT (rtptheorapay,
839           "interval since last config %" GST_TIME_FORMAT, GST_TIME_ARGS (diff));
840
841       /* bigger than interval, queue config */
842       /* FIXME should convert timestamps to running time */
843       if (GST_TIME_AS_SECONDS (diff) >= rtptheorapay->config_interval) {
844         GST_DEBUG_OBJECT (rtptheorapay, "time to send config");
845         send_config = TRUE;
846       }
847     } else {
848       /* no known previous config time, send now */
849       GST_DEBUG_OBJECT (rtptheorapay, "no previous config time, send now");
850       send_config = TRUE;
851     }
852
853     if (send_config) {
854       /* we need to send config now first */
855       /* different TDT type forces flush */
856       gst_rtp_theora_pay_payload_buffer (rtptheorapay, 1,
857           NULL, rtptheorapay->config_data, rtptheorapay->config_size,
858           timestamp, GST_CLOCK_TIME_NONE, rtptheorapay->config_extra_len);
859
860       if (timestamp != -1) {
861         rtptheorapay->last_config = timestamp;
862       }
863     }
864   }
865
866   ret =
867       gst_rtp_theora_pay_payload_buffer (rtptheorapay, TDT, buffer, data, size,
868       timestamp, duration, 0);
869
870   gst_buffer_unmap (buffer, &map);
871   gst_buffer_unref (buffer);
872
873 done:
874   return ret;
875
876   /* ERRORS */
877 parse_id_failed:
878   {
879     gst_buffer_unmap (buffer, &map);
880     gst_buffer_unref (buffer);
881     return GST_FLOW_ERROR;
882   }
883 unknown_header:
884   {
885     GST_ELEMENT_WARNING (rtptheorapay, STREAM, DECODE,
886         (NULL), ("Ignoring unknown header received"));
887     gst_buffer_unmap (buffer, &map);
888     gst_buffer_unref (buffer);
889     return GST_FLOW_OK;
890   }
891 header_error:
892   {
893     GST_ELEMENT_WARNING (rtptheorapay, STREAM, DECODE,
894         (NULL), ("Error initializing header config"));
895     gst_buffer_unmap (buffer, &map);
896     gst_buffer_unref (buffer);
897     return GST_FLOW_OK;
898   }
899 }
900
901 static gboolean
902 gst_rtp_theora_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event)
903 {
904   GstRtpTheoraPay *rtptheorapay = GST_RTP_THEORA_PAY (payload);
905
906   switch (GST_EVENT_TYPE (event)) {
907     case GST_EVENT_FLUSH_STOP:
908       gst_rtp_theora_pay_clear_packet (rtptheorapay);
909       break;
910     default:
911       break;
912   }
913   /* false to let parent handle event as well */
914   return GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (payload, event);
915 }
916
917 static GstStateChangeReturn
918 gst_rtp_theora_pay_change_state (GstElement * element,
919     GstStateChange transition)
920 {
921   GstRtpTheoraPay *rtptheorapay;
922
923   GstStateChangeReturn ret;
924
925   rtptheorapay = GST_RTP_THEORA_PAY (element);
926
927   switch (transition) {
928     default:
929       break;
930   }
931
932   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
933
934   switch (transition) {
935     case GST_STATE_CHANGE_PAUSED_TO_READY:
936       gst_rtp_theora_pay_cleanup (rtptheorapay);
937       break;
938     default:
939       break;
940   }
941   return ret;
942 }
943
944 static void
945 gst_rtp_theora_pay_set_property (GObject * object, guint prop_id,
946     const GValue * value, GParamSpec * pspec)
947 {
948   GstRtpTheoraPay *rtptheorapay;
949
950   rtptheorapay = GST_RTP_THEORA_PAY (object);
951
952   switch (prop_id) {
953     case PROP_CONFIG_INTERVAL:
954       rtptheorapay->config_interval = g_value_get_uint (value);
955       break;
956     default:
957       break;
958   }
959 }
960
961 static void
962 gst_rtp_theora_pay_get_property (GObject * object, guint prop_id,
963     GValue * value, GParamSpec * pspec)
964 {
965   GstRtpTheoraPay *rtptheorapay;
966
967   rtptheorapay = GST_RTP_THEORA_PAY (object);
968
969   switch (prop_id) {
970     case PROP_CONFIG_INTERVAL:
971       g_value_set_uint (value, rtptheorapay->config_interval);
972       break;
973     default:
974       break;
975   }
976 }
977
978 gboolean
979 gst_rtp_theora_pay_plugin_init (GstPlugin * plugin)
980 {
981   return gst_element_register (plugin, "rtptheorapay",
982       GST_RANK_SECONDARY, GST_TYPE_RTP_THEORA_PAY);
983 }