#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include <ogg/ogg.h>
#include <theora/codec.h>
#include <theora/theora.h>
#include <theora/theoraenc.h>
#include <theora/theoradec.h>
#include "const-c.inc"
MODULE = Ogg::Theora::LibTheora PACKAGE = Ogg::Theora::LibTheora PREFIX = LibTheora_
INCLUDE: const-xs.inc
PROTOTYPES: DISABLE
=head1 Functions (malloc)
L<http://www.theora.org/doc/libtheora-1.0/annotated.html>
=cut
=head2 make_th_info
Creates a memory allocation for th_info.
-Input:
Void
-Output:
Memory Pointer
=cut
th_info *
LibTheora_make_th_info()
PREINIT:
th_info *memory;
CODE:
New(0, memory, 1, th_info);
RETVAL = memory;
OUTPUT:
RETVAL
=head2 make_th_huff_code
Creates a memory allocation for th_huff_code.
-Input:
void
-Output:
Memory Pointer
=cut
th_huff_code *
LibTheora_make_th_huff_code()
PREINIT:
th_huff_code *memory;
CODE:
New(0, memory, 1, th_huff_code);
RETVAL = memory;
OUTPUT:
RETVAL
=head2 make_th_img_plane
Creates a memory allocation for th_img_plane.
-Input:
void
-Output:
Memory Pointer
=cut
th_img_plane *
LibTheora_make_th_img_plane()
PREINIT:
th_img_plane *memory;
CODE:
New(0, memory, 1, th_img_plane);
RETVAL = memory;
OUTPUT:
RETVAL
=head2 make_th_quant_info
Creates a memory allocation for th_quant_info.
-Input:
void
-Output:
Memory Pointer
=cut
th_quant_info *
LibTheora_make_th_quant_info()
PREINIT:
th_quant_info *memory;
CODE:
New(0, memory, 1, th_quant_info);
RETVAL = memory;
OUTPUT:
RETVAL
=head2 make_th_quant_ranges
Creates a memory allocation for th_quant_ranges.
-Input:
void
-Output:
Memory Pointer
=cut
th_quant_ranges *
LibTheora_make_th_quant_ranges()
PREINIT:
th_quant_ranges *memory;
CODE:
New(0, memory, 1, th_quant_ranges);
RETVAL = memory;
OUTPUT:
RETVAL
=head2 make_th_stripe_callback
Creates a memory allocation for th_stripe_callback.
-Input:
void
-Output:
Memory Pointer
=cut
th_stripe_callback *
LibTheora_make_th_stripe_callback()
PREINIT:
th_stripe_callback *memory;
CODE:
New(0, memory, 1, th_stripe_callback);
RETVAL = memory;
OUTPUT:
RETVAL
=head2 make_th_ycbcr_buffer
Creates a memory allocation for th_ycbcr_buffer.
-Input:
void
-Output:
Memory Pointer
=cut
th_ycbcr_buffer *
LibTheora_make_th_ycbcr_buffer()
PREINIT:
th_ycbcr_buffer *memory;
CODE:
New(0, memory, 1, th_ycbcr_buffer);
RETVAL = memory;
OUTPUT:
RETVAL
=head2 make_th_comment
Creates a memory allocation for th_comment.
-Input:
void
-Output:
Memory Pointer
=cut
th_comment *
LibTheora_make_th_comment()
PREINIT:
th_comment *memory;
CODE:
New(0, memory, 1, th_comment);
RETVAL = memory;
OUTPUT:
RETVAL
=head1 Functions (Basic shared functions)
L<http://www.theora.org/doc/libtheora-1.0/group__basefuncs.html>
=cut
=head2 th_version_number
Retrieves the library version number.
-Input:
void
-Output:
ogg_uint32_t (IV)
=cut
ogg_uint32_t
LibTheora_th_version_number()
PREINIT:
ogg_uint32_t version;
CODE:
version = th_version_number();
RETVAL = version;
OUTPUT:
RETVAL
=head2 th_version_string
Retrieves a human-readable string to identify the library vendor and version.
-Input:
void
-Output:
const char * (T_PV)
=cut
const char *
LibTheora_th_version_string()
PREINIT:
const char *version;
CODE:
version = th_version_string();
RETVAL = version;
OUTPUT:
RETVAL
=head2 th_packet_isheader
Determines whether a Theora packet is a header or not.
-Input:
_op An ogg_packet containing encoded Theora data.
-Output:
1 packet is a header packet,
0 packet is a video data packet.
=cut
int
LibTheora_th_packet_isheader(_op)
ogg_packet * _op
CODE:
RETVAL = th_packet_isheader(_op);
OUTPUT:
RETVAL
=head2 th_granule_frame
Converts a granule position to an absolute frame index, starting at 0.
-Input:
void * _encdec (previously allocated th_enc_ctx or th_dec_ctx handle),
ogg_int64_t _granpos (granule position to convert).
-Output:
absolute frame index corresponding to _granpos,
-1 on error.
=cut
int
LibTheora_th_granule_frame(_encdec, _granpos)
void * _encdec
ogg_int64_t _granpos
CODE:
RETVAL = th_granule_frame(_encdec, _granpos);
OUTPUT:
RETVAL
=head2 th_granule_time
Converts a granule position to an absolute time in seconds.
-Input:
void * _encdec (previously allocated th_enc_ctx or th_dec_ctx handle),
ogg_int64_t _granpos (granule position to convert).
-Output:
absolute time in seconds corresponding to _granpos,
-1 on error.
=cut
double
LibTheora_th_granule_time(_encdec, _granpos)
void * _encdec
ogg_int64_t _granpos
CODE:
RETVAL = th_granule_time(_encdec, _granpos);
OUTPUT:
RETVAL
=head2 th_packet_iskeyframe
Determines whether a theora packet is a key frame or not.
-Input:
_op An ogg_packet containing encoded Theora data.
-Output:
1 packet is a key frame,
0 packet is a delta frame,
-1 packet is not a video data packet.
=cut
int
LibTheora_th_packet_iskeyframe(_op)
ogg_packet * _op
CODE:
RETVAL = th_packet_iskeyframe(_op);
OUTPUT:
RETVAL
=head1 Functions (Manipulating Header Data)
=cut
=head2 th_comment_init
Initialize a th_comment structure.
-Input:
th_comment *
-Output:
void
=cut
void
LibTheora_th_comment_init(_tc)
th_comment * _tc
CODE:
th_comment_init(_tc);
=head2 th_info_init
Initializes a th_info structure.
-Input:
th_info
-Output:
void
=cut
void
LibTheora_th_info_init(_info)
th_info * _info
CODE:
th_info_init(_info);
=head2 th_info_clear
Clears a th_info structure.
-Input:
th_info
-Output:
void
=cut
void
LibTheora_th_info_clear(_info)
th_info * _info
CODE:
th_info_clear(_info);
=head2 th_comment_add
Add a comment to an initialized th_comment structure.
-Input:
th_comment,
char * (null-terminated UTF-8 string containing the comment in "TAG=the value" form).
-Output:
void
=cut
void
LibTheora_th_comment_add(_tc, _comment)
th_comment * _tc
char * _comment
CODE:
int i;
th_comment_add(_tc, _comment);
=head2 th_comment_add_tag
Add a comment to an initialized th_comment structure.
-Input:
th_comment,
char * (null-terminated string containing the tag associated with the comment),
char * (corresponding value as a null-terminated string).
=cut
void
LibTheora_th_comment_add_tag(_tc, _tag, _val)
th_comment * _tc
char * _tag
char * _val
CODE:
th_comment_add_tag(_tc, _tag, _val);
=head2 th_comment_query_count
Look up the number of instances of a tag.
-Input:
th_comment,
char * (tag to look up).
-Output:
int (number on instances of this particular tag)
=cut
int
LibTheora_th_comment_query_count(_tc, _tag)
th_comment * _tc
char * _tag
CODE:
RETVAL = th_comment_query_count(_tc, _tag);
OUTPUT:
RETVAL
=head2 th_comment_query
Look up a comment value by its tag.
-Input:
th_comment,
char * (tag to look-up)
int (instance of the tag, it starts from 0)
-Output:
char * if matched pointer to the queried tag's value,
NULL if no matching tag is found
=cut
char *
LibTheora_th_comment_query(_tc, _tag, _count)
th_comment * _tc
char * _tag
int _count
CODE:
RETVAL = th_comment_query(_tc, _tag, _count);
OUTPUT:
RETVAL
=head1 Functions (For Decoding)
L<http://www.theora.org/doc/libtheora-1.0/group__decfuncs.html>
=cut
=head2 th_decode_headerin
Decodes the header packets of a Theora stream.
-Input:
th_info,
th_comment,
th_setup_info, (initialized to NULL on the first call & returned value be passed on subsequent calls)
ogg_packet
-Output:
0 first video data packet was encountered after all required header packets were parsed,
TH_EFAULT if one of _info, _tc, or _setup was NULL,
TH_EBADHEADER _op was NULL,
TH_EVERSION not decodable with current libtheoradec version,
TH_ENOTFORMAT not a Theora header
=cut
void
LibTheora_th_decode_headerin(_info, _tc, _setup_addr, _op)
th_info * _info
th_comment * _tc
int _setup_addr
ogg_packet * _op
PREINIT:
int status;
th_setup_info *_setup;
PPCODE:
_setup = (th_setup_info *) _setup_addr;
status = th_decode_headerin(_info, _tc, &_setup, _op);
XPUSHs(sv_2mortal(newSViv(status)));
XPUSHs(sv_2mortal(newSViv((unsigned int) _setup)));
=head2 th_decode_alloc
Allocates a decoder instance.
-Input:
th_info,
th_setup_info
-Output:
th_dec_ctx
=cut
th_dec_ctx *
LibTheora_th_decode_alloc(_info, _setup)
th_info * _info
int _setup
CODE:
RETVAL = th_decode_alloc(_info, (th_setup_info *) _setup);
OUTPUT:
RETVAL
=head2 th_setup_free
Releases all storage used for the decoder setup information.
-Input:
th_setup_info
-Output:
void
=cut
void
LibTheora_th_setup_free(_setup)
int _setup
CODE:
th_setup_free((th_setup_info *) _setup);
=head2 th_decode_packetin
Submits a packet containing encoded video data to the decoder.
-Input:
th_dec_ctx,
ogg_packet,
ogg_int64_t gran_pos, returns the granule position of the decoded packet
-Output:
0 success,
TH_DUPFRAME packet represented a dropped (0-byte) frame,
TH_EFAULT _dec or _op was NULL,
TH_EBADPACKET _op does not contain encoded video data,
TH_EIMPL video data uses bitstream features which this library does not support.
=cut
void
LibTheora_th_decode_packetin(_dec, _op, _granpos)
th_dec_ctx * _dec
ogg_packet * _op
unsigned int _granpos
PREINIT:
int status;
PPCODE:
status = th_decode_packetin(_dec, _op, (ogg_int64_t *) &_granpos);
XPUSHs(sv_2mortal(newSViv(status)));
XPUSHs(sv_2mortal(newSViv((unsigned int) _granpos)));
=head2 th_decode_ycbcr_out
Outputs the next available frame of decoded Y'CbCr data.
-Input:
th_dec_ctx,
th_ycbcr_buffer (video buffer structure to fill in)
-Output:
0 Success
=cut
int
LibTheora_th_decode_ycbcr_out(_dec, _ycbcr)
th_dec_ctx * _dec
th_ycbcr_buffer * _ycbcr
CODE:
RETVAL = th_decode_ycbcr_out(_dec, *_ycbcr);
OUTPUT:
RETVAL
=head2 th_decode_free
Frees an allocated decoder instance.
-Input:
th_dec_ctx
-Output:
void
=cut
void
LibTheora_th_decode_free(_dec)
th_dec_ctx * _dec
CODE:
th_decode_free(_dec);
=head2 th_decode_ctl
Decoder control function. (i haven't tested this)
-Input:
th_dec_ctx,
int _req (control code to process),
void * _buf (parameters for this control code),
size_t _buf_sz (size of the parameter buffer)
-Output:
int (not documented)
=cut
int
LibTheora_th_decode_ctl(_dec, _req, _buf, _buf_sz)
th_dec_ctx * _dec
int _req
void * _buf
size_t _buf_sz
CODE:
RETVAL = th_decode_ctl(_dec, _req, _buf, _buf_sz);
OUTPUT:
RETVAL
=head1 Functions (for Encoding)
L<http://www.theora.org/doc/libtheora-1.0/group__encfuncs.html>
=cut
=head2 th_encode_alloc
Allocates an encoder instance.
-Input:
th_info.
-Output:
th_enc_ctx handle,
NULL (if the encoding parameters were invalid).
=cut
th_enc_ctx *
LibTheora_th_encode_alloc(_info)
th_info * _info
CODE:
RETVAL = th_encode_alloc(_info);
OUTPUT:
RETVAL
=head2 th_encode_flushheader
-Input:
th_enc_ctx,
th_comment,
ogg_packet.
-Output:
> 1 (indicates that a header packet was successfully produced),
0 (no packet was produced, and no more header packets remain),
TH_EFAULT (_enc, _comments, or _op was NULL).
=cut
int
LibTheora_th_encode_flushheader(_enc, _comments, _op)
th_enc_ctx * _enc
th_comment * _comments
ogg_packet * _op
CODE:
RETVAL = th_encode_flushheader(_enc, _comments, _op);
OUTPUT:
RETVAL
=head2 th_encode_ycbcr_in
Submits an uncompressed frame to the encoder. (if you don't have ycbcr buffer
you can try using the *unoptimized* rgb_th_encode_ycbcr_in, better you write
your own).
-Input:
th_enc_ctx,
th_ycbcr_buffer
-Output:
0 Success,
TH_EFAULT _enc or _ycbcr is NULL,
TH_EINVAL buffer size does not match the frame size encoder was initialized.
=cut
int
LibTheora_th_encode_ycbcr_in(_enc, _ycbcr)
th_enc_ctx * _enc
th_ycbcr_buffer * _ycbcr
CODE:
RETVAL = th_encode_ycbcr_in(_enc, *_ycbcr);
OUTPUT:
RETVAL
=head2 th_encode_packetout
Retrieves encoded video data packets.
-Input:
th_enc_ctx,
int (non-zero value if no more uncompressed frames will be submitted),
ogg_packet.
-Output:
> 0 a video data packet was successfully produced,
0 no packet was produced, and no more encoded video data remains,
TH_EFAULT _enc or _op was NULL.
=cut
int
LibTheora_th_encode_packetout(_enc, _last, _op)
th_enc_ctx * _enc
int _last
ogg_packet * _op
CODE:
RETVAL = th_encode_packetout(_enc, _last, _op);
OUTPUT:
RETVAL
=head2 th_encode_free
Frees an allocated encoder instance.
-Input:
th_enc_ctx
-Output:
void
=cut
void
LibTheora_th_encode_free(_enc)
th_enc_ctx * _enc
CODE:
th_encode_free(_enc);
=head1 Miscellaneous Functions
These functions are not found in libtheora*, but is written by the XS author
to simplify few tasks.
=cut
=head2 get_th_info
Returns a HashRef with th_info struct values.
-Input:
th_info
-Output:
HashRef
=cut
HV *
LibTheora_get_th_info(_info)
th_info * _info
PREINIT:
HV * hash;
CODE:
hash = newHV();
sv_2mortal((SV *)hash); /* convert the HASH to a mortal */
hv_store(hash, "frame_width", strlen("frame_width"), newSVnv(_info->frame_width), 0);
hv_store(hash, "frame_height", strlen("frame_height"), newSVnv(_info->frame_height), 0);
hv_store(hash, "pic_width", strlen("pic_width"), newSVnv(_info->pic_width), 0);
hv_store(hash, "pic_height", strlen("pic_height"), newSVnv(_info->pic_height), 0);
hv_store(hash, "pic_x", strlen("pic_x"), newSVnv(_info->pic_x), 0);
hv_store(hash, "pic_y", strlen("pic_y"), newSVnv(_info->pic_y), 0);
hv_store(hash, "colorspace", strlen("colorspace"), newSVnv(_info->colorspace), 0);
hv_store(hash, "pixel_fmt", strlen("pixel_fmt"), newSVnv(_info->pixel_fmt), 0);
hv_store(hash, "target_bitrate", strlen("target_bitrate"), newSVnv(_info->target_bitrate), 0);
hv_store(hash, "quality", strlen("quality"), newSVnv(_info->quality), 0);
hv_store(hash, "version_major", strlen("version_major"), newSVnv(_info->version_major), 0);
hv_store(hash, "version_minor", strlen("version_minor"), newSVnv(_info->version_minor), 0);
hv_store(hash, "version_subminor", strlen("version_subminor"), newSVnv(_info->version_subminor), 0);
hv_store(hash, "fps_numerator", strlen("fps_numerator"), newSVnv(_info->fps_numerator), 0);
hv_store(hash, "fps_denominator", strlen("fps_denominator"), newSVnv(_info->fps_denominator), 0);
hv_store(hash, "aspect_numerator", strlen("aspect_numerator"), newSVnv(_info->aspect_numerator), 0);
hv_store(hash, "aspect_denominator", strlen("aspect_denominator"), newSVnv(_info->aspect_denominator), 0);
hv_store(hash, "keyframe_granule_shift", strlen("keyframe_granule_shift"), newSVnv(_info->keyframe_granule_shift), 0);
RETVAL = hash;
OUTPUT:
RETVAL
=head2 ycbcr_to_rgb_buffer
reads the data from the ycbcr buffer and converts to its equivalent
rgb buffer. (this is NOT an optimized code, there will be better ycbcr
to rgb convertors, some intel gpu processors have mnemonic that does
the conversion)
-Input:
th_ycbcr_buffer
-Output:
RGB string
=cut
SV *
LibTheora_ycbcr_to_rgb_buffer(_ycbcr)
th_ycbcr_buffer * _ycbcr;
PREINIT:
th_ycbcr_buffer buffer;
char *rgb;
long size, size1, size2, size3;
int i, i2, j, j2;
long pos, pos1, pos2;
int Y, U, V;
int R, G, B;
CODE:
memcpy(buffer,_ycbcr, sizeof(buffer));
size1 = buffer[0].width * buffer[0].height;
size2 = buffer[1].width * buffer[1].height;
size3 = buffer[2].width * buffer[2].height;
size = size1*3;
// this way, i don't have to worry about free'ing
RETVAL = newSV(size); // returns a pointer of type (SV *)
SvPOK_on(RETVAL);
// SvPV_nolen returns the pointer to array in RETVAL
rgb = (char *)SvPV_nolen(RETVAL);
// rgb == SvPV(RETVAL, size), i was curious :-)
for(i=0;i<buffer[0].height;i++) {
for(j=0;j<buffer[0].width;j++) {
i2 = (int) i/2;
j2 = (int) j/2;
pos = i*buffer[0].stride +j;
pos1 = i2*buffer[1].stride + j2;
pos2 = i2*buffer[2].stride + j2;
Y = (int) buffer[0].data[pos];
U = (int) buffer[1].data[pos1];
V = (int) buffer[2].data[pos2];
Y = Y - 128 - 16;
U = U - 128;
V = V - 128;
R = Y + 1.140*V;
G = Y - 0.395*U - 0.581*V;
B = Y + 2.032*U;
R += 128;
G += 128;
B += 128;
if (R > 255) R = 255;
if (R < 0) R = 0;
if (G > 255) G = 255;
if (G < 0) G = 0;
if (B > 255) B = 255;
if (B < 0) B = 0;
pos2 = (i*buffer[0].width+j)*3;
rgb[pos2] = R;
rgb[pos2+1] = G;
rgb[pos2+2] = B;
}
}
SvCUR_set(RETVAL, size);
OUTPUT:
RETVAL
=head2 get_th_comment
return an array of comments
-Input:
th_comment
-Output:
array of comments
=cut
void
LibTheora_get_th_comment(_tc)
th_comment * _tc
PREINIT:
int i = 0;
PPCODE:
EXTEND(SP, _tc->comments);
for(i=0; i < _tc->comments; i++) {
PUSHs((SV *)sv_2mortal(newSVpv(_tc->user_comments[i], strlen(_tc->user_comments[i]))));
}
=head2 set_th_info
sets the th_info structure to default values unless specified in hash. frame_width and frame_height
is mandatory.
-Input:
Hash of elements
-Output:
void
=cut
void
LibTheora_set_th_info(_info, hash)
th_info * _info
HV * hash
PREINIT:
char * key;
I32 klen;
SV *val;
int flag = 0;
int frame_width = 0;
int frame_height = 0;
int pic_width = 0;
int pic_height = 0;
int pic_x = 0;
int pic_y = 0;
int colorspace = TH_CS_ITU_REC_470M;
int pixel_fmt = TH_PF_420;
int quality = 0;
int keyframe_granule_shift = 6;
int target_bitrate = 0;
int aspect_denominator = 1;
int aspect_numerator = 1;
int fps_numerator = 25000;
int fps_denominator = 1000;
CODE:
/* get the values from the hash and override the defaults */
(void)hv_iterinit(hash);
while ((val = hv_iternextsv(hash, (char **) &key, &klen))) {
if (strEQ(key, "frame_width")) {
frame_width = SvIV(val);
flag++;
continue;
}
if (strEQ(key, "frame_height")) {
frame_height = SvIV(val);
flag++;
continue;
}
if (strEQ(key, "pic_width")) {
pic_width = SvIV(val);
continue;
}
if (strEQ(key, "pic_height")) {
pic_height = SvIV(val);
continue;
}
if (strEQ(key, "pic_x")) {
pic_x = SvIV(val);
continue;
}
if (strEQ(key, "pic_y")) {
pic_y = SvIV(val);
continue;
}
if (strEQ(key, "colorspace")) {
colorspace = SvIV(val);
continue;
}
if (strEQ(key, "pixel_fmt")) {
pixel_fmt = SvIV(val);
continue;
}
if (strEQ(key, "target_bitrate")) {
target_bitrate = SvIV(val);
continue;
}
if (strEQ(key, "aspect_denominator")) {
aspect_denominator = SvIV(val);
continue;
}
if (strEQ(key, "aspect_numerator")) {
aspect_numerator = SvIV(val);
continue;
}
if (strEQ(key, "fps_numerator")) {
fps_numerator = SvIV(val);
continue;
}
if (strEQ(key, "fps_denominator")) {
fps_denominator = SvIV(val);
continue;
}
if (strEQ(key, "quality")) {
quality = SvIV(val);
continue;
}
if (strEQ(key, "keyframe_granule_shift")) {
keyframe_granule_shift = SvIV(val);
continue;
}
}
if(flag != 2)
Perl_croak(aTHX_ "please give 'frame_width' and 'frame_height'");
_info->frame_width = frame_width;
_info->frame_height = frame_height;
_info->pic_width = (pic_width == 0 ? frame_width : pic_width);
_info->pic_height = (pic_height == 0 ? frame_height : pic_height);
_info->pic_x = pic_x;
_info->pic_y = pic_y;
_info->colorspace = colorspace;
_info->pixel_fmt = pixel_fmt;
_info->target_bitrate = target_bitrate;
_info->aspect_denominator = aspect_denominator;
_info->aspect_numerator = aspect_numerator;
_info->fps_numerator = fps_numerator;
_info->fps_denominator = fps_denominator;
_info->quality = quality;
_info->keyframe_granule_shift = keyframe_granule_shift;
=head2 rgb_th_encode_ycbcr_in
Converts a rgb to ycbcr buffer. (this is not an optimized code)
-Input:
th_enc_ctx
char * (rgb string),
width,
height.
-Output:
th_ycbcr_buffer
=cut
int
LibTheora_rgb_th_encode_ycbcr_in(_enc, rgb, w, h)
th_enc_ctx * _enc
char * rgb
int w
int h
PREINIT:
int c_out;
int size2;
unsigned int address;
char *data;
int i, j, n,nn;
int i1, i2, i3, i4;
int p1, p2, p3, p4;
float r1, g1, b1, r2, g2, b2;
float r3, g3, b3, r4, g4, b4;
float y1, u1, v1, y2, u2, v2;
float y3, u3, v3, y4, u4, v4;
float u, v;
unsigned char iy1, iy2, iy3, iy4, iu, iv;
th_ycbcr_buffer ycbcr;
INIT:
data = rgb;
CODE:
ycbcr[0].data = (unsigned char *) malloc(w*h);
ycbcr[1].data = (unsigned char *) malloc(w*h/4);
ycbcr[2].data = (unsigned char *) malloc(w*h/4);
ycbcr[0].width = w;
ycbcr[0].height = h;
ycbcr[0].stride = w;
ycbcr[1].width = w/2;
ycbcr[1].height = h/2;
ycbcr[1].stride = w/2;
ycbcr[2].width = w/2;
ycbcr[2].height = h/2;
ycbcr[2].stride = w/2;
n = w*h/2;
nn = 0;
for (i =0; i < h; i+=2) {
for (j =0; j < w; j+=2) {
i1 = i*w+j;
i2 = i*w+(j+1);
i3 = (i+1)*w+j;
i4 = (i+1)*w+(j+1);
p1 = i1*3;
p2 = i2*3;
p3 = i3*3;
p4 = i4*3;
r1 = (float) (((unsigned char) data[p1]) - 128);
g1 = (float) (((unsigned char) data[p1+1]) - 128);
b1 = (float) (((unsigned char) data[p1+2]) - 128);
r2 = (float) (((unsigned char) data[p2]) - 128);
g2 = (float) (((unsigned char) data[p2+1]) - 128);
b2 = (float) (((unsigned char) data[p2+2]) - 128);
r3 = (float) (((unsigned char) data[p3]) - 128);
g3 = (float) (((unsigned char) data[p3+1]) - 128);
b3 = (float) (((unsigned char) data[p3+2]) - 128);
r4 = (float) (((unsigned char) data[p4]) - 128);
g4 = (float) (((unsigned char) data[p4+1]) - 128);
b4 = (float) (((unsigned char) data[p4+2]) - 128);
r1 *= 0.80;
r2 *= 0.80;
r3 *= 0.80;
r4 *= 0.80;
y1 = 0.299*r1 + 0.587*g1 + 0.114*b1 + 128;
u1 = -0.14713*r1 -0.28886*g1 + 0.436*b1 + 128;
v1 = 0.615*r1 + -0.51499*g1 + -0.10001*b1 + 128;
y2 = 0.299*r2 + 0.587*g2 + 0.114*b2 + 128;
u2 = -0.14713*r2 -0.28886*g2 + 0.436*b2 + 128;
v2 = 0.615*r2 + -0.51499*g2 + -0.10001*b2 + 128;
y3 = 0.299*r3 + 0.587*g3 + 0.114*b3 + 128;
u3 = -0.14713*r3 -0.28886*g3 + 0.436*b3 + 128;
v3 = 0.615*r3 + -0.51499*g3 + -0.10001*b3 + 128;
y4 = 0.299*r4 + 0.587*g4 + 0.114*b4 + 128;
u4 = -0.14713*r4 -0.28886*g4 + 0.436*b4 + 128;
v4 = 0.615*r4 + -0.51499*g4 + -0.10001*b4 + 128;
u = (u1 + u2 + u3 + u4)/4;
v = (v1 + v2 + v3 + v4)/4;
iy1 = (unsigned char) (y1);
iy2 = (unsigned char) (y2);
iy3 = (unsigned char) (y2);
iy4 = (unsigned char) (y2);
iu = (unsigned char) (u);
iv = (unsigned char) (v);
ycbcr[0].data[i1] = iy1;
ycbcr[0].data[i2] = iy2;
ycbcr[0].data[i3] = iy3;
ycbcr[0].data[i4] = iy4;
ycbcr[1].data[nn] = iu;
ycbcr[2].data[nn] = iv;
++nn;
}
}
RETVAL = th_encode_ycbcr_in(_enc, ycbcr);
free(ycbcr[0].data);
free(ycbcr[1].data);
free(ycbcr[2].data);
OUTPUT:
RETVAL
=head2 get_th_ycbcr_buffer_info
Returns an arrayref of hashrefs containing width, height, stride
and data_pointer for each plane (issue#1)
-Input:
th_ycbcr_buffer
-Output:
arrayref
=cut
SV *
LibTheora_get_th_ycbcr_buffer_info(_ycbcr)
th_ycbcr_buffer * _ycbcr;
PREINIT:
AV * ycbcr_info;
int i = 0;
th_ycbcr_buffer buffer;
INIT:
HV * ycbcr;
ycbcr_info = (AV *)sv_2mortal((SV *)newAV());
CODE:
memcpy(buffer,_ycbcr, sizeof(buffer));
for (i=0; i<3; i++) {
ycbcr = (HV *)sv_2mortal((SV *)newHV());
hv_store(ycbcr, "height", strlen("height"), newSVuv(buffer[i].height), 0);
hv_store(ycbcr, "width", strlen("width"), newSVuv(buffer[i].width), 0);
hv_store(ycbcr, "stride", strlen("stride"), newSVuv(buffer[i].stride), 0);
hv_store(ycbcr, "data", strlen("data"), newSVuv((int)buffer[i].data), 0);
// ycbcr is a local variable
av_push(ycbcr_info, newRV((SV *)ycbcr));
}
/* returning a reference */
RETVAL = newRV((SV *)ycbcr_info);
OUTPUT:
RETVAL
=head2 get_th_ycbcr_buffer_ptr
Returns an data pointer for specified plane index (0 - Y, 1 - Cb, 2 - Cr)
-Input:
th_ycbcr_buffer
index
-Output:
pointer
=cut
void *
LibTheora_get_th_ycbcr_buffer_ptr(_ycbcr, i)
th_ycbcr_buffer * _ycbcr;
int i;
CODE:
RETVAL = (*_ycbcr)[i].data;
OUTPUT:
RETVAL
=head2 get_th_ycbcr_buffer_data
Returns an data for specified plane index (0 - Y, 1 - Cb, 2 - Cr)
-Input:
th_ycbcr_buffer
index
-Output:
string
=cut
SV *
LibTheora_get_th_ycbcr_buffer_data(_ycbcr, i)
th_ycbcr_buffer * _ycbcr;
int i;
PREINIT:
th_ycbcr_buffer buffer;
CODE:
memcpy(buffer, _ycbcr, sizeof(buffer));
RETVAL = newSVpv(buffer[i].data, buffer[i].height * buffer[i].stride);
OUTPUT:
RETVAL