diff options
| -rw-r--r-- | ChangeLog | 13 | ||||
| -rw-r--r-- | gst/rtp/gstrtptheoradepay.c | 169 | ||||
| -rw-r--r-- | gst/rtp/gstrtptheorapay.c | 148 | ||||
| -rw-r--r-- | gst/rtp/gstrtpvorbisdepay.c | 20 | 
4 files changed, 249 insertions, 101 deletions
@@ -1,5 +1,18 @@  2007-05-14  Wim Taymans  <wim@fluendo.com> +	* gst/rtp/gstrtptheoradepay.c: (decode_base64), +	(gst_rtp_theora_depay_parse_configuration): +	* gst/rtp/gstrtptheorapay.c: (encode_base64), +	(gst_rtp_theora_pay_finish_headers), +	(gst_rtp_theora_pay_handle_buffer): +	Update theora pay/depayloader in a similar to vorbis. + +	* gst/rtp/gstrtpvorbisdepay.c: +	(gst_rtp_vorbis_depay_parse_configuration): +	Update docs. + +2007-05-14  Wim Taymans  <wim@fluendo.com> +  	* gst/rtsp/gstrtspsrc.c: (gst_rtspsrc_send):  	When we try to execute a method that is not supported by the server,  	don't error out but remove the method from the accepted methods so that diff --git a/gst/rtp/gstrtptheoradepay.c b/gst/rtp/gstrtptheoradepay.c index 47ab3826..a8a642e6 100644 --- a/gst/rtp/gstrtptheoradepay.c +++ b/gst/rtp/gstrtptheoradepay.c @@ -59,7 +59,9 @@ GST_STATIC_PAD_TEMPLATE ("sink",          "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"THEORA\""          /* All required parameters            * -         * "encoding-params = (string) <num channels>" +         * "sampling = (string) { "YCbCr-4:2:0", "YCbCr-4:2:2", "YCbCr-4:4:4" } " +         * "width = (string) [1, 1048561] (multiples of 16) " +         * "height = (string) [1, 1048561] (multiples of 16) "           * "delivery-method = (string) { inline, in_band, out_band/<specific_name> } "            * "configuration = (string) ANY"            */ @@ -77,9 +79,6 @@ GST_STATIC_PAD_TEMPLATE ("src",      GST_STATIC_CAPS ("video/x-theora")      ); -/* 42 bytes for the theora identification packet length */ -#define THEORA_ID_LEN	42 -  GST_BOILERPLATE (GstRtpTheoraDepay, gst_rtp_theora_depay, GstBaseRTPDepayload,      GST_TYPE_BASE_RTP_DEPAYLOAD); @@ -151,28 +150,70 @@ gst_rtp_theora_depay_finalize (GObject * object)    G_OBJECT_CLASS (parent_class)->finalize (object);  } +static const guint8 a2bin[256] = { +  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, +  52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, +  64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, +  15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, +  64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, +  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, +  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 +}; + +static guint +decode_base64 (const gchar * in, guint8 * out) +{ +  guint8 v1, v2; +  guint len = 0; + +  v1 = a2bin[(gint) * in]; +  while (v1 <= 63) { +    /* read 4 bytes, write 3 bytes, invalid base64 are zeroes */ +    v2 = a2bin[(gint) * ++in]; +    *out++ = (v1 << 2) | ((v2 & 0x3f) >> 4); +    v1 = (v2 > 63 ? 64 : a2bin[(gint) * ++in]); +    *out++ = (v2 << 4) | ((v1 & 0x3f) >> 2); +    v2 = (v1 > 63 ? 64 : a2bin[(gint) * ++in]); +    *out++ = (v1 << 6) | (v2 & 0x3f); +    v1 = (v2 > 63 ? 64 : a2bin[(gint) * ++in]); +    len += 3; +  } +  /* move to '\0' */ +  while (*in != '\0') +    in++; + +  /* subtract padding */ +  while (len > 0 && *--in == '=') +    len--; + +  return len; +} +  static gboolean  gst_rtp_theora_depay_parse_configuration (GstRtpTheoraDepay * rtptheoradepay,      const gchar * configuration)  { -  GValue v = { 0 };    GstBuffer *buf;    guint32 num_headers;    guint8 *data;    guint size; -  gint i; - -  /* deserialize base16 to buffer */ -  g_value_init (&v, GST_TYPE_BUFFER); -  if (!gst_value_deserialize (&v, configuration)) -    goto wrong_configuration; +  gint i, j; -  buf = gst_value_get_buffer (&v); -  gst_buffer_ref (buf); -  g_value_unset (&v); +  /* deserialize base64 to buffer */ +  size = strlen (configuration); +  GST_DEBUG_OBJECT (rtptheoradepay, "base64 config size %u", size); -  data = GST_BUFFER_DATA (buf); -  size = GST_BUFFER_SIZE (buf); +  data = g_malloc (size); +  size = decode_base64 (configuration, data);    GST_DEBUG_OBJECT (rtptheoradepay, "config size %u", size); @@ -201,74 +242,96 @@ gst_rtp_theora_depay_parse_configuration (GstRtpTheoraDepay * rtptheoradepay,    /*  0                   1                   2                   3     *  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     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -   * |                   Ident                       |              .. +   * |                   Ident                       | length       .. +   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +   * ..              | n. of headers |    length1    |    length2   .. +   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +   * ..              |             Identification Header            .. +   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +   * .................................................................     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -   * ..   length     |              Identification Header           .. +   * ..              |         Comment Header                       ..     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -   * ..                    Identification Header                     | +   * ................................................................. +   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +   * ..                        Comment Header                        |     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+     * |                          Setup Header                        ..     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +   * ................................................................. +   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+     * ..                         Setup Header                         |     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -   *     */    for (i = 0; i < num_headers; i++) {      guint32 ident;      guint16 length; +    guint8 n_headers, b;      GstRtpTheoraConfig *conf; -    GstTagList *list; +    guint *h_sizes; -    if (size < 5) +    if (size < 6)        goto too_small;      ident = (data[0] << 16) | (data[1] << 8) | data[2];      length = (data[3] << 8) | data[4]; -    size -= 5; -    data += 5; +    n_headers = data[5]; +    size -= 6; +    data += 6; -    GST_DEBUG_OBJECT (rtptheoradepay, "header %d, ident %08x, length %u", i, -        ident, length); +    GST_DEBUG_OBJECT (rtptheoradepay, +        "header %d, ident 0x%08x, length %u, left %u", i, ident, length, size); -    if (size < length + THEORA_ID_LEN) +    if (size < length)        goto too_small; -    GST_DEBUG_OBJECT (rtptheoradepay, "preparing headers"); +    /* read header sizes we read 2 sizes, the third size (for which we allocate +     * space) must be derived from the total packed header length. */ +    h_sizes = g_newa (guint, n_headers + 1); +    for (j = 0; j < n_headers; j++) { +      guint h_size; + +      h_size = 0; +      do { +        if (size < 1) +          goto too_small; +        b = *data++; +        size--; +        h_size = (h_size << 7) | (b & 0x7f); +      } while (b & 0x80); +      GST_DEBUG_OBJECT (rtptheoradepay, "headers %d: size: %u", j, h_size); +      h_sizes[j] = h_size; +      length -= h_size; +    } +    /* last header length is the remaining space */ +    GST_DEBUG_OBJECT (rtptheoradepay, "last header size: %u", length); +    h_sizes[j] = length; +    GST_DEBUG_OBJECT (rtptheoradepay, "preparing headers");      conf = g_new0 (GstRtpTheoraConfig, 1);      conf->ident = ident; -    buf = gst_buffer_new_and_alloc (THEORA_ID_LEN); -    memcpy (GST_BUFFER_DATA (buf), data, THEORA_ID_LEN); -    conf->headers = g_list_append (conf->headers, buf); -    data += THEORA_ID_LEN; -    size -= THEORA_ID_LEN; - -    /* create a dummy comment */ -    list = gst_tag_list_new (); -    buf = -        gst_tag_list_to_vorbiscomment_buffer (list, (guint8 *) "\201theora", 7, -        "Theora RTP depayloader"); -    conf->headers = g_list_append (conf->headers, buf); -    gst_tag_list_free (list); - -    buf = gst_buffer_new_and_alloc (length); -    memcpy (GST_BUFFER_DATA (buf), data, length); -    conf->headers = g_list_append (conf->headers, buf); -    data += length; -    size -= length; +    for (j = 0; j <= n_headers; j++) { +      guint h_size; + +      h_size = h_sizes[j]; +      if (size < h_size) +        goto too_small; +      GST_DEBUG_OBJECT (rtptheoradepay, "reading header %d, size %u", j, +          h_size); + +      buf = gst_buffer_new_and_alloc (h_size); +      memcpy (GST_BUFFER_DATA (buf), data, h_size); +      conf->headers = g_list_append (conf->headers, buf); +      data += h_size; +      size -= h_size; +    }      rtptheoradepay->configs = g_list_append (rtptheoradepay->configs, conf);    } -    return TRUE;    /* ERRORS */ -wrong_configuration: -  { -    GST_DEBUG_OBJECT (rtptheoradepay, "error parsing configuration"); -    return FALSE; -  }  too_small:    {      GST_DEBUG_OBJECT (rtptheoradepay, "configuration too small"); diff --git a/gst/rtp/gstrtptheorapay.c b/gst/rtp/gstrtptheorapay.c index 418b9f0c..c8f907e6 100644 --- a/gst/rtp/gstrtptheorapay.c +++ b/gst/rtp/gstrtptheorapay.c @@ -212,31 +212,46 @@ gst_rtp_theora_pay_flush_packet (GstRtpTheoraPay * rtptheorapay)    return ret;  } +static gchar * +encode_base64 (const guint8 * in, guint size, guint * len) +{ +  gchar *ret, *d; +  static const gchar *v = +      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +  *len = ((size + 2) / 3) * 4; +  d = ret = (gchar *) g_malloc (*len + 1); +  for (; size; in += 3) {       /* process tuplets */ +    *d++ = v[in[0] >> 2];       /* byte 1: high 6 bits (1) */ +    /* byte 2: low 2 bits (1), high 4 bits (2) */ +    *d++ = v[((in[0] << 4) + (--size ? (in[1] >> 4) : 0)) & 0x3f]; +    /* byte 3: low 4 bits (2), high 2 bits (3) */ +    *d++ = size ? v[((in[1] << 2) + (--size ? (in[2] >> 6) : 0)) & 0x3f] : '='; +    /* byte 4: low 6 bits (3) */ +    *d++ = size ? v[in[2] & 0x3f] : '='; +    if (size) +      size--;                   /* count third character if processed */ +  } +  *d = '\0';                    /* tie off string */ + +  return ret;                   /* return the resulting string */ +} +  static gboolean  gst_rtp_theora_pay_finish_headers (GstBaseRTPPayload * basepayload)  {    GstRtpTheoraPay *rtptheorapay = GST_RTP_THEORA_PAY (basepayload);    GList *walk; -  guint length; -  gchar *configuration; -  gchar *wstr, *hstr; -  guint8 *data; +  guint length, size, n_headers, configlen; +  gchar *wstr, *hstr, *configuration; +  guint8 *data, *config;    guint32 ident; -  GValue v = { 0 }; -  GstBuffer *config;    GST_DEBUG_OBJECT (rtptheorapay, "finish headers");    if (!rtptheorapay->headers)      goto no_headers; -  /* we need exactly 2 header packets */ -  if (g_list_length (rtptheorapay->headers) != 2) { -    GST_DEBUG_OBJECT (rtptheorapay, "We need 2 headers but have %d", -        g_list_length (rtptheorapay->headers)); -    goto no_headers; -  } -    /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+     * |                     Number of packed headers                  |     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -255,35 +270,60 @@ gst_rtp_theora_pay_finish_headers (GstBaseRTPPayload * basepayload)     *  0                   1                   2                   3     *  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     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -   * |                   Ident                       |              .. +   * |                   Ident                       | length       .. +   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +   * ..              | n. of headers |    length1    |    length2   .. +   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +   * ..              |             Identification Header            .. +   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +   * ................................................................. +   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +   * ..              |         Comment Header                       ..     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -   * ..   length     |              Identification Header           .. +   * .................................................................     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -   * ..                    Identification Header                     | +   * ..                        Comment Header                        |     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+     * |                          Setup Header                        ..     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +   * ................................................................. +   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+     * ..                         Setup Header                         |     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -   *     */ -  /* count the size of the headers first */ +  /* we need 4 bytes for the number of headers (which is always 1), 3 bytes for +   * the ident, 2 bytes for length, 1 byte for n. of headers. */ +  size = 4 + 3 + 2 + 1; + +  /* count the size of the headers first and update the hash */    length = 0; +  n_headers = 0;    ident = fnv1_hash_32_new ();    for (walk = rtptheorapay->headers; walk; walk = g_list_next (walk)) {      GstBuffer *buf = GST_BUFFER_CAST (walk->data); - -    ident = -        fnv1_hash_32_update (ident, GST_BUFFER_DATA (buf), +    guint bsize; + +    bsize = GST_BUFFER_SIZE (buf); +    length += bsize; +    n_headers++; + +    /* count number of bytes needed for length fields, we don't need this for +     * the last header. */ +    if (g_list_next (walk)) { +      do { +        size++; +        bsize >>= 7; +      } while (bsize); +    } +    /* update hash */ +    ident = fnv1_hash_32_update (ident, GST_BUFFER_DATA (buf),          GST_BUFFER_SIZE (buf)); -    length += GST_BUFFER_SIZE (buf);    } -  /* total config length is 4 bytes header number + size of the -   *  headers + 2 bytes length + 3 bytes for the ident */ -  config = gst_buffer_new_and_alloc (length + 4 + 2 + 3); -  data = GST_BUFFER_DATA (config); +  /* packet length is header size + packet length */ +  configlen = size + length; +  config = data = g_malloc (configlen);    /* number of packed headers, we only pack 1 header */    data[0] = 0; @@ -293,18 +333,51 @@ gst_rtp_theora_pay_finish_headers (GstBaseRTPPayload * basepayload)    ident = fnv1_hash_32_to_24 (ident);    rtptheorapay->payload_ident = ident; +  GST_DEBUG_OBJECT (rtptheorapay, "ident 0x%08x", ident);    /* take lower 3 bytes */    data[4] = (ident >> 16) & 0xff;    data[5] = (ident >> 8) & 0xff;    data[6] = ident & 0xff; -  /* store length minus the length of the fixed theora header */ -  data[7] = ((length - THEORA_ID_LEN) >> 8) & 0xff; -  data[8] = (length - THEORA_ID_LEN) & 0xff; +  /* store length of all theora headers */ +  data[7] = ((length) >> 8) & 0xff; +  data[8] = (length) & 0xff; + +  /* store number of headers minus one. */ +  data[9] = n_headers - 1; +  data += 10; + +  /* store length for each header */ +  for (walk = rtptheorapay->headers; walk; walk = g_list_next (walk)) { +    GstBuffer *buf = GST_BUFFER_CAST (walk->data); +    guint bsize, size, temp; + +    /* only need to store the length when it's not the last header */ +    if (!g_list_next (walk)) +      break; + +    bsize = GST_BUFFER_SIZE (buf); + +    /* calc size */ +    size = 0; +    do { +      size++; +      bsize >>= 7; +    } while (bsize); +    temp = size; + +    bsize = GST_BUFFER_SIZE (buf); +    /* write the size backwards */ +    while (size) { +      size--; +      data[size] = bsize & 0x7f; +      bsize >>= 7; +    } +    data += temp; +  }    /* copy header data */ -  data += 9;    for (walk = rtptheorapay->headers; walk; walk = g_list_next (walk)) {      GstBuffer *buf = GST_BUFFER_CAST (walk->data); @@ -312,10 +385,9 @@ gst_rtp_theora_pay_finish_headers (GstBaseRTPPayload * basepayload)      data += GST_BUFFER_SIZE (buf);    } -  /* serialize buffer to base16 */ -  g_value_init (&v, GST_TYPE_BUFFER); -  gst_value_take_buffer (&v, config); -  configuration = gst_value_serialize (&v); +  /* serialize to base64 */ +  configuration = encode_base64 (config, configlen, &size); +  g_free (config);    /* configure payloader settings */    wstr = g_strdup_printf ("%d", rtptheorapay->width); @@ -333,7 +405,6 @@ gst_rtp_theora_pay_finish_headers (GstBaseRTPPayload * basepayload)    g_free (wstr);    g_free (hstr);    g_free (configuration); -  g_value_unset (&v);    return TRUE; @@ -454,12 +525,7 @@ gst_rtp_theora_pay_handle_buffer (GstBaseRTPPayload * basepayload,    if (rtptheorapay->need_headers) {      /* we need to collect the headers and construct a config string from them */ -    if (TDT == 2) { -      GST_DEBUG_OBJECT (rtptheorapay, -          "discard comment packet while collecting headers"); -      ret = GST_FLOW_OK; -      goto done; -    } else if (TDT != 0) { +    if (TDT != 0) {        GST_DEBUG_OBJECT (rtptheorapay, "collecting header, buffer %p", buffer);        /* append header to the list of headers */        rtptheorapay->headers = g_list_append (rtptheorapay->headers, buffer); diff --git a/gst/rtp/gstrtpvorbisdepay.c b/gst/rtp/gstrtpvorbisdepay.c index a4fd209c..dfcb78ee 100644 --- a/gst/rtp/gstrtpvorbisdepay.c +++ b/gst/rtp/gstrtpvorbisdepay.c @@ -81,9 +81,6 @@ GST_STATIC_PAD_TEMPLATE ("src",      GST_STATIC_CAPS ("audio/x-vorbis")      ); -/* 30 bytes for the vorbis identification packet length */ -#define VORBIS_ID_LEN	30 -  GST_BOILERPLATE (GstRtpVorbisDepay, gst_rtp_vorbis_depay, GstBaseRTPDepayload,      GST_TYPE_BASE_RTP_DEPAYLOAD); @@ -247,17 +244,26 @@ gst_rtp_vorbis_depay_parse_configuration (GstRtpVorbisDepay * rtpvorbisdepay,    /*  0                   1                   2                   3     *  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     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -   * |                   Ident                       |              .. +   * |                   Ident                       | length       .. +   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +   * ..              | n. of headers |    length1    |    length2   .. +   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +   * ..              |             Identification Header            .. +   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +   * .................................................................     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -   * ..   length     |              Identification Header           .. +   * ..              |         Comment Header                       ..     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -   * ..                    Identification Header                     | +   * ................................................................. +   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +   * ..                        Comment Header                        |     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+     * |                          Setup Header                        ..     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +   * ................................................................. +   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+     * ..                         Setup Header                         |     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -   *     */    for (i = 0; i < num_headers; i++) {      guint32 ident;  | 
