2 * Copyright (C) <2006> Wim Taymans <wim.taymans@gmail.com>
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.
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.
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.
24 #include <gst/tag/tag.h>
25 #include <gst/rtp/gstrtpbuffer.h>
28 #include "gstrtptheoradepay.h"
30 GST_DEBUG_CATEGORY_STATIC (rtptheoradepay_debug);
31 #define GST_CAT_DEFAULT (rtptheoradepay_debug)
33 static GstStaticPadTemplate gst_rtp_theora_depay_sink_template =
34 GST_STATIC_PAD_TEMPLATE ("sink",
37 GST_STATIC_CAPS ("application/x-rtp, "
38 "media = (string) \"video\", "
39 "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
40 "clock-rate = (int) 90000, " "encoding-name = (string) \"THEORA\","
41 /* only support inline delivery */
42 "delivery-method = (string) \"inline\""
43 /* All required parameters
45 * "sampling = (string) { "YCbCr-4:2:0", "YCbCr-4:2:2", "YCbCr-4:4:4" } "
46 * "width = (string) [1, 1048561] (multiples of 16) "
47 * "height = (string) [1, 1048561] (multiples of 16) "
48 * "delivery-method = (string) { inline, in_band, out_band/<specific_name> } "
49 * "configuration = (string) ANY"
51 /* All optional parameters
53 * "configuration-uri ="
58 static GstStaticPadTemplate gst_rtp_theora_depay_src_template =
59 GST_STATIC_PAD_TEMPLATE ("src",
62 GST_STATIC_CAPS ("video/x-theora")
65 GST_BOILERPLATE (GstRtpTheoraDepay, gst_rtp_theora_depay, GstBaseRTPDepayload,
66 GST_TYPE_BASE_RTP_DEPAYLOAD);
68 static gboolean gst_rtp_theora_depay_setcaps (GstBaseRTPDepayload * depayload,
70 static GstBuffer *gst_rtp_theora_depay_process (GstBaseRTPDepayload * depayload,
73 static void gst_rtp_theora_depay_finalize (GObject * object);
77 gst_rtp_theora_depay_base_init (gpointer klass)
79 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
81 gst_element_class_add_pad_template (element_class,
82 gst_static_pad_template_get (&gst_rtp_theora_depay_sink_template));
83 gst_element_class_add_pad_template (element_class,
84 gst_static_pad_template_get (&gst_rtp_theora_depay_src_template));
86 gst_element_class_set_details_simple (element_class, "RTP Theora depayloader",
87 "Codec/Depayloader/Network",
88 "Extracts Theora video from RTP packets (draft-01 of RFC XXXX)",
89 "Wim Taymans <wim.taymans@gmail.com>");
93 gst_rtp_theora_depay_class_init (GstRtpTheoraDepayClass * klass)
95 GObjectClass *gobject_class;
96 GstBaseRTPDepayloadClass *gstbasertpdepayload_class;
98 gobject_class = (GObjectClass *) klass;
99 gstbasertpdepayload_class = (GstBaseRTPDepayloadClass *) klass;
101 gobject_class->finalize = gst_rtp_theora_depay_finalize;
103 gstbasertpdepayload_class->process = gst_rtp_theora_depay_process;
104 gstbasertpdepayload_class->set_caps = gst_rtp_theora_depay_setcaps;
106 GST_DEBUG_CATEGORY_INIT (rtptheoradepay_debug, "rtptheoradepay", 0,
107 "Theora RTP Depayloader");
111 gst_rtp_theora_depay_init (GstRtpTheoraDepay * rtptheoradepay,
112 GstRtpTheoraDepayClass * klass)
114 rtptheoradepay->adapter = gst_adapter_new ();
118 gst_rtp_theora_depay_finalize (GObject * object)
120 GstRtpTheoraDepay *rtptheoradepay = GST_RTP_THEORA_DEPAY (object);
122 g_object_unref (rtptheoradepay->adapter);
124 G_OBJECT_CLASS (parent_class)->finalize (object);
128 gst_rtp_theora_depay_parse_configuration (GstRtpTheoraDepay * rtptheoradepay,
129 const gchar * configuration)
137 /* deserialize base64 to buffer */
138 size = strlen (configuration);
139 GST_DEBUG_OBJECT (rtptheoradepay, "base64 config size %" G_GSIZE_FORMAT,
142 data = g_base64_decode (configuration, &size);
144 GST_DEBUG_OBJECT (rtptheoradepay, "config size %" G_GSIZE_FORMAT, size);
146 /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
147 * | Number of packed headers |
148 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
149 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
151 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
152 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
154 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
155 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
157 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
162 num_headers = GST_READ_UINT32_BE (data);
166 GST_DEBUG_OBJECT (rtptheoradepay, "have %u headers", num_headers);
169 * 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
170 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
171 * | Ident | length ..
172 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
173 * .. | n. of headers | length1 | length2 ..
174 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
175 * .. | Identification Header ..
176 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
177 * .................................................................
178 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
179 * .. | Comment Header ..
180 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
181 * .................................................................
182 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
183 * .. Comment Header |
184 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
186 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
187 * .................................................................
188 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
190 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
192 for (i = 0; i < num_headers; i++) {
196 GstRtpTheoraConfig *conf;
202 ident = (data[0] << 16) | (data[1] << 8) | data[2];
203 length = (data[3] << 8) | data[4];
208 GST_DEBUG_OBJECT (rtptheoradepay,
209 "header %d, ident 0x%08x, length %u, left %" G_GSIZE_FORMAT, i, ident,
215 /* read header sizes we read 2 sizes, the third size (for which we allocate
216 * space) must be derived from the total packed header length. */
217 h_sizes = g_newa (guint, n_headers + 1);
218 for (j = 0; j < n_headers; j++) {
227 h_size = (h_size << 7) | (b & 0x7f);
229 GST_DEBUG_OBJECT (rtptheoradepay, "headers %d: size: %u", j, h_size);
233 /* last header length is the remaining space */
234 GST_DEBUG_OBJECT (rtptheoradepay, "last header size: %u", length);
237 GST_DEBUG_OBJECT (rtptheoradepay, "preparing headers");
238 conf = g_new0 (GstRtpTheoraConfig, 1);
241 for (j = 0; j <= n_headers; j++) {
248 GST_DEBUG_OBJECT (rtptheoradepay, "reading header %d, size %u", j,
251 buf = gst_buffer_new_and_alloc (h_size);
252 memcpy (GST_BUFFER_DATA (buf), data, h_size);
253 conf->headers = g_list_append (conf->headers, buf);
257 rtptheoradepay->configs = g_list_append (rtptheoradepay->configs, conf);
264 GST_DEBUG_OBJECT (rtptheoradepay, "configuration too small");
270 gst_rtp_theora_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps)
272 GstStructure *structure;
273 GstRtpTheoraDepay *rtptheoradepay;
275 const gchar *delivery_method;
276 const gchar *configuration;
279 rtptheoradepay = GST_RTP_THEORA_DEPAY (depayload);
281 structure = gst_caps_get_structure (caps, 0);
283 /* see how the configuration parameters will be transmitted */
284 delivery_method = gst_structure_get_string (structure, "delivery-method");
285 if (delivery_method == NULL)
286 goto no_delivery_method;
288 if (!g_ascii_strcasecmp (delivery_method, "inline")) {
289 /* configure string is in the caps */
290 } else if (!g_ascii_strcasecmp (delivery_method, "in_band")) {
291 /* headers will (also) be transmitted in the RTP packets */
292 goto unsupported_delivery_method;
293 } else if (g_str_has_prefix (delivery_method, "out_band/")) {
294 /* some other method of header delivery. */
295 goto unsupported_delivery_method;
297 goto unsupported_delivery_method;
299 /* read and parse configuration string */
300 configuration = gst_structure_get_string (structure, "configuration");
301 if (configuration == NULL)
302 goto no_configuration;
304 if (!gst_rtp_theora_depay_parse_configuration (rtptheoradepay, configuration))
305 goto invalid_configuration;
307 /* set caps on pad and on header */
308 srccaps = gst_caps_new_simple ("video/x-theora", NULL);
309 res = gst_pad_set_caps (depayload->srcpad, srccaps);
310 gst_caps_unref (srccaps);
312 /* Clock rate is always 90000 according to draft-barbato-avt-rtp-theora-01 */
313 depayload->clock_rate = 90000;
318 unsupported_delivery_method:
320 GST_ERROR_OBJECT (rtptheoradepay,
321 "unsupported delivery-method \"%s\" specified", delivery_method);
326 GST_ERROR_OBJECT (rtptheoradepay, "no delivery-method specified");
331 GST_ERROR_OBJECT (rtptheoradepay, "no configuration specified");
334 invalid_configuration:
336 GST_ERROR_OBJECT (rtptheoradepay, "invalid configuration specified");
342 gst_rtp_theora_depay_switch_codebook (GstRtpTheoraDepay * rtptheoradepay,
346 gboolean res = FALSE;
348 for (walk = rtptheoradepay->configs; walk; walk = g_list_next (walk)) {
349 GstRtpTheoraConfig *conf = (GstRtpTheoraConfig *) walk->data;
351 if (conf->ident == ident) {
354 /* FIXME, remove pads, create new pad.. */
356 /* push out all the headers */
357 for (headers = conf->headers; headers; headers = g_list_next (headers)) {
358 GstBuffer *header = GST_BUFFER_CAST (headers->data);
360 gst_buffer_ref (header);
361 gst_base_rtp_depayload_push (GST_BASE_RTP_DEPAYLOAD (rtptheoradepay),
364 /* remember the current config */
365 rtptheoradepay->config = conf;
370 /* we don't know about the headers, figure out an alternative method for
371 * getting the codebooks. FIXME, fail for now. */
377 gst_rtp_theora_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
379 GstRtpTheoraDepay *rtptheoradepay;
383 guint8 *payload, *to_free = NULL;
385 guint32 header, ident;
386 guint8 F, TDT, packets;
388 rtptheoradepay = GST_RTP_THEORA_DEPAY (depayload);
390 payload_len = gst_rtp_buffer_get_payload_len (buf);
392 GST_DEBUG_OBJECT (depayload, "got RTP packet of size %d", payload_len);
394 /* we need at least 4 bytes for the packet header */
395 if (G_UNLIKELY (payload_len < 4))
398 payload = gst_rtp_buffer_get_payload (buf);
400 header = GST_READ_UINT32_BE (payload);
403 * 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
404 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
405 * | Ident | F |TDT|# pkts.|
406 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
408 * F: Fragment type (0=none, 1=start, 2=cont, 3=end)
409 * TDT: Theora data type (0=theora, 1=config, 2=comment, 3=reserved)
410 * pkts: number of packets.
412 TDT = (header & 0x30) >> 4;
413 if (G_UNLIKELY (TDT == 3))
414 goto ignore_reserved;
416 ident = (header >> 8) & 0xffffff;
417 F = (header & 0xc0) >> 6;
418 packets = (header & 0xf);
420 GST_DEBUG_OBJECT (depayload, "ident: 0x%08x, F: %d, TDT: %d, packets: %d",
421 ident, F, TDT, packets);
424 gboolean do_switch = FALSE;
426 /* we have a raw payload, find the codebook for the ident */
427 if (!rtptheoradepay->config) {
428 /* we don't have an active codebook, find the codebook and
431 } else if (rtptheoradepay->config->ident != ident) {
432 /* codebook changed */
436 if (!gst_rtp_theora_depay_switch_codebook (rtptheoradepay, ident))
445 /* fragmented packets, assemble */
451 /* if we start a packet, clear adapter and start assembling. */
452 gst_adapter_clear (rtptheoradepay->adapter);
453 GST_DEBUG_OBJECT (depayload, "start assemble");
454 rtptheoradepay->assembling = TRUE;
457 if (!rtptheoradepay->assembling)
460 /* first assembled packet, reuse 2 bytes to store the length */
461 headerskip = (F == 1 ? 4 : 6);
462 /* skip header and length. */
463 vdata = gst_rtp_buffer_get_payload_subbuffer (buf, headerskip, -1);
465 GST_DEBUG_OBJECT (depayload, "assemble theora packet");
466 gst_adapter_push (rtptheoradepay->adapter, vdata);
468 /* packet is not complete, we are done */
472 /* construct assembled buffer */
473 payload_len = gst_adapter_available (rtptheoradepay->adapter);
474 payload = gst_adapter_take (rtptheoradepay->adapter, payload_len);
476 payload[0] = ((payload_len - 2) >> 8) & 0xff;
477 payload[1] = (payload_len - 2) & 0xff;
481 GST_DEBUG_OBJECT (depayload, "assemble done");
483 /* we not assembling anymore now */
484 rtptheoradepay->assembling = FALSE;
485 gst_adapter_clear (rtptheoradepay->adapter);
487 /* payload now points to a length with that many theora data bytes.
488 * Iterate over the packets and send them out.
491 * 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
492 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
493 * | length | theora data ..
494 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
496 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
497 * | length | next theora packet data ..
498 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
500 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*
502 timestamp = gst_rtp_buffer_get_timestamp (buf);
504 while (payload_len > 2) {
507 length = GST_READ_UINT16_BE (payload);
511 GST_DEBUG_OBJECT (depayload, "read length %u, avail: %d", length,
514 /* skip packet if something odd happens */
515 if (G_UNLIKELY (length > payload_len))
518 /* create buffer for packet */
519 if (G_UNLIKELY (to_free)) {
520 outbuf = gst_buffer_new ();
521 GST_BUFFER_DATA (outbuf) = payload;
522 GST_BUFFER_MALLOCDATA (outbuf) = to_free;
523 GST_BUFFER_SIZE (outbuf) = length;
526 outbuf = gst_buffer_new_and_alloc (length);
527 memcpy (GST_BUFFER_DATA (outbuf), payload, length);
531 payload_len -= length;
534 /* push with timestamp of the last packet, which is the same timestamp that
535 * should apply to the first assembled packet. */
536 ret = gst_base_rtp_depayload_push_ts (depayload, timestamp, outbuf);
538 ret = gst_base_rtp_depayload_push (depayload, outbuf);
540 if (ret != GST_FLOW_OK)
543 /* make sure we don't set a timestamp on next buffers */
558 GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE,
559 (NULL), ("Could not switch codebooks"));
564 GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE,
565 (NULL), ("Packet was too short (%d < 4)", payload_len));
570 GST_WARNING_OBJECT (rtptheoradepay, "reserved TDT ignored");
575 GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE,
576 (NULL), ("Packet contains invalid data"));
582 gst_rtp_theora_depay_plugin_init (GstPlugin * plugin)
584 return gst_element_register (plugin, "rtptheoradepay",
585 GST_RANK_MARGINAL, GST_TYPE_RTP_THEORA_DEPAY);