]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gst-plugins-ugly0-10.git/blob - gst/asfdemux/gstrtpasfdepay.c
7138be2c26edcd31bc18b5e7b53b3ffc23b5c44a
[glsdk/gst-plugins-ugly0-10.git] / gst / asfdemux / gstrtpasfdepay.c
1 /* GStreamer RTP ASF depayloader
2  * Copyright (C) 2006 Tim-Philipp Müller  <tim centricular net>
3  *               2009 Wim Taymans  <wim.taymans@gmail.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 #include "gstrtpasfdepay.h"
26 #include <gst/rtp/gstrtpbuffer.h>
28 #include <string.h>
29 #include <stdlib.h>
31 GST_DEBUG_CATEGORY_STATIC (rtpasfdepayload_debug);
32 #define GST_CAT_DEFAULT rtpasfdepayload_debug
34 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
35     GST_PAD_SRC,
36     GST_PAD_ALWAYS,
37     GST_STATIC_CAPS ("video/x-ms-asf")
38     );
40 /* Other parameters: config, maxps */
41 #define SINK_CAPS \
42   "application/x-rtp, "                                          \
43   "media = (string) { \"application\", \"video\", \"audio\" }, " \
44   "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "         \
45   "clock-rate = (int) [1, MAX ], "                               \
46   "encoding-name = (string) \"X-ASF-PF\""
48 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
49     GST_PAD_SINK,
50     GST_PAD_ALWAYS,
51     GST_STATIC_CAPS (SINK_CAPS)
52     );
54 GST_BOILERPLATE (GstRtpAsfDepay, gst_rtp_asf_depay, GstBaseRTPDepayload,
55     GST_TYPE_BASE_RTP_DEPAYLOAD);
57 static void gst_rtp_asf_depay_finalize (GObject * object);
59 static GstStateChangeReturn gst_rtp_asf_depay_change_state (GstElement *
60     element, GstStateChange transition);
62 static gboolean gst_rtp_asf_depay_setcaps (GstBaseRTPDepayload * depay,
63     GstCaps * caps);
64 static GstBuffer *gst_rtp_asf_depay_process (GstBaseRTPDepayload * basedepay,
65     GstBuffer * buf);
67 static void
68 gst_rtp_asf_depay_base_init (gpointer klass)
69 {
70   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
72   gst_element_class_add_pad_template (element_class,
73       gst_static_pad_template_get (&src_factory));
74   gst_element_class_add_pad_template (element_class,
75       gst_static_pad_template_get (&sink_factory));
77   gst_element_class_set_details_simple (element_class,
78       "RTP ASF packet depayloader", "Codec/Depayloader/Network",
79       "Extracts ASF streams from RTP",
80       "Tim-Philipp Müller <tim centricular net>, "
81       "Wim Taymans <wim.taymans@gmail.com>");
82 }
84 static void
85 gst_rtp_asf_depay_class_init (GstRtpAsfDepayClass * klass)
86 {
87   GObjectClass *gobject_class;
88   GstElementClass *gstelement_class;
89   GstBaseRTPDepayloadClass *gstbasertpdepayload_class;
91   gobject_class = (GObjectClass *) klass;
92   gstelement_class = (GstElementClass *) klass;
93   gstbasertpdepayload_class = (GstBaseRTPDepayloadClass *) klass;
95   gobject_class->finalize = gst_rtp_asf_depay_finalize;
97   gstelement_class->change_state =
98       GST_DEBUG_FUNCPTR (gst_rtp_asf_depay_change_state);
100   gstbasertpdepayload_class->set_caps =
101       GST_DEBUG_FUNCPTR (gst_rtp_asf_depay_setcaps);
102   gstbasertpdepayload_class->process =
103       GST_DEBUG_FUNCPTR (gst_rtp_asf_depay_process);
105   GST_DEBUG_CATEGORY_INIT (rtpasfdepayload_debug, "rtpasfdepayload", 0,
106       "RTP asf depayloader element");
109 static void
110 gst_rtp_asf_depay_init (GstRtpAsfDepay * depay, GstRtpAsfDepayClass * klass)
112   depay->adapter = gst_adapter_new ();
115 static void
116 gst_rtp_asf_depay_finalize (GObject * object)
118   GstRtpAsfDepay *depay;
120   depay = GST_RTP_ASF_DEPAY (object);
122   g_object_unref (depay->adapter);
124   G_OBJECT_CLASS (parent_class)->finalize (object);
127 static const guint8 asf_marker[16] = { 0x30, 0x26, 0xb2, 0x75, 0x8e, 0x66,
128   0xcf, 0x11, 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c
129 };
131 static gboolean
132 gst_rtp_asf_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps)
134   GstRtpAsfDepay *depay;
135   GstStructure *s;
136   const gchar *config_str, *ps_string;
137   GstBuffer *buf;
138   GstCaps *src_caps;
139   guint8 *headers;
140   gsize headers_len;
141   gint clock_rate;
143   depay = GST_RTP_ASF_DEPAY (depayload);
145   s = gst_caps_get_structure (caps, 0);
147   if (!gst_structure_get_int (s, "clock-rate", &clock_rate) || clock_rate < 0)
148     clock_rate = 1000;
149   depayload->clock_rate = clock_rate;
151   /* config contains the asf headers in base64 coding */
152   config_str = gst_structure_get_string (s, "config");
153   if (config_str == NULL || *config_str == '\0')
154     goto no_config;
156   ps_string = gst_structure_get_string (s, "maxps");
157   if (ps_string == NULL || *ps_string == '\0')
158     goto no_packetsize;
160   if (depay->packet_size) {
161     /* header sent again following seek;
162      * discard to avoid confusing upstream */
163     if (depay->packet_size == atoi (ps_string)) {
164       goto duplicate_header;
165     } else {
166       /* since we should fiddle with downstream state to handle this */
167       goto refuse_renegotiation;
168     }
169   } else
170     depay->packet_size = atoi (ps_string);
171   if (depay->packet_size <= 16)
172     goto invalid_packetsize;
174   headers = (guint8 *) g_base64_decode (config_str, &headers_len);
176   if (headers == NULL || headers_len < 16
177       || memcmp (headers, asf_marker, 16) != 0)
178     goto invalid_headers;
180   src_caps = gst_caps_new_simple ("video/x-ms-asf", NULL);
181   gst_pad_set_caps (depayload->srcpad, src_caps);
183   buf = gst_buffer_new ();
184   GST_BUFFER_DATA (buf) = headers;
185   GST_BUFFER_MALLOCDATA (buf) = headers;
186   GST_BUFFER_SIZE (buf) = headers_len;
187   gst_buffer_set_caps (buf, src_caps);
188   gst_caps_unref (src_caps);
190   gst_base_rtp_depayload_push (depayload, buf);
192   return TRUE;
194   /* ERRORS */
195 no_config:
196   {
197     GST_WARNING_OBJECT (depay, "caps without 'config' field with asf headers");
198     return FALSE;
199   }
200 no_packetsize:
201   {
202     GST_WARNING_OBJECT (depay, "caps without 'maxps' (packet size) field");
203     return FALSE;
204   }
205 invalid_packetsize:
206   {
207     GST_WARNING_OBJECT (depay, "packet size %u invalid", depay->packet_size);
208     return FALSE;
209   }
210 invalid_headers:
211   {
212     GST_WARNING_OBJECT (depay, "headers don't look like valid ASF headers");
213     g_free (headers);
214     return FALSE;
215   }
216 duplicate_header:
217   {
218     GST_DEBUG_OBJECT (depayload, "discarding duplicate header");
219     return TRUE;
220   }
221 refuse_renegotiation:
222   {
223     GST_WARNING_OBJECT (depayload, "cannot renegotiate to different header");
224     return FALSE;
225   }
228 static gint
229 field_size (guint8 field)
231   switch (field) {
232       /* DWORD - 32 bits */
233     case 3:
234       return 4;
236       /* WORD - 16 bits */
237     case 2:
238       return 2;
240       /* BYTE - 8 bits */
241     case 1:
242       return 1;
244       /* non-exitent */
245     case 0:
246     default:
247       return 0;
248   }
251 /* 
252  * Set the padding field to te correct value as the spec
253  * says it should be se to 0 in the rtp packets
254  */
255 static void
256 gst_rtp_asf_depay_set_padding (GstRtpAsfDepay * depayload,
257     GstBuffer * buf, guint32 padding)
259   guint8 *data = GST_BUFFER_DATA (buf);
260   gint offset = 0;
261   guint8 aux;
262   guint8 seq_type;
263   guint8 pad_type;
264   guint8 pkt_type;
266   aux = data[offset++];
267   if (aux & 0x80) {
268     guint8 err_len = 0;
269     if (aux & 0x60) {
270       GST_WARNING_OBJECT (depayload, "Error correction length type should be "
271           "set to 0");
272       /* this packet doesn't follow the spec */
273       return;
274     }
275     err_len = aux & 0x0F;
276     offset += err_len;
278     aux = data[offset++];
279   }
280   seq_type = (aux >> 1) & 0x3;
281   pad_type = (aux >> 3) & 0x3;
282   pkt_type = (aux >> 5) & 0x3;
284   offset += 1;                  /* skip property flags */
285   offset += field_size (pkt_type);      /* skip packet length */
286   offset += field_size (seq_type);      /* skip sequence field */
288   /* write padding */
289   switch (pad_type) {
290       /* DWORD */
291     case 3:
292       GST_WRITE_UINT32_LE (&(data[offset]), padding);
293       break;
295       /* WORD */
296     case 2:
297       GST_WRITE_UINT16_LE (&(data[offset]), padding);
298       break;
300       /* BYTE */
301     case 1:
302       data[offset] = (guint8) padding;
303       break;
305       /* non-existent */
306     case 0:
307     default:
308       break;
309   }
312 /* Docs: 'RTSP Protocol PDF' document from http://sdp.ppona.com/ (page 8) */
314 static GstBuffer *
315 gst_rtp_asf_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
317   GstRtpAsfDepay *depay;
318   const guint8 *payload;
319   GstBuffer *outbuf;
320   gboolean S, L, R, D, I;
321   guint payload_len, hdr_len, offset;
322   guint len_offs;
323   GstClockTime timestamp;
325   depay = GST_RTP_ASF_DEPAY (depayload);
327   /* flush remaining data on discont */
328   if (GST_BUFFER_IS_DISCONT (buf)) {
329     GST_LOG_OBJECT (depay, "got DISCONT");
330     gst_adapter_clear (depay->adapter);
331     depay->discont = TRUE;
332   }
334   timestamp = GST_BUFFER_TIMESTAMP (buf);
336   payload_len = gst_rtp_buffer_get_payload_len (buf);
337   payload = gst_rtp_buffer_get_payload (buf);
338   offset = 0;
340   GST_LOG_OBJECT (depay, "got payload len of %u", payload_len);
342   do {
343     guint packet_len;
345     /* packet header is at least 4 bytes */
346     if (payload_len < 4)
347       goto too_small;
349     /*                      1                   2                   3
350      *  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 
351      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
352      * |S|L|R|D|I|RES  | Length/Offset                                 |
353      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
354      * | Relative Timestamp (optional)                                 |
355      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
356      * | Duration (optional)                                           |
357      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
358      * | LocationId (optional)                                         |
359      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
360      *
361      * S: packet contains a keyframe.
362      * L: If 1, Length/Offset contains length, else contains the byte offset
363      *    of the fragment's first byte counted from the beginning of the
364      *    complete ASF data packet.
365      * R: relative timestamp present
366      * D: duration present
367      * I: locationid present
368      */
370     S = ((payload[0] & 0x80) != 0);
371     L = ((payload[0] & 0x40) != 0);
372     R = ((payload[0] & 0x20) != 0);
373     D = ((payload[0] & 0x10) != 0);
374     I = ((payload[0] & 0x08) != 0);
376     hdr_len = 4;
378     len_offs = (payload[1] << 16) | (payload[2] << 8) | payload[3];
380     if (R) {
381       GST_DEBUG ("Relative timestamp field present : %u",
382           GST_READ_UINT32_BE (payload + hdr_len));
383       hdr_len += 4;
384     }
385     if (D) {
386       GST_DEBUG ("Duration field present : %u",
387           GST_READ_UINT32_BE (payload + hdr_len));
388       hdr_len += 4;
389     }
390     if (I) {
391       GST_DEBUG ("LocationId field present : %u",
392           GST_READ_UINT32_BE (payload + hdr_len));
393       hdr_len += 4;
394     }
396     GST_LOG_OBJECT (depay, "S %d, L %d, R %d, D %d, I %d", S, L, R, D, I);
397     GST_LOG_OBJECT (depay, "payload_len:%d, hdr_len:%d, len_offs:%d",
398         payload_len, hdr_len, len_offs);
400     if (payload_len < hdr_len)
401       goto too_small;
403     /* skip headers */
404     payload_len -= hdr_len;
405     payload += hdr_len;
406     offset += hdr_len;
408     if (L) {
409       /* L bit set, len contains the length of the packet */
410       packet_len = len_offs;
411     } else {
412       /* else it contains an offset which we don't handle yet */
413       GST_LOG_OBJECT (depay, "We have a fragmented packet");
414       packet_len = payload_len;
415     }
417     if (packet_len > payload_len)
418       packet_len = payload_len;
420     GST_LOG_OBJECT (depay, "packet len %u, payload len %u, packet_size:%u",
421         packet_len, payload_len, depay->packet_size);
423     if (!L) {
424       guint available;
425       GstBuffer *sub;
427       /* Fragmented packet handling */
428       outbuf = NULL;
430       if (len_offs == (available = gst_adapter_available (depay->adapter))) {
431         /* fragment aligns with what we have, add it */
432         GST_LOG_OBJECT (depay, "collecting fragment");
433         sub = gst_rtp_buffer_get_payload_subbuffer (buf, offset, packet_len);
434         gst_adapter_push (depay->adapter, sub);
435         /* RTP marker bit M is set if this is last fragment */
436         if (gst_rtp_buffer_get_marker (buf)) {
437           GST_LOG_OBJECT (depay, "last fragment, assembling packet");
438           outbuf =
439               gst_adapter_take_buffer (depay->adapter, available + packet_len);
440         }
441       } else {
442         if (available) {
443           GST_WARNING_OBJECT (depay, "Offset doesn't match previous data?!");
444           GST_DEBUG_OBJECT (depay, "clearing for re-sync");
445           gst_adapter_clear (depay->adapter);
446         } else
447           GST_DEBUG_OBJECT (depay, "waiting for start of packet");
448       }
449     } else {
450       GST_LOG_OBJECT (depay, "collecting packet");
451       outbuf = gst_rtp_buffer_get_payload_subbuffer (buf, offset, packet_len);
452     }
454     /* If we haven't completed a full ASF packet, return */
455     if (!outbuf)
456       return NULL;
458     /* we need to pad with zeroes to packet_size if it's smaller */
459     if (GST_BUFFER_SIZE (outbuf) < depay->packet_size) {
460       GstBuffer *tmp;
461       gint plen = GST_BUFFER_SIZE (outbuf);
463       GST_LOG_OBJECT (depay, "padding buffer size %d to packet size %d",
464           plen, depay->packet_size);
465       tmp = gst_buffer_new_and_alloc (depay->packet_size);
466       memcpy (GST_BUFFER_DATA (tmp), GST_BUFFER_DATA (outbuf), plen);
467       gst_buffer_copy_metadata (tmp, outbuf, GST_BUFFER_COPY_ALL);
468       gst_buffer_unref (outbuf);
469       outbuf = tmp;
470       memset (GST_BUFFER_DATA (outbuf) + plen, 0, depay->packet_size - plen);
471       gst_rtp_asf_depay_set_padding (depay, outbuf, depay->packet_size - plen);
472     }
474     gst_buffer_set_caps (outbuf, GST_PAD_CAPS (depayload->srcpad));
476     if (!S)
477       GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
479     if (depay->discont) {
480       GST_LOG_OBJECT (depay, "setting DISCONT");
481       GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
482       depay->discont = FALSE;
483     }
485     GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
487     gst_base_rtp_depayload_push (depayload, outbuf);
489     /* only apply the timestamp to the first buffer of this packet */
490     timestamp = -1;
492     /* skip packet data */
493     payload += packet_len;
494     offset += packet_len;
495     payload_len -= packet_len;
496   } while (payload_len > 0);
498   return NULL;
500 /* ERRORS */
501 too_small:
502   {
503     GST_WARNING_OBJECT (depayload, "Payload too small, expected at least 4 "
504         "bytes for header, but got only %d bytes", payload_len);
505     return NULL;
506   }
509 static GstStateChangeReturn
510 gst_rtp_asf_depay_change_state (GstElement * element, GstStateChange trans)
512   GstStateChangeReturn ret;
513   GstRtpAsfDepay *depay;
515   depay = GST_RTP_ASF_DEPAY (element);
517   switch (trans) {
518     case GST_STATE_CHANGE_READY_TO_PAUSED:
519       gst_adapter_clear (depay->adapter);
520       depay->discont = TRUE;
521       break;
522     default:
523       break;
524   }
526   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, trans);
528   switch (trans) {
529     case GST_STATE_CHANGE_PAUSED_TO_READY:
530       gst_adapter_clear (depay->adapter);
531       break;
532     default:
533       break;
534   }
536   return ret;