[FFmpeg-devel] [PATCH] Apple Video Encoder (rpza)
Michael Niedermayer
michaelni
Mon Jan 24 02:27:07 CET 2011
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
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/attachments/20110124/e634e2ed/attachment.pgp>
More information about the ffmpeg-devel
mailing list