| /******************************************************************** |
| * * |
| * 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 <stdlib.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <ogg/ogg.h> |
| #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); |
| } |