[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