diff options
-rw-r--r-- | .gitmodules | 3 | ||||
m--------- | common | 0 | ||||
-rw-r--r-- | ext/tarkin/Makefile.am | 15 | ||||
-rw-r--r-- | ext/tarkin/README | 8 | ||||
-rw-r--r-- | ext/tarkin/TODO | 42 | ||||
-rw-r--r-- | ext/tarkin/WHAT_THE_HECK_IS_THIS_CODE_DOING | 61 | ||||
-rw-r--r-- | ext/tarkin/golomb.h | 132 | ||||
-rw-r--r-- | ext/tarkin/gsttarkin.c | 156 | ||||
-rw-r--r-- | ext/tarkin/gsttarkindec.c | 301 | ||||
-rw-r--r-- | ext/tarkin/gsttarkindec.h | 83 | ||||
-rw-r--r-- | ext/tarkin/gsttarkinenc.c | 371 | ||||
-rw-r--r-- | ext/tarkin/gsttarkinenc.h | 84 | ||||
-rw-r--r-- | ext/tarkin/info.c | 551 | ||||
-rw-r--r-- | ext/tarkin/mem.c | 143 | ||||
-rw-r--r-- | ext/tarkin/mem.h | 32 | ||||
-rw-r--r-- | ext/tarkin/rle.h | 143 | ||||
-rw-r--r-- | ext/tarkin/tarkin.c | 418 | ||||
-rw-r--r-- | ext/tarkin/tarkin.h | 239 | ||||
-rw-r--r-- | ext/tarkin/wavelet.c | 121 | ||||
-rw-r--r-- | ext/tarkin/wavelet.h | 55 | ||||
-rw-r--r-- | ext/tarkin/wavelet_coeff.c | 506 | ||||
-rw-r--r-- | ext/tarkin/wavelet_xform.c | 391 | ||||
-rw-r--r-- | ext/tarkin/yuv.c | 224 | ||||
-rw-r--r-- | ext/tarkin/yuv.h | 21 |
24 files changed, 4100 insertions, 0 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..1ba590b4 --- /dev/null +++ b/.gitmodules @@ -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 +Subproject e35bdb888e14d9e5b5f19a7d15be8157ca01923 diff --git a/ext/tarkin/Makefile.am b/ext/tarkin/Makefile.am new file mode 100644 index 00000000..86acce1f --- /dev/null +++ b/ext/tarkin/Makefile.am @@ -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 index 00000000..ee91356d --- /dev/null +++ b/ext/tarkin/README @@ -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 index 00000000..909d3771 --- /dev/null +++ b/ext/tarkin/TODO @@ -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 index 00000000..46602605 --- /dev/null +++ b/ext/tarkin/WHAT_THE_HECK_IS_THIS_CODE_DOING @@ -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 index 00000000..fa66d750 --- /dev/null +++ b/ext/tarkin/golomb.h @@ -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 index 00000000..7bfb78f3 --- /dev/null +++ b/ext/tarkin/gsttarkin.c @@ -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 index 00000000..39c58d76 --- /dev/null +++ b/ext/tarkin/gsttarkindec.c @@ -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 index 00000000..78629851 --- /dev/null +++ b/ext/tarkin/gsttarkindec.h @@ -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 index 00000000..8042430a --- /dev/null +++ b/ext/tarkin/gsttarkinenc.c @@ -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 index 00000000..ca74c989 --- /dev/null +++ b/ext/tarkin/gsttarkinenc.h @@ -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 index 00000000..e555e381 --- /dev/null +++ b/ext/tarkin/info.c @@ -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 index 00000000..c2cf18c0 --- /dev/null +++ b/ext/tarkin/mem.c @@ -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 index 00000000..f218f8e4 --- /dev/null +++ b/ext/tarkin/mem.h @@ -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 index 00000000..7cf79517 --- /dev/null +++ b/ext/tarkin/rle.h @@ -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 index 00000000..053c03f7 --- /dev/null +++ b/ext/tarkin/tarkin.c @@ -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 index 00000000..633f9a14 --- /dev/null +++ b/ext/tarkin/tarkin.h @@ -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 index 00000000..a83aa499 --- /dev/null +++ b/ext/tarkin/wavelet.c @@ -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 index 00000000..914c2799 --- /dev/null +++ b/ext/tarkin/wavelet.h @@ -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 index 00000000..2cc0a8f0 --- /dev/null +++ b/ext/tarkin/wavelet_coeff.c @@ -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 index 00000000..768446a2 --- /dev/null +++ b/ext/tarkin/wavelet_xform.c @@ -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 index 00000000..6f5fb5c0 --- /dev/null +++ b/ext/tarkin/yuv.c @@ -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 index 00000000..42ceb072 --- /dev/null +++ b/ext/tarkin/yuv.h @@ -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 + |