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 /* All required parameters
43 * "sampling = (string) { "YCbCr-4:2:0", "YCbCr-4:2:2", "YCbCr-4:4:4" } "
44 * "width = (string) [1, 1048561] (multiples of 16) "
45 * "height = (string) [1, 1048561] (multiples of 16) "
46 * "delivery-method = (string) { inline, in_band, out_band/<specific_name> } "
47 * "configuration = (string) ANY"
49 /* All optional parameters
51 * "configuration-uri ="
56 static GstStaticPadTemplate gst_rtp_theora_depay_src_template =
57 GST_STATIC_PAD_TEMPLATE ("src",
60 GST_STATIC_CAPS ("video/x-theora")
63 #define gst_rtp_theora_depay_parent_class parent_class
64 G_DEFINE_TYPE (GstRtpTheoraDepay, gst_rtp_theora_depay,
65 GST_TYPE_RTP_BASE_DEPAYLOAD);
67 static gboolean gst_rtp_theora_depay_setcaps (GstRTPBaseDepayload * depayload,
69 static GstBuffer *gst_rtp_theora_depay_process (GstRTPBaseDepayload * depayload,
71 static gboolean gst_rtp_theora_depay_packet_lost (GstRTPBaseDepayload *
72 depayload, GstEvent * event);
74 static void gst_rtp_theora_depay_finalize (GObject * object);
77 gst_rtp_theora_depay_class_init (GstRtpTheoraDepayClass * klass)
79 GObjectClass *gobject_class;
80 GstElementClass *gstelement_class;
81 GstRTPBaseDepayloadClass *gstrtpbasedepayload_class;
83 gobject_class = (GObjectClass *) klass;
84 gstelement_class = (GstElementClass *) klass;
85 gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass;
87 gobject_class->finalize = gst_rtp_theora_depay_finalize;
89 gstrtpbasedepayload_class->process = gst_rtp_theora_depay_process;
90 gstrtpbasedepayload_class->set_caps = gst_rtp_theora_depay_setcaps;
91 gstrtpbasedepayload_class->packet_lost = gst_rtp_theora_depay_packet_lost;
93 gst_element_class_add_pad_template (gstelement_class,
94 gst_static_pad_template_get (&gst_rtp_theora_depay_sink_template));
95 gst_element_class_add_pad_template (gstelement_class,
96 gst_static_pad_template_get (&gst_rtp_theora_depay_src_template));
98 gst_element_class_set_details_simple (gstelement_class,
99 "RTP Theora depayloader", "Codec/Depayloader/Network/RTP",
100 "Extracts Theora video from RTP packets (draft-01 of RFC XXXX)",
101 "Wim Taymans <wim.taymans@gmail.com>");
103 GST_DEBUG_CATEGORY_INIT (rtptheoradepay_debug, "rtptheoradepay", 0,
104 "Theora RTP Depayloader");
108 gst_rtp_theora_depay_init (GstRtpTheoraDepay * rtptheoradepay)
110 rtptheoradepay->adapter = gst_adapter_new ();
114 gst_rtp_theora_depay_finalize (GObject * object)
116 GstRtpTheoraDepay *rtptheoradepay = GST_RTP_THEORA_DEPAY (object);
118 g_object_unref (rtptheoradepay->adapter);
120 G_OBJECT_CLASS (parent_class)->finalize (object);
124 gst_rtp_theora_depay_parse_configuration (GstRtpTheoraDepay * rtptheoradepay,
129 guint8 *data, *bdata;
133 data = bdata = gst_buffer_map (confbuf, &size, NULL, GST_MAP_READ);
135 GST_DEBUG_OBJECT (rtptheoradepay, "config size %" G_GSIZE_FORMAT, size);
137 /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
138 * | Number of packed headers |
139 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
140 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
142 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
143 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
145 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
146 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
148 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
153 num_headers = GST_READ_UINT32_BE (data);
157 GST_DEBUG_OBJECT (rtptheoradepay, "have %u headers", num_headers);
160 * 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
161 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
162 * | Ident | length ..
163 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
164 * .. | n. of headers | length1 | length2 ..
165 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
166 * .. | Identification Header ..
167 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
168 * .................................................................
169 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
170 * .. | Comment Header ..
171 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
172 * .................................................................
173 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
174 * .. Comment Header |
175 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
177 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
178 * .................................................................
179 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
181 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
183 for (i = 0; i < num_headers; i++) {
187 GstRtpTheoraConfig *conf;
194 ident = (data[0] << 16) | (data[1] << 8) | data[2];
195 length = (data[3] << 8) | data[4];
200 GST_DEBUG_OBJECT (rtptheoradepay,
201 "header %d, ident 0x%08x, length %u, left %u", i, ident, length, size);
203 /* FIXME check if we already got this ident */
205 /* length might also include count of following size fields */
206 if (size < length && size + 1 != length)
209 /* read header sizes we read 2 sizes, the third size (for which we allocate
210 * space) must be derived from the total packed header length. */
211 h_sizes = g_newa (guint, n_headers + 1);
212 for (j = 0; j < n_headers; j++) {
222 h_size = (h_size << 7) | (b & 0x7f);
224 GST_DEBUG_OBJECT (rtptheoradepay, "headers %d: size: %u", j, h_size);
228 /* last header length is the remaining space */
229 GST_DEBUG_OBJECT (rtptheoradepay, "last header size: %u", length);
232 GST_DEBUG_OBJECT (rtptheoradepay, "preparing headers");
233 conf = g_new0 (GstRtpTheoraConfig, 1);
236 for (j = 0; j <= n_headers; j++) {
242 if (j != n_headers || size + extra != h_size) {
245 /* otherwise means that overall length field contained total length,
246 * including extra fields */
251 GST_DEBUG_OBJECT (rtptheoradepay, "reading header %d, size %u", j,
254 buf = gst_buffer_new_and_alloc (h_size);
255 odata = gst_buffer_map (buf, NULL, NULL, GST_MAP_WRITE);
256 memcpy (odata, data, h_size);
257 gst_buffer_unmap (buf, odata, -1);
258 conf->headers = g_list_append (conf->headers, buf);
262 rtptheoradepay->configs = g_list_append (rtptheoradepay->configs, conf);
265 gst_buffer_unmap (confbuf, bdata, -1);
271 GST_DEBUG_OBJECT (rtptheoradepay, "configuration too small");
272 gst_buffer_unmap (confbuf, bdata, -1);
278 gst_rtp_theora_depay_parse_inband_configuration (GstRtpTheoraDepay *
279 rtptheoradepay, guint ident, guint8 * configuration, guint size,
285 if (G_UNLIKELY (size < 4))
288 /* transform inline to out-of-band and parse that one */
289 confbuf = gst_buffer_new_and_alloc (size + 9);
290 conf = gst_buffer_map (confbuf, NULL, NULL, GST_MAP_WRITE);
292 GST_WRITE_UINT32_BE (conf, 1);
294 GST_WRITE_UINT24_BE (conf + 4, ident);
295 /* write sort-of-length */
296 GST_WRITE_UINT16_BE (conf + 7, length);
298 memcpy (conf + 9, configuration, size);
299 gst_buffer_unmap (confbuf, conf, -1);
301 return gst_rtp_theora_depay_parse_configuration (rtptheoradepay, confbuf);
305 gst_rtp_theora_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
307 GstStructure *structure;
308 GstRtpTheoraDepay *rtptheoradepay;
310 const gchar *configuration;
313 rtptheoradepay = GST_RTP_THEORA_DEPAY (depayload);
315 rtptheoradepay->needs_keyframe = FALSE;
317 structure = gst_caps_get_structure (caps, 0);
319 /* read and parse configuration string */
320 configuration = gst_structure_get_string (structure, "configuration");
326 /* configure string should be in the caps */
327 if (configuration == NULL)
328 goto no_configuration;
330 /* deserialize base64 to buffer */
331 data = g_base64_decode (configuration, &size);
333 confbuf = gst_buffer_new ();
334 gst_buffer_take_memory (confbuf, -1,
335 gst_memory_new_wrapped (0, data, g_free, size, 0, size));
337 if (!gst_rtp_theora_depay_parse_configuration (rtptheoradepay, confbuf))
338 goto invalid_configuration;
341 /* set caps on pad and on header */
342 srccaps = gst_caps_new_empty_simple ("video/x-theora");
343 res = gst_pad_set_caps (depayload->srcpad, srccaps);
344 gst_caps_unref (srccaps);
346 /* Clock rate is always 90000 according to draft-barbato-avt-rtp-theora-01 */
347 depayload->clock_rate = 90000;
354 GST_ERROR_OBJECT (rtptheoradepay, "no configuration specified");
357 invalid_configuration:
359 GST_ERROR_OBJECT (rtptheoradepay, "invalid configuration specified");
365 gst_rtp_theora_depay_switch_codebook (GstRtpTheoraDepay * rtptheoradepay,
369 gboolean res = FALSE;
371 for (walk = rtptheoradepay->configs; walk; walk = g_list_next (walk)) {
372 GstRtpTheoraConfig *conf = (GstRtpTheoraConfig *) walk->data;
374 if (conf->ident == ident) {
377 /* FIXME, remove pads, create new pad.. */
379 /* push out all the headers */
380 for (headers = conf->headers; headers; headers = g_list_next (headers)) {
381 GstBuffer *header = GST_BUFFER_CAST (headers->data);
383 gst_buffer_ref (header);
384 gst_rtp_base_depayload_push (GST_RTP_BASE_DEPAYLOAD (rtptheoradepay),
387 /* remember the current config */
388 rtptheoradepay->config = conf;
393 /* we don't know about the headers, figure out an alternative method for
394 * getting the codebooks. FIXME, fail for now. */
400 gst_rtp_theora_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf)
402 GstRtpTheoraDepay *rtptheoradepay;
406 guint8 *payload, *to_free = NULL;
407 guint32 header, ident;
408 guint8 F, TDT, packets;
411 rtptheoradepay = GST_RTP_THEORA_DEPAY (depayload);
413 gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp);
415 payload_len = gst_rtp_buffer_get_payload_len (&rtp);
417 GST_DEBUG_OBJECT (depayload, "got RTP packet of size %d", payload_len);
419 /* we need at least 4 bytes for the packet header */
420 if (G_UNLIKELY (payload_len < 4))
423 payload = gst_rtp_buffer_get_payload (&rtp);
425 header = GST_READ_UINT32_BE (payload);
428 * 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
429 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
430 * | Ident | F |TDT|# pkts.|
431 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
433 * F: Fragment type (0=none, 1=start, 2=cont, 3=end)
434 * TDT: Theora data type (0=theora, 1=config, 2=comment, 3=reserved)
435 * pkts: number of packets.
437 TDT = (header & 0x30) >> 4;
438 if (G_UNLIKELY (TDT == 3))
439 goto ignore_reserved;
441 ident = (header >> 8) & 0xffffff;
442 F = (header & 0xc0) >> 6;
443 packets = (header & 0xf);
445 GST_DEBUG_OBJECT (depayload, "ident: 0x%08x, F: %d, TDT: %d, packets: %d",
446 ident, F, TDT, packets);
449 gboolean do_switch = FALSE;
451 /* we have a raw payload, find the codebook for the ident */
452 if (!rtptheoradepay->config) {
453 /* we don't have an active codebook, find the codebook and
456 } else if (rtptheoradepay->config->ident != ident) {
457 /* codebook changed */
461 if (!gst_rtp_theora_depay_switch_codebook (rtptheoradepay, ident))
470 /* fragmented packets, assemble */
476 /* if we start a packet, clear adapter and start assembling. */
477 gst_adapter_clear (rtptheoradepay->adapter);
478 GST_DEBUG_OBJECT (depayload, "start assemble");
479 rtptheoradepay->assembling = TRUE;
482 if (!rtptheoradepay->assembling)
485 /* first assembled packet, reuse 2 bytes to store the length */
486 headerskip = (F == 1 ? 4 : 6);
487 /* skip header and length. */
488 vdata = gst_rtp_buffer_get_payload_subbuffer (&rtp, headerskip, -1);
490 GST_DEBUG_OBJECT (depayload, "assemble theora packet");
491 gst_adapter_push (rtptheoradepay->adapter, vdata);
493 /* packet is not complete, we are done */
497 /* construct assembled buffer */
498 payload_len = gst_adapter_available (rtptheoradepay->adapter);
499 payload = gst_adapter_take (rtptheoradepay->adapter, payload_len);
501 payload[0] = ((payload_len - 2) >> 8) & 0xff;
502 payload[1] = (payload_len - 2) & 0xff;
506 GST_DEBUG_OBJECT (depayload, "assemble done, payload_len %d", payload_len);
508 /* we not assembling anymore now */
509 rtptheoradepay->assembling = FALSE;
510 gst_adapter_clear (rtptheoradepay->adapter);
512 /* payload now points to a length with that many theora data bytes.
513 * Iterate over the packets and send them out.
516 * 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
517 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
518 * | length | theora data ..
519 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
521 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
522 * | length | next theora packet data ..
523 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
525 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*
527 while (payload_len >= 2) {
530 length = GST_READ_UINT16_BE (payload);
534 GST_DEBUG_OBJECT (depayload, "read length %u, avail: %d", length,
537 /* skip packet if something odd happens */
538 if (G_UNLIKELY (length > payload_len))
541 /* handle in-band configuration */
542 if (G_UNLIKELY (TDT == 1)) {
543 GST_DEBUG_OBJECT (rtptheoradepay, "in-band configuration");
544 if (!gst_rtp_theora_depay_parse_inband_configuration (rtptheoradepay,
545 ident, payload, payload_len, length))
546 goto invalid_configuration;
550 /* create buffer for packet */
551 if (G_UNLIKELY (to_free)) {
552 outbuf = gst_buffer_new ();
553 gst_buffer_take_memory (buf, -1,
554 gst_memory_new_wrapped (0, to_free, g_free,
555 (payload - to_free) + length, payload - to_free, length));
560 outbuf = gst_buffer_new_and_alloc (length);
561 odata = gst_buffer_map (outbuf, NULL, NULL, GST_MAP_WRITE);
562 memcpy (odata, payload, length);
563 gst_buffer_unmap (outbuf, odata, -1);
566 if (payload_len > 0 && (payload[0] & 0xC0) == 0x0)
567 rtptheoradepay->needs_keyframe = FALSE;
570 payload_len -= length;
572 ret = gst_rtp_base_depayload_push (depayload, outbuf);
573 if (ret != GST_FLOW_OK)
579 if (rtptheoradepay->needs_keyframe)
580 goto request_keyframe;
582 gst_rtp_buffer_unmap (&rtp);
587 gst_rtp_buffer_unmap (&rtp);
593 GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE,
594 (NULL), ("Could not switch codebooks"));
599 GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE,
600 (NULL), ("Packet was too short (%d < 4)", payload_len));
601 goto request_keyframe;
605 GST_WARNING_OBJECT (rtptheoradepay, "reserved TDT ignored");
606 gst_rtp_buffer_unmap (&rtp);
611 GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE,
612 (NULL), ("Packet contains invalid data"));
613 goto request_keyframe;
615 invalid_configuration:
617 /* fatal, as we otherwise risk carrying on without output */
618 GST_ELEMENT_ERROR (rtptheoradepay, STREAM, DECODE,
619 (NULL), ("Packet contains invalid configuration"));
624 gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD_SINKPAD (depayload),
625 gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
626 gst_structure_new ("GstForceKeyUnit",
627 "all-headers", G_TYPE_BOOLEAN, TRUE, NULL)));
628 gst_rtp_buffer_unmap (&rtp);
633 rtptheoradepay->needs_keyframe = TRUE;
634 gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD_SINKPAD (depayload),
635 gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
636 gst_structure_new_empty ("GstForceKeyUnit")));
637 gst_rtp_buffer_unmap (&rtp);
643 gst_rtp_theora_depay_plugin_init (GstPlugin * plugin)
645 return gst_element_register (plugin, "rtptheoradepay",
646 GST_RANK_SECONDARY, GST_TYPE_RTP_THEORA_DEPAY);
650 gst_rtp_theora_depay_packet_lost (GstRTPBaseDepayload * depayload,
653 GstRtpTheoraDepay *rtptheoradepay = GST_RTP_THEORA_DEPAY (depayload);
656 gst_structure_get_uint (gst_event_get_structure (event), "seqnum", &seqnum);
657 GST_LOG_OBJECT (depayload, "Requested keyframe because frame with seqnum %u"
658 " is missing", seqnum);
659 rtptheoradepay->needs_keyframe = TRUE;
661 gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD_SINKPAD (depayload),
662 gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
663 gst_structure_new_empty ("GstForceKeyUnit")));