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,
134 gst_buffer_map (confbuf, &map, GST_MAP_READ);
138 GST_DEBUG_OBJECT (rtptheoradepay, "config size %" G_GSIZE_FORMAT, size);
140 /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
141 * | Number of packed headers |
142 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
143 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
145 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
146 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
148 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
149 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
151 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
156 num_headers = GST_READ_UINT32_BE (data);
160 GST_DEBUG_OBJECT (rtptheoradepay, "have %u headers", num_headers);
163 * 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
164 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
165 * | Ident | length ..
166 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
167 * .. | n. of headers | length1 | length2 ..
168 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
169 * .. | Identification Header ..
170 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
171 * .................................................................
172 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
173 * .. | Comment Header ..
174 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
175 * .................................................................
176 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
177 * .. Comment Header |
178 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
180 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
181 * .................................................................
182 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
184 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
186 for (i = 0; i < num_headers; i++) {
190 GstRtpTheoraConfig *conf;
197 ident = (data[0] << 16) | (data[1] << 8) | data[2];
198 length = (data[3] << 8) | data[4];
203 GST_DEBUG_OBJECT (rtptheoradepay,
204 "header %d, ident 0x%08x, length %u, left %" G_GSIZE_FORMAT, i, ident,
207 /* FIXME check if we already got this ident */
209 /* length might also include count of following size fields */
210 if (size < length && size + 1 != length)
213 /* read header sizes we read 2 sizes, the third size (for which we allocate
214 * space) must be derived from the total packed header length. */
215 h_sizes = g_newa (guint, n_headers + 1);
216 for (j = 0; j < n_headers; j++) {
226 h_size = (h_size << 7) | (b & 0x7f);
228 GST_DEBUG_OBJECT (rtptheoradepay, "headers %d: size: %u", j, h_size);
232 /* last header length is the remaining space */
233 GST_DEBUG_OBJECT (rtptheoradepay, "last header size: %u", length);
236 GST_DEBUG_OBJECT (rtptheoradepay, "preparing headers");
237 conf = g_new0 (GstRtpTheoraConfig, 1);
240 for (j = 0; j <= n_headers; j++) {
245 if (j != n_headers || size + extra != h_size) {
248 /* otherwise means that overall length field contained total length,
249 * including extra fields */
254 GST_DEBUG_OBJECT (rtptheoradepay, "reading header %d, size %u", j,
257 buf = gst_buffer_new_and_alloc (h_size);
258 gst_buffer_fill (buf, 0, data, h_size);
259 conf->headers = g_list_append (conf->headers, buf);
263 rtptheoradepay->configs = g_list_append (rtptheoradepay->configs, conf);
266 gst_buffer_unmap (confbuf, &map);
272 GST_DEBUG_OBJECT (rtptheoradepay, "configuration too small");
273 gst_buffer_unmap (confbuf, &map);
279 gst_rtp_theora_depay_parse_inband_configuration (GstRtpTheoraDepay *
280 rtptheoradepay, guint ident, guint8 * configuration, guint size,
286 if (G_UNLIKELY (size < 4))
289 /* transform inline to out-of-band and parse that one */
290 confbuf = gst_buffer_new_and_alloc (size + 9);
291 gst_buffer_map (confbuf, &map, GST_MAP_WRITE);
293 GST_WRITE_UINT32_BE (map.data, 1);
295 GST_WRITE_UINT24_BE (map.data + 4, ident);
296 /* write sort-of-length */
297 GST_WRITE_UINT16_BE (map.data + 7, length);
299 memcpy (map.data + 9, configuration, size);
300 gst_buffer_unmap (confbuf, &map);
302 return gst_rtp_theora_depay_parse_configuration (rtptheoradepay, confbuf);
306 gst_rtp_theora_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
308 GstStructure *structure;
309 GstRtpTheoraDepay *rtptheoradepay;
311 const gchar *configuration;
314 rtptheoradepay = GST_RTP_THEORA_DEPAY (depayload);
316 rtptheoradepay->needs_keyframe = FALSE;
318 structure = gst_caps_get_structure (caps, 0);
320 /* read and parse configuration string */
321 configuration = gst_structure_get_string (structure, "configuration");
327 /* deserialize base64 to buffer */
328 data = g_base64_decode (configuration, &size);
330 confbuf = gst_buffer_new ();
331 gst_buffer_take_memory (confbuf, -1,
332 gst_memory_new_wrapped (0, data, g_free, size, 0, size));
334 if (!gst_rtp_theora_depay_parse_configuration (rtptheoradepay, confbuf))
335 goto invalid_configuration;
338 /* set caps on pad and on header */
339 srccaps = gst_caps_new_empty_simple ("video/x-theora");
340 res = gst_pad_set_caps (depayload->srcpad, srccaps);
341 gst_caps_unref (srccaps);
343 /* Clock rate is always 90000 according to draft-barbato-avt-rtp-theora-01 */
344 depayload->clock_rate = 90000;
349 invalid_configuration:
351 GST_ERROR_OBJECT (rtptheoradepay, "invalid configuration specified");
357 gst_rtp_theora_depay_switch_codebook (GstRtpTheoraDepay * rtptheoradepay,
361 gboolean res = FALSE;
363 for (walk = rtptheoradepay->configs; walk; walk = g_list_next (walk)) {
364 GstRtpTheoraConfig *conf = (GstRtpTheoraConfig *) walk->data;
366 if (conf->ident == ident) {
369 /* FIXME, remove pads, create new pad.. */
371 /* push out all the headers */
372 for (headers = conf->headers; headers; headers = g_list_next (headers)) {
373 GstBuffer *header = GST_BUFFER_CAST (headers->data);
375 gst_buffer_ref (header);
376 gst_rtp_base_depayload_push (GST_RTP_BASE_DEPAYLOAD (rtptheoradepay),
379 /* remember the current config */
380 rtptheoradepay->config = conf;
385 /* we don't know about the headers, figure out an alternative method for
386 * getting the codebooks. FIXME, fail for now. */
392 gst_rtp_theora_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf)
394 GstRtpTheoraDepay *rtptheoradepay;
398 guint8 *payload, *to_free = NULL;
399 guint32 header, ident;
400 guint8 F, TDT, packets;
401 GstRTPBuffer rtp = { NULL };
403 rtptheoradepay = GST_RTP_THEORA_DEPAY (depayload);
405 gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp);
407 payload_len = gst_rtp_buffer_get_payload_len (&rtp);
409 GST_DEBUG_OBJECT (depayload, "got RTP packet of size %d", payload_len);
411 /* we need at least 4 bytes for the packet header */
412 if (G_UNLIKELY (payload_len < 4))
415 payload = gst_rtp_buffer_get_payload (&rtp);
417 header = GST_READ_UINT32_BE (payload);
420 * 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
421 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
422 * | Ident | F |TDT|# pkts.|
423 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
425 * F: Fragment type (0=none, 1=start, 2=cont, 3=end)
426 * TDT: Theora data type (0=theora, 1=config, 2=comment, 3=reserved)
427 * pkts: number of packets.
429 TDT = (header & 0x30) >> 4;
430 if (G_UNLIKELY (TDT == 3))
431 goto ignore_reserved;
433 ident = (header >> 8) & 0xffffff;
434 F = (header & 0xc0) >> 6;
435 packets = (header & 0xf);
437 GST_DEBUG_OBJECT (depayload, "ident: 0x%08x, F: %d, TDT: %d, packets: %d",
438 ident, F, TDT, packets);
441 gboolean do_switch = FALSE;
443 /* we have a raw payload, find the codebook for the ident */
444 if (!rtptheoradepay->config) {
445 /* we don't have an active codebook, find the codebook and
448 } else if (rtptheoradepay->config->ident != ident) {
449 /* codebook changed */
453 if (!gst_rtp_theora_depay_switch_codebook (rtptheoradepay, ident))
462 /* fragmented packets, assemble */
468 /* if we start a packet, clear adapter and start assembling. */
469 gst_adapter_clear (rtptheoradepay->adapter);
470 GST_DEBUG_OBJECT (depayload, "start assemble");
471 rtptheoradepay->assembling = TRUE;
474 if (!rtptheoradepay->assembling)
477 /* first assembled packet, reuse 2 bytes to store the length */
478 headerskip = (F == 1 ? 4 : 6);
479 /* skip header and length. */
480 vdata = gst_rtp_buffer_get_payload_subbuffer (&rtp, headerskip, -1);
482 GST_DEBUG_OBJECT (depayload, "assemble theora packet");
483 gst_adapter_push (rtptheoradepay->adapter, vdata);
485 /* packet is not complete, we are done */
489 /* construct assembled buffer */
490 payload_len = gst_adapter_available (rtptheoradepay->adapter);
491 payload = gst_adapter_take (rtptheoradepay->adapter, payload_len);
493 payload[0] = ((payload_len - 2) >> 8) & 0xff;
494 payload[1] = (payload_len - 2) & 0xff;
498 GST_DEBUG_OBJECT (depayload, "assemble done, payload_len %d", payload_len);
500 /* we not assembling anymore now */
501 rtptheoradepay->assembling = FALSE;
502 gst_adapter_clear (rtptheoradepay->adapter);
504 /* payload now points to a length with that many theora data bytes.
505 * Iterate over the packets and send them out.
508 * 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
509 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
510 * | length | theora data ..
511 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
513 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
514 * | length | next theora packet data ..
515 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
517 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*
519 while (payload_len >= 2) {
522 length = GST_READ_UINT16_BE (payload);
526 GST_DEBUG_OBJECT (depayload, "read length %u, avail: %d", length,
529 /* skip packet if something odd happens */
530 if (G_UNLIKELY (length > payload_len))
533 /* handle in-band configuration */
534 if (G_UNLIKELY (TDT == 1)) {
535 GST_DEBUG_OBJECT (rtptheoradepay, "in-band configuration");
536 if (!gst_rtp_theora_depay_parse_inband_configuration (rtptheoradepay,
537 ident, payload, payload_len, length))
538 goto invalid_configuration;
542 /* create buffer for packet */
543 if (G_UNLIKELY (to_free)) {
544 outbuf = gst_buffer_new ();
545 gst_buffer_take_memory (buf, -1,
546 gst_memory_new_wrapped (0, to_free, g_free,
547 (payload - to_free) + length, payload - to_free, length));
550 outbuf = gst_buffer_new_and_alloc (length);
551 gst_buffer_fill (outbuf, 0, payload, length);
554 if (payload_len > 0 && (payload[0] & 0xC0) == 0x0)
555 rtptheoradepay->needs_keyframe = FALSE;
558 payload_len -= length;
560 ret = gst_rtp_base_depayload_push (depayload, outbuf);
561 if (ret != GST_FLOW_OK)
567 if (rtptheoradepay->needs_keyframe)
568 goto request_keyframe;
570 gst_rtp_buffer_unmap (&rtp);
575 gst_rtp_buffer_unmap (&rtp);
581 GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE,
582 (NULL), ("Could not switch codebooks"));
587 GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE,
588 (NULL), ("Packet was too short (%d < 4)", payload_len));
589 goto request_keyframe;
593 GST_WARNING_OBJECT (rtptheoradepay, "reserved TDT ignored");
594 gst_rtp_buffer_unmap (&rtp);
599 GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE,
600 (NULL), ("Packet contains invalid data"));
601 goto request_keyframe;
603 invalid_configuration:
605 /* fatal, as we otherwise risk carrying on without output */
606 GST_ELEMENT_ERROR (rtptheoradepay, STREAM, DECODE,
607 (NULL), ("Packet contains invalid configuration"));
612 gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD_SINKPAD (depayload),
613 gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
614 gst_structure_new ("GstForceKeyUnit",
615 "all-headers", G_TYPE_BOOLEAN, TRUE, NULL)));
616 gst_rtp_buffer_unmap (&rtp);
621 rtptheoradepay->needs_keyframe = TRUE;
622 gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD_SINKPAD (depayload),
623 gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
624 gst_structure_new_empty ("GstForceKeyUnit")));
625 gst_rtp_buffer_unmap (&rtp);
631 gst_rtp_theora_depay_plugin_init (GstPlugin * plugin)
633 return gst_element_register (plugin, "rtptheoradepay",
634 GST_RANK_SECONDARY, GST_TYPE_RTP_THEORA_DEPAY);
638 gst_rtp_theora_depay_packet_lost (GstRTPBaseDepayload * depayload,
641 GstRtpTheoraDepay *rtptheoradepay = GST_RTP_THEORA_DEPAY (depayload);
644 gst_structure_get_uint (gst_event_get_structure (event), "seqnum", &seqnum);
645 GST_LOG_OBJECT (depayload, "Requested keyframe because frame with seqnum %u"
646 " is missing", seqnum);
647 rtptheoradepay->needs_keyframe = TRUE;
649 gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD_SINKPAD (depayload),
650 gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
651 gst_structure_new_empty ("GstForceKeyUnit")));