]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gstreamer0-10.git/blob - libs/gst/dataprotocol/dataprotocol.c
DEBIAN: Debianization
[glsdk/gstreamer0-10.git] / libs / gst / dataprotocol / dataprotocol.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) 2004,2006 Thomas Vander Stichele <thomas at apestaart dot org>
4  *
5  * dataprotocol.c: Functions implementing the GStreamer Data Protocol
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
23 /**
24  * SECTION:gstdataprotocol
25  * @short_description: Serialization of caps, buffers and events.
26  * @see_also: #GstCaps, #GstEvent, #GstBuffer
27  *
28  * This helper library provides serialization of GstBuffer, GstCaps and
29  * GstEvent structures.
30  *
31  * This serialization is useful when GStreamer needs to interface with
32  * the outside world to transport data between distinct GStreamer pipelines.
33  * The connections with the outside world generally don't have mechanisms
34  * to transport properties of these structures.
35  *
36  * For example, transporting buffers across named pipes or network connections
37  * doesn't maintain the buffer size and separation.
38  *
39  * This data protocol assumes a reliable connection-oriented transport, such as
40  * TCP, a pipe, or a file.  The protocol does not serialize the caps for
41  * each buffer; instead, it transport the caps only when they change in the
42  * stream.  This implies that there will always be a caps packet before any
43  * buffer packets.
44  *
45  * The versioning of the protocol is independent of GStreamer's version.
46  * The major number gets incremented, and the minor reset, for incompatible
47  * changes.  The minor number gets incremented for compatible changes that
48  * allow clients who do not completely understand the newer protocol version
49  * to still decode what they do understand.
50  *
51  * Version 0.2 serializes only a small subset of all events, with a custom
52  * payload for each type.  Also, all GDP streams start with the initial caps
53  * packet.
54  *
55  * Version 1.0 serializes all events by taking the string representation of
56  * the event as the payload.  In addition, GDP streams can now start with
57  * events as well, as required by the new data stream model in GStreamer 0.10.
58  *
59  * Converting buffers, caps and events to GDP buffers is done using a
60  * #GstDPPacketizer object and invoking its packetizer functions.
61  * For backwards-compatibility reasons, the old 0.2 methods are still
62  * available but deprecated.
63  *
64  * For reference, this image shows the byte layout of the GDP header:
65  *
66  * <inlinegraphic format="PNG" fileref="gdp-header.png"></inlinegraphic>
67  */
69 #ifdef HAVE_CONFIG_H
70 #include "config.h"
71 #endif
73 #include <gst/gst.h>
74 #include <gst/dataprotocol/dataprotocol.h>
75 #include <glib/gprintf.h>       /* g_sprintf */
76 #include <string.h>             /* strlen */
77 #include "dp-private.h"
79 /* debug category */
80 GST_DEBUG_CATEGORY_STATIC (data_protocol_debug);
81 #define GST_CAT_DEFAULT data_protocol_debug
83 /* helper macros */
85 /* write first 6 bytes of header */
86 #define GST_DP_INIT_HEADER(h, version, flags, type)             \
87 G_STMT_START {                                                  \
88   gint maj = 0, min = 0;                                        \
89   switch (version) {                                            \
90     case GST_DP_VERSION_0_2: maj = 0; min = 2; break;           \
91     case GST_DP_VERSION_1_0: maj = 1; min = 0; break;           \
92   }                                                             \
93   h[0] = (guint8) maj;                                          \
94   h[1] = (guint8) min;                                          \
95   h[2] = (guint8) flags;                                        \
96   h[3] = 0; /* padding byte */                                  \
97   GST_WRITE_UINT16_BE (h + 4, type);                            \
98 } G_STMT_END
100 #define GST_DP_SET_CRC(h, flags, payload, length);              \
101 G_STMT_START {                                                  \
102   guint16 crc = 0;                                              \
103   if (flags & GST_DP_HEADER_FLAG_CRC_HEADER)                    \
104     /* we don't crc the last four bytes since they are crc's */ \
105     crc = gst_dp_crc (h, 58);                                   \
106   GST_WRITE_UINT16_BE (h + 58, crc);                            \
107                                                                 \
108   crc = 0;                                                      \
109   if (length && (flags & GST_DP_HEADER_FLAG_CRC_PAYLOAD))       \
110     crc = gst_dp_crc (payload, length);                         \
111   GST_WRITE_UINT16_BE (h + 60, crc);                            \
112 } G_STMT_END
114 /* calculate a CCITT 16 bit CRC check value for a given byte array */
115 /*
116  * this code snippet is adapted from a web page I found
117  * it is identical except for cleanups, and a final XOR with 0xffff
118  * as outlined in the uecp spec
119  *
120  * XMODEM    x^16 + x^12 + x^5 + 1
121  */
123 #define POLY       0x1021
124 #define CRC_INIT   0xFFFF
126 /*** HELPER FUNCTIONS ***/
128 static gboolean
129 gst_dp_header_from_buffer_any (const GstBuffer * buffer, GstDPHeaderFlag flags,
130     guint * length, guint8 ** header, GstDPVersion version)
132   guint8 *h;
133   guint16 flags_mask;
135   g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
136   g_return_val_if_fail (length, FALSE);
137   g_return_val_if_fail (header, FALSE);
139   *length = GST_DP_HEADER_LENGTH;
140   h = g_malloc0 (GST_DP_HEADER_LENGTH);
142   /* version, flags, type */
143   GST_DP_INIT_HEADER (h, version, flags, GST_DP_PAYLOAD_BUFFER);
145   /* buffer properties */
146   GST_WRITE_UINT32_BE (h + 6, GST_BUFFER_SIZE (buffer));
147   GST_WRITE_UINT64_BE (h + 10, GST_BUFFER_TIMESTAMP (buffer));
148   GST_WRITE_UINT64_BE (h + 18, GST_BUFFER_DURATION (buffer));
149   GST_WRITE_UINT64_BE (h + 26, GST_BUFFER_OFFSET (buffer));
150   GST_WRITE_UINT64_BE (h + 34, GST_BUFFER_OFFSET_END (buffer));
152   /* data flags; eats two bytes from the ABI area */
153   /* we copy everything but the read-only flags */
154   flags_mask = GST_BUFFER_FLAG_PREROLL | GST_BUFFER_FLAG_DISCONT |
155       GST_BUFFER_FLAG_IN_CAPS | GST_BUFFER_FLAG_GAP |
156       GST_BUFFER_FLAG_DELTA_UNIT;
158   GST_WRITE_UINT16_BE (h + 42, GST_BUFFER_FLAGS (buffer) & flags_mask);
160   GST_DP_SET_CRC (h, flags, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
162   GST_LOG ("created header from buffer:");
163   gst_dp_dump_byte_array (h, GST_DP_HEADER_LENGTH);
164   *header = h;
165   return TRUE;
168 static gboolean
169 gst_dp_packet_from_caps_any (const GstCaps * caps, GstDPHeaderFlag flags,
170     guint * length, guint8 ** header, guint8 ** payload, GstDPVersion version)
172   guint8 *h;
173   guchar *string;
174   guint payload_length;
176   g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
177   g_return_val_if_fail (length, FALSE);
178   g_return_val_if_fail (header, FALSE);
179   g_return_val_if_fail (payload, FALSE);
181   *length = GST_DP_HEADER_LENGTH;
182   h = g_malloc0 (GST_DP_HEADER_LENGTH);
184   string = (guchar *) gst_caps_to_string (caps);
185   payload_length = strlen ((gchar *) string) + 1;       /* include trailing 0 */
187   /* version, flags, type */
188   GST_DP_INIT_HEADER (h, version, flags, GST_DP_PAYLOAD_CAPS);
190   /* buffer properties */
191   GST_WRITE_UINT32_BE (h + 6, payload_length);
192   GST_WRITE_UINT64_BE (h + 10, (guint64) 0);
193   GST_WRITE_UINT64_BE (h + 18, (guint64) 0);
194   GST_WRITE_UINT64_BE (h + 26, (guint64) 0);
195   GST_WRITE_UINT64_BE (h + 34, (guint64) 0);
197   GST_DP_SET_CRC (h, flags, string, payload_length);
199   GST_LOG ("created header from caps:");
200   gst_dp_dump_byte_array (h, GST_DP_HEADER_LENGTH);
201   *header = h;
202   *payload = string;
203   return TRUE;
207 /*** PUBLIC FUNCTIONS ***/
209 static const guint16 gst_dp_crc_table[256] = {
210   0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
211   0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
212   0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
213   0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
214   0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
215   0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
216   0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
217   0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
218   0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
219   0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
220   0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
221   0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
222   0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
223   0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
224   0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
225   0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
226   0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
227   0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
228   0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
229   0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
230   0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
231   0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
232   0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
233   0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
234   0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
235   0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
236   0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
237   0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
238   0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
239   0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
240   0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
241   0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
242 };
244 /**
245  * gst_dp_crc:
246  * @buffer: array of bytes
247  * @length: the length of @buffer
248  *
249  * Calculate a CRC for the given buffer over the given number of bytes.
250  * This is only provided for verification purposes; typical GDP users
251  * will not need this function.
252  *
253  * Returns: a two-byte CRC checksum.
254  */
255 guint16
256 gst_dp_crc (const guint8 * buffer, guint length)
258   guint16 crc_register = CRC_INIT;
260   g_return_val_if_fail (buffer != NULL || length == 0, 0);
262   /* calc CRC */
263   for (; length--;) {
264     crc_register = (guint16) ((crc_register << 8) ^
265         gst_dp_crc_table[((crc_register >> 8) & 0x00ff) ^ *buffer++]);
266   }
267   return (0xffff ^ crc_register);
270 /* debugging function; dumps byte array values per 8 bytes */
271 /* FIXME: would be nice to merge this with gst_util_dump_mem () */
272 void
273 gst_dp_dump_byte_array (guint8 * array, guint length)
275   int i;
276   int n = 8;                    /* number of bytes per line */
277   gchar *line = g_malloc0 (3 * n + 1);
279   GST_LOG ("dumping byte array of length %d", length);
280   for (i = 0; i < length; ++i) {
281     g_sprintf (line + 3 * (i % n), "%02x ", array[i]);
282     if (i % n == (n - 1)) {
283       GST_LOG ("%03d: %s", i - (n - 1), line);
284     }
285   }
286   if (i % n != 0) {
287     GST_LOG ("%03d: %s", (i / n) * n, line);
288   }
289   g_free (line);
292 GType
293 gst_dp_version_get_type (void)
295   static gsize gst_dp_version_type = 0;
296   static const GEnumValue gst_dp_version[] = {
297     {GST_DP_VERSION_0_2, "GST_DP_VERSION_0_2", "0.2"},
298     {GST_DP_VERSION_1_0, "GST_DP_VERSION_1_0", "1.0"},
299     {0, NULL, NULL},
300   };
302   if (g_once_init_enter (&gst_dp_version_type)) {
303     GType tmp = g_enum_register_static ("GstDPVersion", gst_dp_version);
304     g_once_init_leave (&gst_dp_version_type, tmp);
305   }
307   return (GType) gst_dp_version_type;
308 };
310 /**
311  * gst_dp_init:
312  *
313  * Initialize GStreamer Data Protocol library.
314  *
315  * Should be called before using these functions from source linking
316  * to this source file.
317  */
318 void
319 gst_dp_init (void)
321   static gboolean _gst_dp_initialized = FALSE;
323   if (_gst_dp_initialized)
324     return;
326   _gst_dp_initialized = TRUE;
328   g_type_class_ref (gst_dp_version_get_type ());
330   GST_DEBUG_CATEGORY_INIT (data_protocol_debug, "gdp", 0,
331       "GStreamer Data Protocol");
334 /**
335  * gst_dp_header_payload_length:
336  * @header: the byte header of the packet array
337  *
338  * Get the length of the payload described by @header.
339  *
340  * Returns: the length of the payload this header describes.
341  */
342 guint32
343 gst_dp_header_payload_length (const guint8 * header)
345   g_return_val_if_fail (header != NULL, 0);
347   return GST_DP_HEADER_PAYLOAD_LENGTH (header);
350 /**
351  * gst_dp_header_payload_type:
352  * @header: the byte header of the packet array
353  *
354  * Get the type of the payload described by @header.
355  *
356  * Returns: the #GstDPPayloadType the payload this header describes.
357  */
358 GstDPPayloadType
359 gst_dp_header_payload_type (const guint8 * header)
361   g_return_val_if_fail (header != NULL, GST_DP_PAYLOAD_NONE);
363   return GST_DP_HEADER_PAYLOAD_TYPE (header);
366 /*** PACKETIZER FUNCTIONS ***/
368 /**
369  * gst_dp_header_from_buffer:
370  * @buffer: a #GstBuffer to create a header for
371  * @flags: the #GstDPHeaderFlag to create the header with
372  * @length: a guint pointer to store the header length in
373  * @header: a guint8 * pointer to store a newly allocated header byte array in
374  *
375  * Creates a GDP header from the given buffer.
376  *
377  * Deprecated: use a #GstDPPacketizer
378  *
379  * Returns: %TRUE if the header was successfully created.
380  */
381 #ifndef GST_REMOVE_DEPRECATED
382 #ifdef GST_DISABLE_DEPRECATED
383 gboolean
384 gst_dp_header_from_buffer (const GstBuffer * buffer, GstDPHeaderFlag flags,
385     guint * length, guint8 ** header);
386 #endif
387 gboolean
388 gst_dp_header_from_buffer (const GstBuffer * buffer, GstDPHeaderFlag flags,
389     guint * length, guint8 ** header)
391   return gst_dp_header_from_buffer_any (buffer, flags, length, header,
392       GST_DP_VERSION_0_2);
394 #endif
396 static gboolean
397 gst_dp_header_from_buffer_1_0 (const GstBuffer * buffer, GstDPHeaderFlag flags,
398     guint * length, guint8 ** header)
400   return gst_dp_header_from_buffer_any (buffer, flags, length, header,
401       GST_DP_VERSION_1_0);
404 /**
405  * gst_dp_packet_from_caps:
406  * @caps: a #GstCaps to create a packet for
407  * @flags: the #GstDPHeaderFlag to create the header with
408  * @length: a guint pointer to store the header length in
409  * @header: a guint8 pointer to store a newly allocated header byte array in
410  * @payload: a guint8 pointer to store a newly allocated payload byte array in
411  *
412  * Creates a GDP packet from the given caps.
413  *
414  * Deprecated: use a #GstDPPacketizer
415  *
416  * Returns: %TRUE if the packet was successfully created.
417  */
418 #ifndef GST_REMOVE_DEPRECATED
419 #ifdef GST_DISABLE_DEPRECATED
420 gboolean
421 gst_dp_packet_from_caps (const GstCaps * caps, GstDPHeaderFlag flags,
422     guint * length, guint8 ** header, guint8 ** payload);
423 #endif
424 gboolean
425 gst_dp_packet_from_caps (const GstCaps * caps, GstDPHeaderFlag flags,
426     guint * length, guint8 ** header, guint8 ** payload)
428   return gst_dp_packet_from_caps_any (caps, flags, length, header, payload,
429       GST_DP_VERSION_0_2);
431 #endif
433 static gboolean
434 gst_dp_packet_from_caps_1_0 (const GstCaps * caps, GstDPHeaderFlag flags,
435     guint * length, guint8 ** header, guint8 ** payload)
437   return gst_dp_packet_from_caps_any (caps, flags, length, header, payload,
438       GST_DP_VERSION_1_0);
441 /**
442  * gst_dp_packet_from_event:
443  * @event: a #GstEvent to create a packet for
444  * @flags: the #GstDPHeaderFlag to create the header with
445  * @length: a guint pointer to store the header length in
446  * @header: a guint8 pointer to store a newly allocated header byte array in
447  * @payload: a guint8 pointer to store a newly allocated payload byte array in
448  *
449  * Creates a GDP packet from the given event.
450  *
451  * Deprecated: use a #GstDPPacketizer
452  *
453  * Returns: %TRUE if the packet was successfully created.
454  */
455 #ifndef GST_REMOVE_DEPRECATED
456 #ifdef GST_DISABLE_DEPRECATED
457 gboolean
458 gst_dp_packet_from_event (const GstEvent * event, GstDPHeaderFlag flags,
459     guint * length, guint8 ** header, guint8 ** payload);
460 #endif
461 gboolean
462 gst_dp_packet_from_event (const GstEvent * event, GstDPHeaderFlag flags,
463     guint * length, guint8 ** header, guint8 ** payload)
465   guint8 *h;
466   guint pl_length;              /* length of payload */
468   g_return_val_if_fail (GST_IS_EVENT (event), FALSE);
469   g_return_val_if_fail (length, FALSE);
470   g_return_val_if_fail (header, FALSE);
471   g_return_val_if_fail (payload, FALSE);
473   /* first construct payload, since we need the length */
474   switch (GST_EVENT_TYPE (event)) {
475     case GST_EVENT_UNKNOWN:
476       GST_WARNING ("Unknown event, ignoring");
477       return FALSE;
478     case GST_EVENT_EOS:
479     case GST_EVENT_FLUSH_START:
480     case GST_EVENT_FLUSH_STOP:
481     case GST_EVENT_NEWSEGMENT:
482       pl_length = 0;
483       *payload = NULL;
484       break;
485     case GST_EVENT_SEEK:
486     {
487       gdouble rate;
488       GstFormat format;
489       GstSeekFlags flags;
490       GstSeekType cur_type, stop_type;
491       gint64 cur, stop;
493       gst_event_parse_seek ((GstEvent *) event, &rate, &format, &flags,
494           &cur_type, &cur, &stop_type, &stop);
496       pl_length = 4 + 4 + 4 + 8 + 4 + 8;
497       *payload = g_malloc0 (pl_length);
498       /* FIXME write rate */
499       GST_WRITE_UINT32_BE (*payload, (guint32) format);
500       GST_WRITE_UINT32_BE (*payload + 4, (guint32) flags);
501       GST_WRITE_UINT32_BE (*payload + 8, (guint32) cur_type);
502       GST_WRITE_UINT64_BE (*payload + 12, (guint64) cur);
503       GST_WRITE_UINT32_BE (*payload + 20, (guint32) stop_type);
504       GST_WRITE_UINT64_BE (*payload + 24, (guint64) stop);
505       break;
506     }
507     case GST_EVENT_QOS:
508     case GST_EVENT_NAVIGATION:
509     case GST_EVENT_TAG:
510       GST_WARNING ("Unhandled event type %d, ignoring", GST_EVENT_TYPE (event));
511       return FALSE;
512     default:
513       GST_WARNING ("Unknown event type %d, ignoring", GST_EVENT_TYPE (event));
514       return FALSE;
515   }
517   /* now we can create and fill the header */
518   h = g_malloc0 (GST_DP_HEADER_LENGTH);
519   *length = GST_DP_HEADER_LENGTH;
521   /* version, flags, type */
522   GST_DP_INIT_HEADER (h, GST_DP_VERSION_0_2, flags,
523       GST_DP_PAYLOAD_EVENT_NONE + GST_EVENT_TYPE (event));
525   /* length */
526   GST_WRITE_UINT32_BE (h + 6, (guint32) pl_length);
527   /* timestamp */
528   GST_WRITE_UINT64_BE (h + 10, GST_EVENT_TIMESTAMP (event));
530   GST_DP_SET_CRC (h, flags, *payload, pl_length);
532   GST_LOG ("created header from event:");
533   gst_dp_dump_byte_array (h, GST_DP_HEADER_LENGTH);
535   *header = h;
536   return TRUE;
538 #endif
540 static gboolean
541 gst_dp_packet_from_event_1_0 (const GstEvent * event, GstDPHeaderFlag flags,
542     guint * length, guint8 ** header, guint8 ** payload)
544   guint8 *h;
545   guint32 pl_length;            /* length of payload */
546   guchar *string = NULL;
548   g_return_val_if_fail (GST_IS_EVENT (event), FALSE);
549   g_return_val_if_fail (length, FALSE);
550   g_return_val_if_fail (header, FALSE);
551   g_return_val_if_fail (payload, FALSE);
553   *length = GST_DP_HEADER_LENGTH;
554   h = g_malloc0 (GST_DP_HEADER_LENGTH);
556   if (event->structure) {
557     string = (guchar *) gst_structure_to_string (event->structure);
558     GST_LOG ("event %p has structure, string %s", event, string);
559     pl_length = strlen ((gchar *) string) + 1;  /* include trailing 0 */
560   } else {
561     GST_LOG ("event %p has no structure", event);
562     pl_length = 0;
563   }
565   /* version, flags, type */
566   GST_DP_INIT_HEADER (h, GST_DP_VERSION_1_0, flags,
567       GST_DP_PAYLOAD_EVENT_NONE + GST_EVENT_TYPE (event));
569   /* length */
570   GST_WRITE_UINT32_BE (h + 6, pl_length);
571   /* timestamp */
572   GST_WRITE_UINT64_BE (h + 10, GST_EVENT_TIMESTAMP (event));
574   GST_DP_SET_CRC (h, flags, string, pl_length);
576   GST_LOG ("created header from event:");
577   gst_dp_dump_byte_array (h, GST_DP_HEADER_LENGTH);
578   *header = h;
579   *payload = string;
580   return TRUE;
583 /*** DEPACKETIZING FUNCTIONS ***/
585 /**
586  * gst_dp_buffer_from_header:
587  * @header_length: the length of the packet header
588  * @header: the byte array of the packet header
589  *
590  * Creates a newly allocated #GstBuffer from the given header.
591  * The buffer data needs to be copied into it before validating.
592  *
593  * Use this function if you want to pre-allocate a buffer based on the
594  * packet header to read the packet payload in to.
595  *
596  * This function does not check the header passed to it, use
597  * gst_dp_validate_header() first if the header data is unchecked.
598  *
599  * Returns: A #GstBuffer if the buffer was successfully created, or NULL.
600  */
601 GstBuffer *
602 gst_dp_buffer_from_header (guint header_length, const guint8 * header)
604   GstBuffer *buffer;
606   g_return_val_if_fail (header != NULL, NULL);
607   g_return_val_if_fail (header_length >= GST_DP_HEADER_LENGTH, NULL);
608   g_return_val_if_fail (GST_DP_HEADER_PAYLOAD_TYPE (header) ==
609       GST_DP_PAYLOAD_BUFFER, NULL);
611   buffer =
612       gst_buffer_new_and_alloc ((guint) GST_DP_HEADER_PAYLOAD_LENGTH (header));
614   GST_BUFFER_TIMESTAMP (buffer) = GST_DP_HEADER_TIMESTAMP (header);
615   GST_BUFFER_DURATION (buffer) = GST_DP_HEADER_DURATION (header);
616   GST_BUFFER_OFFSET (buffer) = GST_DP_HEADER_OFFSET (header);
617   GST_BUFFER_OFFSET_END (buffer) = GST_DP_HEADER_OFFSET_END (header);
618   GST_BUFFER_FLAGS (buffer) = GST_DP_HEADER_BUFFER_FLAGS (header);
620   return buffer;
623 /**
624  * gst_dp_caps_from_packet:
625  * @header_length: the length of the packet header
626  * @header: the byte array of the packet header
627  * @payload: the byte array of the packet payload
628  *
629  * Creates a newly allocated #GstCaps from the given packet.
630  *
631  * This function does not check the arguments passed to it, use
632  * gst_dp_validate_packet() first if the header and payload data are
633  * unchecked.
634  *
635  * Returns: A #GstCaps containing the caps represented in the packet,
636  *          or NULL if the packet could not be converted.
637  */
638 GstCaps *
639 gst_dp_caps_from_packet (guint header_length, const guint8 * header,
640     const guint8 * payload)
642   GstCaps *caps;
643   gchar *string;
645   g_return_val_if_fail (header, NULL);
646   g_return_val_if_fail (header_length >= GST_DP_HEADER_LENGTH, NULL);
647   g_return_val_if_fail (GST_DP_HEADER_PAYLOAD_TYPE (header) ==
648       GST_DP_PAYLOAD_CAPS, NULL);
649   g_return_val_if_fail (payload, NULL);
651   /* 0 sized payload length will work create NULL string */
652   string = g_strndup ((gchar *) payload, GST_DP_HEADER_PAYLOAD_LENGTH (header));
653   caps = gst_caps_from_string (string);
654   g_free (string);
656   return caps;
659 static GstEvent *
660 gst_dp_event_from_packet_0_2 (guint header_length, const guint8 * header,
661     const guint8 * payload)
663   GstEvent *event = NULL;
664   GstEventType type;
666   type = GST_DP_HEADER_PAYLOAD_TYPE (header) - GST_DP_PAYLOAD_EVENT_NONE;
667   switch (type) {
668     case GST_EVENT_UNKNOWN:
669       GST_WARNING ("Unknown event, ignoring");
670       return NULL;
671     case GST_EVENT_EOS:
672     case GST_EVENT_FLUSH_START:
673     case GST_EVENT_FLUSH_STOP:
674     case GST_EVENT_NEWSEGMENT:
675       event = gst_event_new_custom (type, NULL);
676       GST_EVENT_TIMESTAMP (event) = GST_DP_HEADER_TIMESTAMP (header);
677       break;
678     case GST_EVENT_SEEK:
679     {
680       gdouble rate;
681       GstFormat format;
682       GstSeekFlags flags;
683       GstSeekType cur_type, stop_type;
684       gint64 cur, stop;
686       g_return_val_if_fail (payload != NULL, NULL);
688       /* FIXME, read rate */
689       rate = 1.0;
690       format = (GstFormat) GST_READ_UINT32_BE (payload);
691       flags = (GstSeekFlags) GST_READ_UINT32_BE (payload + 4);
692       cur_type = (GstSeekType) GST_READ_UINT32_BE (payload + 8);
693       cur = (gint64) GST_READ_UINT64_BE (payload + 12);
694       stop_type = (GstSeekType) GST_READ_UINT32_BE (payload + 20);
695       stop = (gint64) GST_READ_UINT64_BE (payload + 24);
697       event = gst_event_new_seek (rate, format, flags, cur_type, cur,
698           stop_type, stop);
699       GST_EVENT_TIMESTAMP (event) = GST_DP_HEADER_TIMESTAMP (header);
700       break;
701     }
702     case GST_EVENT_QOS:
703     case GST_EVENT_NAVIGATION:
704     case GST_EVENT_TAG:
705       GST_WARNING ("Unhandled event type %d, ignoring", type);
706       return NULL;
707     default:
708       GST_WARNING ("Unknown event type %d, ignoring", type);
709       return NULL;
710   }
712   return event;
715 static GstEvent *
716 gst_dp_event_from_packet_1_0 (guint header_length, const guint8 * header,
717     const guint8 * payload)
719   GstEvent *event = NULL;
720   GstEventType type;
721   gchar *string = NULL;
722   GstStructure *s = NULL;
724   type = GST_DP_HEADER_PAYLOAD_TYPE (header) - GST_DP_PAYLOAD_EVENT_NONE;
725   if (payload) {
726     string =
727         g_strndup ((gchar *) payload, GST_DP_HEADER_PAYLOAD_LENGTH (header));
728     s = gst_structure_from_string (string, NULL);
729     g_free (string);
730   }
731   event = gst_event_new_custom (type, s);
732   return event;
736 /**
737  * gst_dp_event_from_packet:
738  * @header_length: the length of the packet header
739  * @header: the byte array of the packet header
740  * @payload: the byte array of the packet payload
741  *
742  * Creates a newly allocated #GstEvent from the given packet.
743  *
744  * This function does not check the arguments passed to it, use
745  * gst_dp_validate_packet() first if the header and payload data are
746  * unchecked.
747  *
748  * Returns: A #GstEvent if the event was successfully created,
749  *          or NULL if an event could not be read from the payload.
750  */
751 GstEvent *
752 gst_dp_event_from_packet (guint header_length, const guint8 * header,
753     const guint8 * payload)
755   guint8 major, minor;
757   g_return_val_if_fail (header, NULL);
758   g_return_val_if_fail (header_length >= GST_DP_HEADER_LENGTH, NULL);
760   major = GST_DP_HEADER_MAJOR_VERSION (header);
761   minor = GST_DP_HEADER_MINOR_VERSION (header);
763   if (major == 0 && minor == 2)
764     return gst_dp_event_from_packet_0_2 (header_length, header, payload);
765   else if (major == 1 && minor == 0)
766     return gst_dp_event_from_packet_1_0 (header_length, header, payload);
767   else {
768     GST_ERROR ("Unknown GDP version %d.%d", major, minor);
769     return NULL;
770   }
773 /**
774  * gst_dp_validate_header:
775  * @header_length: the length of the packet header
776  * @header: the byte array of the packet header
777  *
778  * Validates the given packet header by checking the CRC checksum.
779  *
780  * Returns: %TRUE if the CRC matches, or no CRC checksum is present.
781  */
782 gboolean
783 gst_dp_validate_header (guint header_length, const guint8 * header)
785   guint16 crc_read, crc_calculated;
787   g_return_val_if_fail (header != NULL, FALSE);
788   g_return_val_if_fail (header_length >= GST_DP_HEADER_LENGTH, FALSE);
790   if (!(GST_DP_HEADER_FLAGS (header) & GST_DP_HEADER_FLAG_CRC_HEADER))
791     return TRUE;
793   crc_read = GST_DP_HEADER_CRC_HEADER (header);
795   /* don't include the last two crc fields for the crc check */
796   crc_calculated = gst_dp_crc (header, header_length - 4);
797   if (crc_read != crc_calculated)
798     goto crc_error;
800   GST_LOG ("header crc validation: %02x", crc_read);
801   return TRUE;
803   /* ERRORS */
804 crc_error:
805   {
806     GST_WARNING ("header crc mismatch: read %02x, calculated %02x", crc_read,
807         crc_calculated);
808     return FALSE;
809   }
812 /**
813  * gst_dp_validate_payload:
814  * @header_length: the length of the packet header
815  * @header: the byte array of the packet header
816  * @payload: the byte array of the packet payload
817  *
818  * Validates the given packet payload using the given packet header
819  * by checking the CRC checksum.
820  *
821  * Returns: %TRUE if the CRC matches, or no CRC checksum is present.
822  */
823 gboolean
824 gst_dp_validate_payload (guint header_length, const guint8 * header,
825     const guint8 * payload)
827   guint16 crc_read, crc_calculated;
829   g_return_val_if_fail (header != NULL, FALSE);
830   g_return_val_if_fail (header_length >= GST_DP_HEADER_LENGTH, FALSE);
832   if (!(GST_DP_HEADER_FLAGS (header) & GST_DP_HEADER_FLAG_CRC_PAYLOAD))
833     return TRUE;
835   crc_read = GST_DP_HEADER_CRC_PAYLOAD (header);
836   crc_calculated = gst_dp_crc (payload, GST_DP_HEADER_PAYLOAD_LENGTH (header));
837   if (crc_read != crc_calculated)
838     goto crc_error;
840   GST_LOG ("payload crc validation: %02x", crc_read);
841   return TRUE;
843   /* ERRORS */
844 crc_error:
845   {
846     GST_WARNING ("payload crc mismatch: read %02x, calculated %02x", crc_read,
847         crc_calculated);
848     return FALSE;
849   }
852 /**
853  * gst_dp_validate_packet:
854  * @header_length: the length of the packet header
855  * @header: the byte array of the packet header
856  * @payload: the byte array of the packet payload
857  *
858  * Validates the given packet by checking version information and checksums.
859  *
860  * Returns: %TRUE if the packet validates.
861  */
862 gboolean
863 gst_dp_validate_packet (guint header_length, const guint8 * header,
864     const guint8 * payload)
866   if (!gst_dp_validate_header (header_length, header))
867     return FALSE;
868   if (!gst_dp_validate_payload (header_length, header, payload))
869     return FALSE;
871   return TRUE;
874 /**
875  * gst_dp_packetizer_new:
876  * @version: the #GstDPVersion of the protocol to packetize for.
877  *
878  * Creates a new packetizer.
879  *
880  * Returns: a newly allocated #GstDPPacketizer
881  */
882 GstDPPacketizer *
883 gst_dp_packetizer_new (GstDPVersion version)
885   GstDPPacketizer *ret;
887   ret = g_malloc0 (sizeof (GstDPPacketizer));
888   ret->version = version;
890   switch (version) {
891 #ifndef GST_REMOVE_DEPRECATED
892     case GST_DP_VERSION_0_2:
893       ret->header_from_buffer = gst_dp_header_from_buffer;
894       ret->packet_from_caps = gst_dp_packet_from_caps;
895       ret->packet_from_event = gst_dp_packet_from_event;
896       break;
897 #endif
898     case GST_DP_VERSION_1_0:
899       ret->header_from_buffer = gst_dp_header_from_buffer_1_0;
900       ret->packet_from_caps = gst_dp_packet_from_caps_1_0;
901       ret->packet_from_event = gst_dp_packet_from_event_1_0;
902       break;
903     default:
904       g_free (ret);
905       ret = NULL;
906       break;
907   }
909   return ret;
912 /**
913  * gst_dp_packetizer_free:
914  * @packetizer: the #GstDPPacketizer to free.
915  *
916  * Free the given packetizer.
917  */
918 void
919 gst_dp_packetizer_free (GstDPPacketizer * packetizer)
921   g_free (packetizer);