fa07d0cab97a83d030faa9c14d7f27834219da97
[platform/upstream/gst-plugins-base.git] / gst / gdp / 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  */
22
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
65 #ifdef HAVE_CONFIG_H
66 #include "config.h"
67 #endif
68
69 #include <gst/gst.h>
70 #include <gst/dataprotocol/dataprotocol.h>
71 #include <glib/gprintf.h>       /* g_sprintf */
72 #include <string.h>             /* strlen */
73 #include "dp-private.h"
74
75 /* debug category */
76 GST_DEBUG_CATEGORY (data_protocol_debug);
77 #define GST_CAT_DEFAULT data_protocol_debug
78
79 /* helper macros */
80
81 /* write first 6 bytes of header, as well as ABI padding */
82 #define GST_DP_INIT_HEADER(h, version, flags, type)             \
83 G_STMT_START {                                                  \
84   gint maj = 0, min = 0;                                        \
85   switch (version) {                                            \
86     case GST_DP_VERSION_0_2: maj = 0; min = 2; break;           \
87     case GST_DP_VERSION_1_0: maj = 1; min = 0; break;           \
88   }                                                             \
89   h[0] = (guint8) maj;                                          \
90   h[1] = (guint8) min;                                          \
91   h[2] = (guint8) flags;                                        \
92   h[3] = 0; /* padding byte */                                  \
93   GST_WRITE_UINT16_BE (h + 4, type);                            \
94                                                                 \
95   GST_WRITE_UINT64_BE (h + 42, (guint64) 0); /* ABI padding */  \
96   GST_WRITE_UINT64_BE (h + 50, (guint64) 0); /* ABI padding */  \
97 } G_STMT_END
98
99 #define GST_DP_SET_CRC(h, flags, payload, length);              \
100 G_STMT_START {                                                  \
101   guint16 crc = 0;                                              \
102   if (flags & GST_DP_HEADER_FLAG_CRC_HEADER)                    \
103     /* we don't crc the last four bytes since they are crc's */ \
104     crc = gst_dp_crc (h, 58);                                   \
105   GST_WRITE_UINT16_BE (h + 58, crc);                            \
106                                                                 \
107   crc = 0;                                                      \
108   if (length && (flags & GST_DP_HEADER_FLAG_CRC_PAYLOAD))       \
109     crc = gst_dp_crc (payload, length);                         \
110   GST_WRITE_UINT16_BE (h + 60, crc);                            \
111 } G_STMT_END
112
113 /* calculate a CCITT 16 bit CRC check value for a given byte array */
114 /*
115  * this code snippet is adapted from a web page I found
116  * it is identical except for cleanups, and a final XOR with 0xffff
117  * as outlined in the uecp spec
118  *
119  * XMODEM    x^16 + x^12 + x^5 + 1
120  */
121
122 #define POLY       0x1021
123 #define CRC_INIT   0xFFFF
124
125 /*** HELPER FUNCTIONS ***/
126
127 static gboolean
128 gst_dp_header_from_buffer_any (const GstBuffer * buffer, GstDPHeaderFlag flags,
129     guint * length, guint8 ** header, GstDPVersion version)
130 {
131   guint8 *h;
132   guint16 flags_mask;
133
134   g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
135   g_return_val_if_fail (header, FALSE);
136
137   *length = GST_DP_HEADER_LENGTH;
138   h = g_malloc0 (GST_DP_HEADER_LENGTH);
139
140   /* version, flags, type */
141   GST_DP_INIT_HEADER (h, version, flags, GST_DP_PAYLOAD_BUFFER);
142
143   /* buffer properties */
144   GST_WRITE_UINT32_BE (h + 6, GST_BUFFER_SIZE (buffer));
145   GST_WRITE_UINT64_BE (h + 10, GST_BUFFER_TIMESTAMP (buffer));
146   GST_WRITE_UINT64_BE (h + 18, GST_BUFFER_DURATION (buffer));
147   GST_WRITE_UINT64_BE (h + 26, GST_BUFFER_OFFSET (buffer));
148   GST_WRITE_UINT64_BE (h + 34, GST_BUFFER_OFFSET_END (buffer));
149
150   /* data flags; eats two bytes from the ABI area */
151   /* we copy everything but the read-only flags */
152   flags_mask = GST_BUFFER_FLAG_PREROLL | GST_BUFFER_FLAG_DISCONT |
153       GST_BUFFER_FLAG_IN_CAPS | GST_BUFFER_FLAG_GAP |
154       GST_BUFFER_FLAG_DELTA_UNIT;
155
156   GST_WRITE_UINT16_BE (h + 42, GST_BUFFER_FLAGS (buffer) & flags_mask);
157
158   GST_DP_SET_CRC (h, flags, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
159
160   GST_LOG ("created header from buffer:");
161   gst_dp_dump_byte_array (h, GST_DP_HEADER_LENGTH);
162   *header = h;
163   return TRUE;
164 }
165
166 static gboolean
167 gst_dp_packet_from_caps_any (const GstCaps * caps, GstDPHeaderFlag flags,
168     guint * length, guint8 ** header, guint8 ** payload, GstDPVersion version)
169 {
170   guint8 *h;
171   guchar *string;
172   guint payload_length;
173
174   /* FIXME: GST_IS_CAPS doesn't work
175      g_return_val_if_fail (GST_IS_CAPS (caps), FALSE); */
176   g_return_val_if_fail (caps, FALSE);
177   g_return_val_if_fail (header, FALSE);
178   g_return_val_if_fail (payload, FALSE);
179
180   *length = GST_DP_HEADER_LENGTH;
181   h = g_malloc0 (GST_DP_HEADER_LENGTH);
182
183   string = (guchar *) gst_caps_to_string (caps);
184   payload_length = strlen ((gchar *) string) + 1;       /* include trailing 0 */
185
186   /* version, flags, type */
187   GST_DP_INIT_HEADER (h, version, flags, GST_DP_PAYLOAD_CAPS);
188
189   /* buffer properties */
190   GST_WRITE_UINT32_BE (h + 6, payload_length);
191   GST_WRITE_UINT64_BE (h + 10, (guint64) 0);
192   GST_WRITE_UINT64_BE (h + 18, (guint64) 0);
193   GST_WRITE_UINT64_BE (h + 26, (guint64) 0);
194   GST_WRITE_UINT64_BE (h + 34, (guint64) 0);
195
196   GST_DP_SET_CRC (h, flags, string, payload_length);
197
198   GST_LOG ("created header from caps:");
199   gst_dp_dump_byte_array (h, GST_DP_HEADER_LENGTH);
200   *header = h;
201   *payload = string;
202   return TRUE;
203 }
204
205
206 /*** PUBLIC FUNCTIONS ***/
207
208 /**
209  * gst_dp_crc:
210  *
211  * Calculate a CRC for the given buffer over the given number of bytes.
212  * This is only provided for verification purposes; typical GDP users
213  * will not need this function.
214  *
215  * Returns: a two-byte CRC checksum.
216  */
217 guint16
218 gst_dp_crc (const guint8 * buffer, guint length)
219 {
220   static gboolean initialized = FALSE;
221   static guint16 crc_table[256];
222   guint16 crc_register = CRC_INIT;
223   unsigned long i, j, k;
224
225   if (!initialized) {
226     for (i = 0; i < 256; i++) {
227       j = i << 8;
228       for (k = 8; k--;) {
229         j = j & 0x8000 ? (j << 1) ^ POLY : j << 1;
230       }
231
232       crc_table[i] = (guint16) j;
233     }
234     initialized = TRUE;
235   }
236
237   /* calc CRC */
238   for (; length--;) {
239     crc_register = (guint16) ((crc_register << 8) ^
240         crc_table[((crc_register >> 8) & 0x00ff) ^ *buffer++]);
241   }
242   return (0xffff ^ crc_register);
243 }
244
245 /* debugging function; dumps byte array values per 8 bytes */
246 /* FIXME: would be nice to merge this with gst_util_dump_mem () */
247 void
248 gst_dp_dump_byte_array (guint8 * array, guint length)
249 {
250   int i;
251   int n = 8;                    /* number of bytes per line */
252   gchar *line = g_malloc0 (3 * n + 1);
253
254   GST_LOG ("dumping byte array of length %d", length);
255   for (i = 0; i < length; ++i) {
256     g_sprintf (line + 3 * (i % n), "%02x ", array[i]);
257     if (i % n == (n - 1)) {
258       GST_LOG ("%03d: %s", i - (n - 1), line);
259     }
260   }
261   if (i % n != 0) {
262     GST_LOG ("%03d: %s", (i / n) * n, line);
263   }
264   g_free (line);
265 }
266
267 GType
268 gst_dp_version_get_type (void)
269 {
270   static GType gst_dp_version_type = 0;
271   static const GEnumValue gst_dp_version[] = {
272     {GST_DP_VERSION_0_2, "GDP Version 0.2", "0.2"},
273     {GST_DP_VERSION_1_0, "GDP Version 1.0", "1.0"},
274     {0, NULL, NULL},
275   };
276
277   if (!gst_dp_version_type) {
278     gst_dp_version_type =
279         g_enum_register_static ("GstDPVersion", gst_dp_version);
280   }
281   return gst_dp_version_type;
282 };
283
284 /**
285  * gst_dp_init:
286  *
287  * Initialize GStreamer Data Protocol library.
288  *
289  * Should be called before using these functions from source linking
290  * to this source file.
291  */
292 void
293 gst_dp_init (void)
294 {
295   static gboolean _gst_dp_initialized = FALSE;
296
297   if (_gst_dp_initialized)
298     return;
299
300   _gst_dp_initialized = TRUE;
301
302   gst_dp_version_get_type ();
303
304   GST_DEBUG_CATEGORY_INIT (data_protocol_debug, "gdp", 0,
305       "GStreamer Data Protocol");
306 }
307
308 /**
309  * gst_dp_header_payload_length:
310  * @header: the byte header of the packet array
311  *
312  * Get the length of the payload described by @header.
313  *
314  * Returns: the length of the payload this header describes.
315  */
316 guint32
317 gst_dp_header_payload_length (const guint8 * header)
318 {
319   return GST_DP_HEADER_PAYLOAD_LENGTH (header);
320 }
321
322 /**
323  * gst_dp_header_payload_type:
324  * @header: the byte header of the packet array
325  *
326  * Get the type of the payload described by @header.
327  *
328  * Returns: the #GstDPPayloadType the payload this header describes.
329  */
330 GstDPPayloadType
331 gst_dp_header_payload_type (const guint8 * header)
332 {
333   return GST_DP_HEADER_PAYLOAD_TYPE (header);
334 }
335
336 /*** PACKETIZER FUNCTIONS ***/
337
338 /**
339  * gst_dp_header_from_buffer:
340  * @buffer: a #GstBuffer to create a header for
341  * @flags: the #GDPHeaderFlags to create the header with
342  * @length: a guint pointer to store the header length in
343  * @header: a guint8 * pointer to store a newly allocated header byte array in
344  *
345  * Creates a GDP header from the given buffer.
346  *
347  * Deprecated: use a #GstDPPacketizer
348  *
349  * Returns: %TRUE if the header was successfully created.
350  */
351 gboolean
352 gst_dp_header_from_buffer (const GstBuffer * buffer, GstDPHeaderFlag flags,
353     guint * length, guint8 ** header)
354 {
355   return gst_dp_header_from_buffer_any (buffer, flags, length, header,
356       GST_DP_VERSION_0_2);
357 }
358
359 static gboolean
360 gst_dp_header_from_buffer_1_0 (const GstBuffer * buffer, GstDPHeaderFlag flags,
361     guint * length, guint8 ** header)
362 {
363   return gst_dp_header_from_buffer_any (buffer, flags, length, header,
364       GST_DP_VERSION_1_0);
365 }
366
367  /**
368  * gst_dp_packet_from_caps:
369  * @caps: a #GstCaps to create a packet for
370  * @flags: the #GDPHeaderFlags to create the header with
371  * @length: a guint pointer to store the header length in
372  * @header: a guint8 pointer to store a newly allocated header byte array in
373  * @payload: a guint8 pointer to store a newly allocated payload byte array in
374  *
375  * Creates a GDP packet from the given caps.
376  *
377  * Deprecated: use a #GstDPPacketizer
378  *
379  * Returns: %TRUE if the packet was successfully created.
380  */
381 gboolean
382 gst_dp_packet_from_caps (const GstCaps * caps, GstDPHeaderFlag flags,
383     guint * length, guint8 ** header, guint8 ** payload)
384 {
385   return gst_dp_packet_from_caps_any (caps, flags, length, header, payload,
386       GST_DP_VERSION_0_2);
387 }
388
389 gboolean
390 gst_dp_packet_from_caps_1_0 (const GstCaps * caps, GstDPHeaderFlag flags,
391     guint * length, guint8 ** header, guint8 ** payload)
392 {
393   return gst_dp_packet_from_caps_any (caps, flags, length, header, payload,
394       GST_DP_VERSION_1_0);
395 }
396
397 /**
398  * gst_dp_packet_from_event:
399  * @event: a #GstEvent to create a packet for
400  * @flags: the #GDPHeaderFlags to create the header with
401  * @length: a guint pointer to store the header length in
402  * @header: a guint8 pointer to store a newly allocated header byte array in
403  * @payload: a guint8 pointer to store a newly allocated payload byte array in
404  *
405  * Creates a GDP packet from the given event.
406  *
407  * Deprecated: use a #GstDPPacketizer
408  *
409  * Returns: %TRUE if the packet was successfully created.
410  */
411 gboolean
412 gst_dp_packet_from_event (const GstEvent * event, GstDPHeaderFlag flags,
413     guint * length, guint8 ** header, guint8 ** payload)
414 {
415   guint8 *h;
416   guint pl_length;              /* length of payload */
417
418   g_return_val_if_fail (event, FALSE);
419   g_return_val_if_fail (GST_IS_EVENT (event), FALSE);
420   g_return_val_if_fail (header, FALSE);
421   g_return_val_if_fail (payload, FALSE);
422
423   *length = GST_DP_HEADER_LENGTH;
424   h = g_malloc0 (GST_DP_HEADER_LENGTH);
425
426   /* first construct payload, since we need the length */
427   switch (GST_EVENT_TYPE (event)) {
428     case GST_EVENT_UNKNOWN:
429       GST_WARNING ("Unknown event, ignoring");
430       *length = 0;
431       g_free (h);
432       return FALSE;
433     case GST_EVENT_EOS:
434     case GST_EVENT_FLUSH_START:
435     case GST_EVENT_FLUSH_STOP:
436     case GST_EVENT_NEWSEGMENT:
437       pl_length = 0;
438       *payload = NULL;
439       break;
440     case GST_EVENT_SEEK:
441     {
442       gdouble rate;
443       GstFormat format;
444       GstSeekFlags flags;
445       GstSeekType cur_type, stop_type;
446       gint64 cur, stop;
447
448       gst_event_parse_seek ((GstEvent *) event, &rate, &format, &flags,
449           &cur_type, &cur, &stop_type, &stop);
450
451       pl_length = 4 + 4 + 4 + 8 + 4 + 8;
452       *payload = g_malloc0 (pl_length);
453       /* FIXME write rate */
454       GST_WRITE_UINT32_BE (*payload, (guint32) format);
455       GST_WRITE_UINT32_BE (*payload + 4, (guint32) flags);
456       GST_WRITE_UINT32_BE (*payload + 8, (guint32) cur_type);
457       GST_WRITE_UINT64_BE (*payload + 12, (guint64) cur);
458       GST_WRITE_UINT32_BE (*payload + 20, (guint32) stop_type);
459       GST_WRITE_UINT64_BE (*payload + 24, (guint64) stop);
460       break;
461     }
462     case GST_EVENT_QOS:
463     case GST_EVENT_NAVIGATION:
464     case GST_EVENT_TAG:
465       GST_WARNING ("Unhandled event type %d, ignoring", GST_EVENT_TYPE (event));
466       *length = 0;
467       g_free (h);
468       return FALSE;
469     default:
470       GST_WARNING ("Unknown event type %d, ignoring", GST_EVENT_TYPE (event));
471       *length = 0;
472       g_free (h);
473       return FALSE;
474   }
475
476   /* version, flags, type */
477   GST_DP_INIT_HEADER (h, GST_DP_VERSION_0_2, flags,
478       GST_DP_PAYLOAD_EVENT_NONE + GST_EVENT_TYPE (event));
479
480   /* length */
481   GST_WRITE_UINT32_BE (h + 6, (guint32) pl_length);
482   /* timestamp */
483   GST_WRITE_UINT64_BE (h + 10, GST_EVENT_TIMESTAMP (event));
484
485   GST_DP_SET_CRC (h, flags, *payload, pl_length);
486
487   GST_LOG ("created header from event:");
488   gst_dp_dump_byte_array (h, GST_DP_HEADER_LENGTH);
489   *header = h;
490   return TRUE;
491 }
492
493 static gboolean
494 gst_dp_packet_from_event_1_0 (const GstEvent * event, GstDPHeaderFlag flags,
495     guint * length, guint8 ** header, guint8 ** payload)
496 {
497   guint8 *h;
498   guint32 pl_length;            /* length of payload */
499   guchar *string = NULL;
500
501   g_return_val_if_fail (event, FALSE);
502   g_return_val_if_fail (GST_IS_EVENT (event), FALSE);
503   g_return_val_if_fail (header, FALSE);
504   g_return_val_if_fail (payload, FALSE);
505
506   *length = GST_DP_HEADER_LENGTH;
507   h = g_malloc0 (GST_DP_HEADER_LENGTH);
508
509   if (event->structure) {
510     string = (guchar *) gst_structure_to_string (event->structure);
511     GST_LOG ("event %p has structure, string %s", event, string);
512     pl_length = strlen ((gchar *) string) + 1;  /* include trailing 0 */
513   } else {
514     GST_LOG ("event %p has no structure");
515     pl_length = 0;
516   }
517
518   /* version, flags, type */
519   GST_DP_INIT_HEADER (h, GST_DP_VERSION_1_0, flags,
520       GST_DP_PAYLOAD_EVENT_NONE + GST_EVENT_TYPE (event));
521
522   /* length */
523   GST_WRITE_UINT32_BE (h + 6, pl_length);
524   /* timestamp */
525   GST_WRITE_UINT64_BE (h + 10, GST_EVENT_TIMESTAMP (event));
526
527   GST_DP_SET_CRC (h, flags, *payload, pl_length);
528
529   GST_LOG ("created header from event:");
530   gst_dp_dump_byte_array (h, GST_DP_HEADER_LENGTH);
531   *header = h;
532   *payload = string;
533   return TRUE;
534 }
535
536 /*** DEPACKETIZING FUNCTIONS ***/
537
538 /**
539  * gst_dp_buffer_from_header:
540  * @header_length: the length of the packet header
541  * @header: the byte array of the packet header
542  *
543  * Creates a newly allocated #GstBuffer from the given header.
544  * The buffer data needs to be copied into it before validating.
545  *
546  * Use this function if you want to pre-allocate a buffer based on the
547  * packet header to read the packet payload in to.
548  *
549  * Returns: A #GstBuffer if the buffer was successfully created, or NULL.
550  */
551 GstBuffer *
552 gst_dp_buffer_from_header (guint header_length, const guint8 * header)
553 {
554   GstBuffer *buffer;
555
556   g_return_val_if_fail (GST_DP_HEADER_PAYLOAD_TYPE (header) ==
557       GST_DP_PAYLOAD_BUFFER, NULL);
558   buffer =
559       gst_buffer_new_and_alloc ((guint) GST_DP_HEADER_PAYLOAD_LENGTH (header));
560   GST_BUFFER_TIMESTAMP (buffer) = GST_DP_HEADER_TIMESTAMP (header);
561   GST_BUFFER_DURATION (buffer) = GST_DP_HEADER_DURATION (header);
562   GST_BUFFER_OFFSET (buffer) = GST_DP_HEADER_OFFSET (header);
563   GST_BUFFER_OFFSET_END (buffer) = GST_DP_HEADER_OFFSET_END (header);
564   GST_BUFFER_FLAGS (buffer) = GST_DP_HEADER_BUFFER_FLAGS (header);
565
566   return buffer;
567 }
568
569 /**
570  * gst_dp_caps_from_packet:
571  * @header_length: the length of the packet header
572  * @header: the byte array of the packet header
573  * @payload: the byte array of the packet payload
574  *
575  * Creates a newly allocated #GstCaps from the given packet.
576  *
577  * Returns: A #GstCaps containing the caps represented in the packet,
578  *          or NULL if the packet could not be converted.
579  */
580 GstCaps *
581 gst_dp_caps_from_packet (guint header_length, const guint8 * header,
582     const guint8 * payload)
583 {
584   GstCaps *caps;
585   gchar *string;
586
587   g_return_val_if_fail (header, NULL);
588   g_return_val_if_fail (payload, NULL);
589   g_return_val_if_fail (GST_DP_HEADER_PAYLOAD_TYPE (header) ==
590       GST_DP_PAYLOAD_CAPS, NULL);
591
592   string = g_strndup ((gchar *) payload, GST_DP_HEADER_PAYLOAD_LENGTH (header));
593   caps = gst_caps_from_string (string);
594   g_free (string);
595   return caps;
596 }
597
598 static GstEvent *
599 gst_dp_event_from_packet_0_2 (guint header_length, const guint8 * header,
600     const guint8 * payload)
601 {
602   GstEvent *event = NULL;
603   GstEventType type;
604
605   type = GST_DP_HEADER_PAYLOAD_TYPE (header) - GST_DP_PAYLOAD_EVENT_NONE;
606   switch (type) {
607     case GST_EVENT_UNKNOWN:
608       GST_WARNING ("Unknown event, ignoring");
609       return FALSE;
610     case GST_EVENT_EOS:
611     case GST_EVENT_FLUSH_START:
612     case GST_EVENT_FLUSH_STOP:
613     case GST_EVENT_NEWSEGMENT:
614       event = gst_event_new_custom (type, NULL);
615       GST_EVENT_TIMESTAMP (event) = GST_DP_HEADER_TIMESTAMP (header);
616       break;
617     case GST_EVENT_SEEK:
618     {
619       gdouble rate;
620       GstFormat format;
621       GstSeekFlags flags;
622       GstSeekType cur_type, stop_type;
623       gint64 cur, stop;
624
625       /* FIXME, read rate */
626       rate = 1.0;
627       format = (GstFormat) GST_READ_UINT32_BE (payload);
628       flags = (GstSeekFlags) GST_READ_UINT32_BE (payload + 4);
629       cur_type = (GstSeekType) GST_READ_UINT32_BE (payload + 8);
630       cur = (gint64) GST_READ_UINT64_BE (payload + 12);
631       stop_type = (GstSeekType) GST_READ_UINT32_BE (payload + 20);
632       stop = (gint64) GST_READ_UINT64_BE (payload + 24);
633
634       event = gst_event_new_seek (rate, format, flags, cur_type, cur,
635           stop_type, stop);
636       GST_EVENT_TIMESTAMP (event) = GST_DP_HEADER_TIMESTAMP (header);
637       break;
638     }
639     case GST_EVENT_QOS:
640     case GST_EVENT_NAVIGATION:
641     case GST_EVENT_TAG:
642       GST_WARNING ("Unhandled event type %d, ignoring", type);
643       return FALSE;
644     default:
645       GST_WARNING ("Unknown event type %d, ignoring", type);
646       return FALSE;
647   }
648
649   return event;
650 }
651
652 static GstEvent *
653 gst_dp_event_from_packet_1_0 (guint header_length, const guint8 * header,
654     const guint8 * payload)
655 {
656   GstEvent *event = NULL;
657   GstEventType type;
658   gchar *string;
659   GstStructure *s;
660
661   type = GST_DP_HEADER_PAYLOAD_TYPE (header) - GST_DP_PAYLOAD_EVENT_NONE;
662   string = g_strndup ((gchar *) payload, GST_DP_HEADER_PAYLOAD_LENGTH (header));
663   s = gst_structure_from_string (string, NULL);
664   g_free (string);
665   if (!s)
666     return NULL;
667   event = gst_event_new_custom (type, s);
668   return event;
669 }
670
671
672 /**
673  * gst_dp_event_from_packet:
674  * @header_length: the length of the packet header
675  * @header: the byte array of the packet header
676  * @payload: the byte array of the packet payload
677  *
678  * Creates a newly allocated #GstEvent from the given packet.
679  *
680  * Returns: A #GstEvent if the event was successfully created,
681  *          or NULL if an event could not be read from the payload.
682  */
683 GstEvent *
684 gst_dp_event_from_packet (guint header_length, const guint8 * header,
685     const guint8 * payload)
686 {
687   guint8 major, minor;
688
689   g_return_val_if_fail (header, NULL);
690
691   major = GST_DP_HEADER_MAJOR_VERSION (header);
692   minor = GST_DP_HEADER_MINOR_VERSION (header);
693
694   if (major == 0 && minor == 2)
695     return gst_dp_event_from_packet_0_2 (header_length, header, payload);
696   else if (major == 1 && minor == 0)
697     return gst_dp_event_from_packet_1_0 (header_length, header, payload);
698   else {
699     GST_ERROR ("Unknown GDP version %d.%d", major, minor);
700     return NULL;
701   }
702 }
703
704 /**
705  * gst_dp_validate_header:
706  * @header_length: the length of the packet header
707  * @header: the byte array of the packet header
708  *
709  * Validates the given packet header by checking the CRC checksum.
710  *
711  * Returns: %TRUE if the CRC matches, or no CRC checksum is present.
712  */
713 gboolean
714 gst_dp_validate_header (guint header_length, const guint8 * header)
715 {
716   guint16 crc_read, crc_calculated;
717
718   if (!(GST_DP_HEADER_FLAGS (header) & GST_DP_HEADER_FLAG_CRC_HEADER))
719     return TRUE;
720   crc_read = GST_DP_HEADER_CRC_HEADER (header);
721   /* don't include the last two crc fields for the crc check */
722   crc_calculated = gst_dp_crc (header, header_length - 4);
723   if (crc_read != crc_calculated) {
724     GST_WARNING ("header crc mismatch: read %02x, calculated %02x", crc_read,
725         crc_calculated);
726     return FALSE;
727   }
728   GST_LOG ("header crc validation: %02x", crc_read);
729   return TRUE;
730 }
731
732 /**
733  * gst_dp_validate_payload:
734  * @header_length: the length of the packet header
735  * @header: the byte array of the packet header
736  * @payload: the byte array of the packet payload
737  *
738  * Validates the given packet payload using the given packet header
739  * by checking the CRC checksum.
740  *
741  * Returns: %TRUE if the CRC matches, or no CRC checksum is present.
742  */
743 gboolean
744 gst_dp_validate_payload (guint header_length, const guint8 * header,
745     const guint8 * payload)
746 {
747   guint16 crc_read, crc_calculated;
748
749   if (!(GST_DP_HEADER_FLAGS (header) & GST_DP_HEADER_FLAG_CRC_PAYLOAD))
750     return TRUE;
751   crc_read = GST_DP_HEADER_CRC_PAYLOAD (header);
752   crc_calculated = gst_dp_crc (payload, GST_DP_HEADER_PAYLOAD_LENGTH (header));
753   if (crc_read != crc_calculated) {
754     GST_WARNING ("payload crc mismatch: read %02x, calculated %02x", crc_read,
755         crc_calculated);
756     return FALSE;
757   }
758   GST_LOG ("payload crc validation: %02x", crc_read);
759   return TRUE;
760 }
761
762 /**
763  * gst_dp_validate_packet:
764  * @header_length: the length of the packet header
765  * @header: the byte array of the packet header
766  * @payload: the byte array of the packet payload
767  *
768  * Validates the given packet by checking version information and checksums.
769  *
770  * Returns: %TRUE if the packet validates.
771  */
772 gboolean
773 gst_dp_validate_packet (guint header_length, const guint8 * header,
774     const guint8 * payload)
775 {
776   if (!gst_dp_validate_header (header_length, header))
777     return FALSE;
778   if (!gst_dp_validate_payload (header_length, header, payload))
779     return FALSE;
780
781   return TRUE;
782 }
783
784 /**
785  * gst_dp_packetizer_new:
786  * @version: the #GstDPVersion of the protocol to packetize for.
787  *
788  * Creates a new packetizer.
789  *
790  * Returns: a newly allocated #GstDPPacketizer
791  */
792 GstDPPacketizer *
793 gst_dp_packetizer_new (GstDPVersion version)
794 {
795   GstDPPacketizer *ret;
796
797   ret = g_malloc0 (sizeof (GstDPPacketizer));
798   ret->version = version;
799
800   switch (version) {
801     case GST_DP_VERSION_0_2:
802       ret->header_from_buffer = gst_dp_header_from_buffer;
803       ret->packet_from_caps = gst_dp_packet_from_caps;
804       ret->packet_from_event = gst_dp_packet_from_event;
805       break;
806     case GST_DP_VERSION_1_0:
807       ret->header_from_buffer = gst_dp_header_from_buffer_1_0;
808       ret->packet_from_caps = gst_dp_packet_from_caps_1_0;
809       ret->packet_from_event = gst_dp_packet_from_event_1_0;
810       break;
811     default:
812       g_free (ret);
813       ret = NULL;
814       break;
815   }
816
817   return ret;
818 }
819
820 /**
821  * gst_dp_packetizer_free:
822  * @packetizer: the #GstDPPacketizer to free.
823  *
824  * Free the given packetizer.
825  */
826 void
827 gst_dp_packetizer_free (GstDPPacketizer * packetizer)
828 {
829   g_free (packetizer);
830 }