Added a tarkin encoder/decoder plugin.

Original commit message from CVS:
Added a tarkin encoder/decoder plugin.

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

test with:

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

./gst-launch filesrc location=out.ogg ! tarkindec ! colorspace ! xvideosink
This commit is contained in:
Wim Taymans 2002-02-03 16:30:31 +00:00
parent b1fea04686
commit 57ce0321c8
24 changed files with 4101 additions and 0 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "common"]
path = common
url = git://anongit.freedesktop.org/gstreamer/common

1
common Submodule

@ -0,0 +1 @@
Subproject commit e35bdb888e14d9e5b5f19a7d15be8157ca019234

15
ext/tarkin/Makefile.am Normal file
View file

@ -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

8
ext/tarkin/README Normal file
View file

@ -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).

42
ext/tarkin/TODO Normal file
View file

@ -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

View file

@ -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>

132
ext/tarkin/golomb.h Normal file
View file

@ -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

156
ext/tarkin/gsttarkin.c Normal file
View file

@ -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
};

301
ext/tarkin/gsttarkindec.c Normal file
View file

@ -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;
}
}

83
ext/tarkin/gsttarkindec.h Normal file
View file

@ -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__ */

371
ext/tarkin/gsttarkinenc.c Normal file
View file

@ -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;
}
}

84
ext/tarkin/gsttarkinenc.h Normal file
View file

@ -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__ */

551
ext/tarkin/info.c Normal file
View file

@ -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);
}

143
ext/tarkin/mem.c Normal file
View file

@ -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

32
ext/tarkin/mem.h Normal file
View file

@ -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

143
ext/tarkin/rle.h Normal file
View file

@ -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

418
ext/tarkin/tarkin.c Normal file
View file

@ -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);
}

239
ext/tarkin/tarkin.h Normal file
View file

@ -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

121
ext/tarkin/wavelet.c Normal file
View file

@ -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

55
ext/tarkin/wavelet.h Normal file
View file

@ -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

506
ext/tarkin/wavelet_coeff.c Normal file
View file

@ -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]);
}
}

391
ext/tarkin/wavelet_xform.c Normal file
View file

@ -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);
}
}
}
}
}

224
ext/tarkin/yuv.c Normal file
View file

@ -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]);
}

21
ext/tarkin/yuv.h Normal file
View file

@ -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