Attached is a patch to add support for Apple Video encoding (rpza). The quality settings are defined at compile time right now since I wasn't really sure how to map ffmpeg's numerous video options to the capabilities of rpza. It supports all rpza encoding opcodes but the 4-color block encoding doesn't try to do multiblock runs yet. -Todd
Hi On Saturday 04 June 2005 09:55, Todd Kirby wrote:
Attached is a patch to add support for Apple Video encoding (rpza). The quality settings are defined at compile time right now since I wasn't really sure how to map ffmpeg's numerous video options to the capabilities of rpza. It supports all rpza encoding opcodes but the 4-color block encoding doesn't try to do multiblock runs yet.
+#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b))
theres FFMIN/FFMAX
+static uint16_t round_rgb24_to_rgb555(uint8_t * rgb24, int bias) +/* + * Round a 24 bit rgb value to a 15 bit rgb value. The bias parameter + * specifies the rounding direction. + */ +{ + uint16_t rgb555 = 0; + uint32_t r, g, b;
comments must be in a doxygen compatible format (u)intXY_t should only be used if an exact length variable is needed
+ double sumx = 0, sumy = 0, sumx2 = 0, sumy2 = 0, sumxy = 0,
floating point variables should be avoided in codecs as they make regression tests impossible and are very slow on architectures with no FPU
+ printf("\n\nRPZA DEBUG STATS\n");
printf->av_log()
r = (uint32_t)rgb24[0] + (uint32_t)(random() % 8);
random() cannot be used in libs, as it changes the state of the random number generator and isnt binary identical between platforms -- Michael
On 6/5/05, Michael Niedermayer <michaelni at gmx.at> wrote:
Hi
On Saturday 04 June 2005 09:55, Todd Kirby wrote:
Attached is a patch to add support for Apple Video encoding (rpza). The quality settings are defined at compile time right now since I wasn't really sure how to map ffmpeg's numerous video options to the capabilities of rpza. It supports all rpza encoding opcodes but the 4-color block encoding doesn't try to do multiblock runs yet.
+#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b))
theres FFMIN/FFMAX
+static uint16_t round_rgb24_to_rgb555(uint8_t * rgb24, int bias) +/* + * Round a 24 bit rgb value to a 15 bit rgb value. The bias parameter + * specifies the rounding direction. + */ +{ + uint16_t rgb555 = 0; + uint32_t r, g, b;
comments must be in a doxygen compatible format (u)intXY_t should only be used if an exact length variable is needed
+ double sumx = 0, sumy = 0, sumx2 = 0, sumy2 = 0, sumxy = 0,
floating point variables should be avoided in codecs as they make regression tests impossible and are very slow on architectures with no FPU
+ printf("\n\nRPZA DEBUG STATS\n");
printf->av_log()
r = (uint32_t)rgb24[0] + (uint32_t)(random() % 8);
random() cannot be used in libs, as it changes the state of the random number generator and isnt binary identical between platforms
Ok, most of this stuff is easy but, what is the alternative to random? Is this a new requirement? I see lots of uses of random in the libraries. -Todd
Todd Kirby wrote:
Ok, most of this stuff is easy but, what is the alternative to random? Is this a new requirement? I see lots of uses of random in the libraries.
Better question: Why are you generating random numbers in a video encoder? I am genuinely curious... -- -Mike Melanson
Todd Kirby wrote:
Ok, most of this stuff is easy but, what is the alternative to random? Is this a new requirement? I see lots of uses of random in the libraries.
This surprised me, so I did a 'grep', as you probably did: grep random\(\) libavcodec/*.c Indeed, there are a number of places where random() is called. Let's dig a little deeper, though: They all appear to be used in #if'd-out test functions. -- -Mike Melanson
Hi On Monday 06 June 2005 18:14, Todd Kirby wrote:
On 6/5/05, Michael Niedermayer <michaelni at gmx.at> wrote:
Hi
On Saturday 04 June 2005 09:55, Todd Kirby wrote:
Attached is a patch to add support for Apple Video encoding (rpza). The quality settings are defined at compile time right now since I wasn't really sure how to map ffmpeg's numerous video options to the capabilities of rpza. It supports all rpza encoding opcodes but the 4-color block encoding doesn't try to do multiblock runs yet.
+#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b))
theres FFMIN/FFMAX
+static uint16_t round_rgb24_to_rgb555(uint8_t * rgb24, int bias) +/* + * Round a 24 bit rgb value to a 15 bit rgb value. The bias parameter + * specifies the rounding direction. + */ +{ + uint16_t rgb555 = 0; + uint32_t r, g, b;
comments must be in a doxygen compatible format (u)intXY_t should only be used if an exact length variable is needed
+ double sumx = 0, sumy = 0, sumx2 = 0, sumy2 = 0, sumxy = 0,
floating point variables should be avoided in codecs as they make regression tests impossible and are very slow on architectures with no FPU
+ printf("\n\nRPZA DEBUG STATS\n");
printf->av_log()
r = (uint32_t)rgb24[0] + (uint32_t)(random() % 8);
random() cannot be used in libs, as it changes the state of the random number generator and isnt binary identical between platforms
Ok, most of this stuff is easy but, what is the alternative to random?
IIRC random() is only used for dithering, and random dither sucks anyway so it shouldnt be a problem OTOH writing your own random number generator is trivial try google with "linear congruential generator" or "lagged fibonacci generator" these 2 are the most common ones IIRC
Is this a new requirement?
no, random() is forbidden in libav*, ill shoot everyone who commited non debug code which uses random() ;) [...] -- Michael
Is this a new requirement?
no, random() is forbidden in libav*, ill shoot everyone who commited non debug code which uses random() ;)
I think I've found a definition for NULL that will satisfy everyone: #define NULL ((void *)((((unsigned)random()) % 2) >> 1) Fran?ois.
On 6/5/05, Michael Niedermayer <michaelni at gmx.at> wrote:
Hi
On Saturday 04 June 2005 09:55, Todd Kirby wrote:
Attached is a patch to add support for Apple Video encoding (rpza). The quality settings are defined at compile time right now since I wasn't really sure how to map ffmpeg's numerous video options to the capabilities of rpza. It supports all rpza encoding opcodes but the 4-color block encoding doesn't try to do multiblock runs yet.
+#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b))
theres FFMIN/FFMAX
+static uint16_t round_rgb24_to_rgb555(uint8_t * rgb24, int bias) +/* + * Round a 24 bit rgb value to a 15 bit rgb value. The bias parameter + * specifies the rounding direction. + */ +{ + uint16_t rgb555 = 0; + uint32_t r, g, b;
comments must be in a doxygen compatible format (u)intXY_t should only be used if an exact length variable is needed
+ double sumx = 0, sumy = 0, sumx2 = 0, sumy2 = 0, sumxy = 0,
floating point variables should be avoided in codecs as they make regression tests impossible and are very slow on architectures with no FPU
+ printf("\n\nRPZA DEBUG STATS\n");
printf->av_log()
r = (uint32_t)rgb24[0] + (uint32_t)(random() % 8);
random() cannot be used in libs, as it changes the state of the random number generator and isnt binary identical between platforms
Ok, most of this stuff is easy but, what is the alternative to random? Is this a new requirement? I see lots of uses of random in the libraries. -Todd
Todd Kirby wrote:
Ok, most of this stuff is easy but, what is the alternative to random? Is this a new requirement? I see lots of uses of random in the libraries.
I believe one of the overriding goals here is repeatability. It helps for testing and validation. Using random numbers makes this difficult. Even when using the same random seeds, I am not sure if the ANSI C spec requires the actual random number generator to be implemented the same way on different platforms. I looked at the way random() is used in the video encoder: + r = (uint32_t)rgb24[0] + (uint32_t)(random() % 8); + g = (uint32_t)rgb24[1] + (uint32_t)(random() % 8); + b = (uint32_t)rgb24[2] + (uint32_t)(random() % 8); + int channel_value = color[channel] + ((random() % range) / 3.0) - (range / 6.0); To get around using random(), maybe you could just write a program to generate a static table of random values from 0..7. Copy the text of those tables into the source and just iterate through the values, wrapping when you hit the end. Encoding results should be repeatable. -- -Mike Melanson
On Mon, Jun 06, 2005 at 11:04:27AM -0600, Mike Melanson wrote:
Todd Kirby wrote:
Ok, most of this stuff is easy but, what is the alternative to random? Is this a new requirement? I see lots of uses of random in the libraries.
I believe one of the overriding goals here is repeatability. It helps for testing and validation. Using random numbers makes this difficult. Even when using the same random seeds, I am not sure if the ANSI C spec requires the actual random number generator to be implemented the same way on different platforms.
I looked at the way random() is used in the video encoder:
+ r = (uint32_t)rgb24[0] + (uint32_t)(random() % 8); + g = (uint32_t)rgb24[1] + (uint32_t)(random() % 8); + b = (uint32_t)rgb24[2] + (uint32_t)(random() % 8);
+ int channel_value = color[channel] + ((random() % range) / 3.0) - (range / 6.0);
To get around using random(), maybe you could just write a program to generate a static table of random values from 0..7. Copy the text of those tables into the source and just iterate through the values, wrapping when you hit the end. Encoding results should be repeatable.
This code should not even be in the encoder to begin with. If the user wants this behavior they can use a noise-adding filter before encoding. Rich
On Mon, Jun 06, 2005 at 11:04:27AM -0600, Mike Melanson wrote:
Even when using the same random seeds, I am not sure if the ANSI C spec requires the actual random number generator to be implemented the same way on different platforms.
The C standard requires a given implementation to produce the same sequence of pseudorandom numbers each time it is seeded with the same value. The default seed is 1. There's no guarantee that you'll get the same sequence on two different implementations, and it could probably be argued that the standard doesn't guarantee that a given implementation will produce the same sequence on two separate runs of the same program.
I looked at the way random() is used in the video encoder:
The random() function isn't part of standard C in any case. It's a POSIX/UNIX extension. All that ANSI gives you is rand() and srand(). If you need consistent pseudorandom values between implementations, options include: - I think the UNIX nrand48 function makes some guarantees about the exact algorithm it uses, and it allows you to provide your own state data rather than using a global state. - You can implement your own. The C standard provides a very simple implementation of rand and srand as an example. - There are several strong random number generators available as open source, with Mersenne-Twister and ISAAC probably being the most well-known. At least for MT, the example implementation does use a global state but can be easily patched to encapsulate all of that into a structure and allow you to generate several independent streams of data at once. -Dave Dodge
On Wed, Jun 08, 2005 at 04:10:50PM -0400, Dave Dodge wrote:
On Mon, Jun 06, 2005 at 11:04:27AM -0600, Mike Melanson wrote:
Even when using the same random seeds, I am not sure if the ANSI C spec requires the actual random number generator to be implemented the same way on different platforms.
The C standard requires a given implementation to produce the same sequence of pseudorandom numbers each time it is seeded with the same value. The default seed is 1. There's no guarantee that you'll get the same sequence on two different implementations, and it could probably be argued that the standard doesn't guarantee that a given implementation will produce the same sequence on two separate runs of the same program.
I looked at the way random() is used in the video encoder:
The random() function isn't part of standard C in any case. It's a POSIX/UNIX extension. All that ANSI gives you is rand() and srand().
If you need consistent pseudorandom values between implementations, options include:
- I think the UNIX nrand48 function makes some guarantees about the exact algorithm it uses, and it allows you to provide your own state data rather than using a global state.
- You can implement your own. The C standard provides a very simple implementation of rand and srand as an example.
- There are several strong random number generators available as open source, with Mersenne-Twister and ISAAC probably being the most well-known. At least for MT, the example implementation does use a global state but can be easily patched to encapsulate all of that into a structure and allow you to generate several independent streams of data at once.
All this is good info, but aside from the point, which is that random numbers ARE NOT USEFUL for a video codec lib. Any attempt to use them is misguided, regardless of whether they're deterministic or corrupt the calling program's state... Rich
Todd Kirby wrote:
Attached is a patch to add support for Apple Video encoding (rpza). The quality settings are defined at compile time right now since I wasn't really sure how to map ffmpeg's numerous video options to the capabilities of rpza. It supports all rpza encoding opcodes but the 4-color block encoding doesn't try to do multiblock runs yet.
Can you generate files that play correctly under Apple's QuickTime player? Thanks... -- -Mike Melanson
Yeah, that's what my target was. I needed something that could play on even the oldest version of Quicktime Player. I used your excellent description of the rpza format at... http://www.pcisys.net/~melanson/codecs/ ...as a guide. I notice this document not there anymore. If you've moved it, let me know and I'll change my link in the source. -Todd On 6/5/05, Mike Melanson <mike at multimedia.cx> wrote:
Todd Kirby wrote:
Attached is a patch to add support for Apple Video encoding (rpza). The quality settings are defined at compile time right now since I wasn't really sure how to map ffmpeg's numerous video options to the capabilities of rpza. It supports all rpza encoding opcodes but the 4-color block encoding doesn't try to do multiblock runs yet.
Can you generate files that play correctly under Apple's QuickTime player?
Thanks... -- -Mike Melanson
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel at mplayerhq.hu http://mplayerhq.hu/mailman/listinfo/ffmpeg-devel
Todd Kirby wrote:
Yeah, that's what my target was. I needed something that could play on even the oldest version of Quicktime Player. I used your excellent description of the rpza format at...
http://www.pcisys.net/~melanson/codecs/
...as a guide. I notice this document not there anymore. If you've moved it, let me know and I'll change my link in the source.
The document is located @ multimedia.cx. I need to update all of those description links. BTW, credit where credit is due: The excellent RPZA description was the work of one Roberto T. -- -Mike Melanson
On 6/5/05, Mike Melanson <mike at multimedia.cx> wrote:
Todd Kirby wrote:
Yeah, that's what my target was. I needed something that could play on even the oldest version of Quicktime Player. I used your excellent description of the rpza format at...
http://www.pcisys.net/~melanson/codecs/
...as a guide. I notice this document not there anymore. If you've moved it, let me know and I'll change my link in the source.
The document is located @ multimedia.cx. I need to update all of those description links. BTW, credit where credit is due: The excellent RPZA description was the work of one Roberto T.
A big thanks to Roberto then! It was invaluable. The only snag I hit that the spec didn't cover was when I created movies where the multi-block runs were spanning more than one row of the image. Apparently the spec doesn't allow that. Once you hit the end of a row you must end the run and start a new run*. ffmpeg's rpza decoder doesn't make this assumption and so the movies played fine, but when I'd play them with QT player they would come out garbled. You might want to add a little blurb about that to the 'Final Notes' section of the rpza format specification. *This is true for skip and 1-color opcodes (my implementation doesn't do runs of 4-color but I'd imagine the same is true for 4-color.
On 6/5/05, Mike Melanson <mike at multimedia.cx> wrote:
Todd Kirby wrote:
Yeah, that's what my target was. I needed something that could play on even the oldest version of Quicktime Player. I used your excellent description of the rpza format at...
http://www.pcisys.net/~melanson/codecs/
...as a guide. I notice this document not there anymore. If you've moved it, let me know and I'll change my link in the source.
The document is located @ multimedia.cx. I need to update all of those description links.
Here's a patch to update the description links.
Todd Kirby wrote:
On 6/5/05, Mike Melanson <mike at multimedia.cx> wrote:
Todd Kirby wrote:
Yeah, that's what my target was. I needed something that could play on even the oldest version of Quicktime Player. I used your excellent description of the rpza format at...
http://www.pcisys.net/~melanson/codecs/
...as a guide. I notice this document not there anymore. If you've moved it, let me know and I'll change my link in the source.
The document is located @ multimedia.cx. I need to update all of those description links.
Here's a patch to update the description links.
Well, this never got applied... Is there more to it now or is it still good?
On 09/02/2007 09:15 PM, Ramiro Polla wrote:
Todd Kirby wrote:
On 6/5/05, Mike Melanson<mike at multimedia.cx> wrote:
Todd Kirby wrote:
Yeah, that's what my target was. I needed something that could play on even the oldest version of Quicktime Player. I used your excellent description of the rpza format at...
http://www.pcisys.net/~melanson/codecs/
...as a guide. I notice this document not there anymore. If you've moved it, let me know and I'll change my link in the source.
The document is located @ multimedia.cx. I need to update all of those description links.
Here's a patch to update the description links.
Well, this never got applied... Is there more to it now or is it still good?
Git-friendly patch attached so patchwork will catch it up. -Vitor
From ffmpeg.phpatgmail.com Sun Jan 23 18:05:03 2011 From: ffmpeg.phpatgmail.com (Todd Kirby) Date: Sun, 23 Jan 2011 18:05:03 +0100 Subject: [PATCH] Apple Video Encoder (rpza) Message-ID: <mailman.467.1295802395.1307.ffmpeg-devel@mplayerhq.hu>
--- libavcodec/Makefile | 3 + libavcodec/allcodecs.c | 3 + libavcodec/avcodec.h | 1 + libavcodec/rpzaenc.c | 1169 ++++++++++++++++++++++++++++++++++++++++++++++++ libavformat/movenc.c | 1 + 5 files changed, 1177 insertions(+), 0 deletions(-) create mode 100644 libavcodec/rpzaenc.c diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 9e38eef..d75d66a 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -136,6 +136,9 @@ endif ifneq ($(CONFIG_SVQ1_DECODER)$(CONFIG_SVQ1_ENCODER),) OBJS+= svq1.o endif +ifneq ($(CONFIG_RPZA_ENCODER),) + OBJS+= rpzaenc.o +endif ifeq ($(CONFIG_TRUEMOTION1_DECODER),yes) OBJS+= truemotion1.o endif diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index a0a7d7f..5e0d3f9 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -189,6 +189,9 @@ void avcodec_register_all(void) #ifdef CONFIG_LIBGSM register_avcodec(&libgsm_encoder); #endif //CONFIG_LIBGSM +#ifdef CONFIG_RPZA_ENCODER + register_avcodec(&rpza_encoder); +#endif //CONFIG_RPZA_ENCODER #endif /* CONFIG_ENCODERS */ #ifdef CONFIG_RAWVIDEO_ENCODER register_avcodec(&rawvideo_encoder); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 2abb391..60c0158 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -1943,6 +1943,7 @@ extern AVCodec sonic_encoder; extern AVCodec sonic_ls_encoder; extern AVCodec svq1_encoder; extern AVCodec x264_encoder; +extern AVCodec rpza_encoder; extern AVCodec h263_decoder; extern AVCodec h261_decoder; diff --git a/libavcodec/rpzaenc.c b/libavcodec/rpzaenc.c new file mode 100644 index 0000000..d8b5ab7 --- /dev/null +++ b/libavcodec/rpzaenc.c @@ -0,0 +1,1169 @@ +/* + * Quicktime RPZA Video Encoder. + * Copyright (C) 2005 the ffmpeg project + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 + * + */ + +/** + * @file rpzaenc.c + * QT RPZA Video Encoder by Todd Kirby <doubleshot at pacbell.net> and David Adler + * + * For more information about the RPZA format, visit: + * http://www.pcisys.net/~melanson/codecs/ + */ + +#include "avcodec.h" +#include "dsputil.h" +#include "bitstream.h" +#include "assert.h" + +#ifdef CONFIG_ENCODERS + +typedef struct RpzaContext { + + AVCodecContext *avctx; + DSPContext dsp; + + AVFrame current_frame; // buffer for current 24 bit source frame + AVFrame prev_frame; // buffer for previous 24 bit source frame + PutBitContext pb; // buffer for encoded frame data. + + int frame_width; // width in pixels of source frame + int frame_height; // height in pixesl of source frame + + unsigned char *buf; + int first_frame; // flag set to one when the first frame is being processed + // so that comparisons with previous frame data in not attempted + +#ifdef DEBUG_STATS + int one_count, one_blocks_count; + int four_count, sixteen_count; + int skip_count, skip_blocks_count; +#endif +} RpzaContext; + + +typedef struct rgb { + uint8_t r; + uint8_t g; + uint8_t b; +} rgb; + +#define PIXELSTRIDE 3 + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +#define SQR(x) ((x) * (x)) + +/* 15 bit components */ +#define GET_CHAN(color, chan) ((color) >> ((chan) * 5) & 0x1F) +#define R(color) GET_CHAN(color, RED) +#define G(color) GET_CHAN(color, GREEN) +#define B(color) GET_CHAN(color, BLUE) + +/* 8 bit rounding constants */ +#define ROUND_UP 7 +#define ROUND_NEAREST 4 +#define ROUND_DOWN 0 + +/* tuning parameters */ +#define SKIP_FRAME_THRESH 13 +#define START_ONE_COLOR_THRESH 8 +#define CONTINUE_ONE_COLOR_THRESH 0 +#define SIXTEEN_COLOR_THRESH 24 + +//#define RPZA_DITHER + +/* debug modes */ +//#define DEBUG_SKIP +//#define DEBUG_SIXTEEN +//#define DEBUG_STATS + +typedef enum channel_offset { + RED = 2, + GREEN = 1, + BLUE = 0, +} channel_offset; + +typedef struct BlockInfo { + int row; + int col; + int block_width; + int block_height; + int image_width; + int image_height; + int block_index; + uint16_t start; + int rowstride; + int blocks_per_row; + int total_blocks; +} BlockInfo; + + +static void get_colors(uint8_t *colorB, uint8_t *colorA, uint8_t color4[4][3]) +{ + uint8_t step; + + color4[0][0] = colorB[0]; + color4[0][1] = colorB[1]; + color4[0][2] = colorB[2]; + + color4[3][0] = colorA[0]; + color4[3][1] = colorA[1]; + color4[3][2] = colorA[2]; + + // red components + step = (color4[3][0] - color4[0][0] + 1) / 3; + color4[1][0] = color4[0][0] + step; + color4[2][0] = color4[3][0] - step; + + // green components + step = (color4[3][1] - color4[0][1] + 1) / 3; + color4[1][1] = color4[0][1] + step; + color4[2][1] = color4[3][1] - step; + + // blue components + step = (color4[3][2] - color4[0][2] + 1) / 3; + color4[1][2] = color4[0][2] + step; + color4[2][2] = color4[3][2] - step; +} + + +static int get_block_info(BlockInfo *bi, int block) +/* Fill BlockInfo struct with information about a 4x4 block of the image */ +{ + bi->row = block / bi->blocks_per_row; + bi->col = block % bi->blocks_per_row; + + // test for right edge block + if (bi->col == bi->blocks_per_row - 1 && (bi->image_width % 4) != 0) { + bi->block_width = bi->image_width % 4; + } else { + bi->block_width = 4; + } + + // test for bottom edge block + if (bi->row == (bi->image_height / 4) && (bi->image_height % 4) != 0) { + bi->block_height = bi->image_height % 4; + } else { + bi->block_height = 4; + } + + return block ? (bi->col * 4 * PIXELSTRIDE) + (bi->row * bi->rowstride * 4) : 0; +} + + +static uint16_t round_rgb24_to_rgb555(uint8_t * rgb24, int bias) +/* + * Round a 24 bit rgb value to a 15 bit rgb value. The bias parameter + * specifies the rounding direction. + */ +{ + uint16_t rgb555 = 0; + uint32_t r, g, b; + + r = (uint32_t)rgb24[0] + bias; + g = (uint32_t)rgb24[1] + bias; + b = (uint32_t)rgb24[2] + bias; + + r = r / 8; + g = g / 8; + b = b / 8; + + /* clamp 0-31 */ + if (r > 31) { + r = 31; + } + if (g > 31) { + g = 31; + } + if (b > 31) { + b = 31; + } + + rgb555 |= (r << 10); + rgb555 |= (g << 5); + rgb555 |= (b << 0); + + return rgb555; +} + +#ifdef RPZA_DITHER + + +static uint16_t dither_rgb24_to_rgb555(uint8_t * rgb24) +/* + * Dither a 24 bit rgb value to a 15 bit rgb value. + */ +{ + uint16_t rgb555 = 0; + uint32_t r, g, b; + + r = (uint32_t)rgb24[0] + (uint32_t)(random() % 8); + g = (uint32_t)rgb24[1] + (uint32_t)(random() % 8); + b = (uint32_t)rgb24[2] + (uint32_t)(random() % 8); + + r = r / 8; + g = g / 8; + b = b / 8; + + /* clamp 0-31 */ + if (r > 31) { + r = 31; + } + if (g > 31) { + g = 31; + } + if (b > 31) { + b = 31; + } + + rgb555 |= (r << 10); + rgb555 |= (g << 5); + rgb555 |= (b << 0); + + return rgb555; +} +#endif + + +static int diff_colors(uint8_t *colorA, uint8_t *colorB) +/* + * Returns the total difference between two 24 bit color values + */ +{ + int tot; + tot = SQR(colorA[0] - colorB[0]); + tot += SQR(colorA[1] - colorB[1]); + tot += SQR(colorA[2] - colorB[2]); + return tot; +} + +static int max_component_diff(uint8_t *colorA, uint8_t *colorB) +/* + * Returns the maximum channel difference between two 24 bit color values + */ +{ + int i, diff, max = 0; + + for (i = 0; i < 3; i++) { + diff = abs(colorA[i] - colorB[i]); + if (diff > max) { + max = diff; + } + } + return max; +} + +#if 0 +static void print_rgb24_color(uint8_t *color) +{ + printf("r{%02d} g{%02d} b{%02d}", color[0], color[1], color[2]); +} + +static void print_block(uint8_t *block, BlockInfo *bi) +{ + int x, y; + + for (y = 0; y < bi->block_height; y++) { + for (x = 0; x < bi->block_width; x++) { + printf("%d: ", x); + print_rgb24_color(&block[x * PIXELSTRIDE]); + printf("\n"); + } + printf("\n"); + block += bi->rowstride; + } +} +#endif + +static void get_max_component_diff(BlockInfo *bi, uint8_t *block_ptr, + uint8_t *min, uint8_t *max, channel_offset *chan) +/* + * Find the channel that has the largest difference between minimum and maximum + * color values. Put the minimum value in min, maximum in max and the channel + * in chan. + */ +{ + int x, y; + uint8_t min_r, max_r, min_g, max_g, min_b, max_b; + uint8_t r, g, b; + + // fix warning about uninitialized vars + min_r = min_g = min_b = UINT8_MAX; + max_r = max_g = max_b = 0; + + // loop thru and compare pixels + for (y = 0; y < bi->block_height; y++) { + for (x = 0; x < bi->block_width; x++){ + // TODO: optimize + min_r = MIN(block_ptr[(x * PIXELSTRIDE) + 2], min_r); + min_g = MIN(block_ptr[(x * PIXELSTRIDE) + 1], min_g); + min_b = MIN(block_ptr[(x * PIXELSTRIDE) + 0], min_b); + + max_r = MAX(block_ptr[(x * PIXELSTRIDE) + 2], max_r); + max_g = MAX(block_ptr[(x * PIXELSTRIDE) + 1], max_g); + max_b = MAX(block_ptr[(x * PIXELSTRIDE) + 0], max_b); + } + block_ptr += bi->rowstride; + } + + r = max_r - min_r; + g = max_g - min_g; + b = max_b - min_b; + + if (r > g && r > b) { + *max = max_r; + *min = min_r; + *chan = RED; + } else if (g > b && g >= r) { + *max = max_g; + *min = min_g; + *chan = GREEN; + } else { + *max = max_b; + *min = min_b; + *chan = BLUE; + } +} + +static int compare_blocks(uint8_t *block1, uint8_t *block2, BlockInfo *bi, int thresh) +/* + * Compare two 4x4 blocks to determine if the total difference between the + * blocks is greater than the thresh parameter. Returns -1 if difference + * exceeds threshold or zero otherwise. + */ +{ + int x, y, diff = 0; + for (y = 0; y < bi->block_height; y++) { + for (x = 0; x < bi->block_width; x++) { + diff = max_component_diff(&block1[x * PIXELSTRIDE], &block2[x * PIXELSTRIDE]); + if (diff >= thresh) { + return -1; + } + } + block1 += bi->rowstride; + block2 += bi->rowstride; + } + return 0; +} + + +static int leastsquares(uint8_t *block_ptr, BlockInfo *bi, + channel_offset xchannel, channel_offset ychannel, + double *slope, double *y_intercept, double *correlation_coef) +/* + * Determine the fit of one channel to another within a 4x4 block. This + * is used to determine the best palette choices for 4-color encoding. + */ +{ + double sumx = 0, sumy = 0, sumx2 = 0, sumy2 = 0, sumxy = 0, + sumx_sq = 0, sumy_sq = 0, tmp, tmp2; + int i, j, count; + uint8_t x, y; + + count = bi->block_height * bi->block_width; + + if (count < 2) { + return -1; + } + + for (i = 0; i < bi->block_height; i++) { + for (j = 0; j < bi->block_width; j++){ + x = block_ptr[j * PIXELSTRIDE + xchannel]; + y = block_ptr[j * PIXELSTRIDE + ychannel]; + sumx += x; + sumy += y; + sumx2 += x * x; + sumy2 += y * y; + sumxy += x * y; + } + block_ptr += bi->rowstride; + } + + + sumx_sq = sumx * sumx; + tmp = (count * sumx2 - sumx_sq); + + // guard against div/0 + if (tmp == 0) { + return -2; + } + + sumy_sq = sumy * sumy; + + *slope = (count * sumxy - sumx * sumy) / tmp; + *y_intercept = (sumy - (*slope) * sumx) / count; + + tmp2 = count * sumy2 - sumy_sq; + if (tmp2 == 0) { + *correlation_coef = 0.0; + } else { + *correlation_coef = (count * sumxy - sumx * sumy) / + sqrt(tmp * tmp2); + } + + return 0; // success +} + +static int +calc_lsq_max_fit_error(uint8_t *block_ptr, BlockInfo *bi, + int min, int max, int tmp_min, int tmp_max, + channel_offset xchannel, channel_offset ychannel) +/* + * Determine the amount of error in the leastsquares fit. + */ +{ + int i, j, x, y; + int err; + int max_err = 0; + + for (i = 0; i < bi->block_height; i++) { + for (j = 0; j < bi->block_width; j++){ + int x_inc, lin_y, lin_x; + x = block_ptr[j * PIXELSTRIDE + xchannel]; + y = block_ptr[j * PIXELSTRIDE + ychannel]; + + /* calculate x_inc as the 4-color index (0..3) */ + x_inc = floor( (x - min) * 3.0 / (max - min) + 0.5); + x_inc = MAX(MIN(3, x_inc), 0); + + /* calculate lin_y corresponding to x_inc */ + lin_y = (int)(tmp_min + (tmp_max - tmp_min) * x_inc / 3.0 + 0.5); + + err = abs(lin_y - y); + if (err > max_err) + max_err = err; + + /* calculate lin_x corresponding to x_inc */ + lin_x = (int)(min + (max - min) * x_inc / 3.0 + 0.5); + + err = abs(lin_x - x); + if (err > max_err) + max_err += err; + } + block_ptr += bi->rowstride; + } + + return max_err; +} + +static int match_color(uint8_t *color, uint8_t colors[4][3]) +/* + * Find the closest match to a color within the 4-color palette + */ +{ + int ret = 0, channel, palette_entry, variance; + int smallest_variance = INT_MAX; + uint8_t dithered_color[3]; + int range; + + + for (channel = 0; channel < 3; channel++) { + range = abs(colors[3][channel] - colors[0][channel]); + +#ifdef RPZA_DITHER + if (range > 0) { + int channel_value = color[channel] + ((random() % range) / 3.0) - (range / 6.0); + if (channel_value < 0) { + channel_value = 0; + } + if (channel_value > 255) { + channel_value = 255; + } + dithered_color[channel] = channel_value; + } else { +#endif + dithered_color[channel] = color[channel]; +#ifdef RPZA_DITHER + } +#endif + } + + for (palette_entry = 0; palette_entry < 4; palette_entry++) { + variance = diff_colors(dithered_color, colors[palette_entry]); + + if (variance < smallest_variance) { + smallest_variance = variance; + ret = palette_entry; + } + } + return ret; +} + + +static int encode_four_color_block(uint8_t *min_color, uint8_t *max_color, + PutBitContext *pb, uint8_t *block_ptr, BlockInfo *bi) +/* + * Encode a block using the 4-color opcode and palette. return number of + * blocks encoded (until we implement multi-block 4 color runs this will + * always be 1) + */ +{ + int x, y, idx; + uint8_t color4[4][3]; + uint16_t rounded_max, rounded_min; + + // round min and max wider + rounded_min = round_rgb24_to_rgb555(min_color, ROUND_DOWN); + rounded_max = round_rgb24_to_rgb555(max_color, ROUND_UP); + + // put a and b colors + // encode 4 colors = first 16 bit color with MSB zeroed and... + put_bits(pb, 16, rounded_max & ~0x8000); + // ...second 16 bit color with MSB on. + put_bits(pb, 16, rounded_min | 0x8000); + + // scale back up to 24 bit + min_color[0] = R(rounded_min) * 8; + min_color[1] = G(rounded_min) * 8; + min_color[2] = B(rounded_min) * 8; + + max_color[0] = R(rounded_max) * 8; + max_color[1] = G(rounded_max) * 8; + max_color[2] = B(rounded_max) * 8; + + get_colors(min_color, max_color, color4); + + for (y = 0; y < 4; y++) { + for (x = 0; x < 4; x++) { + idx = match_color(&block_ptr[x * PIXELSTRIDE], color4); + put_bits(pb, 2, idx); + } + block_ptr += bi->rowstride; + } + return 1; // num blocks encoded +} + + + +static void update_block_in_prev_frame(const uint8_t *src_pixels, + uint8_t *dest_pixels, const BlockInfo *bi, int block_counter) +/* + * Copy a 4x4 block from the current frame buffer to the previous frame buffer. + */ +{ + int y; + + for (y = 0; y < 4; y++) { + memcpy (dest_pixels, src_pixels, 4 * PIXELSTRIDE); + dest_pixels += bi->rowstride; + src_pixels += bi->rowstride; + } +} + + +static int +update_block_stats(BlockInfo *bi, uint8_t *block, + uint8_t min_color[3], uint8_t max_color[3], + int *total_rgb, int *total_pixels, + uint8_t avg_color[3], int first_block) +/* + update_block_stats updates statistics for the specified block. If first_block, + it initializes the statistics. Otherwise it updates the statistics IF THIS + BLOCK IS SUITABLE TO CONTINUE A 1-COLOR RUN. That is, it checks whether + the range of colors (since the routine was called first_block != 0) are + all close enough intensities to be represented by a single color. + + The routine returns 0 if this block is too different to be part of + the same run of 1-color blocks. The routine returns 1 if this + block can be part of the same 1-color block run. + + If the routine returns 1, it also updates its arguments to include + the statistics of this block. Otherwise, the stats are unchanged + and don't include the current block. + */ +{ + int x, y; + int is_in_range; + int total_pixels_blk; + int threshold; + + uint8_t min_color_blk[3], max_color_blk[3]; + int total_rgb_blk[3]; + uint8_t avg_color_blk[3]; + + if (first_block) { + min_color[0] = UINT8_MAX; + min_color[1] = UINT8_MAX; + min_color[2] = UINT8_MAX; + max_color[0] = 0; + max_color[1] = 0; + max_color[2] = 0; + total_rgb[0] = 0; + total_rgb[1] = 0; + total_rgb[2] = 0; + *total_pixels = 0; + threshold = START_ONE_COLOR_THRESH; + } else { + threshold = CONTINUE_ONE_COLOR_THRESH; + } + + /* + The *_blk variables will include the current block. + Initialize them based on the blocks so far. + */ + min_color_blk[0] = min_color[0]; + min_color_blk[1] = min_color[1]; + min_color_blk[2] = min_color[2]; + max_color_blk[0] = max_color[0]; + max_color_blk[1] = max_color[1]; + max_color_blk[2] = max_color[2]; + total_rgb_blk[0] = total_rgb[0]; + total_rgb_blk[1] = total_rgb[1]; + total_rgb_blk[2] = total_rgb[2]; + total_pixels_blk = *total_pixels + bi->block_height * bi->block_width; + + /* + Update stats for this block's pixels + */ + for (y = 0; y < bi->block_height; y++) { + for (x = 0; x < bi->block_width; x++) { + + total_rgb_blk[0] += block[x * PIXELSTRIDE]; + total_rgb_blk[1] += block[x * PIXELSTRIDE + 1]; + total_rgb_blk[2] += block[x * PIXELSTRIDE + 2]; + + min_color_blk[0] = MIN(block[x * PIXELSTRIDE], min_color_blk[0]); + min_color_blk[1] = MIN(block[x * PIXELSTRIDE + 1], min_color_blk[1]); + min_color_blk[2] = MIN(block[x * PIXELSTRIDE + 2], min_color_blk[2]); + + max_color_blk[0] = MAX(block[x * PIXELSTRIDE], max_color_blk[0]); + max_color_blk[1] = MAX(block[x * PIXELSTRIDE + 1], max_color_blk[1]); + max_color_blk[2] = MAX(block[x * PIXELSTRIDE + 2], max_color_blk[2]); + } + block += bi->rowstride; + } + + /* + Calculate average color including current block. + */ + avg_color_blk[0] = total_rgb_blk[0] / total_pixels_blk; + avg_color_blk[1] = total_rgb_blk[1] / total_pixels_blk; + avg_color_blk[2] = total_rgb_blk[2] / total_pixels_blk; + + /* + Are all the pixels within threshold of the average color? + */ + is_in_range = (max_color_blk[0] - avg_color_blk[0] <= threshold && + max_color_blk[1] - avg_color_blk[1] <= threshold && + max_color_blk[2] - avg_color_blk[2] <= threshold && + avg_color_blk[0] - min_color_blk[0] <= threshold && + avg_color_blk[1] - min_color_blk[1] <= threshold && + avg_color_blk[2] - min_color_blk[2] <= threshold); + + if (is_in_range) { + /* + Set the output variables to include this block. + */ + min_color[0] = min_color_blk[0]; + min_color[1] = min_color_blk[1]; + min_color[2] = min_color_blk[2]; + max_color[0] = max_color_blk[0]; + max_color[1] = max_color_blk[1]; + max_color[2] = max_color_blk[2]; + total_rgb[0] = total_rgb_blk[0]; + total_rgb[1] = total_rgb_blk[1]; + total_rgb[2] = total_rgb_blk[2]; + *total_pixels = total_pixels_blk; + avg_color[0] = avg_color_blk[0]; + avg_color[1] = avg_color_blk[1]; + avg_color[2] = avg_color_blk[2]; + } + + return is_in_range; +} + + +static void rpza_encode_stream(RpzaContext *s, AVFrame *pict) +/* + * Encode a single frame and add it to the sequence + */ +{ + BlockInfo bi; + int block_counter = 0; + int n_blocks; + int total_blocks; + int prev_block_offset; + int block_offset = 0; + uint8_t min = 0, max = 0; + channel_offset chan; + int i; + int tmp_min, tmp_max; + int total_rgb[3]; + uint8_t avg_color[3]; + int pixel_count; + uint8_t min_color[3], max_color[3]; + double slope, y_intercept, correlation_coef; + uint8_t *src_pixels = (uint8_t*) pict->data[0]; + uint8_t *prev_pixels = (uint8_t*) s->prev_frame.data[0]; + + /* Number of 4x4 blocks in frame. */ + total_blocks = ((s->frame_width + 3) / 4) * ((s->frame_height + 3) / 4); + + bi.image_width = s->frame_width; + bi.image_height = s->frame_height; + bi.rowstride = pict->linesize[0]; + + bi.blocks_per_row = (s->frame_width + 3) / 4; + + while (block_counter < total_blocks) { + + // SKIP CHECK + // make sure we have a valid previous frame and we're not writing + // a key frame + if (!s->first_frame && s->current_frame.pict_type == FF_P_TYPE) { + + n_blocks = 0; + prev_block_offset = 0; + + while (n_blocks < 32 && block_counter + n_blocks < total_blocks) { + + block_offset = get_block_info(&bi, block_counter + n_blocks); + + // multi-block opcodes cannot span multiple rows. + // If we're starting a new row, break out and write the opcode + /* TODO: Should eventually use bi.row here to determine when a + row break occurs, but that is currently breaking the + quicktime player. This is probably due to a bug in the + way I'm calculating the current row. + */ + if (prev_block_offset && block_offset - prev_block_offset > 12) { + break; + } + + prev_block_offset = block_offset; + + if (compare_blocks(&prev_pixels[block_offset], + &src_pixels[block_offset], &bi, SKIP_FRAME_THRESH) != 0) { + // write out skipable blocks + if (n_blocks) { +#ifdef DEBUG_STATS + s->skip_count++; + s->skip_blocks_count += n_blocks; +#endif + +#ifdef DEBUG_SKIP + // write one color opcode. + put_bits(&s->pb, 8, 0xa0 | (n_blocks - 1)); + // write color to encode. + put_bits(&s->pb, 16, 0x0FF0); +#else + // write skip opcode + put_bits(&s->pb, 8, 0x80 | (n_blocks - 1)); +#endif + block_counter += n_blocks; + + goto post_skip; + } + break; + } + + /* + * NOTE: we don't update skipped blocks in the previous frame buffer + * since skipped needs always to be compared against the first skipped + * block to avoid artifacts during gradual fade in/outs. + */ + + // DEBUG PREV FRAME WRITING + // update_block_in_prev_frame(&src_pixels[block_offset], + // &prev_pixels[block_offset], &bi, block_counter + n_blocks); + + n_blocks++; + } + + // we're either at the end of the frame or we've reached the maximum + // of 32 blocks in a run. Write out the run. + if (n_blocks) { +#ifdef DEBUG_STATS + s->skip_count++; + s->skip_blocks_count += n_blocks; +#endif + +#ifdef DEBUG_SKIP + // write skip opcodes as one color opcodes for debugging + put_bits(&s->pb, 8, 0xa0 | (n_blocks - 1)); + // write debug color to encode skips as. + put_bits(&s->pb, 16, 0x0FF0); +#else + // write skip opcode + put_bits(&s->pb, 8, 0x80 | (n_blocks - 1)); +#endif + block_counter += n_blocks; + + continue; + } + + } else { + block_offset = get_block_info(&bi, block_counter); + } +post_skip : + + // ONE COLOR CHECK + if (update_block_stats(&bi, &src_pixels[block_offset], + min_color, max_color, + total_rgb, &pixel_count, avg_color, 1)) { + int first_block_offset; + first_block_offset = prev_block_offset = block_offset; + + n_blocks = 1; + + /* update this block in the previous frame buffer */ + update_block_in_prev_frame(&src_pixels[block_offset], + &prev_pixels[block_offset], &bi, block_counter + n_blocks); + + // check for subsequent blocks with the same color + while (n_blocks < 32 && block_counter + n_blocks < total_blocks) { + block_offset = get_block_info(&bi, block_counter + n_blocks); + + // multi-block opcodes cannot span multiple rows. + // If we've hit end of a row, break out and write the opcode + if (block_offset - prev_block_offset > 12) { + break; + } + + if (!update_block_stats(&bi, &src_pixels[block_offset], + min_color, max_color, + total_rgb, &pixel_count, avg_color, 0)) { + break; + } + + prev_block_offset = block_offset; + + /* update this block in the previous frame buffer */ + update_block_in_prev_frame(&src_pixels[block_offset], + &prev_pixels[block_offset], &bi, block_counter + n_blocks); + + n_blocks++; + } + + +#ifdef DEBUG_STATS + s->one_count++; + s->one_blocks_count += n_blocks; +#endif + // write one color opcode. + put_bits(&s->pb, 8, 0xa0 | (n_blocks - 1)); + // write color to encode. + put_bits(&s->pb, 16, round_rgb24_to_rgb555(avg_color, ROUND_NEAREST)); + // skip past the blocks we've just encoded. + block_counter += n_blocks; + } else { // FOUR COLOR CHECK + int err = 0; + + // get max component diff for block + get_max_component_diff(&bi, &src_pixels[block_offset], &min, &max, &chan); + + min_color[0] = 0; + max_color[0] = 0; + min_color[1] = 0; + max_color[1] = 0; + min_color[2] = 0; + max_color[2] = 0; + + // run least squares against other two components + for (i = 0; i < 3; i++) { + + if (i == chan) { + min_color[i] = min; + max_color[i] = max; + continue; + } + + slope = y_intercept = correlation_coef = 0; + + if (leastsquares(&src_pixels[block_offset], &bi, chan, i, + &slope, &y_intercept, &correlation_coef)) { + min_color[i] = src_pixels[block_offset + i]; + max_color[i] = src_pixels[block_offset + i]; + + } else { + tmp_min = (int)(0.5 + min * slope + y_intercept); + tmp_max = (int)(0.5 + max * slope + y_intercept); + + // clamp min and max color values + if (tmp_min > 255) { + tmp_min = 255; + } + if (tmp_max > 255) { + tmp_max = 255; + } + if (tmp_min < 0) { + tmp_min = 0; + } + if (tmp_max < 0) { + tmp_max = 0; + } + + err = MAX(calc_lsq_max_fit_error(&src_pixels[block_offset], &bi, + min, max, tmp_min, tmp_max, chan, i), err); + + min_color[i] = tmp_min; + max_color[i] = tmp_max; + } + } + + if (err > SIXTEEN_COLOR_THRESH) { // DO SIXTEEN COLOR BLOCK + int x, y; + uint8_t *row_ptr; + uint16_t rgb555 = 0; + +#ifdef DEBUG_STATS + s->sixteen_count++; +#endif + +#ifdef DEBUG_SIXTEEN + // write one color opcode. + put_bits(&s->pb, 8, 0xa0 | 0); + // write color to encode. + put_bits(&s->pb, 16, 0x0FF0); +#else + block_offset = get_block_info(&bi, block_counter); + + row_ptr = &src_pixels[block_offset]; + // encode 16 colors = first 16 bit color with MSB zeroed and... +#ifdef RPZA_DITHER + rgb555 = dither_rgb24_to_rgb555(row_ptr); +#else + rgb555 = round_rgb24_to_rgb555(row_ptr, ROUND_NEAREST); +#endif + put_bits(&s->pb, 16, rgb555 & ~0x8000); + + row_ptr += PIXELSTRIDE; + + // ...second 16 bit color with MSB zeroed. +#ifdef RPZA_DITHER + rgb555 = dither_rgb24_to_rgb555(row_ptr); +#else + rgb555 = round_rgb24_to_rgb555(row_ptr, ROUND_NEAREST); +#endif + put_bits(&s->pb, 16, rgb555 & ~0x8000); + + row_ptr += PIXELSTRIDE; + + x = 2; // skip first two pixels we just encoded above. + + for (y = 0; y < 4; y++) { + for (; x < 4; x++){ +#ifdef RPZA_DITHER + rgb555 = dither_rgb24_to_rgb555(row_ptr); +#else + rgb555 = round_rgb24_to_rgb555(row_ptr, ROUND_NEAREST); +#endif + put_bits(&s->pb, 16, rgb555); + row_ptr+= PIXELSTRIDE; + } + x = 0; + row_ptr += bi.rowstride - (4 * PIXELSTRIDE); + } +#endif + block_counter++; + + } else { // FOUR COLOR BLOCK +#ifdef DEBUG_STATS + s->four_count++; +#endif + block_counter += encode_four_color_block(min_color, max_color, + &s->pb, &src_pixels[block_offset], &bi); + } + + /* update this block in the previous frame buffer */ + update_block_in_prev_frame(&src_pixels[block_offset], + &prev_pixels[block_offset], &bi, block_counter); + } + } + return; +} + + +static int rpza_encode_init(AVCodecContext *avctx) +/* + * Called by libavcodec before encoding begins + */ +{ + RpzaContext * const s = avctx->priv_data; + + dsputil_init(&s->dsp, avctx); + + avctx->coded_frame = (AVFrame*)&s->current_frame; + + avctx->coded_frame->pict_type = FF_I_TYPE; + avctx->coded_frame->key_frame = 1; + + s->frame_width = avctx->width; + s->frame_height = avctx->height; + + s->avctx = avctx; + +#ifdef DEBUG_STATS + /* init debug counters */ + s->skip_count = 0; + s->skip_blocks_count = 0; + s->one_count = 0; + s->one_blocks_count = 0; + s->four_count = 0; + s->sixteen_count = 0; +#endif + return 0; +} + + +#ifdef DEBUG_SKIP +#include "../libavformat/avformat.h" +static void dump_img_to_sgi(AVFrame *rgb_frame, int width, int height, char *filename) +/* + * Dump frame to an sgi file for debuggimg + */ +{ + int err; + AVImageFormat *image_fmt; + AVImageInfo img_info; + ByteIOContext pb; + + for (image_fmt = first_image_format; image_fmt != NULL; + image_fmt = image_fmt->next) { + if (strncmp(image_fmt->name, "sgi", 3) == 0) { + break; + } + } + img_info.pict.data[0] = rgb_frame->data[0]; + img_info.pict.linesize[0] = rgb_frame->linesize[0]; + img_info.pix_fmt = PIX_FMT_RGB24; + img_info.width = width; + img_info.height = height; + img_info.interleaved = 0; + + // open the file + err = url_fopen(&pb, filename, URL_RDWR); + if (err < 0) { + printf("Could not open %s %d\n", filename, err); + exit(1); + } + + url_setbufsize(&pb, 4096); + image_fmt->img_write(&pb, &img_info); + url_fclose(&pb); +} +#endif + +static int rpza_encode_frame(AVCodecContext *avctx, unsigned char *buf, + int buf_size, void *data) +/* Called by libavformat to encode a single frame into rpza. */ +{ + RpzaContext * const s = avctx->priv_data; + int encoded_frame_size; + AVFrame *pict = data; + AVFrame * const p = (AVFrame*)&s->current_frame; + + s->current_frame.reference = 1; + if(!s->current_frame.data[0]){ + avctx->get_buffer(avctx, &s->current_frame); + } + + init_put_bits(&s->pb, buf, buf_size); + + // skip 4 byte header, write it later once the size of the chunk is known + put_bits(&s->pb, 32, 0x00); + + *p = *pict; + p->pict_type = avctx->frame_number % avctx->gop_size ? FF_P_TYPE : FF_I_TYPE; + p->key_frame = p->pict_type == FF_I_TYPE; + + if(!s->prev_frame.data[0]){ + s->first_frame = 1; + avctx->get_buffer(avctx, &s->prev_frame); + } else { + s->first_frame = 0; + } + + s->prev_frame.linesize[0] = pict->linesize[0]; + + rpza_encode_stream(s, p); + flush_put_bits(&s->pb); + + encoded_frame_size = (put_bits_count(&s->pb) / 8); + + // write header opcode + buf[0] = 0xe1; // chunk opcode + + // write chunk length + buf[1] = encoded_frame_size >> 16; + buf[2] = encoded_frame_size >> 8; + buf[3] = encoded_frame_size; + +#ifdef DEBUG_SKIP + { + // Write out prev and current frame buffers to sgi + static int frame = 1; + + char current[32], prev[32]; + snprintf(current, 17, "current-%04d.sgi", frame); + snprintf(prev, 17, "prev-%04d.sgi", frame); + dump_img_to_sgi(&s->current_frame, s->frame_width, s->frame_height, current); + dump_img_to_sgi(&s->prev_frame, s->frame_width, s->frame_height, prev); + + frame++; + } +#endif + + return encoded_frame_size; +} + + +static int rpza_encode_end(AVCodecContext *avctx) +/* Called by libavformat when encoding is finished. */ +{ + RpzaContext *s = (RpzaContext *)avctx->priv_data; + +#ifdef DEBUG_STATS + int total_blocks = s->skip_blocks_count + s->one_blocks_count + s->four_count + s->sixteen_count; + printf("\n\nRPZA DEBUG STATS\n"); + printf("skip %d\n", s->skip_count); + printf("skip blocks %d (%0.2f%%)\n", s->skip_blocks_count, (float) s->skip_blocks_count / total_blocks * 100); + printf("one %d\n", s->one_count); + printf("one blocks %d (%0.2f%%)\n", s->one_blocks_count, (float) s->one_blocks_count / total_blocks * 100); + printf("four %d\n", s->four_count); + printf("four blocks %d (%0.2f%%)\n", s->four_count, (float) s->four_count / total_blocks * 100); + printf("sixteen %d\n", s->sixteen_count); + printf("sixteen blocks %d (%0.2f%%)\n", s->sixteen_count, (float) s->sixteen_count / total_blocks * 100); + printf("total blocks %d\n", total_blocks); +#endif + + if (s->current_frame.data[0]) { + avctx->release_buffer(avctx, &s->current_frame); + } + + if (s->prev_frame.data[0]) { + avctx->release_buffer(avctx, &s->prev_frame); + } + + return 0; +} + +AVCodec rpza_encoder = { + "rpza", + CODEC_TYPE_VIDEO, + CODEC_ID_RPZA, + sizeof(RpzaContext), + rpza_encode_init, + rpza_encode_frame, + rpza_encode_end, + .pix_fmts= (enum PixelFormat[]){PIX_FMT_RGB24, -1}, +}; + +#endif //CONFIG_ENCODERS diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 20a9421..ae40e3b 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -497,6 +497,7 @@ const CodecTag codec_movvideo_tags[] = { { CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 'v') }, { CODEC_ID_H263, MKTAG('s', '2', '6', '3') }, { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', ' ') }, + { CODEC_ID_RPZA, MKTAG('r', 'p', 'z', 'a') }, { 0, 0 }, }; -- 1.7.1 --------------000806090108070003070803--
On Sun, Jan 23, 2011 at 06:06:17PM +0100, Vitor Sessak wrote:
Git-friendly patch attached so patchwork will catch it up.
--- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -136,6 +136,9 @@ endif ifneq ($(CONFIG_SVQ1_DECODER)$(CONFIG_SVQ1_ENCODER),) OBJS+= svq1.o endif +ifneq ($(CONFIG_RPZA_ENCODER),) + OBJS+= rpzaenc.o +endif
Wow, this is old - it does not apply of course.
--- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -189,6 +189,9 @@ void avcodec_register_all(void) #ifdef CONFIG_LIBGSM register_avcodec(&libgsm_encoder); #endif //CONFIG_LIBGSM +#ifdef CONFIG_RPZA_ENCODER + register_avcodec(&rpza_encoder); +#endif //CONFIG_RPZA_ENCODER #endif /* CONFIG_ENCODERS */
same
--- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -1943,6 +1943,7 @@ extern AVCodec sonic_encoder; extern AVCodec sonic_ls_encoder; extern AVCodec svq1_encoder; extern AVCodec x264_encoder; +extern AVCodec rpza_encoder;
alphabetical order
--- /dev/null +++ b/libavcodec/rpzaenc.c @@ -0,0 +1,1169 @@ +/* + * Quicktime RPZA Video Encoder.
s/.//
+ * Copyright (C) 2005 the ffmpeg project + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 + * + */
not the standard license header
+/** + * @file rpzaenc.c
Drop the filename.
+#ifdef CONFIG_ENCODERS
This looks pointless, the file is only ever compiled if this is set.
+#if 0 +static void print_rgb24_color(uint8_t *color) +{ + printf("r{%02d} g{%02d} b{%02d}", color[0], color[1], color[2]); +} + +static void print_block(uint8_t *block, BlockInfo *bi) +{ + int x, y; + + for (y = 0; y < bi->block_height; y++) { + for (x = 0; x < bi->block_width; x++) { + printf("%d: ", x); + print_rgb24_color(&block[x * PIXELSTRIDE]); + printf("\n"); + } + printf("\n"); + block += bi->rowstride; + } +} +#endif
I think such debug code should be removed from the final version.
+static void get_max_component_diff(BlockInfo *bi, uint8_t *block_ptr, + uint8_t *min, uint8_t *max, channel_offset *chan) +/* + * Find the channel that has the largest difference between minimum and maximum + * color values. Put the minimum value in min, maximum in max and the channel + * in chan. + */ +{
The comment placement looks weird, same below.
+AVCodec rpza_encoder = { + "rpza", + CODEC_TYPE_VIDEO, + CODEC_ID_RPZA, + sizeof(RpzaContext), + rpza_encode_init, + rpza_encode_frame, + rpza_encode_end, + .pix_fmts= (enum PixelFormat[]){PIX_FMT_RGB24, -1}, +};
long_name is missing This is also missing changelog and docs update, version bump. Diego
On Sun, Jan 23, 2011 at 06:06:17PM +0100, Vitor Sessak wrote:
On 09/02/2007 09:15 PM, Ramiro Polla wrote:
Todd Kirby wrote:
On 6/5/05, Mike Melanson<mike at multimedia.cx> wrote:
Todd Kirby wrote:
Yeah, that's what my target was. I needed something that could play on even the oldest version of Quicktime Player. I used your excellent description of the rpza format at...
http://www.pcisys.net/~melanson/codecs/
...as a guide. I notice this document not there anymore. If you've moved it, let me know and I'll change my link in the source.
The document is located @ multimedia.cx. I need to update all of those description links.
Here's a patch to update the description links.
Well, this never got applied... Is there more to it now or is it still good?
Git-friendly patch attached so patchwork will catch it up.
-Vitor
libavcodec/Makefile | 3 libavcodec/allcodecs.c | 3 libavcodec/avcodec.h | 1 libavcodec/rpzaenc.c | 1169 +++++++++++++++++++++++++++++++++++++++++++++++++ libavformat/movenc.c | 1 5 files changed, 1177 insertions(+) e3b19cbd6a5e985c929482784d255f7965107468 0001-Apple-Video-Encoder-rpza.patch From 0569848a37b94eac9d3739ea430f83e2d8cdf013 Mon Sep 17 00:00:00 2001
mails from 2005 patch 10 years old, wow partial review below, i stoped somewhere in the middle as the patch is in rather bad shape and i think it might be quicker and easier to cleanup in a branch instead of throwing trivial comments on the list, i wish we could have done that with svn 5 years ago
From: Todd Kirby <ffmpeg.php at gmail.com> Date: Sun, 23 Jan 2011 18:05:03 +0100 Subject: [PATCH] Apple Video Encoder (rpza)
--- libavcodec/Makefile | 3 + libavcodec/allcodecs.c | 3 + libavcodec/avcodec.h | 1 + libavcodec/rpzaenc.c | 1169 ++++++++++++++++++++++++++++++++++++++++++++++++ libavformat/movenc.c | 1 + 5 files changed, 1177 insertions(+), 0 deletions(-) create mode 100644 libavcodec/rpzaenc.c
diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 9e38eef..d75d66a 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -136,6 +136,9 @@ endif ifneq ($(CONFIG_SVQ1_DECODER)$(CONFIG_SVQ1_ENCODER),) OBJS+= svq1.o endif +ifneq ($(CONFIG_RPZA_ENCODER),) + OBJS+= rpzaenc.o +endif ifeq ($(CONFIG_TRUEMOTION1_DECODER),yes) OBJS+= truemotion1.o endif diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index a0a7d7f..5e0d3f9 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -189,6 +189,9 @@ void avcodec_register_all(void) #ifdef CONFIG_LIBGSM register_avcodec(&libgsm_encoder); #endif //CONFIG_LIBGSM +#ifdef CONFIG_RPZA_ENCODER + register_avcodec(&rpza_encoder); +#endif //CONFIG_RPZA_ENCODER #endif /* CONFIG_ENCODERS */ #ifdef CONFIG_RAWVIDEO_ENCODER register_avcodec(&rawvideo_encoder); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 2abb391..60c0158 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -1943,6 +1943,7 @@ extern AVCodec sonic_encoder; extern AVCodec sonic_ls_encoder; extern AVCodec svq1_encoder; extern AVCodec x264_encoder; +extern AVCodec rpza_encoder;
extern AVCodec h263_decoder; extern AVCodec h261_decoder; diff --git a/libavcodec/rpzaenc.c b/libavcodec/rpzaenc.c new file mode 100644 index 0000000..d8b5ab7 --- /dev/null +++ b/libavcodec/rpzaenc.c @@ -0,0 +1,1169 @@ +/* + * Quicktime RPZA Video Encoder. + * Copyright (C) 2005 the ffmpeg project + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 + * + */ + +/** + * @file rpzaenc.c + * QT RPZA Video Encoder by Todd Kirby <doubleshot at pacbell.net> and David Adler + * + * For more information about the RPZA format, visit: + * http://www.pcisys.net/~melanson/codecs/ + */ + +#include "avcodec.h" +#include "dsputil.h" +#include "bitstream.h" +#include "assert.h" + +#ifdef CONFIG_ENCODERS + +typedef struct RpzaContext { + + AVCodecContext *avctx; + DSPContext dsp; + + AVFrame current_frame; // buffer for current 24 bit source frame + AVFrame prev_frame; // buffer for previous 24 bit source frame
Trailing whitespace & doxy comment
+ PutBitContext pb; // buffer for encoded frame data. + + int frame_width; // width in pixels of source frame + int frame_height; // height in pixesl of source frame + + unsigned char *buf; + int first_frame; // flag set to one when the first frame is being processed + // so that comparisons with previous frame data in not attempted + +#ifdef DEBUG_STATS + int one_count, one_blocks_count; + int four_count, sixteen_count; + int skip_count, skip_blocks_count; +#endif +} RpzaContext; + + +typedef struct rgb { + uint8_t r; + uint8_t g; + uint8_t b; +} rgb; + +#define PIXELSTRIDE 3 +
+#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b))
FFMIN/MAX
+ +#define SQR(x) ((x) * (x)) + +/* 15 bit components */ +#define GET_CHAN(color, chan) ((color) >> ((chan) * 5) & 0x1F) +#define R(color) GET_CHAN(color, RED) +#define G(color) GET_CHAN(color, GREEN) +#define B(color) GET_CHAN(color, BLUE) + +/* 8 bit rounding constants */ +#define ROUND_UP 7 +#define ROUND_NEAREST 4 +#define ROUND_DOWN 0 + +/* tuning parameters */ +#define SKIP_FRAME_THRESH 13 +#define START_ONE_COLOR_THRESH 8 +#define CONTINUE_ONE_COLOR_THRESH 0 +#define SIXTEEN_COLOR_THRESH 24 + +//#define RPZA_DITHER + +/* debug modes */ +//#define DEBUG_SKIP +//#define DEBUG_SIXTEEN +//#define DEBUG_STATS + +typedef enum channel_offset { + RED = 2, + GREEN = 1, + BLUE = 0, +} channel_offset; + +typedef struct BlockInfo { + int row; + int col; + int block_width; + int block_height; + int image_width; + int image_height; + int block_index; + uint16_t start; + int rowstride; + int blocks_per_row; + int total_blocks; +} BlockInfo; + + +static void get_colors(uint8_t *colorB, uint8_t *colorA, uint8_t color4[4][3]) +{ + uint8_t step; + + color4[0][0] = colorB[0]; + color4[0][1] = colorB[1]; + color4[0][2] = colorB[2]; + + color4[3][0] = colorA[0]; + color4[3][1] = colorA[1]; + color4[3][2] = colorA[2]; + + // red components + step = (color4[3][0] - color4[0][0] + 1) / 3; + color4[1][0] = color4[0][0] + step; + color4[2][0] = color4[3][0] - step; + + // green components + step = (color4[3][1] - color4[0][1] + 1) / 3; + color4[1][1] = color4[0][1] + step; + color4[2][1] = color4[3][1] - step; + + // blue components + step = (color4[3][2] - color4[0][2] + 1) / 3;
the divisions can be done by multiplication and shift
+ color4[1][2] = color4[0][2] + step; + color4[2][2] = color4[3][2] - step; +} + +
+static int get_block_info(BlockInfo *bi, int block) +/* Fill BlockInfo struct with information about a 4x4 block of the image */ +{ + bi->row = block / bi->blocks_per_row; + bi->col = block % bi->blocks_per_row; + + // test for right edge block + if (bi->col == bi->blocks_per_row - 1 && (bi->image_width % 4) != 0) { + bi->block_width = bi->image_width % 4;
dont do % of signed numbers its slow use &3
+ } else { + bi->block_width = 4; + } + + // test for bottom edge block + if (bi->row == (bi->image_height / 4) && (bi->image_height % 4) != 0) {
!= 0 is redundant so are some ()
+ bi->block_height = bi->image_height % 4; + } else { + bi->block_height = 4; + } + + return block ? (bi->col * 4 * PIXELSTRIDE) + (bi->row * bi->rowstride * 4) : 0; +} + + +static uint16_t round_rgb24_to_rgb555(uint8_t * rgb24, int bias)
the input format should be PIX_FMT_RGB555
+/* + * Round a 24 bit rgb value to a 15 bit rgb value. The bias parameter + * specifies the rounding direction. + */ +{ + uint16_t rgb555 = 0; + uint32_t r, g, b; + + r = (uint32_t)rgb24[0] + bias; + g = (uint32_t)rgb24[1] + bias; + b = (uint32_t)rgb24[2] + bias; + + r = r / 8; + g = g / 8; + b = b / 8; + + /* clamp 0-31 */ + if (r > 31) { + r = 31; + } + if (g > 31) { + g = 31; + } + if (b > 31) { + b = 31; + } + + rgb555 |= (r << 10); + rgb555 |= (g << 5); + rgb555 |= (b << 0); + + return rgb555; +} +
+#ifdef RPZA_DITHER [...] +#endif
with already dithered 555 format this is unneeded and can thus be deleted i think
+ + +static int diff_colors(uint8_t *colorA, uint8_t *colorB) +/* + * Returns the total difference between two 24 bit color values + */ +{ + int tot; + tot = SQR(colorA[0] - colorB[0]); + tot += SQR(colorA[1] - colorB[1]); + tot += SQR(colorA[2] - colorB[2]); + return tot; +} + +static int max_component_diff(uint8_t *colorA, uint8_t *colorB) +/* + * Returns the maximum channel difference between two 24 bit color values + */ +{ + int i, diff, max = 0; + + for (i = 0; i < 3; i++) { + diff = abs(colorA[i] - colorB[i]); + if (diff > max) { + max = diff; + } + } + return max; +} + +#if 0 +static void print_rgb24_color(uint8_t *color) +{ + printf("r{%02d} g{%02d} b{%02d}", color[0], color[1], color[2]); +} + +static void print_block(uint8_t *block, BlockInfo *bi) +{ + int x, y; + + for (y = 0; y < bi->block_height; y++) { + for (x = 0; x < bi->block_width; x++) { + printf("%d: ", x); + print_rgb24_color(&block[x * PIXELSTRIDE]); + printf("\n"); + } + printf("\n"); + block += bi->rowstride; + } +} +#endif +
+static void get_max_component_diff(BlockInfo *bi, uint8_t *block_ptr, + uint8_t *min, uint8_t *max, channel_offset *chan) +/* + * Find the channel that has the largest difference between minimum and maximum + * color values. Put the minimum value in min, maximum in max and the channel + * in chan. + */ +{ + int x, y; + uint8_t min_r, max_r, min_g, max_g, min_b, max_b; + uint8_t r, g, b;
these should be unsigned int
+ + // fix warning about uninitialized vars + min_r = min_g = min_b = UINT8_MAX; + max_r = max_g = max_b = 0; + + // loop thru and compare pixels + for (y = 0; y < bi->block_height; y++) { + for (x = 0; x < bi->block_width; x++){ + // TODO: optimize + min_r = MIN(block_ptr[(x * PIXELSTRIDE) + 2], min_r); + min_g = MIN(block_ptr[(x * PIXELSTRIDE) + 1], min_g); + min_b = MIN(block_ptr[(x * PIXELSTRIDE) + 0], min_b); + + max_r = MAX(block_ptr[(x * PIXELSTRIDE) + 2], max_r); + max_g = MAX(block_ptr[(x * PIXELSTRIDE) + 1], max_g); + max_b = MAX(block_ptr[(x * PIXELSTRIDE) + 0], max_b); + } + block_ptr += bi->rowstride; + } + + r = max_r - min_r; + g = max_g - min_g; + b = max_b - min_b; + + if (r > g && r > b) { + *max = max_r; + *min = min_r; + *chan = RED; + } else if (g > b && g >= r) { + *max = max_g; + *min = min_g; + *chan = GREEN; + } else { + *max = max_b; + *min = min_b; + *chan = BLUE; + } +} + +static int compare_blocks(uint8_t *block1, uint8_t *block2, BlockInfo *bi, int thresh) +/* + * Compare two 4x4 blocks to determine if the total difference between the + * blocks is greater than the thresh parameter. Returns -1 if difference + * exceeds threshold or zero otherwise. + */ +{ + int x, y, diff = 0; + for (y = 0; y < bi->block_height; y++) { + for (x = 0; x < bi->block_width; x++) { + diff = max_component_diff(&block1[x * PIXELSTRIDE], &block2[x * PIXELSTRIDE]); + if (diff >= thresh) { + return -1; + } + } + block1 += bi->rowstride; + block2 += bi->rowstride; + } + return 0; +} + +
+static int leastsquares(uint8_t *block_ptr, BlockInfo *bi, + channel_offset xchannel, channel_offset ychannel, + double *slope, double *y_intercept, double *correlation_coef) +/* + * Determine the fit of one channel to another within a 4x4 block. This + * is used to determine the best palette choices for 4-color encoding. + */ +{ + double sumx = 0, sumy = 0, sumx2 = 0, sumy2 = 0, sumxy = 0,
FPU should be avoided and its quite trivial to do here
+ sumx_sq = 0, sumy_sq = 0, tmp, tmp2; + int i, j, count; + uint8_t x, y; + + count = bi->block_height * bi->block_width; + + if (count < 2) { + return -1; + } + + for (i = 0; i < bi->block_height; i++) { + for (j = 0; j < bi->block_width; j++){ + x = block_ptr[j * PIXELSTRIDE + xchannel]; + y = block_ptr[j * PIXELSTRIDE + ychannel]; + sumx += x; + sumy += y; + sumx2 += x * x; + sumy2 += y * y; + sumxy += x * y; + } + block_ptr += bi->rowstride; + } + + + sumx_sq = sumx * sumx; + tmp = (count * sumx2 - sumx_sq); + + // guard against div/0 + if (tmp == 0) { + return -2; + } + + sumy_sq = sumy * sumy; + + *slope = (count * sumxy - sumx * sumy) / tmp; + *y_intercept = (sumy - (*slope) * sumx) / count; + + tmp2 = count * sumy2 - sumy_sq; + if (tmp2 == 0) { + *correlation_coef = 0.0; + } else { + *correlation_coef = (count * sumxy - sumx * sumy) / + sqrt(tmp * tmp2); + } + + return 0; // success +} + +static int +calc_lsq_max_fit_error(uint8_t *block_ptr, BlockInfo *bi, + int min, int max, int tmp_min, int tmp_max, + channel_offset xchannel, channel_offset ychannel) +/* + * Determine the amount of error in the leastsquares fit. + */ +{ + int i, j, x, y; + int err; + int max_err = 0; + + for (i = 0; i < bi->block_height; i++) { + for (j = 0; j < bi->block_width; j++){ + int x_inc, lin_y, lin_x; + x = block_ptr[j * PIXELSTRIDE + xchannel]; + y = block_ptr[j * PIXELSTRIDE + ychannel]; + + /* calculate x_inc as the 4-color index (0..3) */ + x_inc = floor( (x - min) * 3.0 / (max - min) + 0.5);
float should be avoided
+ x_inc = MAX(MIN(3, x_inc), 0);
av_clip [...] -- Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB The educated differ from the uneducated as much as the living from the dead. -- Aristotle
participants (9)
-
dalias@aerifal.cx -
diego@biurrun.de -
dododge@dododge.net -
ffmpeg.php@gmail.com -
michaelni@gmx.at -
mike@multimedia.cx -
ramiro@lisha.ufsc.br -
revol@free.fr -
vitor1001@gmail.com