/******************************************************************** * * * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 * * by the XIPHOPHORUS Company http://www.xiph.org/ * ******************************************************************** function: maintain the info structure, info <-> header packets last mod: $Id$ ********************************************************************/ /* general handling of the header and the TarkinInfo structure (and substructures) */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "tarkin.h" #include "yuv.h" #include "mem.h" /* helpers */ static void _v_writestring (oggpack_buffer * o, char *s, int bytes) { while (bytes--) { oggpack_write (o, *s++, 8); } } static void _v_readstring (oggpack_buffer * o, char *buf, int bytes) { while (bytes--) { *buf++ = oggpack_read (o, 8); } } void tarkin_comment_init (TarkinComment * vc) { memset (vc, 0, sizeof (*vc)); } void tarkin_comment_add (TarkinComment * vc, char *comment) { vc->user_comments = REALLOC (vc->user_comments, (vc->comments + 2) * sizeof (*vc->user_comments)); vc->comment_lengths = REALLOC (vc->comment_lengths, (vc->comments + 2) * sizeof (*vc->comment_lengths)); vc->comment_lengths[vc->comments] = strlen (comment); vc->user_comments[vc->comments] = MALLOC (vc->comment_lengths[vc->comments] + 1); strcpy (vc->user_comments[vc->comments], comment); vc->comments++; vc->user_comments[vc->comments] = NULL; } void tarkin_comment_add_tag (TarkinComment * vc, char *tag, char *contents) { char *comment = alloca (strlen (tag) + strlen (contents) + 2); /* +2 for = and \0 */ strcpy (comment, tag); strcat (comment, "="); strcat (comment, contents); tarkin_comment_add (vc, comment); } /* This is more or less the same as strncasecmp - but that doesn't exist * everywhere, and this is a fairly trivial function, so we include it */ static int tagcompare (const char *s1, const char *s2, int n) { int c = 0; while (c < n) { if (toupper (s1[c]) != toupper (s2[c])) return !0; c++; } return 0; } char * tarkin_comment_query (TarkinComment * vc, char *tag, int count) { long i; int found = 0; int taglen = strlen (tag) + 1; /* +1 for the = we append */ char *fulltag = alloca (taglen + 1); strcpy (fulltag, tag); strcat (fulltag, "="); for (i = 0; i < vc->comments; i++) { if (!tagcompare (vc->user_comments[i], fulltag, taglen)) { if (count == found) /* We return a pointer to the data, not a copy */ return vc->user_comments[i] + taglen; else found++; } } return NULL; /* didn't find anything */ } int tarkin_comment_query_count (TarkinComment * vc, char *tag) { int i, count = 0; int taglen = strlen (tag) + 1; /* +1 for the = we append */ char *fulltag = alloca (taglen + 1); strcpy (fulltag, tag); strcat (fulltag, "="); for (i = 0; i < vc->comments; i++) { if (!tagcompare (vc->user_comments[i], fulltag, taglen)) count++; } return count; } void tarkin_comment_clear (TarkinComment * vc) { if (vc) { long i; for (i = 0; i < vc->comments; i++) if (vc->user_comments[i]) FREE (vc->user_comments[i]); if (vc->user_comments) FREE (vc->user_comments); if (vc->comment_lengths) FREE (vc->comment_lengths); if (vc->vendor) FREE (vc->vendor); } memset (vc, 0, sizeof (*vc)); } /* used by synthesis, which has a full, alloced vi */ void tarkin_info_init (TarkinInfo * vi) { memset (vi, 0, sizeof (*vi)); } void tarkin_info_clear (TarkinInfo * vi) { memset (vi, 0, sizeof (*vi)); } /* Header packing/unpacking ********************************************/ static int _tarkin_unpack_info (TarkinInfo * vi, oggpack_buffer * opb) { #ifdef DBG_OGG printf ("dbg_ogg: Decoding Info: "); #endif vi->version = oggpack_read (opb, 32); if (vi->version != 0) return (-TARKIN_VERSION); vi->n_layers = oggpack_read (opb, 8); vi->inter.numerator = oggpack_read (opb, 32); vi->inter.denominator = oggpack_read (opb, 32); vi->bitrate_upper = oggpack_read (opb, 32); vi->bitrate_nominal = oggpack_read (opb, 32); vi->bitrate_lower = oggpack_read (opb, 32); #ifdef DBG_OGG printf (" n_layers %d, interleave: %d/%d, ", vi->n_layers, vi->inter.numerator, vi->inter.denominator); #endif if (vi->inter.numerator < 1) goto err_out; if (vi->inter.denominator < 1) goto err_out; if (vi->n_layers < 1) goto err_out; if (oggpack_read (opb, 1) != 1) goto err_out; /* EOP check */ #ifdef DBG_OGG printf ("Success\n"); #endif return (0); err_out: #ifdef DBG_OGG printf ("Failed\n"); #endif tarkin_info_clear (vi); return (-TARKIN_BAD_HEADER); } static int _tarkin_unpack_comment (TarkinComment * vc, oggpack_buffer * opb) { int i; int vendorlen = oggpack_read (opb, 32); #ifdef DBG_OGG printf ("dbg_ogg: Decoding comment: "); #endif if (vendorlen < 0) goto err_out; vc->vendor = _ogg_calloc (vendorlen + 1, 1); _v_readstring (opb, vc->vendor, vendorlen); vc->comments = oggpack_read (opb, 32); if (vc->comments < 0) goto err_out; vc->user_comments = _ogg_calloc (vc->comments + 1, sizeof (*vc->user_comments)); vc->comment_lengths = _ogg_calloc (vc->comments + 1, sizeof (*vc->comment_lengths)); for (i = 0; i < vc->comments; i++) { int len = oggpack_read (opb, 32); if (len < 0) goto err_out; vc->comment_lengths[i] = len; vc->user_comments[i] = _ogg_calloc (len + 1, 1); _v_readstring (opb, vc->user_comments[i], len); } if (oggpack_read (opb, 1) != 1) goto err_out; /* EOP check */ #ifdef DBG_OGG printf ("Success, read %d comments\n", vc->comments); #endif return (0); err_out: #ifdef DBG_OGG printf ("Failed\n"); #endif tarkin_comment_clear (vc); return (-TARKIN_BAD_HEADER); } /* the real encoding details are here, currently TarkinVideoLayerDesc. */ static int _tarkin_unpack_layer_desc (TarkinInfo * vi, oggpack_buffer * opb) { int i, j; vi->layer = CALLOC (vi->n_layers, (sizeof (*vi->layer))); memset (vi->layer, 0, vi->n_layers * sizeof (*vi->layer)); #ifdef DBG_OGG printf ("ogg: Decoding layers description: "); #endif for (i = 0; i < vi->n_layers; i++) { TarkinVideoLayer *layer = vi->layer + i; layer->desc.width = oggpack_read (opb, 32); layer->desc.height = oggpack_read (opb, 32); layer->desc.a_moments = oggpack_read (opb, 32); layer->desc.s_moments = oggpack_read (opb, 32); layer->desc.frames_per_buf = oggpack_read (opb, 32); layer->desc.bitstream_len = oggpack_read (opb, 32); layer->desc.format = oggpack_read (opb, 32); switch (layer->desc.format) { case TARKIN_GRAYSCALE: layer->n_comp = 1; layer->color_fwd_xform = grayscale_to_y; layer->color_inv_xform = y_to_grayscale; break; case TARKIN_RGB24: layer->n_comp = 3; layer->color_fwd_xform = rgb24_to_yuv; layer->color_inv_xform = yuv_to_rgb24; break; case TARKIN_RGB32: layer->n_comp = 3; layer->color_fwd_xform = rgb32_to_yuv; layer->color_inv_xform = yuv_to_rgb32; break; case TARKIN_RGBA: layer->n_comp = 4; layer->color_fwd_xform = rgba_to_yuv; layer->color_inv_xform = yuv_to_rgba; break; default: return -TARKIN_INVALID_COLOR_FORMAT; }; layer->waveletbuf = (Wavelet3DBuf **) CALLOC (layer->n_comp, sizeof (Wavelet3DBuf *)); layer->packet = MALLOC (layer->n_comp * sizeof (*layer->packet)); memset (layer->packet, 0, layer->n_comp * sizeof (*layer->packet)); for (j = 0; j < layer->n_comp; j++) { layer->waveletbuf[j] = wavelet_3d_buf_new (layer->desc.width, layer->desc.height, layer->desc.frames_per_buf); layer->packet[j].data = MALLOC (layer->desc.bitstream_len); layer->packet[j].storage = layer->desc.bitstream_len; } vi->max_bitstream_len += layer->desc.bitstream_len + 2 * 10 * sizeof (uint32_t) * layer->n_comp; /* truncation tables */ #ifdef DBG_OGG printf ("\n layer%d: size %dx%dx%d, format %d, a_m %d, s_m %d, %d fpb\n", i, layer->desc.width, layer->desc.height, layer->n_comp, layer->desc.format, layer->desc.a_moments, layer->desc.s_moments, layer->desc.frames_per_buf); #endif } /* for each layer */ if (oggpack_read (opb, 1) != 1) goto err_out; /* EOP check */ #ifdef DBG_OGG printf ("Success\n"); #endif return (0); err_out: #ifdef DBG_OGG printf ("Failed\n"); #endif tarkin_info_clear (vi); return (-TARKIN_BAD_HEADER); } /* The Tarkin header is in three packets; the initial small packet in the first page that identifies basic parameters, a second packet with bitstream comments and a third packet that holds the layer description structures. */ TarkinError tarkin_synthesis_headerin (TarkinInfo * vi, TarkinComment * vc, ogg_packet * op) { oggpack_buffer opb; if (op) { oggpack_readinit (&opb, op->packet, op->bytes); /* Which of the three types of header is this? */ /* Also verify header-ness, tarkin */ { char buffer[6]; int packtype = oggpack_read (&opb, 8); memset (buffer, 0, 6); _v_readstring (&opb, buffer, 6); if (memcmp (buffer, "tarkin", 6)) { /* not a tarkin header */ return (-TARKIN_NOT_TARKIN); } switch (packtype) { case 0x01: /* least significant *bit* is read first */ if (!op->b_o_s) { /* Not the initial packet */ return (-TARKIN_BAD_HEADER); } if (vi->inter.numerator != 0) { /* previously initialized info header */ return (-TARKIN_BAD_HEADER); } return (_tarkin_unpack_info (vi, &opb)); case 0x03: /* least significant *bit* is read first */ if (vi->inter.denominator == 0) { /* um... we didn't get the initial header */ return (-TARKIN_BAD_HEADER); } return (_tarkin_unpack_comment (vc, &opb)); case 0x05: /* least significant *bit* is read first */ if (vi->inter.numerator == 0 || vc->vendor == NULL) { /* um... we didn;t get the initial header or comments yet */ return (-TARKIN_BAD_HEADER); } return (_tarkin_unpack_layer_desc (vi, &opb)); default: /* Not a valid tarkin header type */ return (-TARKIN_BAD_HEADER); break; } } } return (-TARKIN_BAD_HEADER); } /* pack side **********************************************************/ static int _tarkin_pack_info (oggpack_buffer * opb, TarkinInfo * vi) { /* preamble */ oggpack_write (opb, 0x01, 8); _v_writestring (opb, "tarkin", 6); /* basic information about the stream */ oggpack_write (opb, 0x00, 32); oggpack_write (opb, vi->n_layers, 8); oggpack_write (opb, vi->inter.numerator, 32); oggpack_write (opb, vi->inter.denominator, 32); oggpack_write (opb, vi->bitrate_upper, 32); oggpack_write (opb, vi->bitrate_nominal, 32); oggpack_write (opb, vi->bitrate_lower, 32); oggpack_write (opb, 1, 1); #ifdef DBG_OGG printf ("dbg_ogg: Putting out info, inter %d/%d, n_layers %d\n", vi->inter.numerator, vi->inter.denominator, vi->n_layers); #endif return (0); } static int _tarkin_pack_comment (oggpack_buffer * opb, TarkinComment * vc) { char temp[] = "libTarkin debugging edition 20011104"; int bytes = strlen (temp); /* preamble */ oggpack_write (opb, 0x03, 8); _v_writestring (opb, "tarkin", 6); /* vendor */ oggpack_write (opb, bytes, 32); _v_writestring (opb, temp, bytes); /* comments */ oggpack_write (opb, vc->comments, 32); if (vc->comments) { int i; for (i = 0; i < vc->comments; i++) { if (vc->user_comments[i]) { oggpack_write (opb, vc->comment_lengths[i], 32); _v_writestring (opb, vc->user_comments[i], vc->comment_lengths[i]); } else { oggpack_write (opb, 0, 32); } } } oggpack_write (opb, 1, 1); #ifdef DBG_OGG printf ("dbg_ogg: Putting out %d comments\n", vc->comments); #endif return (0); } static int _tarkin_pack_layer_desc (oggpack_buffer * opb, TarkinInfo * vi) { int i; TarkinVideoLayer *layer; #ifdef DBG_OGG printf ("dbg_ogg: Putting out layers description:\n"); #endif oggpack_write (opb, 0x05, 8); _v_writestring (opb, "tarkin", 6); for (i = 0; i < vi->n_layers; i++) { layer = vi->layer + i; oggpack_write (opb, layer->desc.width, 32); oggpack_write (opb, layer->desc.height, 32); oggpack_write (opb, layer->desc.a_moments, 32); oggpack_write (opb, layer->desc.s_moments, 32); oggpack_write (opb, layer->desc.frames_per_buf, 32); oggpack_write (opb, layer->desc.bitstream_len, 32); oggpack_write (opb, layer->desc.format, 32); #ifdef DBG_OGG printf (" res. %dx%d, format %d, a_m %d, s_m %d, fpb %d\n", layer->desc.width, layer->desc.height, layer->desc.format, layer->desc.a_moments, layer->desc.s_moments, layer->desc.frames_per_buf); #endif } oggpack_write (opb, 1, 1); #ifdef DBG_OGG printf (" wrote %ld bytes.\n", oggpack_bytes (opb)); #endif return (0); } int tarkin_comment_header_out (TarkinComment * vc, ogg_packet * op) { oggpack_buffer opb; oggpack_writeinit (&opb); if (_tarkin_pack_comment (&opb, vc)) return -TARKIN_NOT_IMPLEMENTED; op->packet = MALLOC (oggpack_bytes (&opb)); memcpy (op->packet, opb.buffer, oggpack_bytes (&opb)); op->bytes = oggpack_bytes (&opb); op->b_o_s = 0; op->e_o_s = 0; op->granulepos = 0; return 0; } TarkinError tarkin_analysis_headerout (TarkinStream * v, TarkinComment * vc, ogg_packet * op, ogg_packet * op_comm, ogg_packet * op_code) { int ret = -TARKIN_NOT_IMPLEMENTED; TarkinInfo *vi; oggpack_buffer opb; tarkin_header_store *b = &v->headers; vi = v->ti; /* first header packet ********************************************* */ oggpack_writeinit (&opb); if (_tarkin_pack_info (&opb, vi)) goto err_out; /* build the packet */ if (b->header) FREE (b->header); b->header = MALLOC (oggpack_bytes (&opb)); memcpy (b->header, opb.buffer, oggpack_bytes (&opb)); op->packet = b->header; op->bytes = oggpack_bytes (&opb); op->b_o_s = 1; op->e_o_s = 0; op->granulepos = 0; /* second header packet (comments) ********************************* */ oggpack_reset (&opb); if (_tarkin_pack_comment (&opb, vc)) goto err_out; if (b->header1) FREE (b->header1); b->header1 = MALLOC (oggpack_bytes (&opb)); memcpy (b->header1, opb.buffer, oggpack_bytes (&opb)); op_comm->packet = b->header1; op_comm->bytes = oggpack_bytes (&opb); op_comm->b_o_s = 0; op_comm->e_o_s = 0; op_comm->granulepos = 0; /* third header packet (modes/codebooks) *************************** */ oggpack_reset (&opb); if (_tarkin_pack_layer_desc (&opb, vi)) goto err_out; if (b->header2) FREE (b->header2); b->header2 = MALLOC (oggpack_bytes (&opb)); memcpy (b->header2, opb.buffer, oggpack_bytes (&opb)); op_code->packet = b->header2; op_code->bytes = oggpack_bytes (&opb); op_code->b_o_s = 0; op_code->e_o_s = 0; op_code->granulepos = 0; oggpack_writeclear (&opb); return (0); err_out: oggpack_writeclear (&opb); memset (op, 0, sizeof (*op)); memset (op_comm, 0, sizeof (*op_comm)); memset (op_code, 0, sizeof (*op_code)); if (b->header) FREE (b->header); if (b->header1) FREE (b->header1); if (b->header2) FREE (b->header2); b->header = NULL; b->header1 = NULL; b->header2 = NULL; return (ret); }