Added a tarkin encoder/decoder plugin.
authorWim Taymans <wim.taymans@gmail.com>
Sun, 3 Feb 2002 16:30:31 +0000 (16:30 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Sun, 3 Feb 2002 16:30:31 +0000 (16:30 +0000)
Original commit message from CVS:
Added a tarkin encoder/decoder plugin.

I moved the tarking CVS code in here temporarily until they have a
library (hence this plugin is in ext)

test with:

./gst-launch filesrc location=/opt/data/shihad.mpg ! mpegdemux video_00! { queue ! mpeg2dec ! colorspace ! tarkinenc bitrate=3000 ! disksink location=out.ogg }

./gst-launch filesrc location=out.ogg ! tarkindec ! colorspace ! xvideosink

24 files changed:
.gitmodules [new file with mode: 0644]
common [new submodule]
ext/tarkin/Makefile.am [new file with mode: 0644]
ext/tarkin/README [new file with mode: 0644]
ext/tarkin/TODO [new file with mode: 0644]
ext/tarkin/WHAT_THE_HECK_IS_THIS_CODE_DOING [new file with mode: 0644]
ext/tarkin/golomb.h [new file with mode: 0644]
ext/tarkin/gsttarkin.c [new file with mode: 0644]
ext/tarkin/gsttarkindec.c [new file with mode: 0644]
ext/tarkin/gsttarkindec.h [new file with mode: 0644]
ext/tarkin/gsttarkinenc.c [new file with mode: 0644]
ext/tarkin/gsttarkinenc.h [new file with mode: 0644]
ext/tarkin/info.c [new file with mode: 0644]
ext/tarkin/mem.c [new file with mode: 0644]
ext/tarkin/mem.h [new file with mode: 0644]
ext/tarkin/rle.h [new file with mode: 0644]
ext/tarkin/tarkin.c [new file with mode: 0644]
ext/tarkin/tarkin.h [new file with mode: 0644]
ext/tarkin/wavelet.c [new file with mode: 0644]
ext/tarkin/wavelet.h [new file with mode: 0644]
ext/tarkin/wavelet_coeff.c [new file with mode: 0644]
ext/tarkin/wavelet_xform.c [new file with mode: 0644]
ext/tarkin/yuv.c [new file with mode: 0644]
ext/tarkin/yuv.h [new file with mode: 0644]

diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..1ba590b
--- /dev/null
@@ -0,0 +1,3 @@
+[submodule "common"]
+        path = common
+        url = git://anongit.freedesktop.org/gstreamer/common
diff --git a/common b/common
new file mode 160000 (submodule)
index 0000000..e35bdb8
--- /dev/null
+++ b/common
@@ -0,0 +1 @@
+Subproject commit e35bdb888e14d9e5b5f19a7d15be8157ca019234
diff --git a/ext/tarkin/Makefile.am b/ext/tarkin/Makefile.am
new file mode 100644 (file)
index 0000000..86acce1
--- /dev/null
@@ -0,0 +1,15 @@
+plugindir = $(libdir)/gst
+
+plugin_LTLIBRARIES = libgsttarkin.la
+
+libgsttarkin_la_SOURCES = tarkin.c \
+                         mem.c wavelet.c wavelet_xform.c \
+                         wavelet_coeff.c yuv.c info.c \
+                         gsttarkin.c gsttarkinenc.c gsttarkindec.c
+
+libgsttarkin_la_CFLAGS = $(GST_CFLAGS) $(VORBIS_CFLAGS) -DTYPE_BITS=10 -DTYPE=int16_t -DRLECODER 
+## AM_PATH_VORBIS also sets VORBISENC_LIBS
+libgsttarkin_la_LIBADD = $(GST_LIBS) $(VORBIS_LIBS) $(VORBISENC_LIBS)
+libgsttarkin_la_LDFLAGS = @GST_PLUGIN_LDFLAGS@
+
+noinst_HEADERS = gsttarkinenc.h 
diff --git a/ext/tarkin/README b/ext/tarkin/README
new file mode 100644 (file)
index 0000000..ee91356
--- /dev/null
@@ -0,0 +1,8 @@
+This is a video codec based on an integer wavelet in 3 dimensions (x,
+y, and time/frame). What documentation exists so far is on the
+vorbis-dev and (now) tarkin-dev mailing lists at xiph.org. Some brief
+documentation can be found in the w3d/docs directory.
+
+For sample / test streams, see http://media.xiph.org/
+(and feel free to submit more streams if you have them).
+
diff --git a/ext/tarkin/TODO b/ext/tarkin/TODO
new file mode 100644 (file)
index 0000000..909d377
--- /dev/null
@@ -0,0 +1,42 @@
+
+
+Most important things:
+
+ - the entropy coder, replace static huffman
+ - clean up the pnsr tools
+ - write docs and do some performance analysis, compare to other codecs
+ - think about a multiresolution multidimensional motion flow detection scheme,
+    Marco posted a good paper comparing different algorithms to do this
+
+
+Open bugs and stuff required to fix them:
+
+ - wavelet xform bug at short rows, see workaround in wavelet_xform.c
+ - (4,x) and (x,4) wavelet implementations have a bug which causes round-off 
+    errors in the two least significand bits
+
+
+Wavelet-related TODO's:
+
+ - remove unecessairy copying in inverse xform
+ - improve truncation table setup
+ - try other approaches to encode coefficients, jack was talking about VQ
+    and reuse vorbis code
+ - write avitotarkin/quicktimetotarkin/mpegtotarkin/player/recorder
+    (a libsndfile/libaudiofile/libao alike video library would be great !)
+ - profile
+ - add special transform functions for large strides to prevent cache misses
+ - mmx/3dnow/sse/altivec
+
+
+Other:
+
+ - u and v buffers could get quarter size already at color conversion
+    this would speed up the whole algorithm; perhaps this should get
+    configurable
+ - fast internal 16bitY/U/V->15/16bitRGB for display could make sense
+ - the wavelet codec could be used for still image compression too
+    (we just have to define a file format with all goodies you can imagine;) 
+ - to make it perfect someone has to write a good bilevel compressor and
+    mask seperation algorithm
+
diff --git a/ext/tarkin/WHAT_THE_HECK_IS_THIS_CODE_DOING b/ext/tarkin/WHAT_THE_HECK_IS_THIS_CODE_DOING
new file mode 100644 (file)
index 0000000..4660260
--- /dev/null
@@ -0,0 +1,61 @@
+
+This is deprecated. Take a look in the w3d/docs directory.
+
+The command line semantics are changed. You have to call the test program
+now like this:
+
+./tarkin_enc ../clips/venuscubes-ppm/AnimSpace00%03d.ppm 5000 4 4
+./tarkin_dec
+
+------------------------------------------------------------------------------
+
+Hi,
+
+this is a experimental 3d-integer-wavelet-video compression codec. Since the
+integer wavelet transform is reversible and a reversible rgb-yuv conversion
+is used (you can understand it as (1,2) integer wavelet transform, too), this
+codec should be lossless if you transmit the whole bitstream.
+The Y/U/V-bitstreams are embedded, thus you can simply get lossy compression 
+and shape the used bandwith by cutting bitstreams, when a user defined limit 
+is reached.
+
+
+Here is how the current code works:
+
+First we grab a block of N_FRAMES frames (defined in main.c) of .ppm files.
+Then each pixel becomes transformed into a YUV-alike colorspace. Take a look in
+yuv.c to see how it is done. Each component is then transformed into frequency
+space by applying the wavelet transform in x, y and frame direction. 
+The frame-direction transform is our high-order 'motion compensation'.
+At boundaries we use (1,1)-Wavelets (== HAAR transform), inside the image 
+(2,2)-Wavelets. (4,4)-Wavelets should be easy to add. See wavelet.c for details.
+
+The resulting coefficients are scanned bitplane by bitplane and 
+runlength-encoded. Runlengths are Huffman-compressed and written into the 
+bitstreams. The bitplanes of higher-frequency scales are offset'ed to ensure a
+fast transmission of high-energy-low-frequency coefficients. (coder.c)
+The huffman coder is quite simple and uses a hardcoded table, this can be done
+much better, but I wanted to get it working fast.
+
+Decompression works exactly like compression but in reversed direction.
+
+The test program writes for each frame the grabbed original image, the y/u/v
+component (may look strange, since u/v can be negative and are not clamped to
+the [0:255] range), the coefficients (look much more like usual wavelet 
+coefficients if you add 128 to each pixel), the coefficients after they are 
+runlength/huffman encoded and decoded, the y/u/v components when inverse wavelet
+transform is done and the output image in .ppm format.
+
+You can call the test program like this:
+
+  $ ./main 20000 5000 5000 ../clips/%i.ppm
+
+which means: images are grabbed from directory ../clips/0.ppm, ../clips/1.ppm,
+etc. The Y component bitstream is limited to 20000 Bytes, the U and V bitstreams
+to 5000 Bytes. If the last argument is omitted, frames are taken from current
+directory.
+
+Good Luck,
+
+- Holger  <hwaechtler@users.sourceforge.net>
+
diff --git a/ext/tarkin/golomb.h b/ext/tarkin/golomb.h
new file mode 100644 (file)
index 0000000..fa66d75
--- /dev/null
@@ -0,0 +1,132 @@
+#ifndef __GOLOMB_H
+#define __GOLOMB_H
+
+
+#include "bitcoder.h"
+
+
+static inline
+unsigned int required_bits (unsigned int x)
+{
+   int bits = 31;
+
+   while ((x & (1 << bits)) == 0 && bits)
+      bits--;
+
+   return bits;
+}
+
+
+static inline
+void write_number_binary (BitCoderState *b, unsigned int x, int bits, int u)
+{
+//printf ("wrote %i with %i bits (%i+%i)\n", x, u+bits, u, bits);
+   while (bits) {
+      bits--;
+      bitcoder_write_bit (b, (x >> bits) & 1);
+   }
+}
+
+
+static inline
+unsigned int read_number_binary (BitCoderState *b, int bits)
+{
+   unsigned int x = 0;
+
+   while (bits) {
+      bits--;
+      x |= bitcoder_read_bit (b) << bits;
+   }
+
+   return x;
+}
+
+
+static inline
+void golomb_write_number (BitCoderState *b, unsigned int x, int bits)
+{
+   unsigned int q, r;
+int i = 0;
+
+   assert (x > 0);
+
+   while ((q = (x - 1) >> bits) > 0) {
+      bitcoder_write_bit (b, 1);   /* fast temporary adaption, write  */ 
+      bits++;                      /* unary representation of q       */
+i++;
+   };
+
+   bitcoder_write_bit (b, 0);
+
+   r = x - 1 - (q << bits); 
+
+   write_number_binary (b, r, bits, i+1);
+}
+
+
+static inline
+unsigned int golomb_read_number (BitCoderState *b, int bits)
+{
+   unsigned int q = 0, r, x;
+
+   while (bitcoder_read_bit (b) != 0) {
+      bits++;
+   }
+
+   r = read_number_binary (b, bits);
+   x = (q << bits) + r + 1;
+
+   return x;
+}
+
+
+typedef struct {
+   uint8_t count;
+   uint8_t bits;          /* a 5.3 fixed point integer  */
+} GolombAdaptiveCoderState;
+
+#define GOLOMB_ADAPTIVE_CODER_STATE_INITIALIZER { 8<<3, 0 }
+
+
+static const int golomb_w_tab [] = { 256, 128, 64 };
+
+
+
+
+static inline
+void golombcoder_encode_number (GolombAdaptiveCoderState *g,
+                                BitCoderState *b,
+                                unsigned int x)
+{
+   golomb_write_number (b, x, g->bits >> 3);
+
+   g->bits = ((256 - golomb_w_tab[g->count]) * (int) g->bits +
+                     golomb_w_tab[g->count] * (required_bits(x)<<3)) / 256;
+   g->count++;
+
+   if (g->count > 2)
+      g->count = 2;
+}
+
+
+static inline
+unsigned int golombcoder_decode_number (GolombAdaptiveCoderState *g,
+                                        BitCoderState *b)
+{
+   unsigned int x;
+
+   x = golomb_read_number (b, g->bits >> 3);
+
+   g->bits = ((256 - golomb_w_tab[g->count]) * g->bits + 
+                     golomb_w_tab[g->count] * (required_bits(x)<<3)) / 256;
+   g->count++;
+
+   if (g->count > 2)
+      g->count = 2;
+
+   return x;
+}
+
+
+#endif
+
diff --git a/ext/tarkin/gsttarkin.c b/ext/tarkin/gsttarkin.c
new file mode 100644 (file)
index 0000000..7bfb78f
--- /dev/null
@@ -0,0 +1,156 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include "gsttarkinenc.h"
+#include "gsttarkindec.h"
+
+extern GstElementDetails tarkinenc_details;
+extern GstElementDetails tarkindec_details;
+
+static GstCaps*        tarkin_typefind         (GstBuffer *buf, gpointer private);
+
+GstPadTemplate *enc_src_template, *enc_sink_template;
+GstPadTemplate *dec_src_template, *dec_sink_template;
+
+static GstCaps*
+tarkin_caps_factory (void)
+{
+  return
+   gst_caps_new (
+       "tarkin_tarkin",
+       "video/x-ogg",
+       NULL);
+}
+
+static GstCaps*
+raw_caps_factory (void)
+{
+  return
+   GST_CAPS_NEW (
+    "tarkin_raw",
+    "video/raw",
+      "format",     GST_PROPS_FOURCC (GST_STR_FOURCC ("RGB ")),
+      "bpp",        GST_PROPS_INT (24),
+      "depth",      GST_PROPS_INT (24),
+      "endianness", GST_PROPS_INT (G_BYTE_ORDER),
+      "red_mask",   GST_PROPS_INT (0xff0000),
+      "green_mask", GST_PROPS_INT (0xff00),
+      "blue_mask",  GST_PROPS_INT (0xff),
+      "width",      GST_PROPS_INT_RANGE (0, G_MAXINT),
+      "height",     GST_PROPS_INT_RANGE (0, G_MAXINT)
+   );
+}
+
+static GstTypeDefinition tarkindefinition = 
+{
+  "tarkin_video/x-ogg",
+  "video/x-ogg",
+  ".ogg",
+  tarkin_typefind,
+};
+
+static GstCaps* 
+tarkin_typefind (GstBuffer *buf, gpointer private) 
+{
+  gulong head = GULONG_FROM_BE (*((gulong *)GST_BUFFER_DATA (buf)));
+
+  /* FIXME */
+  return NULL;
+  
+  if (head  != 0x4F676753)
+    return NULL;
+
+  return gst_caps_new ("tarkin_typefind", "video/x-ogg", NULL);
+}
+
+
+static gboolean
+plugin_init (GModule *module, GstPlugin *plugin)
+{
+  GstElementFactory *enc, *dec;
+  GstTypeFactory *type;
+  GstCaps *raw_caps, *tarkin_caps;
+
+  gst_plugin_set_longname (plugin, "The OGG Vorbis Codec");
+
+  /* create an elementfactory for the tarkinenc element */
+  enc = gst_elementfactory_new ("tarkinenc", GST_TYPE_TARKINENC,
+                                &tarkinenc_details);
+  g_return_val_if_fail (enc != NULL, FALSE);
+
+  raw_caps = raw_caps_factory ();
+  tarkin_caps = tarkin_caps_factory ();
+
+  /* register sink pads */
+  enc_sink_template = gst_padtemplate_new ("sink", 
+                                          GST_PAD_SINK, 
+                                          GST_PAD_ALWAYS, 
+                                          raw_caps, 
+                                          NULL);
+  gst_elementfactory_add_padtemplate (enc, enc_sink_template);
+
+  /* register src pads */
+  enc_src_template = gst_padtemplate_new ("src", 
+                                         GST_PAD_SRC, 
+                                         GST_PAD_ALWAYS, 
+                                         tarkin_caps, 
+                                         NULL);
+  gst_elementfactory_add_padtemplate (enc, enc_src_template);
+
+  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (enc));
+
+  /* create an elementfactory for the tarkindec element */
+  dec = gst_elementfactory_new ("tarkindec", GST_TYPE_TARKINDEC,
+                                &tarkindec_details);
+  g_return_val_if_fail (dec != NULL, FALSE);
+
+  raw_caps = raw_caps_factory ();
+  tarkin_caps = tarkin_caps_factory ();
+
+  /* register sink pads */
+  dec_sink_template = gst_padtemplate_new ("sink", 
+                                          GST_PAD_SINK, 
+                                          GST_PAD_ALWAYS, 
+                                          tarkin_caps, 
+                                          NULL);
+  gst_elementfactory_add_padtemplate (dec, dec_sink_template);
+
+  /* register src pads */
+  dec_src_template = gst_padtemplate_new ("src", 
+                                         GST_PAD_SRC, 
+                                         GST_PAD_ALWAYS, 
+                                         raw_caps, 
+                                         NULL);
+  gst_elementfactory_add_padtemplate (dec, dec_src_template);
+
+  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (dec));
+
+  type = gst_typefactory_new (&tarkindefinition);
+  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type));
+
+  return TRUE;
+}
+
+GstPluginDesc plugin_desc = {
+  GST_VERSION_MAJOR,
+  GST_VERSION_MINOR,
+  "tarkin",
+  plugin_init
+};
diff --git a/ext/tarkin/gsttarkindec.c b/ext/tarkin/gsttarkindec.c
new file mode 100644 (file)
index 0000000..39c58d7
--- /dev/null
@@ -0,0 +1,301 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "gsttarkindec.h"
+
+extern GstPadTemplate *dec_src_template, *dec_sink_template;
+
+/* elementfactory information */
+GstElementDetails tarkindec_details = {
+  "Ogg Tarkin decoder",
+  "Filter/Video/Decoder",
+  "Decodes video in OGG Tarkin format",
+  VERSION,
+  "Monty <monty@xiph.org>, " 
+  "Wim Taymans <wim.taymans@chello.be>",
+  "(C) 2002",
+};
+
+/* TarkinDec signals and args */
+enum
+{
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum
+{
+  ARG_0,
+  ARG_BITRATE,
+};
+
+static void    gst_tarkindec_class_init        (TarkinDecClass *klass);
+static void    gst_tarkindec_init              (TarkinDec *arkindec);
+
+static void    gst_tarkindec_chain             (GstPad *pad, GstBuffer *buf);
+static void    gst_tarkindec_setup             (TarkinDec *tarkindec);
+static GstElementStateReturn
+               gst_tarkindec_change_state      (GstElement *element);
+
+static void    gst_tarkindec_get_property      (GObject *object, guint prop_id, GValue *value,
+                                                GParamSpec *pspec);
+static void    gst_tarkindec_set_property      (GObject *object, guint prop_id, const GValue *value,
+                                                GParamSpec *pspec);
+
+static GstElementClass *parent_class = NULL;
+/*static guint gst_tarkindec_signals[LAST_SIGNAL] = { 0 }; */
+
+GType
+tarkindec_get_type (void)
+{
+  static GType tarkindec_type = 0;
+
+  if (!tarkindec_type) {
+    static const GTypeInfo tarkindec_info = {
+      sizeof (TarkinDecClass), 
+      NULL,
+      NULL,
+      (GClassInitFunc) gst_tarkindec_class_init,
+      NULL,
+      NULL,
+      sizeof (TarkinDec),
+      0,
+      (GInstanceInitFunc) gst_tarkindec_init,
+    };
+
+    tarkindec_type = g_type_register_static (GST_TYPE_ELEMENT, "TarkinDec", &tarkindec_info, 0);
+  }
+  return tarkindec_type;
+}
+
+static void
+gst_tarkindec_class_init (TarkinDecClass *klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BITRATE, 
+    g_param_spec_int ("bitrate", "bitrate", "bitrate", 
+           G_MININT, G_MAXINT, 3000, G_PARAM_READWRITE));
+
+  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+
+  gobject_class->set_property = gst_tarkindec_set_property;
+  gobject_class->get_property = gst_tarkindec_get_property;
+
+  gstelement_class->change_state = gst_tarkindec_change_state;
+}
+
+static void
+gst_tarkindec_init (TarkinDec * tarkindec)
+{
+  tarkindec->sinkpad = gst_pad_new_from_template (dec_sink_template, "sink");
+  gst_element_add_pad (GST_ELEMENT (tarkindec), tarkindec->sinkpad);
+  gst_pad_set_chain_function (tarkindec->sinkpad, gst_tarkindec_chain);
+
+  tarkindec->srcpad = gst_pad_new_from_template (dec_src_template, "src");
+  gst_element_add_pad (GST_ELEMENT (tarkindec), tarkindec->srcpad);
+
+  tarkindec->bitrate = 3000;
+  tarkindec->setup = FALSE;
+  tarkindec->nheader = 0;
+
+  /* we're chained and we can deal with events */
+  GST_FLAG_SET (tarkindec, GST_ELEMENT_EVENT_AWARE);
+}
+
+static void
+gst_tarkindec_setup (TarkinDec *tarkindec)
+{
+  tarkindec->tarkin_stream = tarkin_stream_new ();
+  
+  ogg_sync_init (&tarkindec->oy);
+  ogg_stream_init (&tarkindec->os, 1);
+  tarkin_info_init (&tarkindec->ti);
+  tarkin_comment_init (&tarkindec->tc);
+
+  tarkindec->setup = TRUE;
+}
+
+static void
+gst_tarkindec_chain (GstPad *pad, GstBuffer *buf)
+{
+  TarkinDec *tarkindec;
+
+  g_return_if_fail (pad != NULL);
+  g_return_if_fail (GST_IS_PAD (pad));
+  g_return_if_fail (buf != NULL);
+
+  tarkindec = GST_TARKINDEC (gst_pad_get_parent (pad));
+
+  if (!tarkindec->setup) {
+    gst_element_error (GST_ELEMENT (tarkindec), "decoder not initialized (input is not audio?)");
+    if (GST_IS_BUFFER (buf))
+      gst_buffer_unref (buf);
+    else
+      gst_pad_event_default (pad, GST_EVENT (buf));
+    return;
+  }
+
+  if (GST_IS_EVENT (buf)) {
+    switch (GST_EVENT_TYPE (buf)) {
+      case GST_EVENT_EOS:
+      default:
+       gst_pad_event_default (pad, GST_EVENT (buf));
+       break;
+    }
+  }
+  else {
+    gchar *data;
+    gulong size;
+    gchar *buffer;
+    guchar *rgb;
+    TarkinTime date;
+    TarkinVideoLayerDesc *layer;
+  
+    /* data to decode */
+    data = GST_BUFFER_DATA (buf);
+    size = GST_BUFFER_SIZE (buf);
+
+    buffer = ogg_sync_buffer(&tarkindec->oy, size);
+    memcpy (buffer, data, size);
+    ogg_sync_wrote(&tarkindec->oy, size);
+
+    if (ogg_sync_pageout (&tarkindec->oy, &tarkindec->og)) {
+      ogg_stream_pagein (&tarkindec->os, &tarkindec->og);
+
+      while (ogg_stream_packetout (&tarkindec->os, &tarkindec->op)) {
+        if (tarkindec->op.e_o_s)
+          break;
+       if (tarkindec->nheader < 3) { /* 3 first packets to headerin */
+          tarkin_synthesis_headerin (&tarkindec->ti, &tarkindec->tc, &tarkindec->op);
+
+         if (tarkindec->nheader == 2) {
+           tarkin_synthesis_init (tarkindec->tarkin_stream, &tarkindec->ti);
+          }
+          tarkindec->nheader++;
+        } else {
+         tarkin_synthesis_packetin (tarkindec->tarkin_stream, &tarkindec->op);
+         
+         while (tarkin_synthesis_frameout (tarkindec->tarkin_stream, &rgb, 0, &date) == 0) {
+            GstBuffer *outbuf;
+
+           layer = &tarkindec->tarkin_stream->layer->desc;
+
+           if (!GST_PAD_CAPS (tarkindec->srcpad)) {
+             if (!gst_pad_try_set_caps (tarkindec->srcpad,
+                                     GST_CAPS_NEW (
+                                       "tarkin_raw",
+                                       "video/raw",
+                                       "format",     GST_PROPS_FOURCC (GST_STR_FOURCC ("RGB ")),
+                                       "bpp",        GST_PROPS_INT (24),
+                                       "depth",      GST_PROPS_INT (24),
+                                       "endianness", GST_PROPS_INT (G_BYTE_ORDER),
+                                       "red_mask",   GST_PROPS_INT (0xff0000),
+                                       "green_mask", GST_PROPS_INT (0xff00),
+                                       "blue_mask",  GST_PROPS_INT (0xff),
+                                       "width",      GST_PROPS_INT (layer->width),
+                                       "height",     GST_PROPS_INT (layer->height)
+                                      )))
+             {
+               gst_element_error (GST_ELEMENT (tarkindec), "could not output format");
+               gst_buffer_unref (buf);
+               return;
+             }
+           }
+           outbuf = gst_buffer_new ();
+           GST_BUFFER_DATA (outbuf) = rgb;
+           GST_BUFFER_SIZE (outbuf) = layer->width * layer->height * 3;
+           GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_DONTFREE);
+           gst_pad_push (tarkindec->srcpad, outbuf);
+           
+           tarkin_synthesis_freeframe (tarkindec->tarkin_stream, rgb);
+          }
+        }
+      }
+    }
+    gst_buffer_unref (buf);
+  }
+}
+
+static GstElementStateReturn
+gst_tarkindec_change_state (GstElement *element)
+{
+  TarkinDec *tarkindec;
+
+  tarkindec = GST_TARKINDEC (element);
+
+  switch (GST_STATE_TRANSITION (element)) {
+    case GST_STATE_READY_TO_PAUSED:
+      gst_tarkindec_setup (tarkindec);
+      break;
+    case GST_STATE_PAUSED_TO_READY:
+      break;
+    default:
+      break;
+  }
+  
+  return parent_class->change_state (element);
+}
+
+static void
+gst_tarkindec_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+  TarkinDec *tarkindec;
+
+  /* it's not null if we got it, but it might not be ours */
+  g_return_if_fail (GST_IS_TARKINDEC (object));
+
+  tarkindec = GST_TARKINDEC (object);
+
+  switch (prop_id) {
+    case ARG_BITRATE:
+      g_value_set_int (value, tarkindec->bitrate);
+      break;
+    default:
+      break;
+  }
+}
+
+static void
+gst_tarkindec_set_property (GObject *object, guint prop_id, const GValue *value,
+                           GParamSpec *pspec)
+{
+  TarkinDec *tarkindec;
+
+  /* it's not null if we got it, but it might not be ours */
+  g_return_if_fail (GST_IS_TARKINDEC (object));
+
+  tarkindec = GST_TARKINDEC (object);
+
+  switch (prop_id) {
+    case ARG_BITRATE:
+      tarkindec->bitrate = g_value_get_int (value);
+      break;
+    default:
+      break;
+  }
+}
diff --git a/ext/tarkin/gsttarkindec.h b/ext/tarkin/gsttarkindec.h
new file mode 100644 (file)
index 0000000..7862985
--- /dev/null
@@ -0,0 +1,83 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __TARKINDEC_H__
+#define __TARKINDEC_H__
+
+
+#include <config.h>
+#include <gst/gst.h>
+
+#include "tarkin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define GST_TYPE_TARKINDEC \
+  (tarkindec_get_type())
+#define GST_TARKINDEC(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_TARKINDEC,TarkinDec))
+#define GST_TARKINDEC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_TARKINDEC,TarkinDecClass))
+#define GST_IS_TARKINDEC(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TARKINDEC))
+#define GST_IS_TARKINDEC_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_TARKINDEC))
+
+typedef struct _TarkinDec TarkinDec;
+typedef struct _TarkinDecClass TarkinDecClass;
+
+struct _TarkinDec {
+  GstElement element;
+
+  GstPad *sinkpad,*srcpad;
+
+  ogg_sync_state        oy;
+  ogg_stream_state      os;
+  ogg_page              og;
+  ogg_packet            op;
+
+  TarkinStream                 *tarkin_stream;
+  TarkinComment         tc;
+  TarkinInfo            ti;
+  TarkinVideoLayerDesc          layer[1];
+
+  gint                          frame_num;
+  gint                  nheader;
+        
+  gboolean eos;
+  gint bitrate;
+  gboolean setup;
+};
+
+struct _TarkinDecClass {
+  GstElementClass parent_class;
+};
+
+GType tarkindec_get_type(void);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __TARKINDEC_H__ */
diff --git a/ext/tarkin/gsttarkinenc.c b/ext/tarkin/gsttarkinenc.c
new file mode 100644 (file)
index 0000000..8042430
--- /dev/null
@@ -0,0 +1,371 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "gsttarkinenc.h"
+
+extern GstPadTemplate *enc_src_template, *enc_sink_template;
+
+/* elementfactory information */
+GstElementDetails tarkinenc_details = {
+  "Ogg Tarkin encoder",
+  "Filter/Video/Encoder",
+  "Encodes video in OGG Tarkin format",
+  VERSION,
+  "Monty <monty@xiph.org>, " 
+  "Wim Taymans <wim.taymans@chello.be>",
+  "(C) 2002",
+};
+
+/* TarkinEnc signals and args */
+enum
+{
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum
+{
+  ARG_0,
+  ARG_BITRATE,
+  ARG_S_MOMENTS,
+  ARG_A_MOMENTS,
+};
+
+static void    gst_tarkinenc_class_init        (TarkinEncClass *klass);
+static void    gst_tarkinenc_init              (TarkinEnc *arkinenc);
+
+static void    gst_tarkinenc_chain             (GstPad *pad, GstBuffer *buf);
+static void    gst_tarkinenc_setup             (TarkinEnc *tarkinenc);
+
+static void    gst_tarkinenc_get_property      (GObject *object, guint prop_id, GValue *value,
+                                                GParamSpec *pspec);
+static void    gst_tarkinenc_set_property      (GObject *object, guint prop_id, const GValue *value,
+                                                GParamSpec *pspec);
+
+static GstElementClass *parent_class = NULL;
+/*static guint gst_tarkinenc_signals[LAST_SIGNAL] = { 0 }; */
+
+GType
+tarkinenc_get_type (void)
+{
+  static GType tarkinenc_type = 0;
+
+  if (!tarkinenc_type) {
+    static const GTypeInfo tarkinenc_info = {
+      sizeof (TarkinEncClass), 
+      NULL,
+      NULL,
+      (GClassInitFunc) gst_tarkinenc_class_init,
+      NULL,
+      NULL,
+      sizeof (TarkinEnc),
+      0,
+      (GInstanceInitFunc) gst_tarkinenc_init,
+    };
+
+    tarkinenc_type = g_type_register_static (GST_TYPE_ELEMENT, "TarkinEnc", &tarkinenc_info, 0);
+  }
+  return tarkinenc_type;
+}
+
+static void
+gst_tarkinenc_class_init (TarkinEncClass *klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BITRATE, 
+    g_param_spec_int ("bitrate", "bitrate", "bitrate", 
+           G_MININT, G_MAXINT, 3000, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_S_MOMENTS, 
+    g_param_spec_int ("s_moments", "Synthesis Moments", 
+                     "Number of vanishing moments for the synthesis filter", 
+                     1, 4, 2, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_A_MOMENTS, 
+    g_param_spec_int ("a_moments", "Analysis Moments", 
+                     "Number of vanishing moments for the analysis filter", 
+                     1, 4, 2, G_PARAM_READWRITE));
+
+  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+
+  gobject_class->set_property = gst_tarkinenc_set_property;
+  gobject_class->get_property = gst_tarkinenc_get_property;
+}
+
+static GstPadConnectReturn
+gst_tarkinenc_sinkconnect (GstPad *pad, GstCaps *caps)
+{
+  TarkinEnc *tarkinenc;
+
+  tarkinenc = GST_TARKINENC (gst_pad_get_parent (pad));
+
+  if (!GST_CAPS_IS_FIXED (caps))
+    return GST_PAD_CONNECT_DELAYED;
+
+  gst_caps_debug (caps);
+
+  tarkinenc->layer[0].bitstream_len = tarkinenc->bitrate;
+  tarkinenc->layer[0].a_moments = tarkinenc->a_moments;
+  tarkinenc->layer[0].s_moments = tarkinenc->s_moments;
+  tarkinenc->layer[0].width = gst_caps_get_int (caps, "width");
+  tarkinenc->layer[0].height = gst_caps_get_int (caps, "height");
+  tarkinenc->layer[0].format = TARKIN_RGB24;
+  tarkinenc->layer[0].frames_per_buf = TARKIN_RGB24;
+
+  gst_tarkinenc_setup (tarkinenc);
+
+  if (tarkinenc->setup)
+    return GST_PAD_CONNECT_OK;
+
+  return GST_PAD_CONNECT_REFUSED;
+}
+
+static void
+gst_tarkinenc_init (TarkinEnc * tarkinenc)
+{
+  tarkinenc->sinkpad = gst_pad_new_from_template (enc_sink_template, "sink");
+  gst_element_add_pad (GST_ELEMENT (tarkinenc), tarkinenc->sinkpad);
+  gst_pad_set_chain_function (tarkinenc->sinkpad, gst_tarkinenc_chain);
+  gst_pad_set_connect_function (tarkinenc->sinkpad, gst_tarkinenc_sinkconnect);
+
+  tarkinenc->srcpad = gst_pad_new_from_template (enc_src_template, "src");
+  gst_element_add_pad (GST_ELEMENT (tarkinenc), tarkinenc->srcpad);
+
+  tarkinenc->bitrate = 3000;
+  tarkinenc->s_moments = 2;
+  tarkinenc->a_moments = 2;
+  tarkinenc->setup = FALSE;
+}
+
+TarkinError free_frame (void *s, void *ptr) 
+{
+  return(TARKIN_OK);
+}
+
+TarkinError packet_out (void *stream, ogg_packet *op) 
+{
+  ogg_page og;
+  TarkinStream *s = stream;
+  TarkinEnc *te = s->user_ptr;
+  GstBuffer *outbuf;
+
+  ogg_stream_packetin (&te->os, op);
+
+  if(op->e_o_s){
+    ogg_stream_flush (&te->os, &og);
+    outbuf = gst_buffer_new ();
+    GST_BUFFER_DATA (outbuf) = og.header;
+    GST_BUFFER_SIZE (outbuf) = og.header_len;
+    GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_DONTFREE);
+    gst_pad_push (te->srcpad, outbuf);
+    outbuf = gst_buffer_new ();
+    GST_BUFFER_DATA (outbuf) = og.body;
+    GST_BUFFER_SIZE (outbuf) = og.body_len;
+    GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_DONTFREE);
+    gst_pad_push (te->srcpad, outbuf);
+  } else {
+    while (ogg_stream_pageout (&te->os, &og)){
+      outbuf = gst_buffer_new ();
+      GST_BUFFER_DATA (outbuf) = og.header;
+      GST_BUFFER_SIZE (outbuf) = og.header_len;
+      GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_DONTFREE);
+      gst_pad_push (te->srcpad, outbuf);
+      outbuf = gst_buffer_new ();
+      GST_BUFFER_DATA (outbuf) = og.body;
+      GST_BUFFER_SIZE (outbuf) = og.body_len;
+      GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_DONTFREE);
+      gst_pad_push (te->srcpad, outbuf);
+    }
+  }
+  return (TARKIN_OK);
+}
+
+
+static void
+gst_tarkinenc_setup (TarkinEnc *tarkinenc)
+{
+  gint i;
+  GstBuffer *outbuf;
+
+  ogg_stream_init (&tarkinenc->os, 1);
+  tarkin_info_init (&tarkinenc->ti);
+
+  tarkinenc->ti.inter.numerator = 1;
+  tarkinenc->ti.inter.denominator = 1;
+  
+  tarkin_comment_init (&tarkinenc->tc);
+  tarkin_comment_add_tag (&tarkinenc->tc, "TITLE", "GStreamer produced file");
+  tarkin_comment_add_tag (&tarkinenc->tc, "ARTIST", "C coders ;)");
+
+  tarkinenc->tarkin_stream = tarkin_stream_new ();
+  tarkin_analysis_init (tarkinenc->tarkin_stream, 
+                       &tarkinenc->ti, free_frame, packet_out, (void*)tarkinenc);
+  tarkin_analysis_add_layer(tarkinenc->tarkin_stream, &tarkinenc->layer[0]);
+
+  tarkin_analysis_headerout (tarkinenc->tarkin_stream, &tarkinenc->tc, 
+                 tarkinenc->op, &tarkinenc->op[1], &tarkinenc->op[2]);
+  for(i = 0; i < 3; i++){
+    ogg_stream_packetin(&tarkinenc->os, &tarkinenc->op[i]);
+  }
+
+  ogg_stream_flush (&tarkinenc->os, &tarkinenc->og);
+
+  tarkinenc->frame_num = 0;
+
+  outbuf = gst_buffer_new ();
+  GST_BUFFER_DATA (outbuf) = tarkinenc->og.header;
+  GST_BUFFER_SIZE (outbuf) = tarkinenc->og.header_len;
+  GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_DONTFREE);
+  gst_pad_push (tarkinenc->srcpad, outbuf);
+
+  outbuf = gst_buffer_new ();
+  GST_BUFFER_DATA (outbuf) = tarkinenc->og.body;
+  GST_BUFFER_SIZE (outbuf) = tarkinenc->og.body_len;
+  GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_DONTFREE);
+  gst_pad_push (tarkinenc->srcpad, outbuf);
+
+  tarkinenc->setup = TRUE;
+}
+
+static void
+gst_tarkinenc_chain (GstPad *pad, GstBuffer *buf)
+{
+  TarkinEnc *tarkinenc;
+
+  g_return_if_fail (pad != NULL);
+  g_return_if_fail (GST_IS_PAD (pad));
+  g_return_if_fail (buf != NULL);
+
+  tarkinenc = GST_TARKINENC (gst_pad_get_parent (pad));
+
+  if (!tarkinenc->setup) {
+    gst_element_error (GST_ELEMENT (tarkinenc), "encoder not initialized (input is not audio?)");
+    if (GST_IS_BUFFER (buf))
+      gst_buffer_unref (buf);
+    else
+      gst_pad_event_default (pad, GST_EVENT (buf));
+    return;
+  }
+
+  if (GST_IS_EVENT (buf)) {
+    switch (GST_EVENT_TYPE (buf)) {
+      case GST_EVENT_EOS:
+        tarkin_analysis_framein (tarkinenc->tarkin_stream, NULL, 0, NULL); /* EOS */
+        tarkin_comment_clear (&tarkinenc->tc);
+        tarkin_stream_destroy (tarkinenc->tarkin_stream);
+      default:
+       gst_pad_event_default (pad, GST_EVENT (buf));
+       break;
+    }
+  }
+  else {
+    gchar *data;
+    gulong size;
+    TarkinTime date;
+  
+    /* data to encode */
+    data = GST_BUFFER_DATA (buf);
+    size = GST_BUFFER_SIZE (buf);
+
+    date.numerator = tarkinenc->frame_num;
+    date.denominator = 1;
+    tarkin_analysis_framein (tarkinenc->tarkin_stream, data, 0, &date);
+    tarkinenc->frame_num++;
+
+    gst_buffer_unref (buf);
+  }
+}
+
+static void
+gst_tarkinenc_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+  TarkinEnc *tarkinenc;
+
+  /* it's not null if we got it, but it might not be ours */
+  g_return_if_fail (GST_IS_TARKINENC (object));
+
+  tarkinenc = GST_TARKINENC (object);
+
+  switch (prop_id) {
+    case ARG_BITRATE:
+      g_value_set_int (value, tarkinenc->bitrate);
+      break;
+    case ARG_S_MOMENTS:
+      g_value_set_int (value, tarkinenc->s_moments);
+      break;
+    case ARG_A_MOMENTS:
+      g_value_set_int (value, tarkinenc->a_moments);
+      break;
+    default:
+      break;
+  }
+}
+
+static void
+gst_tarkinenc_set_property (GObject *object, guint prop_id, const GValue *value,
+                           GParamSpec *pspec)
+{
+  TarkinEnc *tarkinenc;
+
+  /* it's not null if we got it, but it might not be ours */
+  g_return_if_fail (GST_IS_TARKINENC (object));
+
+  tarkinenc = GST_TARKINENC (object);
+
+  switch (prop_id) {
+    case ARG_BITRATE:
+      tarkinenc->bitrate = g_value_get_int (value);
+      break;
+    case ARG_S_MOMENTS:
+    {
+      gint s_moments;
+
+      s_moments = g_value_get_int (value);
+      if (s_moments != 1 || s_moments != 2 || s_moments != 4) {
+       g_warning ("tarkinenc: s_moments must be 1, 2 or 4");
+      }
+      else {
+        tarkinenc->s_moments = s_moments;
+      }
+      break;
+    }
+    case ARG_A_MOMENTS:
+    {
+      gint a_moments;
+
+      a_moments = g_value_get_int (value);
+      if (a_moments != 1 || a_moments != 2 || a_moments != 4) {
+       g_warning ("tarkinenc: a_moments must be 1, 2 or 4");
+      }
+      else {
+        tarkinenc->a_moments = a_moments;
+      }
+      break;
+    }
+    default:
+      break;
+  }
+}
diff --git a/ext/tarkin/gsttarkinenc.h b/ext/tarkin/gsttarkinenc.h
new file mode 100644 (file)
index 0000000..ca74c98
--- /dev/null
@@ -0,0 +1,84 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __TARKINENC_H__
+#define __TARKINENC_H__
+
+
+#include <config.h>
+#include <gst/gst.h>
+
+#include "tarkin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define GST_TYPE_TARKINENC \
+  (tarkinenc_get_type())
+#define GST_TARKINENC(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_TARKINENC,TarkinEnc))
+#define GST_TARKINENC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_TARKINENC,TarkinEncClass))
+#define GST_IS_TARKINENC(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TARKINENC))
+#define GST_IS_TARKINENC_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_TARKINENC))
+
+typedef struct _TarkinEnc TarkinEnc;
+typedef struct _TarkinEncClass TarkinEncClass;
+
+struct _TarkinEnc {
+  GstElement element;
+
+  GstPad *sinkpad,*srcpad;
+
+  ogg_stream_state      os; /* take physical pages, weld into a logical
+                                                     stream of packets */
+  ogg_page              og; /* one Ogg bitstream page.  Tarkin packets are inside */
+  ogg_packet            op[3]; /* one raw packet of data for decode */
+
+  TarkinStream                 *tarkin_stream;
+  TarkinComment         tc;
+  TarkinInfo            ti;
+  TarkinVideoLayerDesc          layer[1];
+
+  gint                          frame_num;
+        
+  gboolean eos;
+  gint bitrate;
+  gint s_moments;
+  gint a_moments;
+  gboolean setup;
+};
+
+struct _TarkinEncClass {
+  GstElementClass parent_class;
+};
+
+GType tarkinenc_get_type(void);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __TARKINENC_H__ */
diff --git a/ext/tarkin/info.c b/ext/tarkin/info.c
new file mode 100644 (file)
index 0000000..e555e38
--- /dev/null
@@ -0,0 +1,551 @@
+/********************************************************************
+ *                                                                  *
+ * 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) */
+
+#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);
+}
+
diff --git a/ext/tarkin/mem.c b/ext/tarkin/mem.c
new file mode 100644 (file)
index 0000000..c2cf18c
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ *   Debugging implementation of MALLOC and friends
+ */
+
+
+#include "mem.h"
+
+#if defined(DBG_MEMLEAKS)
+
+typedef struct {
+   void *mem;
+   char *allocated_in_func;
+   char *allocated_in_file;
+   unsigned int allocated_in_line;
+} MemDesc;
+
+
+static int initialized = 0;
+static int alloc_count = 0;
+static MemDesc *alloc_list = NULL;
+
+
+static
+void dbg_memleaks_done (int exitcode, void *dummy)
+{
+   unsigned int i;
+   (void) dummy;
+
+   if (exitcode == 0 && alloc_count != 0) {
+      fprintf (stderr, "\nmemory leak detected !!!\n");
+      fprintf (stderr, "\nalloc_count == %i\n\n", alloc_count);
+      for (i=0; i<alloc_count; i++) {
+         MemDesc *d = &alloc_list[i];
+         fprintf (stderr, "chunk %p allocated in %s (%s: %u) not free'd !!\n",
+                  d->mem, d->allocated_in_func, d->allocated_in_file,
+                  d->allocated_in_line);
+      }
+      free(alloc_list);
+   }
+   fprintf (stderr, "\n");
+}
+
+
+static
+void dbg_memleaks_init (void)
+{
+   on_exit (dbg_memleaks_done, NULL);
+   initialized = 1;
+}
+
+
+void* dbg_malloc (char* file, int line, char *func, size_t bytes)
+{
+   void *mem = (void*) malloc (bytes);
+   MemDesc *d;
+
+   if (!initialized)
+      dbg_memleaks_init();
+
+   alloc_count++;
+   alloc_list = realloc (alloc_list, alloc_count * sizeof(MemDesc));
+
+   d = &alloc_list[alloc_count-1];
+   d->mem = mem;
+   d->allocated_in_func = func;
+   d->allocated_in_file = file;
+   d->allocated_in_line = line;
+
+   return mem;
+}
+
+
+void* dbg_calloc (char* file, int line, char *func, size_t count, size_t bytes)
+{
+   void *mem = (void*) calloc (count, bytes);
+   MemDesc *d;
+
+   if (!initialized)
+      dbg_memleaks_init();
+
+   alloc_count++;
+   alloc_list = realloc (alloc_list, alloc_count * sizeof(MemDesc));
+
+   d = &alloc_list[alloc_count-1];
+   d->mem = mem;
+   d->allocated_in_func = func;
+   d->allocated_in_file = file;
+   d->allocated_in_line = line;
+
+   return mem;
+}
+
+
+void* dbg_realloc (char *file, int line, char *func, char *what,
+                   void *mem, size_t bytes)
+{
+   unsigned int i;
+
+   for (i=0; i<alloc_count; i++) {
+      if (alloc_list[i].mem == mem) {
+         alloc_list[i].mem = (void*) realloc (mem, bytes);
+         return alloc_list[i].mem;
+      }
+   }
+
+   if (mem != NULL) {
+      fprintf (stderr,
+               "%s: trying to reallocate unknown chunk %p (%s)\n"
+               "          in %s (%s: %u) !!!\n",
+               __FUNCTION__, mem, what, func, file, line);
+      exit (-1);
+   }
+
+   return dbg_malloc(file, line, func, bytes);
+}
+
+
+void dbg_free (char *file, int line, char *func, char *what, void *mem)
+{
+   unsigned int i;
+
+   if (!initialized)
+      dbg_memleaks_init();
+
+   for (i=0; i<alloc_count; i++) {
+      if (alloc_list[i].mem == mem) {
+         free (mem);
+         alloc_count--;
+         memmove (&alloc_list[i], &alloc_list[i+1],
+                  (alloc_count - i) * sizeof(MemDesc));
+         return;
+      }
+   }
+
+   fprintf (stderr, "%s: trying to free unknown chunk %p (%s)\n"
+            "          in %s (%s: %u) !!!\n",
+            __FUNCTION__, mem, what, func, file, line);
+   exit (-1);
+}
+
+
+#endif
+
diff --git a/ext/tarkin/mem.h b/ext/tarkin/mem.h
new file mode 100644 (file)
index 0000000..f218f8e
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef __MEM_H
+#define __MEM_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+
+#if defined(DBG_MEMLEAKS)
+
+extern void* dbg_malloc (char *file, int line, char *func, size_t bytes);
+extern void* dbg_calloc (char *file, int line, char *func, size_t count, size_t bytes);
+extern void* dbg_realloc (char *file, int line, char *func, char *what, void *mem, size_t bytes);
+extern void dbg_free (char *file, int line, char *func, char *what, void *mem);
+
+#define MALLOC(bytes)        dbg_malloc(__FILE__,__LINE__,__FUNCTION__,bytes)
+#define CALLOC(count,bytes)  dbg_calloc(__FILE__,__LINE__,__FUNCTION__,count,bytes)
+#define FREE(mem)            dbg_free(__FILE__,__LINE__,__FUNCTION__,#mem,mem)
+#define REALLOC(mem,bytes)   dbg_realloc(__FILE__,__LINE__,__FUNCTION__,#mem,mem,bytes)
+
+#else
+
+#define MALLOC malloc
+#define CALLOC calloc
+#define REALLOC realloc
+#define FREE free
+
+#endif
+
+#endif
+
diff --git a/ext/tarkin/rle.h b/ext/tarkin/rle.h
new file mode 100644 (file)
index 0000000..7cf7951
--- /dev/null
@@ -0,0 +1,143 @@
+#ifndef __RLE_H
+#define __RLE_H
+
+#include <string.h>
+#include <assert.h>
+#include "mem.h"
+#include "bitcoder.h"
+#include "golomb.h"
+
+#if defined(RLECODER)
+
+#define OUTPUT_BIT(rlecoder,bit)          rlecoder_write_bit(rlecoder,bit)
+#define INPUT_BIT(rlecoder)               rlecoder_read_bit(rlecoder)
+#define OUTPUT_BIT_DIRECT(coder,bit)      bitcoder_write_bit(&(coder)->bitcoder,bit)
+#define INPUT_BIT_DIRECT(rlecoder)        bitcoder_read_bit(&(rlecoder)->bitcoder)
+#define ENTROPY_CODER                     RLECoderState
+#define ENTROPY_ENCODER_INIT(coder,limit) rlecoder_encoder_init(coder,limit)
+#define ENTROPY_ENCODER_DONE(coder)       rlecoder_encoder_done(coder)
+#define ENTROPY_ENCODER_FLUSH(coder)      rlecoder_encoder_flush(coder)
+#define ENTROPY_DECODER_INIT(coder,bitstream,limit) \
+   rlecoder_decoder_init(coder,bitstream,limit)
+#define ENTROPY_DECODER_DONE(coder)       /* nothing to do ... */
+#define ENTROPY_CODER_BITSTREAM(coder)    ((coder)->bitcoder.bitstream)
+#define ENTROPY_CODER_EOS(coder)          ((coder)->bitcoder.eos)
+
+#define ENTROPY_CODER_SYMBOL(coder)          ((coder)->symbol)
+#define ENTROPY_CODER_RUNLENGTH(coder)    ((coder)->count)
+#define ENTROPY_CODER_SKIP(coder,skip)    do { (coder)->count -= skip; } while (0)
+#endif
+
+
+
+
+typedef struct {
+   int symbol;
+   uint32_t count;               /*  have seen count symbol's         */
+   BitCoderState bitcoder;
+   GolombAdaptiveCoderState golomb_state [2];  /* 2 states for 2 symbols... */
+   int have_seen_1;
+} RLECoderState;
+
+
+
+/*
+ *   bit should be 0 or 1 !!!
+ */
+static inline
+void rlecoder_write_bit (RLECoderState *s, int bit)
+{
+   assert (bit == 0 || bit == 1);
+
+   if (s->symbol == -1) {
+      s->symbol = bit & 1;
+      s->count = 1;
+      s->have_seen_1 = bit;
+      bitcoder_write_bit (&s->bitcoder, bit);
+   }
+
+   if (s->symbol != bit) {
+      golombcoder_encode_number (&s->golomb_state[s->symbol],
+                                 &s->bitcoder, s->count);
+      s->symbol = ~s->symbol & 1;
+      s->have_seen_1 = 1;
+      s->count = 1;
+   } else
+      s->count++;
+}
+
+static inline
+int rlecoder_read_bit (RLECoderState *s)
+{
+   if (s->count == 0) {
+      s->symbol = ~s->symbol & 1;
+      s->count = golombcoder_decode_number (&s->golomb_state[s->symbol],
+                                            &s->bitcoder);
+      if (s->bitcoder.eos) {
+         s->symbol = 0;
+         s->count = ~0;
+      }
+   }
+   s->count--;
+   return (s->symbol);
+}
+
+
+int coder_id = 0;
+FILE *file = NULL;
+
+static inline
+void rlecoder_encoder_init (RLECoderState *s, uint32_t limit)
+{
+   bitcoder_encoder_init (&s->bitcoder, limit);
+   s->symbol = -1;
+   s->have_seen_1 = 0;
+   s->golomb_state[0].count = 0;
+   s->golomb_state[1].count = 0;
+   s->golomb_state[0].bits = 5 << 3;
+   s->golomb_state[1].bits = 5 << 3;
+}
+
+
+/**
+ *  once you called this, you better should not encode any more symbols ...
+ */
+static inline
+uint32_t rlecoder_encoder_flush (RLECoderState *s)
+{
+   if (s->symbol == -1 || !s->have_seen_1)
+      return 0;
+
+   golombcoder_encode_number (&s->golomb_state[s->symbol],
+                              &s->bitcoder, s->count);
+   return bitcoder_flush (&s->bitcoder);
+}
+
+
+static inline
+void rlecoder_decoder_init (RLECoderState *s, uint8_t *bitstream, uint32_t limit)
+{
+   bitcoder_decoder_init (&s->bitcoder, bitstream, limit);
+   s->golomb_state[0].count = 0;
+   s->golomb_state[1].count = 0;
+   s->golomb_state[0].bits = 5 << 3;
+   s->golomb_state[1].bits = 5 << 3;
+   s->symbol = bitcoder_read_bit (&s->bitcoder);
+   s->count = golombcoder_decode_number (&s->golomb_state[s->symbol],
+                                         &s->bitcoder) - 1;
+   if (s->bitcoder.eos) {
+      s->symbol = 0;
+      s->count = ~0;
+   }
+}
+
+
+static inline
+void rlecoder_encoder_done (RLECoderState *s)
+{
+   bitcoder_encoder_done (&s->bitcoder);
+}
+
+
+#endif
+
diff --git a/ext/tarkin/tarkin.c b/ext/tarkin/tarkin.c
new file mode 100644 (file)
index 0000000..053c03f
--- /dev/null
@@ -0,0 +1,418 @@
+/*
+ *   The real io-stuff is in tarkin-io.c
+ *   (this one has to be rewritten to write ogg streams ...)
+ */
+
+#include "mem.h"
+#include "tarkin.h"
+#include "yuv.h"
+
+
+#define N_FRAMES 1
+
+
+
+TarkinStream* tarkin_stream_new ()
+{
+   TarkinStream *s = (TarkinStream*) CALLOC (1, sizeof(TarkinStream));
+
+   if (!s)
+      return NULL;
+   memset(s,0,sizeof(*s));
+
+   s->frames_per_buf = N_FRAMES;
+
+   return s;
+}
+
+
+void tarkin_stream_destroy (TarkinStream *s)
+{
+   uint32_t i, j;
+
+   if (!s)
+      return;
+
+   for (i=0; i<s->n_layers; i++) {
+      if (s->layer[i].waveletbuf) {
+         for (j=0; j<s->layer[i].n_comp; j++) {
+            wavelet_3d_buf_destroy (s->layer[i].waveletbuf[j]);
+            FREE (s->layer[i].packet[j].data);
+         }
+         FREE(s->layer[i].waveletbuf);
+         FREE(s->layer[i].packet);
+      }
+   }
+   
+   if (s->layer)
+      FREE(s->layer);
+
+   if (s->headers.header)
+      FREE(s->headers.header);
+
+   if (s->headers.header1)
+      FREE(s->headers.header1);
+
+   if (s->headers.header2)
+      FREE(s->headers.header2);
+
+
+   FREE(s);
+}
+
+
+int tarkin_analysis_init(TarkinStream *s, TarkinInfo *ti,
+        TarkinError (*free_frame)(void *s, void *ptr),
+        TarkinError (*packet_out)(void *s, ogg_packet *ptr),
+        void *user_ptr) 
+{
+   if((!ti->inter.numerator)||(!ti->inter.denominator))return (-TARKIN_FAULT);
+   if((!free_frame) || (!packet_out)) return (-TARKIN_FAULT);
+   s->ti = ti;
+   s->free_frame = free_frame;
+   s->packet_out = packet_out;
+   s->user_ptr = user_ptr;
+   return(0);
+}
+
+
+extern int      tarkin_analysis_add_layer(TarkinStream *s,
+                                TarkinVideoLayerDesc *tvld)
+{
+   int i;
+   TarkinVideoLayer *layer;
+   if(s->n_layers) {
+      s->layer = REALLOC(s->layer,(s->n_layers+1) * sizeof(*s->layer));
+   } else {   
+      s->layer = MALLOC(sizeof(*s->layer));
+   }
+   layer = s->layer + s->n_layers;
+   memset(layer,0,sizeof(*s->layer));
+   memcpy (&layer->desc , tvld, sizeof(TarkinVideoLayerDesc));
+   
+   s->n_layers++;
+   s->ti->n_layers = s->n_layers;
+   s->ti->layer = s->layer;
+
+   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;
+   };
+   
+#ifdef DBG_OGG   
+   printf("dbg_ogg:add_layer %d with %d components\n",
+                       s->n_layers, layer->n_comp);
+#endif   
+
+   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 (i=0; i<layer->n_comp; i++){
+       layer->waveletbuf[i] = wavelet_3d_buf_new (layer->desc.width,
+                                                  layer->desc.height,
+                                                  layer->desc.frames_per_buf);
+       layer->packet[i].data = MALLOC(layer->desc.bitstream_len);
+       layer->packet[i].storage = layer->desc.bitstream_len;
+   }
+   /*
+   max_bitstream_len += layer->desc.bitstream_len
+        + 2 * 10 * sizeof(uint32_t) * layer->n_comp;    // truncation tables 
+   */
+   return (TARKIN_OK);
+}
+
+TarkinError _analysis_packetout(TarkinStream *s, uint32_t layer_id, 
+                                                       uint32_t comp)
+{
+   ogg_packet op;
+   oggpack_buffer opb;
+   uint8_t *data;
+   uint32_t data_len;
+   int i;
+   data = s->layer[layer_id].packet[comp].data;
+   data_len = s->layer[layer_id].packet[comp].data_len;
+   
+   oggpack_writeinit(&opb);
+   oggpack_write(&opb,0,8);   /* No feature flags for now */
+   oggpack_write(&opb,layer_id,12);
+   oggpack_write(&opb,comp,12);
+   for(i=0;i<data_len;i++)
+      oggpack_write(&opb,*(data + i), 8);
+
+   op.b_o_s = 0;
+   op.e_o_s = data_len?0:1;
+   op.granulepos = 0;
+   op.bytes = oggpack_bytes(&opb)+4;
+   op.packet = opb.buffer;
+#ifdef DBG_OGG
+   printf("dbg_ogg: writing packet layer %d, comp %d, data_len %d %s\n",
+                   layer_id, comp, data_len, op.e_o_s?"eos":""); 
+#endif
+   s->layer[layer_id].packet[comp].data_len = 0; /* so direct call => eos */
+   return(s->packet_out(s,&op));
+}
+
+void _stream_flush (TarkinStream *s)
+{
+   uint32_t i, j;
+
+   s->current_frame_in_buf=0;
+
+   for (i=0; i<s->n_layers; i++) {
+      TarkinVideoLayer *layer = &s->layer[i];
+
+      for (j=0; j<layer->n_comp; j++) {
+         uint32_t comp_bitstream_len;
+         TarkinPacket *packet = layer->packet + j;
+
+        /**
+         *  implicit 6:1:1 subsampling
+         */
+         if (j == 0)
+            comp_bitstream_len = 6*layer->desc.bitstream_len/(layer->n_comp+5);
+         else
+            comp_bitstream_len = layer->desc.bitstream_len/(layer->n_comp+5);
+
+         if(packet->storage < comp_bitstream_len) {
+           packet->storage = comp_bitstream_len;
+           packet->data = REALLOC (packet->data, comp_bitstream_len);
+         }
+            
+         wavelet_3d_buf_dump ("color-%d-%03d.pgm",
+                              s->current_frame, j,
+                              layer->waveletbuf[j], j == 0 ? 0 : 128);
+
+         wavelet_3d_buf_fwd_xform (layer->waveletbuf[j],
+                                   layer->desc.a_moments,
+                                   layer->desc.s_moments);
+
+         wavelet_3d_buf_dump ("coeff-%d-%03d.pgm",
+                              s->current_frame, j,
+                              layer->waveletbuf[j], 128);
+
+         packet->data_len = wavelet_3d_buf_encode_coeff (layer->waveletbuf[j],
+                                                      packet->data,
+                                                      comp_bitstream_len);
+
+         _analysis_packetout (s, i, j);
+      }
+   }
+}
+
+
+uint32_t tarkin_analysis_framein (TarkinStream *s, uint8_t *frame,
+                                  uint32_t layer_id, TarkinTime *date)
+{
+   TarkinVideoLayer *layer;
+
+   if(!frame) return (_analysis_packetout(s, 0, 0));  /* eos */
+   if((layer_id>=s->n_layers) || (date->denominator==0)) return (TARKIN_FAULT);
+   
+   layer = s->layer + layer_id;
+   layer->color_fwd_xform (frame, layer->waveletbuf,
+                              s->current_frame_in_buf);
+   /* We don't use this feature for now, neither date... */
+   s->free_frame(s,frame);
+
+   s->current_frame_in_buf++;
+
+   if (s->current_frame_in_buf == s->frames_per_buf)
+      _stream_flush (s);
+   
+#ifdef DBG_OGG
+   printf("dbg_ogg: framein at pos %d/%d, n° %d,%d on layer %d\n",
+           date->numerator, date->denominator, 
+          layer->frameno, s->current_frame, layer_id);
+#endif
+   
+   layer->frameno++;
+   return (++s->current_frame);
+}
+
+
+
+
+/**
+ *   tarkin_stream_read_header() is now info.c:_tarkin_unpack_layer_desc()
+ */
+
+
+
+TarkinError tarkin_stream_get_layer_desc (TarkinStream *s,
+                                          uint32_t layer_id,
+                                          TarkinVideoLayerDesc *desc)
+{
+   if (layer_id > s->n_layers-1)
+      return -TARKIN_INVALID_LAYER;
+
+   memcpy (desc, &(s->layer[layer_id].desc), sizeof(TarkinVideoLayerDesc));
+
+   return TARKIN_OK;
+}
+
+TarkinError tarkin_synthesis_init (TarkinStream *s, TarkinInfo *ti)
+{
+   s->ti = ti;
+   s->layer = ti->layer; /* It was malloc()ed by headerin() */
+   s->n_layers = ti->n_layers;
+   return (TARKIN_OK);
+}
+
+TarkinError tarkin_synthesis_packetin (TarkinStream *s, ogg_packet *op)
+{
+   uint32_t i, layer_id, comp, data_len;
+   uint32_t flags, junk;
+   int nread;
+   oggpack_buffer opb;
+   TarkinPacket *packet;
+#ifdef DBG_OGG
+   printf("dbg_ogg: Reading packet n° %lld, granulepos %lld, len %ld, %s%s\n", 
+          op->packetno, op->granulepos, op->bytes,
+          op->b_o_s?"b_o_s":"", op->e_o_s?"e_o_s":"");
+#endif
+   oggpack_readinit(&opb,op->packet,op->bytes);
+   flags = oggpack_read(&opb,8);
+   layer_id = oggpack_read(&opb,12);   /* Theses are  required for */
+   comp = oggpack_read(&opb,12);       /* data hole handling (or maybe
+                                           * packetno would be enough ?) */
+   nread = 4;
+
+   if(flags){     /* This is void "infinite future features" feature ;) */
+     if(flags & 1<<7){
+       junk = flags;
+       while (junk & 1<<7)
+         junk = oggpack_read(&opb,8);    /* allow for many future flags
+                                  that must be correctly ordonned. */
+     }
+     /* This shows how to get a feature's data:
+     if (flags & TARKIN_FLAGS_EXAMPLE){
+        tp->example = oggpack_read(&opb,32);
+        junk = tp->example & 3<<30;
+        tp->example &= 0x4fffffff;
+     }
+     */
+     for(junk=1<<31;junk & 1<<31;)     /* and many future data */
+       while((junk=oggpack_read(&opb,32)) & 1<<30);
+             /* That is, feature data comes in 30 bit chunks. We also have
+         * 31 potentially usefull bits in last chunk. */
+   }
+        
+   nread = (opb.ptr - opb.buffer);
+   data_len = op->bytes - nread;
+   
+#ifdef DBG_OGG
+   printf("   layer_id %d, comp %d, meta-data %dB, w3d data %dB.\n", 
+                  layer_id, comp,nread, data_len);
+#endif
+
+   /* We now have for shure our data. */
+   packet = &s->layer[layer_id].packet[comp];
+   if(packet->data_len)return(-TARKIN_UNUSED); /* Previous data wasn't used */
+   
+   if(packet->storage < data_len){
+      packet->storage = data_len + 255;
+      packet->data = REALLOC (packet->data, packet->storage);
+   }
+  
+   for(i=0;i < data_len ; i++)
+      packet->data[i] = oggpack_read(&opb,8);
+    
+   packet->data_len = data_len;
+
+   return(TARKIN_OK);
+}
+TarkinError tarkin_synthesis_frameout(TarkinStream *s, 
+                                      uint8_t **frame,
+                                      uint32_t layer_id, TarkinTime *date) 
+{  
+   int j;
+   TarkinVideoLayer *layer = &s->layer[layer_id];
+   if (s->current_frame_in_buf == 0) {
+      *frame = MALLOC (layer->desc.width * layer->desc.height * layer->n_comp);
+      for (j=0; j<layer->n_comp; j++) {
+         TarkinPacket *packet = layer->packet + j;
+            
+         if(packet->data_len == 0)goto err_out ;
+            
+         wavelet_3d_buf_decode_coeff (layer->waveletbuf[j], packet->data,
+                                         packet->data_len);
+
+         wavelet_3d_buf_dump ("rcoeff-%d-%03d.pgm",
+                                 s->current_frame, j, layer->waveletbuf[j],
+                                 128);
+
+         wavelet_3d_buf_inv_xform (layer->waveletbuf[j], 
+                                      layer->desc.a_moments,
+                                      layer->desc.s_moments);
+
+         wavelet_3d_buf_dump ("rcolor-%d-%03d.pgm",
+                                 s->current_frame, j,
+                                 layer->waveletbuf[j], j == 0 ? 0 : 128);
+      }
+
+      /* We did successfylly read a block from this layer, acknowledge it. */
+      for (j=0; j < layer->n_comp; j++)
+         layer->packet[j].data_len = 0; 
+   }
+
+   layer->color_inv_xform (layer->waveletbuf, *frame,
+                              s->current_frame_in_buf);
+   s->current_frame_in_buf++;
+   s->current_frame++;
+
+   if (s->current_frame_in_buf == s->frames_per_buf)
+      s->current_frame_in_buf=0;
+   
+   date->numerator   = layer->frameno * s->ti->inter.numerator;
+   date->denominator = s->ti->inter.denominator;
+#ifdef DBG_OGG
+   printf("dbg_ogg: outputting frame pos %d/%d from layer %d.\n",
+           date->numerator, date->denominator, layer_id);
+#endif
+   layer->frameno++;
+   return (TARKIN_OK);
+err_out:
+   FREE(*frame);
+   return (TARKIN_NEED_MORE);
+}
+
+int tarkin_synthesis_freeframe(TarkinStream *s, uint8_t *frame)
+{
+   FREE(frame);
+   
+   return(TARKIN_OK);   
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/ext/tarkin/tarkin.h b/ext/tarkin/tarkin.h
new file mode 100644 (file)
index 0000000..633f9a1
--- /dev/null
@@ -0,0 +1,239 @@
+#ifndef __TARKIN_H
+#define __TARKIN_H
+
+#include <stdio.h>
+#include "wavelet.h"
+#include <ogg/ogg.h>
+
+
+#define BUG(x...)                                                            \
+   do {                                                                      \
+      printf("BUG in %s (%s: line %i): ", __FUNCTION__, __FILE__, __LINE__); \
+      printf(#x);                                                            \
+      printf("\n");                                                          \
+      exit (-1);                                                             \
+   } while (0);
+
+
+/* Theses determine what infos the packet comes with */
+#define TARKIN_PACK_EXAMPLE                1
+
+typedef struct {
+   uint8_t *data;
+   uint32_t data_len;
+   uint32_t storage;
+}   TarkinPacket;
+
+
+typedef enum {
+   TARKIN_GRAYSCALE,
+   TARKIN_RGB24,       /*  tight packed RGB        */
+   TARKIN_RGB32,       /*  32bit, no alphachannel  */
+   TARKIN_RGBA,        /*  dito w/ alphachannel    */
+   TARKIN_YUV2,        /*  16 bits YUV             */
+   TARKIN_YUV12,       /*  12 bits YUV             */
+   TARKIN_FYUV,        /*  Tarkin's Fast YUV-like? */
+} TarkinColorFormat;
+
+#define TARKIN_INTERNAL_FORMAT TARKIN_FYUV
+
+typedef enum {
+   TARKIN_OK = 0,
+   TARKIN_IO_ERROR,
+   TARKIN_SIGNATURE_NOT_FOUND,
+   TARKIN_INVALID_LAYER,
+   TARKIN_INVALID_COLOR_FORMAT,
+   TARKIN_VERSION,
+   TARKIN_BAD_HEADER,
+   TARKIN_NOT_TARKIN,
+   TARKIN_FAULT,
+   TARKIN_UNUSED,
+   TARKIN_NEED_MORE,
+   TARKIN_NOT_IMPLEMENTED
+} TarkinError;
+
+
+
+typedef struct {
+   uint32_t width;
+   uint32_t height;
+   uint32_t a_moments;
+   uint32_t s_moments;
+   uint32_t frames_per_buf;
+   uint32_t bitstream_len;              /*  for all color components, bytes */
+   TarkinColorFormat format;
+} TarkinVideoLayerDesc;
+
+
+typedef struct {
+   TarkinVideoLayerDesc desc;
+   uint32_t n_comp;                     /*  number of color components */
+   Wavelet3DBuf **waveletbuf;
+   TarkinPacket *packet;
+   uint32_t current_frame_in_buf;
+   uint32_t frameno;
+
+   void (*color_fwd_xform) (uint8_t *rgba, Wavelet3DBuf *yuva [], uint32_t count);
+   void (*color_inv_xform) (Wavelet3DBuf *yuva [], uint8_t *rgba, uint32_t count);
+} TarkinVideoLayer;
+
+typedef struct {
+   uint32_t numerator;
+   uint32_t denominator;
+} TarkinTime; /* Let's say the unit is 1 second */
+
+typedef struct TarkinInfo {
+   int version;
+   int n_layers;
+   TarkinVideoLayer *layer;
+   TarkinTime inter;          /* numerator == O if per-frame time info. */
+   int frames_per_block;
+   int comp_per_block;        /* AKA "packets per block" for now */
+   uint32_t max_bitstream_len;
+
+  /* The below bitrate declarations are *hints*.
+     Combinations of the three values carry the following implications:
+     
+     all three set to the same value: 
+       implies a fixed rate bitstream
+     only nominal set: 
+       implies a VBR stream that averages the nominal bitrate.  No hard 
+       upper/lower limit
+     upper and or lower set: 
+       implies a VBR bitstream that obeys the bitrate limits. nominal 
+       may also be set to give a nominal rate.
+     none set:
+       the coder does not care to speculate.
+  */
+
+   long bitrate_upper;
+   long bitrate_nominal;
+   long bitrate_lower;
+   long bitrate_window;
+} TarkinInfo;
+
+/* This is used for encoding */
+typedef struct {
+   unsigned char *header;
+   unsigned char *header1;
+   unsigned char *header2;
+} tarkin_header_store;
+
+
+
+
+ /* Some of the fields in TarkinStream are redundent with TarkinInfo ones
+  * and will probably get deleted, namely n_layers and frames_per_buf */
+typedef struct TarkinStream {
+   uint32_t n_layers;
+   TarkinVideoLayer *layer;
+   uint32_t current_frame;
+   uint32_t current_frame_in_buf;
+   ogg_int64_t packetno;
+   uint32_t frames_per_buf;
+   uint32_t max_bitstream_len;
+   TarkinInfo *ti;
+   tarkin_header_store headers;
+   /* These callbacks are only used for encoding */
+   TarkinError (*free_frame)(void *tarkinstream, void *ptr);
+   /* These thing allows not to buffer but it needs global var in caller. */
+   TarkinError (*packet_out)(void *tarkinstream, ogg_packet *ptr);
+   void * user_ptr;
+} TarkinStream;
+
+
+typedef struct TarkinComment{
+  /* unlimited user comment fields.  libtarkin writes 'libtarkin'
+     whatever vendor is set to in encode */
+  char **user_comments;
+  int   *comment_lengths;
+  int    comments;
+  char  *vendor;
+
+} TarkinComment;
+
+/* Tarkin PRIMITIVES: general ***************************************/
+
+/* The Tarkin header is in three packets, the initial small packet in
+   the first page that identifies basic parameters, that is a TarkinInfo
+   structure, a second packet with bitstream comments and a third packet 
+   that holds the layers description structures. */
+
+
+/* Theses are the very same than Vorbis versions, they could be shared. */
+extern TarkinStream* tarkin_stream_new ();
+extern void          tarkin_stream_destroy (TarkinStream *s);
+extern void     tarkin_info_init(TarkinInfo *vi);
+extern void     tarkin_info_clear(TarkinInfo *vi);
+extern void     tarkin_comment_init(TarkinComment *vc);
+extern void     tarkin_comment_add(TarkinComment *vc, char *comment); 
+extern void     tarkin_comment_add_tag(TarkinComment *vc, 
+                                       char *tag, char *contents);
+extern char    *tarkin_comment_query(TarkinComment *vc, char *tag, int count);
+extern int      tarkin_comment_query_count(TarkinComment *vc, char *tag);
+extern void     tarkin_comment_clear(TarkinComment *vc);
+
+/* Tarkin PRIMITIVES: analysis layer ****************************/
+/* Tarkin encoding is done this way : you init it passing a fresh
+ * TarkinStream and a fresh TarkinInfo which has at least the rate_num
+ * field renseigned. You also pass it two callback functions: free_frame()
+ * is called when the lib doesn't need a frame anymore, and packet_out
+ * is called when a packet is ready. The pointers given as arguments to 
+ * these callback functions are of course only valid at the function call
+ * time. The user_ptr is stored in s and can be used by packet_out(). */
+extern int tarkin_analysis_init(TarkinStream *s,
+               TarkinInfo *ti,
+               TarkinError (*free_frame)(void *tarkinstream, void *ptr),
+               TarkinError (*packet_out)(void *tarkinstream, ogg_packet *ptr),
+               void *user_ptr
+               );
+/* Then you need to add at least a layer in your stream, passing a 
+ * TarkinVideoLayerDesc renseigned at least on the width, height and
+ * format parameters. */
+extern int   tarkin_analysis_add_layer(TarkinStream *s,
+                                TarkinVideoLayerDesc *tvld);
+/* At that point you are ready to get headers out the lib by calling
+ * tarkin_analysis_headerout() passing it a renseigned TarkinComment
+ * structure. It does fill your 3 ogg_packet headers, which are valid
+ * till next call */
+extern int   TarkinCommentheader_out(TarkinComment *vc, ogg_packet *op);
+extern TarkinError  tarkin_analysis_headerout(TarkinStream *s,
+                                          TarkinComment *vc,
+                                          ogg_packet *op,
+                                          ogg_packet *op_comm,
+                                          ogg_packet *op_code);
+/* You are now ready to pass in frames to the codec, however don't free
+ * them before the codec told you so. It'll tell you when packets are
+ * ready to be taken out. When you have no more frame, simply pass NULL.
+ * If you encode multiple layers you have to do it synchronously, putting 
+ * one frame from each layer at a time. */
+extern uint32_t      tarkin_analysis_framein(TarkinStream *s,
+                                uint8_t *frame, /* NULL for EOS */
+                                uint32_t layer,
+                                TarkinTime *date);
+
+/* Tarkin PRIMITIVES: synthesis layer *******************************/
+/* For decoding, you needs first to give the three first packet of the 
+ * stream to tarkin_synthesis_headerin() which will fill for you blank
+ * TarkinInfo and TarkinComment. */
+extern TarkinError tarkin_synthesis_headerin(TarkinInfo *vi,TarkinComment *vc,
+                                        ogg_packet *op);
+/* Then you can init your stream with your TarkinInfo struct. */
+extern TarkinError tarkin_synthesis_init(TarkinStream *s,TarkinInfo *ti);
+/* All subsequent packets are to this be passed to tarkin_synthesis_packetin*/
+extern TarkinError tarkin_synthesis_packetin(TarkinStream *s, ogg_packet *op);
+/* and then tarkin_synthesis_frameout gives you ptr on next frame, or NULL. It
+ * also fills for you date. */
+extern TarkinError  tarkin_synthesis_frameout(TarkinStream *s,
+                      uint8_t **frame, uint32_t layer_id, TarkinTime *date);
+/* When you're done with a frame, tell it to the codec with this. */
+extern int tarkin_synthesis_freeframe(TarkinStream *s, uint8_t *frame);
+
+
+#endif
+
+
+
+
+
+
diff --git a/ext/tarkin/wavelet.c b/ext/tarkin/wavelet.c
new file mode 100644 (file)
index 0000000..a83aa49
--- /dev/null
@@ -0,0 +1,121 @@
+#include "mem.h"
+#include "wavelet.h"
+
+/**
+ *   (The transform code is in wavelet_xform.c)
+ */
+
+
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#define MAX3(a,b,c) (MAX(a,MAX(b,c)))
+
+
+
+Wavelet3DBuf* wavelet_3d_buf_new (uint32_t width, uint32_t height,
+                                  uint32_t frames)
+{
+   Wavelet3DBuf* buf = (Wavelet3DBuf*) MALLOC (sizeof (Wavelet3DBuf));
+   uint32_t _w = width;
+   uint32_t _h = height;
+   uint32_t _f = frames;
+   int level;
+
+   if (!buf)
+      return NULL;
+
+   buf->data = (TYPE*) MALLOC (width * height * frames * sizeof (TYPE));
+
+   if (!buf->data) {
+      wavelet_3d_buf_destroy (buf);
+      return NULL;
+   }
+
+   buf->width = width;
+   buf->height = height;
+   buf->frames = frames;
+   buf->scales = 1;
+
+   while (_w > 1 || _h > 1 || _f > 1) {
+      buf->scales++;
+      _w = (_w+1)/2;
+      _h = (_h+1)/2;
+      _f = (_f+1)/2;
+   }
+
+   buf->w = (uint32_t*) MALLOC (buf->scales * sizeof (uint32_t));
+   buf->h = (uint32_t*) MALLOC (buf->scales * sizeof (uint32_t));
+   buf->f = (uint32_t*) MALLOC (buf->scales * sizeof (uint32_t));
+   buf->offset = (uint32_t (*) [8]) MALLOC (8 * buf->scales * sizeof (uint32_t));
+
+   buf->scratchbuf = (TYPE*) MALLOC (MAX3(width, height, frames) * sizeof (TYPE));
+
+   if (!buf->w || !buf->h || !buf->f || !buf->offset || !buf->scratchbuf) {
+      wavelet_3d_buf_destroy (buf);
+      return NULL;
+   }
+
+   buf->w [buf->scales-1] = width;
+   buf->h [buf->scales-1] = height;
+   buf->f [buf->scales-1] = frames;
+
+   for (level=buf->scales-2; level>=0; level--) {
+      buf->w [level] = (buf->w [level+1] + 1) / 2;
+      buf->h [level] = (buf->h [level+1] + 1) / 2;
+      buf->f [level] = (buf->f [level+1] + 1) / 2;
+      buf->offset[level][0] = 0;
+      buf->offset[level][1] = buf->w [level];
+      buf->offset[level][2] = buf->h [level] * width;
+      buf->offset[level][3] = buf->f [level] * width * height;
+      buf->offset[level][4] = buf->offset [level][2] + buf->w [level];
+      buf->offset[level][5] = buf->offset [level][3] + buf->w [level];
+      buf->offset[level][6] = buf->offset [level][3] + buf->offset [level][2];
+      buf->offset[level][7] = buf->offset [level][6] + buf->w [level];
+   }
+
+   return buf;
+}
+
+
+void wavelet_3d_buf_destroy (Wavelet3DBuf* buf)
+{
+   if (buf) {
+      if (buf->data)
+         FREE (buf->data);
+      if (buf->w)
+         FREE (buf->w);
+      if (buf->h)
+         FREE (buf->h);
+      if (buf->f)
+         FREE (buf->f);
+      if (buf->offset)
+         FREE (buf->offset);
+      if (buf->scratchbuf)
+         FREE (buf->scratchbuf);
+      FREE (buf);
+   }
+}
+
+
+#if defined(DBG_XFORM)
+
+#include "pnm.h"
+
+void wavelet_3d_buf_dump (char *fmt,
+                          uint32_t first_frame_in_buf,
+                          uint32_t id,
+                          Wavelet3DBuf* buf,
+                          int16_t offset)
+{
+   char fname [256];
+   uint32_t f;
+
+   for (f=0; f<buf->frames; f++) {
+      snprintf (fname, 256, fmt, id, first_frame_in_buf + f);
+
+      write_pgm16 (fname, buf->data + f * buf->width * buf->height,
+                   buf->width, buf->height, offset);
+   }
+}
+#endif
+
+
diff --git a/ext/tarkin/wavelet.h b/ext/tarkin/wavelet.h
new file mode 100644 (file)
index 0000000..914c279
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef __WAVELET_H
+#define __WAVELET_H
+
+#include <stdint.h>
+
+
+typedef struct {
+   TYPE *data;
+   uint32_t width;
+   uint32_t height;
+   uint32_t frames;
+   uint32_t scales;
+   uint32_t *w;
+   uint32_t *h;
+   uint32_t *f;
+   uint32_t (*offset)[8];
+   TYPE *scratchbuf;
+} Wavelet3DBuf;
+
+
+extern Wavelet3DBuf* wavelet_3d_buf_new (uint32_t width, uint32_t height,
+                                         uint32_t frames);
+
+extern void wavelet_3d_buf_destroy (Wavelet3DBuf* buf);
+
+/**
+ *  transform buf->data
+ *  a_moments is the number of vanishing moments of the analyzing
+ *  highpass filter,
+ *  s_moments the one of the synthesizing lowpass filter.
+ */
+extern void wavelet_3d_buf_fwd_xform (Wavelet3DBuf* buf,
+                                      int a_moments, int s_moments);
+extern void wavelet_3d_buf_inv_xform (Wavelet3DBuf* buf,
+                                      int a_moments, int s_moments);
+
+extern int  wavelet_3d_buf_encode_coeff (const Wavelet3DBuf* buf,
+                                         uint8_t *bitstream,
+                                         uint32_t limit);
+
+extern void wavelet_3d_buf_decode_coeff (Wavelet3DBuf* buf,
+                                         uint8_t *bitstream,
+                                         uint32_t limit);
+
+#if defined(DBG_XFORM)
+extern void wavelet_3d_buf_dump (char *fmt,
+                                 uint32_t first_frame_in_buf,
+                                 uint32_t id,
+                                 Wavelet3DBuf* buf,
+                                 int16_t offset);
+#else
+#define wavelet_3d_buf_dump(x...)
+#endif
+
+#endif
diff --git a/ext/tarkin/wavelet_coeff.c b/ext/tarkin/wavelet_coeff.c
new file mode 100644 (file)
index 0000000..2cc0a8f
--- /dev/null
@@ -0,0 +1,506 @@
+#include "mem.h"
+#include "wavelet.h"
+#include "rle.h"
+
+#define printf(args...)
+
+#define GRAY_CODES 1
+
+#if defined(GRAY_CODES)
+static inline
+uint16_t binary_to_gray (uint16_t x) { return  x ^ (x >> 1); }
+
+static inline
+uint16_t gray_to_binary (uint16_t x)
+{ int i; for (i=1; i<16; i+=i) x ^= x >> i; return x; }
+#endif
+
+
+static inline
+void encode_coeff (ENTROPY_CODER significand_bitstream [],
+                   ENTROPY_CODER insignificand_bitstream [],
+                   TYPE coeff)
+{
+   int sign = (coeff >> (8*sizeof(TYPE)-1)) & 1;
+#if defined(GRAY_CODES)
+   TYPE significance = binary_to_gray(coeff);
+#else
+   static TYPE mask [2] = { 0, ~0 };
+   TYPE significance = coeff ^ mask[sign];
+#endif
+   int i = TYPE_BITS;
+
+   do {
+      i--;
+      OUTPUT_BIT(&significand_bitstream[i], (significance >> i) & 1);
+   } while (!((significance >> i) & 1) && i > 0);
+
+   OUTPUT_BIT(&significand_bitstream[i], sign);
+
+   while (--i >= 0)
+      OUTPUT_BIT(&insignificand_bitstream[i], (significance >> i) & 1);
+}
+
+
+
+static inline
+TYPE decode_coeff (ENTROPY_CODER significand_bitstream [],
+                   ENTROPY_CODER insignificand_bitstream [])
+{
+#if !defined(GRAY_CODES)
+   static TYPE mask [2] = { 0, ~0 };
+#endif
+   TYPE significance = 0;
+   int sign;
+   int i = TYPE_BITS;
+
+   do {
+      i--;
+      significance |= INPUT_BIT(&significand_bitstream[i]) << i;
+//    if (ENTROPY_CODER_EOS(&significand_bitstream[i]))
+//       return 0;
+   } while (!significance && i > 0);
+
+   sign = INPUT_BIT(&significand_bitstream[i]);
+// if (ENTROPY_CODER_EOS(&significand_bitstream[i]))
+//    return 0;
+
+   while (--i >= 0)
+      significance |= INPUT_BIT(&insignificand_bitstream[i]) << i;
+
+#if defined(GRAY_CODES)
+   significance |= sign << (8*sizeof(TYPE)-1);
+   return gray_to_binary(significance);
+#else
+   return (significance ^ mask[sign]);
+#endif
+}
+
+
+static inline
+uint32_t skip_0coeffs (Wavelet3DBuf* buf,
+                       ENTROPY_CODER s_stream [],
+                       ENTROPY_CODER i_stream [],
+                       uint32_t limit)
+{
+   int i;
+   uint32_t skip = limit;
+
+   for (i=0; i<TYPE_BITS; i++) {
+      if (ENTROPY_CODER_SYMBOL(&s_stream[i]) != 0) {
+         return 0;
+      } else {
+         uint32_t runlength = ENTROPY_CODER_RUNLENGTH(&s_stream[i]);
+         if (i==0)
+            runlength /= 2;     /* sign bits are in this bitplane ... */
+         if (skip > runlength)
+            skip = runlength;
+         if (skip <= 2)
+            return 0;
+      }
+   }
+
+   ENTROPY_CODER_SKIP(&s_stream[0], 2*skip); /* kill sign+significance bits */
+
+   for (i=1; i<TYPE_BITS; i++)
+      ENTROPY_CODER_SKIP(&s_stream[i], skip);
+
+   return skip;
+}
+
+
+
+#if 1
+static inline
+void encode_quadrant (const Wavelet3DBuf* buf,
+                      int level, int quadrant, uint32_t w, uint32_t h, uint32_t f,
+                      ENTROPY_CODER significand_bitstream [],
+                      ENTROPY_CODER insignificand_bitstream [])
+{
+   uint32_t x, y, z;
+
+   for (z=0; z<f; z++) {
+      for (y=0; y<h; y++) {
+         for (x=0; x<w; x++) {
+            unsigned int index = buf->offset [level] [quadrant]
+                                   + z * buf->width * buf->height
+                                   + y * buf->width + x;
+
+            encode_coeff (significand_bitstream, insignificand_bitstream,
+                          buf->data [index]);
+         }
+      }
+   }
+}
+
+
+static
+void encode_coefficients (const Wavelet3DBuf* buf,
+                          ENTROPY_CODER s_stream [],
+                          ENTROPY_CODER i_stream [])
+{
+   int level;
+
+   encode_coeff (s_stream, i_stream, buf->data[0]);
+
+   for (level=0; level<buf->scales-1; level++) {
+      uint32_t w, h, f, w1, h1, f1;
+
+      w = buf->w [level];
+      h = buf->h [level];
+      f = buf->f [level];
+      w1 = buf->w [level+1] - w;
+      h1 = buf->h [level+1] - h;
+      f1 = buf->f [level+1] - f;
+
+      if (w1 > 0) encode_quadrant(buf,level,1,w1,h,f,s_stream,i_stream);
+      if (h1 > 0) encode_quadrant(buf,level,2,w,h1,f,s_stream,i_stream);
+      if (f1 > 0) encode_quadrant(buf,level,3,w,h,f1,s_stream,i_stream);
+      if (w1 > 0 && h1 > 0) encode_quadrant(buf,level,4,w1,h1,f,s_stream,i_stream);
+      if (w1 > 0 && f1 > 0) encode_quadrant(buf,level,5,w1,h,f1,s_stream,i_stream);
+      if (h1 > 0 && f1 > 0) encode_quadrant(buf,level,6,w,h1,f1,s_stream,i_stream);
+      if (h1 > 0 && f1 > 0 && f1 > 0)
+         encode_quadrant (buf,level,7,w1,h1,f1,s_stream,i_stream);
+   }
+}
+
+
+static inline
+void decode_quadrant (Wavelet3DBuf* buf,
+                      int level, int quadrant, uint32_t w, uint32_t h, uint32_t f,
+                      ENTROPY_CODER s_stream [],
+                      ENTROPY_CODER i_stream [])
+{
+   uint32_t x, y, z;
+
+   z = 0;
+   do {
+      y = 0;
+      do {
+         x = 0;
+         do {
+            uint32_t skip;
+            uint32_t index = buf->offset [level] [quadrant]
+                               + z * buf->width * buf->height
+                               + y * buf->width + x;
+
+            buf->data [index] = decode_coeff (s_stream, i_stream);
+
+            skip = skip_0coeffs (buf, s_stream, i_stream,
+                                 (w-x-1)+(h-y-1)*w+(f-z-1)*w*h);
+            if (skip > 0) {
+               x += skip;
+               while (x >= w) {
+                  y++; x -= w;
+                  while (y >= h) {
+                     z++; y -= h;
+                     if (z >= f)
+                        return;
+                  }
+               }
+            }
+            x++;
+         } while (x < w);
+         y++;
+      } while (y < h);
+      z++;
+   } while (z < f);
+}
+
+
+static
+void decode_coefficients (Wavelet3DBuf* buf,
+                          ENTROPY_CODER s_stream [],
+                          ENTROPY_CODER i_stream [])
+{
+   int level;
+
+   buf->data[0] = decode_coeff (s_stream, i_stream);
+
+   for (level=0; level<buf->scales-1; level++) {
+      uint32_t w, h, f, w1, h1, f1;
+
+      w = buf->w [level];
+      h = buf->h [level];
+      f = buf->f [level];
+      w1 = buf->w [level+1] - w;
+      h1 = buf->h [level+1] - h;
+      f1 = buf->f [level+1] - f;
+
+      if (w1 > 0) decode_quadrant(buf,level,1,w1,h,f,s_stream,i_stream);
+      if (h1 > 0) decode_quadrant(buf,level,2,w,h1,f,s_stream,i_stream);
+      if (f1 > 0) decode_quadrant(buf,level,3,w,h,f1,s_stream,i_stream);
+      if (w1 > 0 && h1 > 0) decode_quadrant(buf,level,4,w1,h1,f,s_stream,i_stream);
+      if (w1 > 0 && f1 > 0) decode_quadrant(buf,level,5,w1,h,f1,s_stream,i_stream);
+      if (h1 > 0 && f1 > 0) decode_quadrant(buf,level,6,w,h1,f1,s_stream,i_stream);
+      if (h1 > 0 && f1 > 0 && f1 > 0)
+         decode_quadrant (buf,level,7,w1,h1,f1,s_stream,i_stream);
+   }
+}
+#else
+
+static
+void encode_coefficients (const Wavelet3DBuf* buf,
+                          ENTROPY_CODER s_stream [],
+                          ENTROPY_CODER i_stream [])
+{
+   uint32_t i;
+
+   for (i=0; i<buf->width*buf->height*buf->frames; i++)
+      encode_coeff(s_stream, i_stream, buf->data[i]);
+}
+
+
+
+
+static
+void decode_coefficients (Wavelet3DBuf* buf,
+                          ENTROPY_CODER s_stream [],
+                          ENTROPY_CODER i_stream [])
+{
+   uint32_t i;
+
+   for (i=0; i<buf->width*buf->height*buf->frames; i++) {
+      uint32_t skip;
+
+      buf->data[i] = decode_coeff(s_stream, i_stream);
+
+      skip = skip_0coeffs (buf, s_stream, i_stream,
+                           buf->width*buf->height*buf->frames - i);
+      i += skip;
+   }
+}
+#endif
+
+
+
+static
+uint32_t setup_limittabs (ENTROPY_CODER significand_bitstream [],
+                          ENTROPY_CODER insignificand_bitstream [],
+                          uint32_t significand_limittab [],
+                          uint32_t insignificand_limittab [],
+                          uint32_t limit)
+{
+   uint32_t significand_limit;
+   uint32_t insignificand_limit;
+   uint32_t byte_count;
+   int i;
+
+   assert (limit > 2 * TYPE_BITS * sizeof(uint32_t)); /* limit too small */
+
+   printf ("%s: limit == %u\n", __FUNCTION__, limit);
+   byte_count = 2 * TYPE_BITS * sizeof(uint32_t); /* 2 binary coded limittabs */
+   limit -= byte_count;
+   printf ("%s: rem. limit == %u\n", __FUNCTION__, limit);
+
+   significand_limit = limit * 7 / 8;
+   insignificand_limit = limit - significand_limit;
+
+   printf ("%s: limit == %u\n", __FUNCTION__, limit);
+   printf ("significand limit == %u\n", significand_limit);
+   printf ("insignificand limit == %u\n", insignificand_limit);
+
+   for (i=TYPE_BITS-1; i>=0; i--) {
+      uint32_t s_bytes, i_bytes;
+
+      if (i > 0) {
+         significand_limittab[i] = (significand_limit + 1) / 2;
+         insignificand_limittab[i] = (insignificand_limit + 1) / 2;
+      } else {
+         significand_limittab[0] = significand_limit;
+         insignificand_limittab[0] = insignificand_limit;
+      }
+
+      s_bytes = ENTROPY_ENCODER_FLUSH(&significand_bitstream[i]);
+      i_bytes = ENTROPY_ENCODER_FLUSH(&insignificand_bitstream[i]);
+
+      if (s_bytes < significand_limittab[i])
+         significand_limittab[i] = s_bytes;
+
+      if (i_bytes < insignificand_limittab[i])
+         insignificand_limittab[i] = i_bytes;
+
+      byte_count += significand_limittab[i];
+      byte_count += insignificand_limittab[i];
+
+      printf ("insignificand_limittab[%i]  == %u / %u\n", 
+              i, insignificand_limittab[i], i_bytes);
+      printf ("  significand_limittab[%i]  == %u / %u\n",
+              i, significand_limittab[i], s_bytes);
+
+      significand_limit -= significand_limittab[i];
+      insignificand_limit -= insignificand_limittab[i];
+   }
+
+   printf ("byte_count == %u\n", byte_count);
+
+   return byte_count;
+}
+
+
+/**
+ *  write 'em binary for now, should be easy to compress ...
+ */
+static
+uint8_t* write_limittabs (uint8_t *bitstream,
+                          uint32_t significand_limittab [],
+                          uint32_t insignificand_limittab [])
+{
+   int i;
+
+   for (i=0; i<TYPE_BITS; i++) {
+      *(uint32_t*) bitstream = significand_limittab[i];
+      bitstream += 4;
+   }
+
+   for (i=0; i<TYPE_BITS; i++) {
+      *(uint32_t*) bitstream = insignificand_limittab[i];
+      bitstream += 4;
+   }
+
+   return bitstream;
+}
+
+
+static
+uint8_t* read_limittabs (uint8_t *bitstream,
+                         uint32_t significand_limittab [],
+                         uint32_t insignificand_limittab [])
+{
+   int i;
+
+   for (i=0; i<TYPE_BITS; i++) {
+      significand_limittab[i] = *(uint32_t*) bitstream;
+      printf ("significand_limittab[%i]  == %u\n", i, significand_limittab[i]);
+      bitstream += 4;
+   }
+
+   for (i=0; i<TYPE_BITS; i++) {
+      insignificand_limittab[i] = *(uint32_t*) bitstream;
+      printf ("insignificand_limittab[%i]  == %u\n", i, insignificand_limittab[i]);
+      bitstream += 4;
+   }
+   return bitstream;
+}
+
+
+/**
+ *  concatenate entropy coder bitstreams
+ */
+static
+void merge_bitstreams (uint8_t *bitstream,
+                       ENTROPY_CODER significand_bitstream [],
+                       ENTROPY_CODER insignificand_bitstream [],
+                       uint32_t significand_limittab [],
+                       uint32_t insignificand_limittab [])
+{
+   int i;
+
+   for (i=TYPE_BITS-1; i>=0; i--) {
+      memcpy (bitstream,
+              ENTROPY_CODER_BITSTREAM(&significand_bitstream[i]),
+              significand_limittab[i]);
+
+      bitstream += significand_limittab[i];
+   }
+
+   for (i=TYPE_BITS-1; i>=0; i--) {
+      memcpy (bitstream,
+              ENTROPY_CODER_BITSTREAM(&insignificand_bitstream[i]),
+              insignificand_limittab[i]);
+
+      bitstream += insignificand_limittab[i];
+   }
+}
+
+
+static
+void split_bitstreams (uint8_t *bitstream,
+                       ENTROPY_CODER significand_bitstream [],
+                       ENTROPY_CODER insignificand_bitstream [],
+                       uint32_t significand_limittab [],
+                       uint32_t insignificand_limittab [])
+{
+   uint32_t byte_count;
+   int i;
+
+   for (i=TYPE_BITS-1; i>=0; i--) {
+      byte_count = significand_limittab[i];
+      ENTROPY_DECODER_INIT(&significand_bitstream[i], bitstream, byte_count);
+      bitstream += byte_count;
+   }
+
+   for (i=TYPE_BITS-1; i>=0; i--) {
+      byte_count = insignificand_limittab[i];
+      ENTROPY_DECODER_INIT(&insignificand_bitstream[i], bitstream, byte_count);
+      bitstream += byte_count;
+   }
+}
+
+
+int wavelet_3d_buf_encode_coeff (const Wavelet3DBuf* buf,
+                                 uint8_t *bitstream,
+                                 uint32_t limit)
+{
+   ENTROPY_CODER significand_bitstream [TYPE_BITS];
+   ENTROPY_CODER insignificand_bitstream [TYPE_BITS];
+   uint32_t significand_limittab [TYPE_BITS];
+   uint32_t insignificand_limittab [TYPE_BITS];
+   uint32_t byte_count;
+   int i;
+
+   for (i=0; i<TYPE_BITS; i++)
+      ENTROPY_ENCODER_INIT(&significand_bitstream[i], limit);
+   for (i=0; i<TYPE_BITS; i++)
+      ENTROPY_ENCODER_INIT(&insignificand_bitstream[i], limit);
+
+   encode_coefficients (buf, significand_bitstream, insignificand_bitstream);
+
+   byte_count = setup_limittabs (significand_bitstream, insignificand_bitstream,
+                                 significand_limittab, insignificand_limittab,
+                                 limit);
+
+   bitstream = write_limittabs (bitstream,
+                                significand_limittab, insignificand_limittab);
+
+   merge_bitstreams (bitstream, significand_bitstream, insignificand_bitstream,
+                     significand_limittab, insignificand_limittab);
+
+   for (i=0; i<TYPE_BITS; i++) {
+      ENTROPY_ENCODER_DONE(&significand_bitstream[i]);
+      ENTROPY_ENCODER_DONE(&insignificand_bitstream[i]);
+   }
+
+   return byte_count;
+}
+
+
+void wavelet_3d_buf_decode_coeff (Wavelet3DBuf* buf,
+                                  uint8_t *bitstream,
+                                  uint32_t byte_count)
+{
+   ENTROPY_CODER significand_bitstream [TYPE_BITS];
+   ENTROPY_CODER insignificand_bitstream [TYPE_BITS];
+   uint32_t significand_limittab [TYPE_BITS];
+   uint32_t insignificand_limittab [TYPE_BITS];
+   int i;
+
+   memset (buf->data, 0,
+           buf->width * buf->height * buf->frames * sizeof(TYPE));
+
+   bitstream = read_limittabs (bitstream,
+                               significand_limittab, insignificand_limittab);
+
+   split_bitstreams (bitstream, significand_bitstream, insignificand_bitstream,
+                               significand_limittab, insignificand_limittab);
+
+   decode_coefficients (buf, significand_bitstream, insignificand_bitstream);
+
+   for (i=0; i<TYPE_BITS; i++) {
+      ENTROPY_DECODER_DONE(&significand_bitstream[i]);
+      ENTROPY_DECODER_DONE(&insignificand_bitstream[i]);
+   }
+}
+
+
diff --git a/ext/tarkin/wavelet_xform.c b/ext/tarkin/wavelet_xform.c
new file mode 100644 (file)
index 0000000..768446a
--- /dev/null
@@ -0,0 +1,391 @@
+#include "mem.h"
+#include <assert.h>
+#include "wavelet.h"
+
+
+
+static
+void fwd_analyze_1 (const TYPE *x, TYPE *d, int stride, int n)
+{
+   int i, k=n/2;
+
+   for (i=0; i<k; i++)
+      d[i] = x[(2*i+1)*stride] - x[2*i*stride];
+}
+
+
+static
+void fwd_synthesize_1 (const TYPE *x, TYPE *s, const TYPE *d, int stride, int n)
+{
+   int i, k=n/2;
+
+   for (i=0; i<k; i++)
+      s[i*stride] = x[2*i*stride] + d[i] / 2;
+   if (n & 1)
+      s[k*stride] = x[2*k*stride] + d[k-1] / 2;
+}
+
+
+static
+void inv_analyze_1 (TYPE *x, const TYPE *d, int stride, int n)
+{
+   int i, k=n/2;
+
+   for (i=0; i<k; i++)
+      x[(2*i+1)*stride] = d[i] + x[2*i*stride];
+}
+
+
+static
+void inv_synthesize_1 (TYPE *x, const TYPE *s, const TYPE *d, int stride, int n)
+{
+   int i, k=n/2;
+
+   for (i=0; i<k; i++)
+      x[2*i*stride] = s[i] - d[i] / 2;
+   if (n & 1)
+      x[2*k*stride] = s[k] - d[k-1] / 2;
+}
+
+
+
+static
+void fwd_analyze_2 (const TYPE *x, TYPE *d, int stride, int n)
+{
+   int i, k=n/2;
+
+   if (n & 1) {
+      for (i=0; i<k; i++)
+         d[i] = x[(2*i+1)*stride] - (x[2*i*stride] + x[(2*i+2)*stride]) / 2;
+   } else {
+      for (i=0; i<k-1; i++)
+         d[i] = x[(2*i+1)*stride] - (x[2*i*stride] + x[(2*i+2)*stride]) / 2;
+      d[k-1] = x[(n-1)*stride] - x[(n-2)*stride];
+   }
+}
+
+
+
+static
+void fwd_synthesize_2 (const TYPE *x, TYPE *s, const TYPE *d, int stride, int n)
+{
+   int i, k=n/2;
+
+   s[0] = x[0] + d[1] / 2;
+   for (i=1; i<k; i++)
+      s[i*stride] = x[2*i*stride] + (d[i-1] + d[i]) / 4;
+   if (n & 1)
+      s[k*stride] = x[2*k*stride] + d[k-1] / 2;
+}
+
+
+static inline
+void inv_analyze_2 (TYPE *x, const TYPE *d, int stride, int n)
+{
+   int i, k=n/2;
+
+   if (n & 1) {
+      for (i=0; i<k; i++)
+         x[(2*i+1)*stride] = d[i] + (x[2*i*stride] + x[(2*i+2)*stride]) / 2;
+   } else {
+      for (i=0; i<k-1; i++)
+         x[(2*i+1)*stride] = d[i] + (x[2*i*stride] + x[(2*i+2)*stride]) / 2;
+      x[(n-1)*stride] = d[k-1] + x[(n-2)*stride];
+   }
+}
+
+
+static inline
+void inv_synthesize_2 (TYPE *x, const TYPE *s, const TYPE *d, int stride, int n)
+{
+   int i, k=n/2;
+
+   x[0] = s[0] - d[1] / 2;
+   for (i=1; i<k; i++)
+      x[2*i*stride] = s[i] - (d[i-1] + d[i]) / 4;
+   if (n & 1)
+      x[2*k*stride] = s[k] - d[k-1] / 2;
+}
+
+
+
+static 
+void fwd_analyze_4 (const TYPE *x, TYPE *d, int stride, int n)
+{
+   int i, k=n/2;
+
+   d[0] = x[stride] - (x[0] + x[2*stride]) / 2;
+
+   if (n & 1) {
+      for (i=1; i<k-1; i++)
+         d[i] = x[(2*i+1)*stride]
+               - ((uint32_t) 9 * (x[2*i*stride] + x[(2*i+2)*stride])
+                               - (x[(2*i-2)*stride] + x[(2*i+4)*stride])) / 16;
+      if (k > 1)
+         d[k-1] = x[(2*k-1)*stride] - (x[(2*k-2)*stride] + x[2*k*stride]) / 2;
+   } else {
+      for (i=1; i<k-2; i++)
+         d[i] = x[(2*i+1)*stride]
+               - ((uint32_t) 9 * (x[2*i*stride] + x[(2*i+2)*stride])
+                               - (x[(2*i-2)*stride] + x[(2*i+4)*stride])) / 16;
+      if (k > 2)
+         d[k-2] = x[(2*k-3)*stride] - (x[(2*k-4)*stride]
+                                     + x[(2*k-2)*stride]) / 2;
+      if (k > 1)
+         d[k-1] = x[(n-1)*stride] - x[(n-2)*stride];
+   }
+}
+
+
+static
+void fwd_synthesize_4 (const TYPE *x, TYPE *s, const TYPE *d, int stride, int n)
+{
+   int i, k=n/2;
+
+   s[0] = x[0] + d[1] / 2;
+   if (k > 1)
+      s[stride] = x[2*stride] + (d[0] + d[1]) / 4;
+   for (i=2; i<k-1; i++)
+      s[i*stride] = x[2*i*stride]
+                   + ((uint32_t) 9 * (d[i-1] + d[i]) - (d[i-2] + d[i+1])) / 32;
+   if (k > 2)
+      s[(k-1)*stride] = x[(2*k-2)*stride] + (d[k-2] + d[k-1]) / 4;
+   if (n & 1)
+      s[k*stride] = x[2*k*stride] + d[k-1] / 2;
+}
+
+
+static
+void inv_analyze_4 (TYPE *x, const TYPE *d, int stride, int n)
+{
+   int i, k=n/2;
+
+   x[stride] = d[0] + (x[0] + x[2*stride]) / 2;
+
+   if (n & 1) {
+      for (i=1; i<k-1; i++)
+         x[(2*i+1)*stride] = d[i] 
+               + ((uint32_t) 9 * (x[2*i*stride] + x[(2*i+2)*stride])
+                               - (x[(2*i-2)*stride] + x[(2*i+4)*stride])) / 16;
+      if (k > 1)
+         x[(2*k-1)*stride] = d[k-1] + (x[(2*k-2)*stride] + x[2*k*stride]) / 2;
+   } else {
+      for (i=1; i<k-2; i++)
+         x[(2*i+1)*stride] = d[i]
+               + (9 * (x[2*i*stride] + x[(2*i+2)*stride])
+                    - (x[(2*i-2)*stride] + x[(2*i+4)*stride])) / 16;
+      if (k > 2)
+         x[(2*k-3)*stride] = d[k-2] + (x[(2*k-4)*stride]
+                                     + x[(2*k-2)*stride]) / 2;
+      if (k > 1)
+         x[(n-1)*stride] = d[k-1] + x[(n-2)*stride];
+   }
+}
+
+
+static
+void inv_synthesize_4 (TYPE *x, const TYPE *s, const TYPE *d, int stride, int n)
+{
+   int i, k=n/2;
+
+   x[0] = s[0] - d[1] / 2;
+   if (k > 1)
+      x[2*stride] = s[1] - (d[0] + d[1]) / 4;
+   for (i=2; i<k-1; i++)
+      x[2*i*stride] = s[i] - ((uint32_t) 9 * (d[i-1] + d[i])
+                                            - (d[i-2] + d[i+1])) / 32;
+   if (k > 2)
+      x[(2*k-2)*stride] = s[k-1] - (d[k-2] + d[k-1]) / 4;
+   if (n & 1)
+      x[2*k*stride] = s[k] - d[k-1] / 2;
+}
+
+
+static inline
+void copyback_d (TYPE *x, const TYPE *d, int stride, int n)
+{
+   int i, j, k=n/2;
+
+   for (i=n-k, j=0; i<n; i++, j++)
+      x [i*stride] = d[j];
+}
+
+
+static inline
+void copy_s_d (const TYPE *x, TYPE *s_d, int stride, int n)
+{
+   int i;
+
+   for (i=0; i<n; i++)
+      s_d[i] = x [i*stride];
+}
+
+
+
+typedef
+void (*FwdSFnc) (const TYPE *x, TYPE *s, const TYPE *d, int stride, int n);
+
+typedef
+void (*FwdAFnc) (const TYPE *x, TYPE *d, int stride, int n);
+
+typedef
+void (*InvSFnc) (TYPE *x, const TYPE *s, const TYPE *d, int stride, int n);
+
+typedef
+void (*InvAFnc) (TYPE *x, const TYPE *d, int stride, int n);
+
+
+
+static FwdSFnc fwd_synthesize [] = { NULL, fwd_synthesize_1, fwd_synthesize_2,
+                                     NULL, fwd_synthesize_4 };
+
+static FwdAFnc fwd_analyze [] = { NULL, fwd_analyze_1, fwd_analyze_2,
+                                  NULL, fwd_analyze_4 };
+
+static InvSFnc inv_synthesize [] = { NULL, inv_synthesize_1, inv_synthesize_2,
+                                     NULL, inv_synthesize_4 };
+
+static InvAFnc inv_analyze [] = { NULL, inv_analyze_1, inv_analyze_2,
+                                  NULL, inv_analyze_4 };
+
+
+
+static inline
+void fwd_xform (TYPE *scratchbuf, TYPE *data, int stride, int n,
+                int a_moments, int s_moments)
+{
+   TYPE *x = data;
+   TYPE *d = scratchbuf;
+   TYPE *s = data;
+
+   assert (a_moments == 1 || a_moments == 2 || a_moments == 4);
+   assert (s_moments == 1 || s_moments == 2 || s_moments == 4);
+
+   /*  XXX FIXME: Ugly hack to work around  */
+   /*             the short-row bug in high */
+   /*             order xform functions     */
+   if (n < 9)
+      a_moments = s_moments = 2;
+   if (n < 5)
+      a_moments = s_moments = 1;
+
+   fwd_analyze [a_moments] (x, d, stride, n);
+   fwd_synthesize [s_moments] (x, s, d, stride, n);
+   copyback_d (x, d, stride, n);
+}
+
+
+static inline
+void inv_xform (TYPE *scratchbuf, TYPE *data, int stride, int n,
+                int a_moments, int s_moments)
+{
+   int k=n/2;
+   TYPE *x = data;
+   TYPE *s = scratchbuf;
+   TYPE *d = scratchbuf + n - k;
+
+   assert (a_moments == 1 || a_moments == 2 || a_moments == 4);
+   assert (s_moments == 1 || s_moments == 2 || s_moments == 4);
+
+   /*  XXX FIXME: Ugly hack to work around  */
+   /*             the short-row bug in high */
+   /*             order xform functions     */
+   if (n < 9)
+      a_moments = s_moments = 2;
+   if (n < 5)
+      a_moments = s_moments = 1;
+
+   copy_s_d (data, scratchbuf, stride, n);
+   inv_synthesize [s_moments] (x, s, d, stride, n);
+   inv_analyze [a_moments] (x, d, stride, n);
+}
+
+
+
+void wavelet_3d_buf_fwd_xform (Wavelet3DBuf* buf, int a_moments, int s_moments)
+{
+   int level;
+
+   for (level=buf->scales-1; level>0; level--) {
+      uint32_t w = buf->w[level];
+      uint32_t h = buf->h[level];
+      uint32_t f = buf->f[level];
+
+      if (w > 1) {
+         int row, frame;
+         for (frame=0; frame<f; frame++) {
+            for (row=0; row<h; row++) {
+               TYPE *data = buf->data + (frame * buf->height + row) * buf->width;
+               fwd_xform (buf->scratchbuf, data, 1, w, a_moments, s_moments);
+            }
+         }
+      }
+
+      if (h > 1) {
+         int col, frame;
+         for (frame=0; frame<f; frame++) {
+            for (col=0; col<w; col++) {
+               TYPE *data = buf->data + frame * buf->width * buf->height + col;
+               fwd_xform (buf->scratchbuf, data, buf->width, h,
+                          a_moments, s_moments);
+            }
+         }
+      }
+
+      if (f > 1) {
+         int i, j;
+         for (j=0; j<h; j++) {
+            for (i=0; i<w; i++) {
+               TYPE *data = buf->data + j*buf->width + i;
+               fwd_xform (buf->scratchbuf, data, buf->width * buf->height, f,
+                          a_moments, s_moments);
+            }
+         }
+      }
+   }
+}
+
+
+void wavelet_3d_buf_inv_xform (Wavelet3DBuf* buf, int a_moments, int s_moments)
+{
+   int level;
+
+   for (level=1; level<buf->scales; level++) {
+      uint32_t w = buf->w[level];
+      uint32_t h = buf->h[level];
+      uint32_t f = buf->f[level];
+
+      if (f > 1) {
+         int i, j;
+         for (j=0; j<h; j++) {
+            for (i=0; i<w; i++) {
+               TYPE *data = buf->data + j*buf->width + i;
+               inv_xform (buf->scratchbuf, data, buf->width * buf->height, f,
+                          a_moments, s_moments);
+            }
+         }
+      }
+
+      if (h > 1) {
+         int col, frame;
+         for (frame=0; frame<f; frame++) {
+            for (col=0; col<w; col++) {
+               TYPE *data = buf->data + frame * buf->width * buf->height + col;
+               inv_xform (buf->scratchbuf, data, buf->width, h,
+                          a_moments, s_moments);
+            }
+         }
+      }
+
+      if (w > 1) {
+         int row, frame;
+         for (frame=0; frame<f; frame++) {
+            for (row=0; row<h; row++) {
+               TYPE *data = buf->data + (frame * buf->height + row) * buf->width;
+               inv_xform (buf->scratchbuf, data, 1, w, a_moments, s_moments);
+            }
+         }
+      }
+   }
+}
+
diff --git a/ext/tarkin/yuv.c b/ext/tarkin/yuv.c
new file mode 100644 (file)
index 0000000..6f5fb5c
--- /dev/null
@@ -0,0 +1,224 @@
+#include "yuv.h"
+
+/*#define TARKIN_YUV_EXACT*/
+/*#define TARKIN_YUV_LXY*/
+
+
+static inline 
+uint8_t CLAMP(int16_t x)
+{
+   return  ((x > 255) ? 255 : (x < 0) ? 0 : x);
+}
+
+
+
+void rgb24_to_yuv (uint8_t *rgb, Wavelet3DBuf *yuv [], uint32_t frame)
+{
+   int count = yuv[0]->width * yuv[0]->height;
+   int16_t *y = yuv[0]->data + frame * count;
+   int16_t *u = yuv[1]->data + frame * count;
+   int16_t *v = yuv[2]->data + frame * count;
+   int i;
+
+
+#if defined(TARKIN_YUV_EXACT)
+   for (i=0; i<count; i++, rgb+=3) {
+      y [i] = ((int16_t)  77 * rgb [0] + 150 * rgb [1] +  29 * rgb [2]) / 256;
+      u [i] = ((int16_t) -44 * rgb [0] -  87 * rgb [1] + 131 * rgb [2]) / 256;
+      v [i] = ((int16_t) 131 * rgb [0] - 110 * rgb [1] -  21 * rgb [2]) / 256;
+   }
+#elif defined(TARKIN_YUV_LXY)
+   for (i=0; i<count; i++, rgb+=3) {
+      y [i] = ((int16_t)  54 * rgb [0] + 182 * rgb [1] +  18 * rgb [2]) / 256;
+      u [i] = rgb [0] - y [i];
+      v [i] = rgb [2] - y [i];
+   }
+#else
+   for (i=0; i<count; i++, rgb+=3) {
+      v [i] = rgb [0] - rgb [1];
+      u [i] = rgb [2] - rgb [1];
+      y [i] = rgb [1] + (u [i] + v [i]) / 4;
+   }
+#endif
+}
+
+
+void yuv_to_rgb24 (Wavelet3DBuf *yuv [], uint8_t *rgb, uint32_t frame)
+{
+   int count = yuv[0]->width * yuv[0]->height;
+   int16_t *y = yuv[0]->data + frame * count;
+   int16_t *u = yuv[1]->data + frame * count;
+   int16_t *v = yuv[2]->data + frame * count;
+   int i;
+
+#if defined(TARKIN_YUV_EXACT)
+   for (i=0; i<count; i++, rgb+=3) {
+      rgb [0] = CLAMP(y [i] + 1.371 * v [i]);
+      rgb [1] = CLAMP(y [i] - 0.698 * v [i] - 0.336 * u [i]);
+      rgb [2] = CLAMP(y [i] + 1.732 * u [i]);
+   }
+#elif defined(TARKIN_YUV_LXY)
+   for (i=0; i<count; i++, rgb+=3) {
+      rgb [1] = CLAMP(y [i] - (76 * u [i] - 26 * v [i]) / 256);
+      rgb [0] = CLAMP(y [i] + u [i]);
+      rgb [2] = CLAMP(y [i] + v [i]);
+   }
+#else
+   for (i=0; i<count; i++, rgb+=3) {
+      rgb [1] = CLAMP(y [i] - (u [i] + v [i]) / 4);
+      rgb [2] = CLAMP(u [i] + rgb [1]);
+      rgb [0] = CLAMP(v [i] + rgb [1]);
+   }
+#endif
+}
+
+
+void rgb32_to_yuv (uint8_t *rgb, Wavelet3DBuf *yuv [], uint32_t frame)
+{
+   int count = yuv[0]->width * yuv[0]->height;
+   int16_t *y = yuv[0]->data + frame * count;
+   int16_t *u = yuv[1]->data + frame * count;
+   int16_t *v = yuv[2]->data + frame * count;
+   int i;
+
+#if defined(TARKIN_YUV_EXACT)
+   for (i=0; i<count; i++, rgb+=4) {
+      y [i] = ((int16_t)  77 * rgb [0] + 150 * rgb [1] +  29 * rgb [2]) / 256;
+      u [i] = ((int16_t) -44 * rgb [0] -  87 * rgb [1] + 131 * rgb [2]) / 256;
+      v [i] = ((int16_t) 131 * rgb [0] - 110 * rgb [1] -  21 * rgb [2]) / 256;
+   }
+#elif defined(TARKIN_YUV_LXY)
+   for (i=0; i<count; i++, rgb+=4) {
+      y [i] = ((int16_t)  54 * rgb [0] + 182 * rgb [1] +  18 * rgb [2]) / 256;
+      u [i] = rgb [0] - y [i];
+      v [i] = rgb [2] - y [i];
+   }
+#else
+   for (i=0; i<count; i++, rgb+=4) {
+      v [i] = rgb [0] - rgb [1];
+      u [i] = rgb [2] - rgb [1];
+      y [i] = rgb [1] + (u [i] + v [i]) / 4;
+   }
+#endif
+}
+
+
+void yuv_to_rgb32 (Wavelet3DBuf *yuv [], uint8_t *rgb, uint32_t frame)
+{
+   int count = yuv[0]->width * yuv[0]->height;
+   int16_t *y = yuv[0]->data + frame * count;
+   int16_t *u = yuv[1]->data + frame * count;
+   int16_t *v = yuv[2]->data + frame * count;
+   int i;
+
+#if defined(TARKIN_YUV_EXACT)
+   for (i=0; i<count; i++, rgb+=4) {
+      rgb [0] = CLAMP(y [i] + 1.371 * v [i]);
+      rgb [1] = CLAMP(y [i] - 0.698 * v [i] - 0.336 * u [i]);
+      rgb [2] = CLAMP(y [i] + 1.732 * u [i]);
+   }
+#elif defined(TARKIN_YUV_LXY)
+   for (i=0; i<count; i++, rgb+=4) {
+      rgb [1] = CLAMP(y [i] - (76 * u [i] - 26 * v [i]) / 256);
+      rgb [0] = CLAMP(y [i] + u [i]);
+      rgb [2] = CLAMP(y [i] + v [i]);
+   }
+#else
+   for (i=0; i<count; i++, rgb+=4) {
+      rgb [1] = CLAMP(y [i] - (u [i] + v [i]) / 4);
+      rgb [2] = CLAMP(u [i] + rgb [1]);
+      rgb [0] = CLAMP(v [i] + rgb [1]);
+   }
+#endif
+}
+
+
+void rgba_to_yuv (uint8_t *rgba, Wavelet3DBuf *yuva [], uint32_t frame)
+{
+   int count = yuva[0]->width * yuva[0]->height;
+   int16_t *y = yuva[0]->data + frame * count;
+   int16_t *u = yuva[1]->data + frame * count;
+   int16_t *v = yuva[2]->data + frame * count;
+   int16_t *a = yuva[3]->data + frame * count;
+   int i;
+
+#if defined(TARKIN_YUV_EXACT)
+   for (i=0; i<count; i++, rgba+=4) {
+      y [i] = ((int16_t)  77 * rgba [0] + 150 * rgba [1] +  29 * rgba [2]) / 256;
+      u [i] = ((int16_t) -44 * rgba [0] -  87 * rgba [1] + 131 * rgba [2]) / 256;
+      v [i] = ((int16_t) 131 * rgba [0] - 110 * rgba [1] -  21 * rgba [2]) / 256;
+      a [i] = rgba [3];
+   }
+#elif defined(TARKIN_YUV_LXY)
+   for (i=0; i<count; i++, rgba+=4) {
+      y [i] = ((int16_t)  54 * rgba [0] + 182 * rgba [1] +  18 * rgba [2]) / 256;
+      u [i] = rgba [0] - y [i];
+      v [i] = rgba [2] - y [i];
+      a [i] = rgba [3];
+   }
+#else
+   for (i=0; i<count; i++, rgba+=4) {
+      v [i] = rgba [0] - rgba [1];
+      u [i] = rgba [2] - rgba [1];
+      y [i] = rgba [1] + (u [i] + v [i]) / 4;
+      a [i] = rgba [3];
+   }
+#endif
+}
+
+
+void yuv_to_rgba (Wavelet3DBuf *yuva [], uint8_t *rgba, uint32_t frame)
+{
+   int count = yuva[0]->width * yuva[0]->height;
+   int16_t *y = yuva[0]->data + frame * count;
+   int16_t *u = yuva[1]->data + frame * count;
+   int16_t *v = yuva[2]->data + frame * count;
+   int16_t *a = yuva[3]->data + frame * count;
+   int i;
+
+#if defined(TARKIN_YUV_EXACT)
+   for (i=0; i<count; i++, rgba+=4) {
+      rgba [0] = CLAMP(y [i] + 1.371 * v [i]);
+      rgba [1] = CLAMP(y [i] - 0.698 * v [i] - 0.336 * u [i]);
+      rgba [2] = CLAMP(y [i] + 1.732 * u [i]);
+      rgba [3] = a [i];
+   }
+#elif defined(TARKIN_YUV_LXY)
+   for (i=0; i<count; i++, rgba+=4) {
+      rgba [1] = CLAMP(y [i] - (76 * u [i] - 26 * v [i]) / 256);
+      rgba [0] = CLAMP(y [i] + u [i]);
+      rgba [2] = CLAMP(y [i] + v [i]);
+      rgba [3] = a [i];
+   }
+#else
+   for (i=0; i<count; i++, rgba+=4) {
+      rgba [1] = CLAMP(y [i] - (u [i] + v [i]) / 4);
+      rgba [2] = CLAMP(u [i] + rgba [1]);
+      rgba [0] = CLAMP(v [i] + rgba [1]);
+      rgba [3] = a [i];
+   }
+#endif
+}
+
+void grayscale_to_y (uint8_t *rgba, Wavelet3DBuf *y [], uint32_t frame)
+{
+   int count = y[0]->width * y[0]->height;
+   int16_t *_y = y[0]->data + frame * count;
+   int i;
+
+   for (i=0; i<count; i++)
+      _y [i] = rgba [i];
+}
+
+
+void y_to_grayscale (Wavelet3DBuf *y [], uint8_t *rgba, uint32_t frame)
+{
+   int count = y[0]->width * y[0]->height;
+   int16_t *_y = y[0]->data + frame * count;
+   int i;
+
+   for (i=0; i<count; i++)
+      rgba [i] = CLAMP(_y[i]);
+}
+
+
diff --git a/ext/tarkin/yuv.h b/ext/tarkin/yuv.h
new file mode 100644 (file)
index 0000000..42ceb07
--- /dev/null
@@ -0,0 +1,21 @@
+
+#ifndef __YUV_H
+#define __YUV_H
+
+#include <stdint.h>
+#include "wavelet.h"
+
+extern void rgb24_to_yuv (uint8_t *rgb, Wavelet3DBuf *yuv [], uint32_t frame);
+extern void yuv_to_rgb24 (Wavelet3DBuf *yuv [], uint8_t *rgb, uint32_t frame);
+
+extern void rgb32_to_yuv (uint8_t *rgb, Wavelet3DBuf *yuv [], uint32_t frame);
+extern void yuv_to_rgb32 (Wavelet3DBuf *yuv [], uint8_t *rgb, uint32_t frame);
+
+extern void rgba_to_yuv (uint8_t *rgba, Wavelet3DBuf *yuva [], uint32_t frame);
+extern void yuv_to_rgba (Wavelet3DBuf *yuva [], uint8_t *rgba, uint32_t frame);
+
+extern void grayscale_to_y (uint8_t *rgba, Wavelet3DBuf *y [], uint32_t frame);
+extern void y_to_grayscale (Wavelet3DBuf *y [], uint8_t *rgba, uint32_t frame);
+
+#endif
+