From 31ccd4b91bbd60363bca795a6559f253e7e0495f Mon Sep 17 00:00:00 2001 From: David McPaul Date: Mon, 15 Sep 2008 14:03:17 +0000 Subject: [PATCH] Update avcodec to 20080825 git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27546 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../plugins/avcodec/libavcodec/eac3dec.c | 80 +++ .../media/plugins/avcodec/libavcodec/eacmv.c | 214 ++++++++ .../media/plugins/avcodec/libavcodec/eatgv.c | 340 ++++++++++++ .../media/plugins/avcodec/libavcodec/elbg.c | 425 +++++++++++++++ .../media/plugins/avcodec/libavcodec/elbg.h | 55 ++ .../avcodec/libavcodec/error_resilience.c | 370 +++++++------ .../plugins/avcodec/libavcodec/escape124.c | 385 ++++++++++++++ .../media/plugins/avcodec/libavcodec/eval.c | 496 +++++++++++++----- .../media/plugins/avcodec/libavcodec/eval.h | 87 +++ 9 files changed, 2137 insertions(+), 315 deletions(-) create mode 100644 src/add-ons/media/plugins/avcodec/libavcodec/eac3dec.c create mode 100644 src/add-ons/media/plugins/avcodec/libavcodec/eacmv.c create mode 100644 src/add-ons/media/plugins/avcodec/libavcodec/eatgv.c create mode 100644 src/add-ons/media/plugins/avcodec/libavcodec/elbg.c create mode 100644 src/add-ons/media/plugins/avcodec/libavcodec/elbg.h create mode 100644 src/add-ons/media/plugins/avcodec/libavcodec/escape124.c create mode 100644 src/add-ons/media/plugins/avcodec/libavcodec/eval.h diff --git a/src/add-ons/media/plugins/avcodec/libavcodec/eac3dec.c b/src/add-ons/media/plugins/avcodec/libavcodec/eac3dec.c new file mode 100644 index 0000000000..388e2fc7ea --- /dev/null +++ b/src/add-ons/media/plugins/avcodec/libavcodec/eac3dec.c @@ -0,0 +1,80 @@ +/* + * E-AC-3 decoder + * Copyright (c) 2007 Bartlomiej Wolowiec + * Copyright (c) 2008 Justin Ruggles + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avcodec.h" +#include "ac3.h" +#include "ac3_parser.h" +#include "ac3dec.h" +#include "ac3dec_data.h" + +/** gain adaptive quantization mode */ +typedef enum { + EAC3_GAQ_NO =0, + EAC3_GAQ_12, + EAC3_GAQ_14, + EAC3_GAQ_124 +} EAC3GaqMode; + +#define EAC3_SR_CODE_REDUCED 3 + +/** lrint(M_SQRT2*cos(2*M_PI/12)*(1<<23)) */ +#define COEFF_0 10273905LL + +/** lrint(M_SQRT2*cos(0*M_PI/12)*(1<<23)) = lrint(M_SQRT2*(1<<23)) */ +#define COEFF_1 11863283LL + +/** lrint(M_SQRT2*cos(5*M_PI/12)*(1<<23)) */ +#define COEFF_2 3070444LL + +/** + * Calculate 6-point IDCT of the pre-mantissas. + * All calculations are 24-bit fixed-point. + */ +static void idct6(int pre_mant[6]) +{ + int tmp; + int even0, even1, even2, odd0, odd1, odd2; + + odd1 = pre_mant[1] - pre_mant[3] - pre_mant[5]; + + even2 = ( pre_mant[2] * COEFF_0) >> 23; + tmp = ( pre_mant[4] * COEFF_1) >> 23; + odd0 = ((pre_mant[1] + pre_mant[5]) * COEFF_2) >> 23; + + even0 = pre_mant[0] + (tmp >> 1); + even1 = pre_mant[0] - tmp; + + tmp = even0; + even0 = tmp + even2; + even2 = tmp - even2; + + tmp = odd0; + odd0 = tmp + pre_mant[1] + pre_mant[3]; + odd2 = tmp + pre_mant[5] - pre_mant[3]; + + pre_mant[0] = even0 + odd0; + pre_mant[1] = even1 + odd1; + pre_mant[2] = even2 + odd2; + pre_mant[3] = even2 - odd2; + pre_mant[4] = even1 - odd1; + pre_mant[5] = even0 - odd0; +} diff --git a/src/add-ons/media/plugins/avcodec/libavcodec/eacmv.c b/src/add-ons/media/plugins/avcodec/libavcodec/eacmv.c new file mode 100644 index 0000000000..a9f624e046 --- /dev/null +++ b/src/add-ons/media/plugins/avcodec/libavcodec/eacmv.c @@ -0,0 +1,214 @@ +/* + * Electronic Arts CMV Video Decoder + * Copyright (c) 2007-2008 Peter Ross + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file eacmv.c + * Electronic Arts CMV Video Decoder + * by Peter Ross (suxen_drol at hotmail dot com) + * + * Technical details here: + * http://wiki.multimedia.cx/index.php?title=Electronic_Arts_CMV + */ + +#include "avcodec.h" + +typedef struct CmvContext { + AVCodecContext *avctx; + AVFrame frame; ///< current + AVFrame last_frame; ///< last + AVFrame last2_frame; ///< second-last + int width, height; + unsigned int palette[AVPALETTE_COUNT]; +} CmvContext; + +static av_cold int cmv_decode_init(AVCodecContext *avctx){ + CmvContext *s = avctx->priv_data; + s->avctx = avctx; + avctx->pix_fmt = PIX_FMT_PAL8; + return 0; +} + +static void cmv_decode_intra(CmvContext * s, const uint8_t *buf, const uint8_t *buf_end){ + unsigned char *dst = s->frame.data[0]; + int i; + + for (i=0; i < s->avctx->height && buf+s->avctx->width<=buf_end; i++) { + memcpy(dst, buf, s->avctx->width); + dst += s->frame.linesize[0]; + buf += s->avctx->width; + } +} + +static void cmv_motcomp(unsigned char *dst, int dst_stride, + const unsigned char *src, int src_stride, + int x, int y, + int xoffset, int yoffset, + int width, int height){ + int i,j; + + for(j=y;j=0 && i+xoffset=0 && j+yoffsetavctx->width*s->avctx->height/16); + int x,y,i; + + i = 0; + for(y=0; yavctx->height/4; y++) + for(x=0; xavctx->width/4 && buf+iframe.data[0] + (y*4)*s->frame.linesize[0] + x*4; + if (raw+16frame.linesize[0], raw+4, 4); + memcpy(dst+2*s->frame.linesize[0], raw+8, 4); + memcpy(dst+3*s->frame.linesize[0], raw+12, 4); + raw+=16; + }else if(raw> 4)) - 7; + cmv_motcomp(s->frame.data[0], s->frame.linesize[0], + s->last2_frame.data[0], s->last2_frame.linesize[0], + x*4, y*4, xoffset, yoffset, s->avctx->width, s->avctx->height); + raw++; + } + }else{ /* inter using last frame as reference */ + int xoffset = (buf[i] & 0xF) - 7; + int yoffset = ((buf[i] >> 4)) - 7; + cmv_motcomp(s->frame.data[0], s->frame.linesize[0], + s->last_frame.data[0], s->last_frame.linesize[0], + x*4, y*4, xoffset, yoffset, s->avctx->width, s->avctx->height); + } + i++; + } +} + +static void cmv_process_header(CmvContext *s, const uint8_t *buf, const uint8_t *buf_end) +{ + int pal_start, pal_count, i; + + if(buf+16>=buf_end) { + av_log(s->avctx, AV_LOG_WARNING, "truncated header\n"); + return; + } + + s->width = AV_RL16(&buf[4]); + s->height = AV_RL16(&buf[6]); + if (s->avctx->width!=s->width || s->avctx->height!=s->height) + avcodec_set_dimensions(s->avctx, s->width, s->height); + + s->avctx->time_base.num = 1; + s->avctx->time_base.den = AV_RL16(&buf[10]); + + pal_start = AV_RL16(&buf[12]); + pal_count = AV_RL16(&buf[14]); + + buf += 16; + for (i=pal_start; ipalette[i] = AV_RB24(buf); + buf += 3; + } +} + +#define EA_PREAMBLE_SIZE 8 +#define MVIh_TAG MKTAG('M', 'V', 'I', 'h') + +static int cmv_decode_frame(AVCodecContext *avctx, + void *data, int *data_size, + const uint8_t *buf, int buf_size) +{ + CmvContext *s = avctx->priv_data; + const uint8_t *buf_end = buf + buf_size; + + if (AV_RL32(buf)==MVIh_TAG||AV_RB32(buf)==MVIh_TAG) { + cmv_process_header(s, buf+EA_PREAMBLE_SIZE, buf_end); + return buf_size; + } + + if (avcodec_check_dimensions(s->avctx, s->width, s->height)) + return -1; + + /* shuffle */ + if (s->last2_frame.data[0]) + avctx->release_buffer(avctx, &s->last2_frame); + FFSWAP(AVFrame, s->last_frame, s->last2_frame); + FFSWAP(AVFrame, s->frame, s->last_frame); + + s->frame.reference = 1; + s->frame.buffer_hints = FF_BUFFER_HINTS_VALID; + if (avctx->get_buffer(avctx, &s->frame)<0) { + av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n"); + return -1; + } + + memcpy(s->frame.data[1], s->palette, AVPALETTE_SIZE); + + buf += EA_PREAMBLE_SIZE; + if ((buf[0]&1)) { // subtype + cmv_decode_inter(s, buf+2, buf_end); + s->frame.key_frame = 0; + s->frame.pict_type = FF_P_TYPE; + }else{ + s->frame.key_frame = 1; + s->frame.pict_type = FF_I_TYPE; + cmv_decode_intra(s, buf+2, buf_end); + } + + *data_size = sizeof(AVFrame); + *(AVFrame*)data = s->frame; + + return buf_size; +} + +static av_cold int cmv_decode_end(AVCodecContext *avctx){ + CmvContext *s = avctx->priv_data; + if (s->frame.data[0]) + s->avctx->release_buffer(avctx, &s->frame); + if (s->last_frame.data[0]) + s->avctx->release_buffer(avctx, &s->last_frame); + if (s->last2_frame.data[0]) + s->avctx->release_buffer(avctx, &s->last2_frame); + + return 0; +} + +AVCodec eacmv_decoder = { + "eacmv", + CODEC_TYPE_VIDEO, + CODEC_ID_CMV, + sizeof(CmvContext), + cmv_decode_init, + NULL, + cmv_decode_end, + cmv_decode_frame, + CODEC_CAP_DR1, + .long_name = NULL_IF_CONFIG_SMALL("Electronic Arts CMV Video"), +}; diff --git a/src/add-ons/media/plugins/avcodec/libavcodec/eatgv.c b/src/add-ons/media/plugins/avcodec/libavcodec/eatgv.c new file mode 100644 index 0000000000..a8787bb873 --- /dev/null +++ b/src/add-ons/media/plugins/avcodec/libavcodec/eatgv.c @@ -0,0 +1,340 @@ +/* + * Electronic Arts TGV Video Decoder + * Copyright (c) 2007-2008 Peter Ross + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file eatgv.c + * Electronic Arts TGV Video Decoder + * by Peter Ross (suxen_drol at hotmail dot com) + * + * Technical details here: + * http://wiki.multimedia.cx/index.php?title=Electronic_Arts_TGV + */ + +#include "avcodec.h" +#define ALT_BITSTREAM_READER_LE +#include "bitstream.h" +#include + +#define EA_PREAMBLE_SIZE 8 +#define kVGT_TAG MKTAG('k', 'V', 'G', 'T') + +typedef struct TgvContext { + AVCodecContext *avctx; + AVFrame frame; + AVFrame last_frame; + int width,height; + unsigned int palette[AVPALETTE_COUNT]; + + int (*mv_codebook)[2]; + unsigned char (*block_codebook)[16]; + int num_mvs; ///< current length of mv_codebook + int num_blocks_packed; ///< current length of block_codebook +} TgvContext; + +static av_cold int tgv_decode_init(AVCodecContext *avctx){ + TgvContext *s = avctx->priv_data; + s->avctx = avctx; + avctx->time_base = (AVRational){1, 15}; + avctx->pix_fmt = PIX_FMT_PAL8; + return 0; +} + +/** + * Unpack buffer + * @return 0 on success, -1 on critical buffer underflow + */ +static int unpack(const uint8_t *src, const uint8_t *src_end, unsigned char *dst, int width, int height) { + unsigned char *dst_end = dst + width*height; + int size,size1,size2,offset,run; + unsigned char *dst_start = dst; + + if (src[0] & 0x01) + src += 5; + else + src += 2; + + if (src+3>src_end) + return -1; + size = AV_RB24(src); + src += 3; + + while(size>0 && src> 6 ); + offset = (AV_RB16(&src[1]) & 0x3FFF) + 1; + size2 = (src[0] & 0x3F) + 4; + src += 3; + } + } else { // 0 + offset = ((src[0] & 0x60) << 3) + src[1] + 1; + size2 = ((src[0] & 0x1C) >> 2) + 3; + src += 2; + } + + + /* fetch strip from src */ + if (size1>src_end-src) + break; + + if (size1>0) { + size -= size1; + run = FFMIN(size1, dst_end-dst); + memcpy(dst, src, run); + dst += run; + src += run; + } + + if (size2>0) { + if (dst-dst_startlast_frame.data[0] + s->avctx->width*s->last_frame.linesize[0]; + int num_mvs; + int num_blocks_raw; + int num_blocks_packed; + int vector_bits; + int i,j,x,y; + GetBitContext gb; + int mvbits; + const unsigned char *blocks_raw; + + if(buf+12>buf_end) + return -1; + + num_mvs = AV_RL16(&buf[0]); + num_blocks_raw = AV_RL16(&buf[2]); + num_blocks_packed = AV_RL16(&buf[4]); + vector_bits = AV_RL16(&buf[6]); + buf += 12; + + /* allocate codebook buffers as neccessary */ + if (num_mvs > s->num_mvs) { + s->mv_codebook = av_realloc(s->mv_codebook, num_mvs*2*sizeof(int)); + s->num_mvs = num_mvs; + } + + if (num_blocks_packed > s->num_blocks_packed) { + s->block_codebook = av_realloc(s->block_codebook, num_blocks_packed*16*sizeof(unsigned char)); + s->num_blocks_packed = num_blocks_packed; + } + + /* read motion vectors */ + mvbits = (num_mvs*2*10+31) & ~31; + + if (buf+(mvbits>>3)+16*num_blocks_raw+8*num_blocks_packed>buf_end) + return -1; + + init_get_bits(&gb, buf, mvbits); + for (i=0; imv_codebook[i][0] = get_sbits(&gb, 10); + s->mv_codebook[i][1] = get_sbits(&gb, 10); + } + buf += mvbits>>3; + + /* note ptr to uncompressed blocks */ + blocks_raw = buf; + buf += num_blocks_raw*16; + + /* read compressed blocks */ + init_get_bits(&gb, buf, (buf_end-buf)<<3); + for (i=0; iblock_codebook[i][15-j] = tmp[get_bits(&gb, 2)]; + } + + /* read vectors and build frame */ + for(y=0; yavctx->height/4; y++) + for(x=0; xavctx->width/4; x++) { + unsigned int vector = get_bits(&gb, vector_bits); + const unsigned char *src; + int src_stride; + + if (vector < num_mvs) { + src = s->last_frame.data[0] + + (y*4 + s->mv_codebook[vector][1])*s->last_frame.linesize[0] + + x*4 + s->mv_codebook[vector][0]; + src_stride = s->last_frame.linesize[0]; + if (src+3*src_stride+3>=frame0_end) + continue; + }else{ + int offset = vector - num_mvs; + if (offsetblock_codebook[offset-num_blocks_raw]; + else + continue; + src_stride = 4; + } + + for(j=0; j<4; j++) + for(i=0; i<4; i++) + s->frame.data[0][ (y*4+j)*s->frame.linesize[0] + (x*4+i) ] = + src[j*src_stride + i]; + } + + return 0; +} + +/** release AVFrame buffers if allocated */ +static void cond_release_buffer(AVFrame *pic) +{ + if (pic->data[0]) { + av_freep(&pic->data[0]); + av_free(pic->data[1]); + } +} + +static int tgv_decode_frame(AVCodecContext *avctx, + void *data, int *data_size, + const uint8_t *buf, int buf_size) +{ + TgvContext *s = avctx->priv_data; + const uint8_t *buf_end = buf + buf_size; + int chunk_type; + + chunk_type = AV_RL32(&buf[0]); + buf += EA_PREAMBLE_SIZE; + + if (chunk_type==kVGT_TAG) { + int pal_count, i; + if(buf+12>buf_end) { + av_log(avctx, AV_LOG_WARNING, "truncated header\n"); + return -1; + } + + s->width = AV_RL16(&buf[0]); + s->height = AV_RL16(&buf[2]); + if (s->avctx->width!=s->width || s->avctx->height!=s->height) { + avcodec_set_dimensions(s->avctx, s->width, s->height); + cond_release_buffer(&s->frame); + cond_release_buffer(&s->last_frame); + } + + pal_count = AV_RL16(&buf[6]); + buf += 12; + for(i=0; ipalette[i] = AV_RB24(buf); + buf += 3; + } + } + + if (avcodec_check_dimensions(avctx, s->width, s->height)) + return -1; + + /* shuffle */ + FFSWAP(AVFrame, s->frame, s->last_frame); + if (!s->frame.data[0]) { + s->frame.reference = 1; + s->frame.buffer_hints = FF_BUFFER_HINTS_VALID; + s->frame.linesize[0] = s->width; + + /* allocate additional 12 bytes to accomodate av_memcpy_backptr() OUTBUF_PADDED optimisation */ + s->frame.data[0] = av_malloc(s->width*s->height + 12); + if (!s->frame.data[0]) + return AVERROR_NOMEM; + s->frame.data[1] = av_malloc(AVPALETTE_SIZE); + if (!s->frame.data[1]) { + av_freep(&s->frame.data[0]); + return AVERROR_NOMEM; + } + } + memcpy(s->frame.data[1], s->palette, AVPALETTE_SIZE); + + if(chunk_type==kVGT_TAG) { + s->frame.key_frame = 1; + s->frame.pict_type = FF_I_TYPE; + if (unpack(buf, buf_end, s->frame.data[0], s->avctx->width, s->avctx->height)<0) { + av_log(avctx, AV_LOG_WARNING, "truncated intra frame\n"); + return -1; + } + }else{ + if (!s->last_frame.data[0]) { + av_log(avctx, AV_LOG_WARNING, "inter frame without corresponding intra frame\n"); + return buf_size; + } + s->frame.key_frame = 0; + s->frame.pict_type = FF_P_TYPE; + if (tgv_decode_inter(s, buf, buf_end)<0) { + av_log(avctx, AV_LOG_WARNING, "truncated inter frame\n"); + return -1; + } + } + + *data_size = sizeof(AVFrame); + *(AVFrame*)data = s->frame; + + return buf_size; +} + +static av_cold int tgv_decode_end(AVCodecContext *avctx) +{ + TgvContext *s = avctx->priv_data; + cond_release_buffer(&s->frame); + cond_release_buffer(&s->last_frame); + av_free(s->mv_codebook); + av_free(s->block_codebook); + return 0; +} + +AVCodec eatgv_decoder = { + "eatgv", + CODEC_TYPE_VIDEO, + CODEC_ID_TGV, + sizeof(TgvContext), + tgv_decode_init, + NULL, + tgv_decode_end, + tgv_decode_frame, + .long_name = NULL_IF_CONFIG_SMALL("Electronic Arts TGV Video"), +}; diff --git a/src/add-ons/media/plugins/avcodec/libavcodec/elbg.c b/src/add-ons/media/plugins/avcodec/libavcodec/elbg.c new file mode 100644 index 0000000000..0627482ded --- /dev/null +++ b/src/add-ons/media/plugins/avcodec/libavcodec/elbg.c @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2007 Vitor Sessak + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file cbook_gen.c + * Codebook Generator using the ELBG algorithm + */ + +#include + +#include "random.h" +#include "elbg.h" +#include "avcodec.h" + +#define DELTA_ERR_MAX 0.1 ///< Precision of the ELBG algorithm (as percentual error) + +/** + * In the ELBG jargon, a cell is the set of points that are closest to a + * codebook entry. Not to be confused with a RoQ Video cell. */ +typedef struct cell_s { + int index; + struct cell_s *next; +} cell; + +/** + * ELBG internal data + */ +typedef struct{ + int error; + int dim; + int numCB; + int *codebook; + cell **cells; + int *utility; + int *utility_inc; + int *nearest_cb; + int *points; + AVRandomState *rand_state; +} elbg_data; + +static inline int distance_limited(int *a, int *b, int dim, int limit) +{ + int i, dist=0; + for (i=0; i limit) + return INT_MAX; + } + + return dist; +} + +static inline void vect_division(int *res, int *vect, int div, int dim) +{ + int i; + if (div > 1) + for (i=0; inext) + error += distance_limited(centroid, elbg->points + cells->index*elbg->dim, elbg->dim, INT_MAX); + + return error; +} + +static int get_closest_codebook(elbg_data *elbg, int index) +{ + int i, pick=0, diff, diff_min = INT_MAX; + for (i=0; inumCB; i++) + if (i != index) { + diff = distance_limited(elbg->codebook + i*elbg->dim, elbg->codebook + index*elbg->dim, elbg->dim, diff_min); + if (diff < diff_min) { + pick = i; + diff_min = diff; + } + } + return pick; +} + +static int get_high_utility_cell(elbg_data *elbg) +{ + int i=0; + /* Using linear search, do binary if it ever turns to be speed critical */ + int r = av_random(elbg->rand_state)%(elbg->utility_inc[elbg->numCB-1]-1) + 1; + while (elbg->utility_inc[i] < r) + i++; + + assert(!elbg->cells[i]); + + return i; +} + +/** + * Implementation of the simple LBG algorithm for just two codebooks + */ +static int simple_lbg(int dim, + int *centroid[3], + int newutility[3], + int *points, + cell *cells) +{ + int i, idx; + int numpoints[2] = {0,0}; + int newcentroid[2][dim]; + cell *tempcell; + + memset(newcentroid, 0, sizeof(newcentroid)); + + newutility[0] = + newutility[1] = 0; + + for (tempcell = cells; tempcell; tempcell=tempcell->next) { + idx = distance_limited(centroid[0], points + tempcell->index*dim, dim, INT_MAX)>= + distance_limited(centroid[1], points + tempcell->index*dim, dim, INT_MAX); + numpoints[idx]++; + for (i=0; iindex*dim + i]; + } + + vect_division(centroid[0], newcentroid[0], numpoints[0], dim); + vect_division(centroid[1], newcentroid[1], numpoints[1], dim); + + for (tempcell = cells; tempcell; tempcell=tempcell->next) { + int dist[2] = {distance_limited(centroid[0], points + tempcell->index*dim, dim, INT_MAX), + distance_limited(centroid[1], points + tempcell->index*dim, dim, INT_MAX)}; + int idx = dist[0] > dist[1]; + newutility[idx] += dist[idx]; + } + + return newutility[0] + newutility[1]; +} + +static void get_new_centroids(elbg_data *elbg, int huc, int *newcentroid_i, + int *newcentroid_p) +{ + cell *tempcell; + int min[elbg->dim]; + int max[elbg->dim]; + int i; + + for (i=0; i< elbg->dim; i++) { + min[i]=INT_MAX; + max[i]=0; + } + + for (tempcell = elbg->cells[huc]; tempcell; tempcell = tempcell->next) + for(i=0; idim; i++) { + min[i]=FFMIN(min[i], elbg->points[tempcell->index*elbg->dim + i]); + max[i]=FFMAX(max[i], elbg->points[tempcell->index*elbg->dim + i]); + } + + for (i=0; idim; i++) { + newcentroid_i[i] = min[i] + (max[i] - min[i])/3; + newcentroid_p[i] = min[i] + (2*(max[i] - min[i]))/3; + } +} + +/** + * Add the points in the low utility cell to its closest cell. Split the high + * utility cell, putting the separed points in the (now empty) low utility + * cell. + * + * @param elbg Internal elbg data + * @param indexes {luc, huc, cluc} + * @param newcentroid A vector with the position of the new centroids + */ +static void shift_codebook(elbg_data *elbg, int *indexes, + int *newcentroid[3]) +{ + cell *tempdata; + cell **pp = &elbg->cells[indexes[2]]; + + while(*pp) + pp= &(*pp)->next; + + *pp = elbg->cells[indexes[0]]; + + elbg->cells[indexes[0]] = NULL; + tempdata = elbg->cells[indexes[1]]; + elbg->cells[indexes[1]] = NULL; + + while(tempdata) { + cell *tempcell2 = tempdata->next; + int idx = distance_limited(elbg->points + tempdata->index*elbg->dim, + newcentroid[0], elbg->dim, INT_MAX) > + distance_limited(elbg->points + tempdata->index*elbg->dim, + newcentroid[1], elbg->dim, INT_MAX); + + tempdata->next = elbg->cells[indexes[idx]]; + elbg->cells[indexes[idx]] = tempdata; + tempdata = tempcell2; + } +} + +static void evaluate_utility_inc(elbg_data *elbg) +{ + int i, inc=0; + + for (i=0; i < elbg->numCB; i++) { + if (elbg->numCB*elbg->utility[i] > elbg->error) + inc += elbg->utility[i]; + elbg->utility_inc[i] = inc; + } +} + + +static void update_utility_and_n_cb(elbg_data *elbg, int idx, int newutility) +{ + cell *tempcell; + + elbg->utility[idx] = newutility; + for (tempcell=elbg->cells[idx]; tempcell; tempcell=tempcell->next) + elbg->nearest_cb[tempcell->index] = idx; +} + +/** + * Evaluate if a shift lower the error. If it does, call shift_codebooks + * and update elbg->error, elbg->utility and elbg->nearest_cb. + * + * @param elbg Internal elbg data + * @param indexes {luc (low utility cell, huc (high utility cell), cluc (closest cell to low utility cell)} + */ +static void try_shift_candidate(elbg_data *elbg, int idx[3]) +{ + int j, k, olderror=0, newerror, cont=0; + int newutility[3]; + int newcentroid[3][elbg->dim]; + int *newcentroid_ptrs[3]; + cell *tempcell; + + newcentroid_ptrs[0] = newcentroid[0]; + newcentroid_ptrs[1] = newcentroid[1]; + newcentroid_ptrs[2] = newcentroid[2]; + + for (j=0; j<3; j++) + olderror += elbg->utility[idx[j]]; + + memset(newcentroid[2], 0, elbg->dim*sizeof(int)); + + for (k=0; k<2; k++) + for (tempcell=elbg->cells[idx[2*k]]; tempcell; tempcell=tempcell->next) { + cont++; + for (j=0; jdim; j++) + newcentroid[2][j] += elbg->points[tempcell->index*elbg->dim + j]; + } + + vect_division(newcentroid[2], newcentroid[2], cont, elbg->dim); + + get_new_centroids(elbg, idx[1], newcentroid[0], newcentroid[1]); + + newutility[2] = eval_error_cell(elbg, newcentroid[2], elbg->cells[idx[0]]); + newutility[2] += eval_error_cell(elbg, newcentroid[2], elbg->cells[idx[2]]); + + newerror = newutility[2]; + + newerror += simple_lbg(elbg->dim, newcentroid_ptrs, newutility, elbg->points, + elbg->cells[idx[1]]); + + if (olderror > newerror) { + shift_codebook(elbg, idx, newcentroid_ptrs); + + elbg->error += newerror - olderror; + + for (j=0; j<3; j++) + update_utility_and_n_cb(elbg, idx[j], newutility[j]); + + evaluate_utility_inc(elbg); + } + } + +/** + * Implementation of the ELBG block + */ +static void do_shiftings(elbg_data *elbg) +{ + int idx[3]; + + evaluate_utility_inc(elbg); + + for (idx[0]=0; idx[0] < elbg->numCB; idx[0]++) + if (elbg->numCB*elbg->utility[idx[0]] < elbg->error) { + if (elbg->utility_inc[elbg->numCB-1] == 0) + return; + + idx[1] = get_high_utility_cell(elbg); + idx[2] = get_closest_codebook(elbg, idx[0]); + + if (idx[1] != idx[0] && idx[1] != idx[2]) + try_shift_candidate(elbg, idx); + } +} + +#define BIG_PRIME 433494437LL + +void ff_init_elbg(int *points, int dim, int numpoints, int *codebook, + int numCB, int max_steps, int *closest_cb, + AVRandomState *rand_state) +{ + int i, k; + + if (numpoints > 24*numCB) { + /* ELBG is very costly for a big number of points. So if we have a lot + of them, get a good initial codebook to save on iterations */ + int *temp_points = av_malloc(dim*(numpoints/8)*sizeof(int)); + for (i=0; ierror = INT_MAX; + elbg->dim = dim; + elbg->numCB = numCB; + elbg->codebook = codebook; + elbg->cells = av_malloc(numCB*sizeof(cell *)); + elbg->utility = av_malloc(numCB*sizeof(int)); + elbg->nearest_cb = closest_cb; + elbg->points = points; + elbg->utility_inc = av_malloc(numCB*sizeof(int)); + + elbg->rand_state = rand_state; + + do { + free_cells = list_buffer; + last_error = elbg->error; + steps++; + memset(elbg->utility, 0, numCB*sizeof(int)); + memset(elbg->cells, 0, numCB*sizeof(cell *)); + + elbg->error = 0; + + /* This loop evaluate the actual Voronoi partition. It is the most + costly part of the algorithm. */ + for (i=0; i < numpoints; i++) { + dist_cb[i] = INT_MAX; + for (k=0; k < elbg->numCB; k++) { + dist = distance_limited(elbg->points + i*elbg->dim, elbg->codebook + k*elbg->dim, dim, dist_cb[i]); + if (dist < dist_cb[i]) { + dist_cb[i] = dist; + elbg->nearest_cb[i] = k; + } + } + elbg->error += dist_cb[i]; + elbg->utility[elbg->nearest_cb[i]] += dist_cb[i]; + free_cells->index = i; + free_cells->next = elbg->cells[elbg->nearest_cb[i]]; + elbg->cells[elbg->nearest_cb[i]] = free_cells; + free_cells++; + } + + do_shiftings(elbg); + + memset(size_part, 0, numCB*sizeof(int)); + + memset(elbg->codebook, 0, elbg->numCB*dim*sizeof(int)); + + for (i=0; i < numpoints; i++) { + size_part[elbg->nearest_cb[i]]++; + for (j=0; j < elbg->dim; j++) + elbg->codebook[elbg->nearest_cb[i]*elbg->dim + j] += + elbg->points[i*elbg->dim + j]; + } + + for (i=0; i < elbg->numCB; i++) + vect_division(elbg->codebook + i*elbg->dim, + elbg->codebook + i*elbg->dim, size_part[i], elbg->dim); + + } while(((last_error - elbg->error) > DELTA_ERR_MAX*elbg->error) && + (steps < max_steps)); + + av_free(dist_cb); + av_free(size_part); + av_free(elbg->utility); + av_free(list_buffer); + av_free(elbg->cells); + av_free(elbg->utility_inc); +} diff --git a/src/add-ons/media/plugins/avcodec/libavcodec/elbg.h b/src/add-ons/media/plugins/avcodec/libavcodec/elbg.h new file mode 100644 index 0000000000..1b2e45c4ec --- /dev/null +++ b/src/add-ons/media/plugins/avcodec/libavcodec/elbg.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2007 Vitor Sessak + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef FFMPEG_ELBG_H +#define FFMPEG_ELBG_H + +#include "random.h" + +/** + * Implementation of the Enhanced LBG Algorithm + * Based on the paper "Neural Networks 14:1219-1237" that can be found in + * http://citeseer.ist.psu.edu/patan01enhanced.html . + * + * @param points Input points. + * @param dim Dimension of the points. + * @param numpoints Num of points in **points. + * @param codebook Pointer to the output codebook. Must be allocated. + * @param numCB Number of points in the codebook. + * @param num_steps The maximum number of steps. One step is already a good compromise between time and quality. + * @param closest_cb Return the closest codebook to each point. Must be allocated. + * @param rand_state A random number generator state. Should be already initialised by av_init_random. + */ +void ff_do_elbg(int *points, int dim, int numpoints, int *codebook, + int numCB, int num_steps, int *closest_cb, + AVRandomState *rand_state); + +/** + * Initialize the **codebook vector for the elbg algorithm. If you have already + * a codebook and you want to refine it, you shouldn't call this function. + * If numpoints < 8*numCB this function fills **codebook with random numbers. + * If not, it calls ff_do_elbg for a (smaller) random sample of the points in + * **points. Get the same parameters as ff_do_elbg. + */ +void ff_init_elbg(int *points, int dim, int numpoints, int *codebook, + int numCB, int num_steps, int *closest_cb, + AVRandomState *rand_state); + +#endif /* FFMPEG_ELBG_H */ diff --git a/src/add-ons/media/plugins/avcodec/libavcodec/error_resilience.c b/src/add-ons/media/plugins/avcodec/libavcodec/error_resilience.c index 5067a248f5..59254a4146 100644 --- a/src/add-ons/media/plugins/avcodec/libavcodec/error_resilience.c +++ b/src/add-ons/media/plugins/avcodec/libavcodec/error_resilience.c @@ -3,39 +3,40 @@ * * Copyright (c) 2002-2004 Michael Niedermayer * - * This library is free software; you can redistribute it and/or + * This file is part of FFmpeg. + * + * FFmpeg 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. + * version 2.1 of the License, or (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * FFmpeg 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 + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - + /** * @file error_resilience.c * Error resilience / concealment. */ #include - + #include "avcodec.h" #include "dsputil.h" #include "mpegvideo.h" -#include "common.h" static void decode_mb(MpegEncContext *s){ s->dest[0] = s->current_picture.data[0] + (s->mb_y * 16* s->linesize ) + s->mb_x * 16; s->dest[1] = s->current_picture.data[1] + (s->mb_y * 8 * s->uvlinesize) + s->mb_x * 8; s->dest[2] = s->current_picture.data[2] + (s->mb_y * 8 * s->uvlinesize) + s->mb_x * 8; - MPV_decode_mb(s, s->block); + MPV_decode_mb(s, s->block); } /** @@ -45,7 +46,7 @@ static void put_dc(MpegEncContext *s, uint8_t *dest_y, uint8_t *dest_cb, uint8_t { int dc, dcu, dcv, y, i; for(i=0; i<4; i++){ - dc= s->dc_val[0][mb_x*2+1 + (i&1) + (mb_y*2+1 + (i>>1))*(s->mb_width*2+2)]; + dc= s->dc_val[0][mb_x*2 + (i&1) + (mb_y*2 + (i>>1))*s->b8_stride]; if(dc<0) dc=0; else if(dc>2040) dc=2040; for(y=0; y<8; y++){ @@ -55,8 +56,8 @@ static void put_dc(MpegEncContext *s, uint8_t *dest_y, uint8_t *dest_cb, uint8_t } } } - dcu = s->dc_val[1][mb_x+1 + (mb_y+1)*(s->mb_width+2)]; - dcv = s->dc_val[2][mb_x+1 + (mb_y+1)*(s->mb_width+2)]; + dcu = s->dc_val[1][mb_x + mb_y*s->mb_stride]; + dcv = s->dc_val[2][mb_x + mb_y*s->mb_stride]; if (dcu<0 ) dcu=0; else if(dcu>2040) dcu=2040; if (dcv<0 ) dcv=0; @@ -79,8 +80,8 @@ static void filter181(int16_t *data, int width, int height, int stride){ for(x=1; x>16; @@ -88,15 +89,15 @@ static void filter181(int16_t *data, int width, int height, int stride){ data[x + y*stride]= dc; } } - + /* vertical filter */ for(x=1; x>16; @@ -107,9 +108,9 @@ static void filter181(int16_t *data, int width, int height, int stride){ } /** - * guess the dc of blocks which dont have a undamaged dc - * @param w width in 8 pixel blocks - * @param h height in 8 pixel blocks + * guess the dc of blocks which do not have an undamaged dc + * @param w width in 8 pixel blocks + * @param h height in 8 pixel blocks */ static void guess_dc(MpegEncContext *s, int16_t *dc, int w, int h, int stride, int is_luma){ int b_x, b_y; @@ -120,14 +121,14 @@ static void guess_dc(MpegEncContext *s, int16_t *dc, int w, int h, int stride, i int distance[4]={9999,9999,9999,9999}; int mb_index, error, j; int64_t guess, weight_sum; - + mb_index= (b_x>>is_luma) + (b_y>>is_luma)*s->mb_stride; - + error= s->error_status_table[mb_index]; - + if(IS_INTER(s->current_picture.mb_type[mb_index])) continue; //inter if(!(error&DC_ERROR)) continue; //dc-ok - + /* right block */ for(j=b_x+1; j>is_luma) + (b_y>>is_luma)*s->mb_stride; @@ -139,7 +140,7 @@ static void guess_dc(MpegEncContext *s, int16_t *dc, int w, int h, int stride, i break; } } - + /* left block */ for(j=b_x-1; j>=0; j--){ int mb_index_j= (j>>is_luma) + (b_y>>is_luma)*s->mb_stride; @@ -175,7 +176,7 @@ static void guess_dc(MpegEncContext *s, int16_t *dc, int w, int h, int stride, i break; } } - + weight_sum=0; guess=0; for(j=0; j<4; j++){ @@ -192,12 +193,12 @@ static void guess_dc(MpegEncContext *s, int16_t *dc, int w, int h, int stride, i /** * simple horizontal deblocking filter used for error resilience - * @param w width in 8 pixel blocks - * @param h height in 8 pixel blocks + * @param w width in 8 pixel blocks + * @param h height in 8 pixel blocks */ static void h_block_filter(MpegEncContext *s, uint8_t *dst, int w, int h, int stride, int is_luma){ int b_x, b_y; - uint8_t *cm = cropTbl + MAX_NEG_CROP; + uint8_t *cm = ff_cropTbl + MAX_NEG_CROP; for(b_y=0; b_ycurrent_picture.motion_val[0][s->block_wrap[0]*((b_y<<(1-is_luma)) + 1) + ( b_x <<(1-is_luma))]; - int16_t *right_mv= s->current_picture.motion_val[0][s->block_wrap[0]*((b_y<<(1-is_luma)) + 1) + ((b_x+1)<<(1-is_luma))]; - + int16_t *left_mv= s->current_picture.motion_val[0][s->b8_stride*(b_y<<(1-is_luma)) + ( b_x <<(1-is_luma))]; + int16_t *right_mv= s->current_picture.motion_val[0][s->b8_stride*(b_y<<(1-is_luma)) + ((b_x+1)<<(1-is_luma))]; + if(!(left_damage||right_damage)) continue; // both undamaged - - if( (!left_intra) && (!right_intra) - && ABS(left_mv[0]-right_mv[0]) + ABS(left_mv[1]+right_mv[1]) < 2) continue; - + + if( (!left_intra) && (!right_intra) + && FFABS(left_mv[0]-right_mv[0]) + FFABS(left_mv[1]+right_mv[1]) < 2) continue; + for(y=0; y<8; y++){ int a,b,c,d; - + a= dst[offset + 7 + y*stride] - dst[offset + 6 + y*stride]; b= dst[offset + 8 + y*stride] - dst[offset + 7 + y*stride]; c= dst[offset + 9 + y*stride] - dst[offset + 8 + y*stride]; - - d= ABS(b) - ((ABS(a) + ABS(c) + 1)>>1); + + d= FFABS(b) - ((FFABS(a) + FFABS(c) + 1)>>1); d= FFMAX(d, 0); if(b<0) d= -d; - + if(d==0) continue; if(!(left_damage && right_damage)) d= d*16/9; - + if(left_damage){ dst[offset + 7 + y*stride] = cm[dst[offset + 7 + y*stride] + ((d*7)>>4)]; dst[offset + 6 + y*stride] = cm[dst[offset + 6 + y*stride] + ((d*5)>>4)]; @@ -252,12 +253,12 @@ static void h_block_filter(MpegEncContext *s, uint8_t *dst, int w, int h, int st /** * simple vertical deblocking filter used for error resilience - * @param w width in 8 pixel blocks - * @param h height in 8 pixel blocks + * @param w width in 8 pixel blocks + * @param h height in 8 pixel blocks */ static void v_block_filter(MpegEncContext *s, uint8_t *dst, int w, int h, int stride, int is_luma){ int b_x, b_y; - uint8_t *cm = cropTbl + MAX_NEG_CROP; + uint8_t *cm = ff_cropTbl + MAX_NEG_CROP; for(b_y=0; b_ycurrent_picture.motion_val[0][s->block_wrap[0]*(( b_y <<(1-is_luma)) + 1) + (b_x<<(1-is_luma))]; - int16_t *bottom_mv= s->current_picture.motion_val[0][s->block_wrap[0]*(((b_y+1)<<(1-is_luma)) + 1) + (b_x<<(1-is_luma))]; - + int16_t *top_mv= s->current_picture.motion_val[0][s->b8_stride*( b_y <<(1-is_luma)) + (b_x<<(1-is_luma))]; + int16_t *bottom_mv= s->current_picture.motion_val[0][s->b8_stride*((b_y+1)<<(1-is_luma)) + (b_x<<(1-is_luma))]; + if(!(top_damage||bottom_damage)) continue; // both undamaged - - if( (!top_intra) && (!bottom_intra) - && ABS(top_mv[0]-bottom_mv[0]) + ABS(top_mv[1]+bottom_mv[1]) < 2) continue; - + + if( (!top_intra) && (!bottom_intra) + && FFABS(top_mv[0]-bottom_mv[0]) + FFABS(top_mv[1]+bottom_mv[1]) < 2) continue; + for(x=0; x<8; x++){ int a,b,c,d; - + a= dst[offset + x + 7*stride] - dst[offset + x + 6*stride]; b= dst[offset + x + 8*stride] - dst[offset + x + 7*stride]; c= dst[offset + x + 9*stride] - dst[offset + x + 8*stride]; - - d= ABS(b) - ((ABS(a) + ABS(c)+1)>>1); + + d= FFABS(b) - ((FFABS(a) + FFABS(c)+1)>>1); d= FFMAX(d, 0); if(b<0) d= -d; - + if(d==0) continue; if(!(top_damage && bottom_damage)) d= d*16/9; - + if(top_damage){ dst[offset + x + 7*stride] = cm[dst[offset + x + 7*stride] + ((d*7)>>4)]; dst[offset + x + 6*stride] = cm[dst[offset + x + 6*stride] + ((d*5)>>4)]; @@ -320,7 +321,7 @@ static void guess_mv(MpegEncContext *s){ const int mb_height= s->mb_height; int i, depth, num_avail; int mb_x, mb_y; - + num_avail=0; for(i=0; imb_num; i++){ const int mb_xy= s->mb_index2xy[ i ]; @@ -329,26 +330,26 @@ static void guess_mv(MpegEncContext *s){ if(IS_INTRA(s->current_picture.mb_type[mb_xy])) f=MV_FROZEN; //intra //FIXME check if(!(error&MV_ERROR)) f=MV_FROZEN; //inter with undamaged MV - + fixed[mb_xy]= f; if(f==MV_FROZEN) num_avail++; } - + if((!(s->avctx->error_concealment&FF_EC_GUESS_MVS)) || num_avail <= mb_width/2){ for(mb_y=0; mb_ymb_height; mb_y++){ for(mb_x=0; mb_xmb_width; mb_x++){ const int mb_xy= mb_x + mb_y*s->mb_stride; - + if(IS_INTRA(s->current_picture.mb_type[mb_xy])) continue; if(!(s->error_status_table[mb_xy]&MV_ERROR)) continue; s->mv_dir = MV_DIR_FORWARD; s->mb_intra=0; s->mv_type = MV_TYPE_16X16; - s->mb_skiped=0; + s->mb_skipped=0; - s->dsp.clear_blocks(s->block[0]); + s->dsp.clear_blocks(s->block[0]); s->mb_x= mb_x; s->mb_y= mb_y; @@ -359,7 +360,7 @@ static void guess_mv(MpegEncContext *s){ } return; } - + for(depth=0;; depth++){ int changed, pass, none_left; @@ -368,7 +369,7 @@ static void guess_mv(MpegEncContext *s){ for(pass=0; (changed || pass<2) && pass<10; pass++){ int mb_x, mb_y; int score_sum=0; - + changed=0; for(mb_y=0; mb_ymb_height; mb_y++){ for(mb_x=0; mb_xmb_width; mb_x++){ @@ -378,17 +379,17 @@ int score_sum=0; int j; int best_score=256*256*256*64; int best_pred=0; - const int mot_stride= mb_width*2+2; - const int mot_index= mb_x*2 + 1 + (mb_y*2+1)*mot_stride; + const int mot_stride= s->b8_stride; + const int mot_index= mb_x*2 + mb_y*2*mot_stride; int prev_x= s->current_picture.motion_val[0][mot_index][0]; int prev_y= s->current_picture.motion_val[0][mot_index][1]; if((mb_x^mb_y^pass)&1) continue; - + if(fixed[mb_xy]==MV_FROZEN) continue; assert(!IS_INTRA(s->current_picture.mb_type[mb_xy])); assert(s->last_picture_ptr && s->last_picture_ptr->data[0]); - + j=0; if(mb_x>0 && fixed[mb_xy-1 ]==MV_FROZEN) j=1; if(mb_x+10 && fixed[mb_xy-mb_stride]==MV_CHANGED) j=1; if(mb_y+11) continue; - + none_left=0; - + if(mb_x>0 && fixed[mb_xy-1]){ mv_predictor[pred_count][0]= s->current_picture.motion_val[0][mot_index - 2][0]; mv_predictor[pred_count][1]= s->current_picture.motion_val[0][mot_index - 2][1]; @@ -426,7 +427,7 @@ int score_sum=0; pred_count++; } if(pred_count==0) continue; - + if(pred_count>1){ int sum_x=0, sum_y=0; int max_x, max_y, min_x, min_y; @@ -435,11 +436,11 @@ int score_sum=0; sum_x+= mv_predictor[j][0]; sum_y+= mv_predictor[j][1]; } - + /* mean */ mv_predictor[pred_count][0] = sum_x/j; mv_predictor[pred_count][1] = sum_y/j; - + /* median */ if(pred_count>=3){ min_y= min_x= 99999; @@ -455,28 +456,28 @@ int score_sum=0; } mv_predictor[pred_count+1][0] = sum_x - max_x - min_x; mv_predictor[pred_count+1][1] = sum_y - max_y - min_y; - + if(pred_count==4){ mv_predictor[pred_count+1][0] /= 2; mv_predictor[pred_count+1][1] /= 2; } pred_count+=2; } - + /* zero MV */ pred_count++; /* last MV */ mv_predictor[pred_count][0]= s->current_picture.motion_val[0][mot_index][0]; mv_predictor[pred_count][1]= s->current_picture.motion_val[0][mot_index][1]; - pred_count++; - + pred_count++; + s->mv_dir = MV_DIR_FORWARD; s->mb_intra=0; s->mv_type = MV_TYPE_16X16; - s->mb_skiped=0; + s->mb_skipped=0; - s->dsp.clear_blocks(s->block[0]); + s->dsp.clear_blocks(s->block[0]); s->mb_x= mb_x; s->mb_y= mb_y; @@ -489,28 +490,28 @@ int score_sum=0; s->current_picture.motion_val[0][mot_index][1]= s->mv[0][0][1]= mv_predictor[j][1]; decode_mb(s); - + if(mb_x>0 && fixed[mb_xy-1]){ int k; for(k=0; k<16; k++) - score += ABS(src[k*s->linesize-1 ]-src[k*s->linesize ]); + score += FFABS(src[k*s->linesize-1 ]-src[k*s->linesize ]); } if(mb_x+1linesize+15]-src[k*s->linesize+16]); + score += FFABS(src[k*s->linesize+15]-src[k*s->linesize+16]); } if(mb_y>0 && fixed[mb_xy-mb_stride]){ int k; for(k=0; k<16; k++) - score += ABS(src[k-s->linesize ]-src[k ]); + score += FFABS(src[k-s->linesize ]-src[k ]); } if(mb_y+1linesize*15]-src[k+s->linesize*16]); + score += FFABS(src[k+s->linesize*15]-src[k+s->linesize*16]); } - + if(score <= best_score){ // <= will favor the last MV best_score= score; best_pred= j; @@ -523,7 +524,7 @@ score_sum+= best_score; decode_mb(s); - + if(s->mv[0][0][0] != prev_x || s->mv[0][0][1] != prev_y){ fixed[mb_xy]=MV_CHANGED; changed++; @@ -534,10 +535,10 @@ score_sum+= best_score; // printf(".%d/%d", changed, score_sum); fflush(stdout); } - - if(none_left) + + if(none_left) return; - + for(i=0; imb_num; i++){ int mb_xy= s->mb_index2xy[i]; if(fixed[mb_xy]) @@ -546,10 +547,10 @@ score_sum+= best_score; // printf(":"); fflush(stdout); } } - + static int is_intra_more_likely(MpegEncContext *s){ int is_intra_likely, i, j, undamaged_count, skip_amount, mb_x, mb_y; - + if(s->last_picture_ptr==NULL) return 1; //no previous frame available -> use spatial prediction undamaged_count=0; @@ -559,10 +560,15 @@ static int is_intra_more_likely(MpegEncContext *s){ if(!((error&DC_ERROR) && (error&MV_ERROR))) undamaged_count++; } - - if(undamaged_count < 5) return 0; //allmost all MBs damaged -> use temporal prediction - - skip_amount= FFMAX(undamaged_count/50, 1); //check only upto 50 MBs + + if(undamaged_count < 5) return 0; //almost all MBs damaged -> use temporal prediction + +#ifdef HAVE_XVMC + //prevent dsp.sad() check, that requires access to the image + if(s->avctx->xvmc_acceleration && s->pict_type==FF_I_TYPE) return 1; +#endif + + skip_amount= FFMAX(undamaged_count/50, 1); //check only upto 50 MBs is_intra_likely=0; j=0; @@ -574,15 +580,15 @@ static int is_intra_more_likely(MpegEncContext *s){ error= s->error_status_table[mb_xy]; if((error&DC_ERROR) && (error&MV_ERROR)) continue; //skip damaged - - j++; + + j++; if((j%skip_amount) != 0) continue; //skip a few to speed things up - - if(s->pict_type==I_TYPE){ + + if(s->pict_type==FF_I_TYPE){ uint8_t *mb_ptr = s->current_picture.data[0] + mb_x*16 + mb_y*16*s->linesize; uint8_t *last_mb_ptr= s->last_picture.data [0] + mb_x*16 + mb_y*16*s->linesize; - - is_intra_likely += s->dsp.sad[0](NULL, last_mb_ptr, mb_ptr , s->linesize, 16); + + is_intra_likely += s->dsp.sad[0](NULL, last_mb_ptr, mb_ptr , s->linesize, 16); is_intra_likely -= s->dsp.sad[0](NULL, last_mb_ptr, last_mb_ptr+s->linesize*16, s->linesize, 16); }else{ if(IS_INTRA(s->current_picture.mb_type[mb_xy])) @@ -593,7 +599,7 @@ static int is_intra_more_likely(MpegEncContext *s){ } } //printf("is_intra_likely: %d type:%d\n", is_intra_likely, s->pict_type); - return is_intra_likely > 0; + return is_intra_likely > 0; } void ff_er_frame_start(MpegEncContext *s){ @@ -607,15 +613,20 @@ void ff_er_frame_start(MpegEncContext *s){ * adds a slice. * @param endx x component of the last macroblock, can be -1 for the last of the previous line * @param status the status at the end (MV_END, AC_ERROR, ...), it is assumed that no earlier end or - * error of the same type occured + * error of the same type occurred */ void ff_er_add_slice(MpegEncContext *s, int startx, int starty, int endx, int endy, int status){ - const int start_i= clip(startx + starty * s->mb_width , 0, s->mb_num-1); - const int end_i = clip(endx + endy * s->mb_width , 0, s->mb_num); + const int start_i= av_clip(startx + starty * s->mb_width , 0, s->mb_num-1); + const int end_i = av_clip(endx + endy * s->mb_width , 0, s->mb_num); const int start_xy= s->mb_index2xy[start_i]; const int end_xy = s->mb_index2xy[end_i]; int mask= -1; - + + if(start_i > end_i || start_xy > end_xy){ + av_log(s->avctx, AV_LOG_ERROR, "internal error, slice end before start\n"); + return; + } + if(!s->error_resilience) return; mask &= ~VP_START; @@ -643,59 +654,63 @@ void ff_er_add_slice(MpegEncContext *s, int startx, int starty, int endx, int en } } - if(end_i == s->mb_num) + if(end_i == s->mb_num) s->error_count= INT_MAX; else{ s->error_status_table[end_xy] &= mask; s->error_status_table[end_xy] |= status; } - + s->error_status_table[start_xy] |= VP_START; - if(start_xy > 0){ + if(start_xy > 0 && s->avctx->thread_count <= 1 && s->avctx->skip_top*s->mb_width < start_i){ int prev_status= s->error_status_table[ s->mb_index2xy[start_i - 1] ]; - + prev_status &= ~ VP_START; if(prev_status != (MV_END|DC_END|AC_END)) s->error_count= INT_MAX; } } void ff_er_frame_end(MpegEncContext *s){ - int i, mb_x, mb_y, error, error_type; + int i, mb_x, mb_y, error, error_type, dc_error, mv_error, ac_error; int distance; int threshold_part[4]= {100,100,100}; int threshold= 50; int is_intra_likely; - - if(!s->error_resilience || s->error_count==0) return; + int size = s->b8_stride * 2 * s->mb_height; + Picture *pic= s->current_picture_ptr; + + if(!s->error_resilience || s->error_count==0 || + s->error_count==3*s->mb_width*(s->avctx->skip_top + s->avctx->skip_bottom)) return; - av_log(s->avctx, AV_LOG_INFO, "concealing errors\n"); - if(s->current_picture.motion_val[0] == NULL){ - int size = (2 * s->mb_width + 2) * (2 * s->mb_height + 2); - Picture *pic= s->current_picture_ptr; - av_log(s->avctx, AV_LOG_ERROR, "Warning MVs not available\n"); - + for(i=0; i<2; i++){ - pic->motion_val_base[i]= av_mallocz((size+1) * 2 * sizeof(uint16_t)); //FIXME size - pic->motion_val[i]= pic->motion_val_base[i]+1; + pic->ref_index[i]= av_mallocz(size * sizeof(uint8_t)); + pic->motion_val_base[i]= av_mallocz((size+4) * 2 * sizeof(uint16_t)); + pic->motion_val[i]= pic->motion_val_base[i]+4; } pic->motion_subsample_log2= 3; s->current_picture= *s->current_picture_ptr; } - + + for(i=0; i<2; i++){ + if(pic->ref_index[i]) + memset(pic->ref_index[i], 0, size * sizeof(uint8_t)); + } + if(s->avctx->debug&FF_DEBUG_ER){ for(mb_y=0; mb_ymb_height; mb_y++){ for(mb_x=0; mb_xmb_width; mb_x++){ int status= s->error_status_table[mb_x + mb_y*s->mb_stride]; - - av_log(s->avctx, AV_LOG_DEBUG, "%2X ", status); + + av_log(s->avctx, AV_LOG_DEBUG, "%2X ", status); } av_log(s->avctx, AV_LOG_DEBUG, "\n"); } } - + #if 1 /* handle overlapping slices */ for(error_type=1; error_type<=3; error_type++){ @@ -704,7 +719,7 @@ void ff_er_frame_end(MpegEncContext *s){ for(i=s->mb_num-1; i>=0; i--){ const int mb_xy= s->mb_index2xy[i]; int error= s->error_status_table[mb_xy]; - + if(error&(1<mb_num-1; i>=0; i--){ const int mb_xy= s->mb_index2xy[i]; int error= s->error_status_table[mb_xy]; - + if(error&AC_END) end_ok=0; if((error&MV_END) || (error&DC_END) || (error&AC_ERROR)) @@ -743,26 +758,26 @@ void ff_er_frame_end(MpegEncContext *s){ /* handle missing slices */ if(s->error_resilience>=4){ int end_ok=1; - + for(i=s->mb_num-2; i>=s->mb_width+100; i--){ //FIXME +100 hack const int mb_xy= s->mb_index2xy[i]; int error1= s->error_status_table[mb_xy ]; int error2= s->error_status_table[s->mb_index2xy[i+1]]; - + if(error1&VP_START) end_ok=1; - + if( error2==(VP_START|DC_ERROR|AC_ERROR|MV_ERROR|AC_END|DC_END|MV_END) - && error1!=(VP_START|DC_ERROR|AC_ERROR|MV_ERROR|AC_END|DC_END|MV_END) - && ((error1&AC_END) || (error1&DC_END) || (error1&MV_END))){ //end & uninited + && error1!=(VP_START|DC_ERROR|AC_ERROR|MV_ERROR|AC_END|DC_END|MV_END) + && ((error1&AC_END) || (error1&DC_END) || (error1&MV_END))){ //end & uninit end_ok=0; } - + if(!end_ok) s->error_status_table[mb_xy]|= DC_ERROR|AC_ERROR|MV_ERROR; } } - + #if 1 /* backward mark errors */ distance=9999999; @@ -770,9 +785,9 @@ void ff_er_frame_end(MpegEncContext *s){ for(i=s->mb_num-1; i>=0; i--){ const int mb_xy= s->mb_index2xy[i]; int error= s->error_status_table[mb_xy]; - + if(!s->mbskip_table[mb_xy]) //FIXME partition specific - distance++; + distance++; if(error&(1<mb_num; i++){ const int mb_xy= s->mb_index2xy[i]; int old_error= s->error_status_table[mb_xy]; - + if(old_error&VP_START) error= old_error& (DC_ERROR|AC_ERROR|MV_ERROR); else{ @@ -815,6 +830,17 @@ void ff_er_frame_end(MpegEncContext *s){ } } #endif + + dc_error= ac_error= mv_error=0; + for(i=0; imb_num; i++){ + const int mb_xy= s->mb_index2xy[i]; + error= s->error_status_table[mb_xy]; + if(error&DC_ERROR) dc_error ++; + if(error&AC_ERROR) ac_error ++; + if(error&MV_ERROR) mv_error ++; + } + av_log(s->avctx, AV_LOG_INFO, "concealing %d DC, %d AC, %d MV errors\n", dc_error, ac_error, mv_error); + is_intra_likely= is_intra_more_likely(s); /* set unknown mb-type to most likely */ @@ -829,7 +855,7 @@ void ff_er_frame_end(MpegEncContext *s){ else s->current_picture.mb_type[mb_xy]= MB_TYPE_16x16 | MB_TYPE_L0; } - + /* handle inter blocks with damaged AC */ for(mb_y=0; mb_ymb_height; mb_y++){ for(mb_x=0; mb_xmb_width; mb_x++){ @@ -840,25 +866,25 @@ void ff_er_frame_end(MpegEncContext *s){ if(IS_INTRA(mb_type)) continue; //intra if(error&MV_ERROR) continue; //inter with damaged MV if(!(error&AC_ERROR)) continue; //undamaged inter - + s->mv_dir = MV_DIR_FORWARD; s->mb_intra=0; - s->mb_skiped=0; + s->mb_skipped=0; if(IS_8X8(mb_type)){ - int mb_index= mb_x*2+1 + (mb_y*2+1)*s->block_wrap[0]; + int mb_index= mb_x*2 + mb_y*2*s->b8_stride; int j; s->mv_type = MV_TYPE_8X8; for(j=0; j<4; j++){ - s->mv[0][j][0] = s->current_picture.motion_val[0][ mb_index + (j&1) + (j>>1)*s->block_wrap[0] ][0]; - s->mv[0][j][1] = s->current_picture.motion_val[0][ mb_index + (j&1) + (j>>1)*s->block_wrap[0] ][1]; + s->mv[0][j][0] = s->current_picture.motion_val[0][ mb_index + (j&1) + (j>>1)*s->b8_stride ][0]; + s->mv[0][j][1] = s->current_picture.motion_val[0][ mb_index + (j&1) + (j>>1)*s->b8_stride ][1]; } }else{ s->mv_type = MV_TYPE_16X16; - s->mv[0][0][0] = s->current_picture.motion_val[0][ mb_x*2+1 + (mb_y*2+1)*s->block_wrap[0] ][0]; - s->mv[0][0][1] = s->current_picture.motion_val[0][ mb_x*2+1 + (mb_y*2+1)*s->block_wrap[0] ][1]; + s->mv[0][0][0] = s->current_picture.motion_val[0][ mb_x*2 + mb_y*2*s->b8_stride ][0]; + s->mv[0][0][1] = s->current_picture.motion_val[0][ mb_x*2 + mb_y*2*s->b8_stride ][1]; } - - s->dsp.clear_blocks(s->block[0]); + + s->dsp.clear_blocks(s->block[0]); s->mb_x= mb_x; s->mb_y= mb_y; @@ -867,10 +893,10 @@ void ff_er_frame_end(MpegEncContext *s){ } /* guess MVs */ - if(s->pict_type==B_TYPE){ + if(s->pict_type==FF_B_TYPE){ for(mb_y=0; mb_ymb_height; mb_y++){ for(mb_x=0; mb_xmb_width; mb_x++){ - int xy= mb_x*2+1 + (mb_y*2+1)*s->block_wrap[0]; + int xy= mb_x*2 + mb_y*2*s->b8_stride; const int mb_xy= mb_x + mb_y * s->mb_stride; const int mb_type= s->current_picture.mb_type[mb_xy]; error= s->error_status_table[mb_xy]; @@ -878,16 +904,16 @@ void ff_er_frame_end(MpegEncContext *s){ if(IS_INTRA(mb_type)) continue; if(!(error&MV_ERROR)) continue; //inter with undamaged MV if(!(error&AC_ERROR)) continue; //undamaged inter - + s->mv_dir = MV_DIR_FORWARD|MV_DIR_BACKWARD; s->mb_intra=0; s->mv_type = MV_TYPE_16X16; - s->mb_skiped=0; - + s->mb_skipped=0; + if(s->pp_time){ int time_pp= s->pp_time; int time_pb= s->pb_time; - + s->mv[0][0][0] = s->next_picture.motion_val[0][xy][0]*time_pb/time_pp; s->mv[0][0][1] = s->next_picture.motion_val[0][xy][1]*time_pb/time_pp; s->mv[1][0][0] = s->next_picture.motion_val[0][xy][0]*(time_pb - time_pp)/time_pp; @@ -920,17 +946,17 @@ void ff_er_frame_end(MpegEncContext *s){ uint8_t *dest_y, *dest_cb, *dest_cr; const int mb_xy= mb_x + mb_y * s->mb_stride; const int mb_type= s->current_picture.mb_type[mb_xy]; - + error= s->error_status_table[mb_xy]; if(IS_INTRA(mb_type) && s->partitioned_frame) continue; // if(error&MV_ERROR) continue; //inter data damaged FIXME is this good? - + dest_y = s->current_picture.data[0] + mb_x*16 + mb_y*16*s->linesize; dest_cb= s->current_picture.data[1] + mb_x*8 + mb_y*8 *s->uvlinesize; dest_cr= s->current_picture.data[2] + mb_x*8 + mb_y*8 *s->uvlinesize; - - dc_ptr= &s->dc_val[0][mb_x*2+1 + (mb_y*2+1)*(s->mb_width*2+2)]; + + dc_ptr= &s->dc_val[0][mb_x*2 + mb_y*2*s->b8_stride]; for(n=0; n<4; n++){ dc=0; for(y=0; y<8; y++){ @@ -939,7 +965,7 @@ void ff_er_frame_end(MpegEncContext *s){ dc+= dest_y[x + (n&1)*8 + (y + (n>>1)*8)*s->linesize]; } } - dc_ptr[(n&1) + (n>>1)*(s->mb_width*2+2)]= (dc+4)>>3; + dc_ptr[(n&1) + (n>>1)*s->b8_stride]= (dc+4)>>3; } dcu=dcv=0; @@ -950,19 +976,19 @@ void ff_er_frame_end(MpegEncContext *s){ dcv+=dest_cr[x + y*(s->uvlinesize)]; } } - s->dc_val[1][mb_x+1 + (mb_y+1)*(s->mb_width+2)]= (dcu+4)>>3; - s->dc_val[2][mb_x+1 + (mb_y+1)*(s->mb_width+2)]= (dcv+4)>>3; + s->dc_val[1][mb_x + mb_y*s->mb_stride]= (dcu+4)>>3; + s->dc_val[2][mb_x + mb_y*s->mb_stride]= (dcv+4)>>3; } } #if 1 /* guess DC for damaged blocks */ - guess_dc(s, s->dc_val[0] + s->mb_width*2+3, s->mb_width*2, s->mb_height*2, s->mb_width*2+2, 1); - guess_dc(s, s->dc_val[1] + s->mb_width +3, s->mb_width , s->mb_height , s->mb_width +2, 0); - guess_dc(s, s->dc_val[2] + s->mb_width +3, s->mb_width , s->mb_height , s->mb_width +2, 0); -#endif + guess_dc(s, s->dc_val[0], s->mb_width*2, s->mb_height*2, s->b8_stride, 1); + guess_dc(s, s->dc_val[1], s->mb_width , s->mb_height , s->mb_stride, 0); + guess_dc(s, s->dc_val[2], s->mb_width , s->mb_height , s->mb_stride, 0); +#endif /* filter luma DC */ - filter181(s->dc_val[0] + s->mb_width*2+3, s->mb_width*2, s->mb_height*2, s->mb_width*2+2); - + filter181(s->dc_val[0], s->mb_width*2, s->mb_height*2, s->b8_stride); + #if 1 /* render DC only intra */ for(mb_y=0; mb_ymb_height; mb_y++){ @@ -975,16 +1001,16 @@ void ff_er_frame_end(MpegEncContext *s){ if(IS_INTER(mb_type)) continue; if(!(error&AC_ERROR)) continue; //undamaged - + dest_y = s->current_picture.data[0] + mb_x*16 + mb_y*16*s->linesize; dest_cb= s->current_picture.data[1] + mb_x*8 + mb_y*8 *s->uvlinesize; dest_cr= s->current_picture.data[2] + mb_x*8 + mb_y*8 *s->uvlinesize; - + put_dc(s, dest_y, dest_cb, dest_cr, mb_x, mb_y); } } #endif - + if(s->avctx->error_concealment&FF_EC_DEBLOCK){ /* filter horizontal block boundaries */ h_block_filter(s, s->current_picture.data[0], s->mb_width*2, s->mb_height*2, s->linesize , 1); @@ -1004,10 +1030,10 @@ ec_clean: for(i=0; imb_num; i++){ const int mb_xy= s->mb_index2xy[i]; int error= s->error_status_table[mb_xy]; - - if(s->pict_type!=B_TYPE && (error&(DC_ERROR|MV_ERROR|AC_ERROR))){ + + if(s->pict_type!=FF_B_TYPE && (error&(DC_ERROR|MV_ERROR|AC_ERROR))){ s->mbskip_table[mb_xy]=0; } s->mbintra_table[mb_xy]=1; - } + } } diff --git a/src/add-ons/media/plugins/avcodec/libavcodec/escape124.c b/src/add-ons/media/plugins/avcodec/libavcodec/escape124.c new file mode 100644 index 0000000000..90ba4795eb --- /dev/null +++ b/src/add-ons/media/plugins/avcodec/libavcodec/escape124.c @@ -0,0 +1,385 @@ +/* + * Escape 124 Video Decoder + * Copyright (C) 2008 Eli Friedman (eli.friedman@gmail.com) + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avcodec.h" + +#define ALT_BITSTREAM_READER_LE +#include "bitstream.h" + +typedef union MacroBlock { + uint16_t pixels[4]; + uint32_t pixels32[2]; +} MacroBlock; + +typedef union SuperBlock { + uint16_t pixels[64]; + uint32_t pixels32[32]; +} SuperBlock; + +typedef struct CodeBook { + unsigned depth; + unsigned size; + MacroBlock* blocks; +} CodeBook; + +typedef struct Escape124Context { + AVFrame frame; + + unsigned num_superblocks; + + CodeBook codebooks[3]; +} Escape124Context; + +static int can_safely_read(GetBitContext* gb, int bits) { + return get_bits_count(gb) + bits <= gb->size_in_bits; +} + +/** + * Initialize the decoder + * @param avctx decoder context + * @return 0 success, negative on error + */ +static av_cold int escape124_decode_init(AVCodecContext *avctx) +{ + Escape124Context *s = avctx->priv_data; + + avctx->pix_fmt = PIX_FMT_RGB555; + + s->num_superblocks = ((unsigned)avctx->width / 8) * + ((unsigned)avctx->height / 8); + + return 0; +} + +static av_cold int escape124_decode_close(AVCodecContext *avctx) +{ + unsigned i; + Escape124Context *s = avctx->priv_data; + + for (i = 0; i < 3; i++) + av_free(s->codebooks[i].blocks); + + if (s->frame.data[0]) + avctx->release_buffer(avctx, &s->frame); + + return 0; +} + +static CodeBook unpack_codebook(GetBitContext* gb, unsigned depth, + unsigned size) +{ + unsigned i, j; + CodeBook cb = { 0 }; + + if (!can_safely_read(gb, size * 34)) + return cb; + + if (size >= INT_MAX / sizeof(MacroBlock)) + return cb; + cb.blocks = av_malloc(size ? size * sizeof(MacroBlock) : 1); + if (!cb.blocks) + return cb; + + cb.depth = depth; + cb.size = size; + for (i = 0; i < size; i++) { + unsigned mask_bits = get_bits(gb, 4); + unsigned color0 = get_bits(gb, 15); + unsigned color1 = get_bits(gb, 15); + + for (j = 0; j < 4; j++) { + if (mask_bits & (1 << j)) + cb.blocks[i].pixels[j] = color1; + else + cb.blocks[i].pixels[j] = color0; + } + } + return cb; +} + +static unsigned decode_skip_count(GetBitContext* gb) +{ + unsigned value; + // This function reads a maximum of 23 bits, + // which is within the padding space + if (!can_safely_read(gb, 1)) + return -1; + value = get_bits1(gb); + if (!value) + return value; + + value += get_bits(gb, 3); + if (value != (1 + ((1 << 3) - 1))) + return value; + + value += get_bits(gb, 7); + if (value != (1 + ((1 << 3) - 1)) + ((1 << 7) - 1)) + return value; + + return value + get_bits(gb, 12); +} + +static MacroBlock decode_macroblock(Escape124Context* s, GetBitContext* gb, + int* codebook_index, int superblock_index) +{ + // This function reads a maximum of 22 bits; the callers + // guard this function appropriately + unsigned block_index, depth; + + if (get_bits1(gb)) { + static const char transitions[3][2] = { {2, 1}, {0, 2}, {1, 0} }; + *codebook_index = transitions[*codebook_index][get_bits1(gb)]; + } + + depth = s->codebooks[*codebook_index].depth; + + // depth = 0 means that this shouldn't read any bits; + // in theory, this is the same as get_bits(gb, 0), but + // that doesn't actually work. + block_index = depth ? get_bits(gb, depth) : 0; + + if (*codebook_index == 1) { + block_index += superblock_index << s->codebooks[1].depth; + } + + // This condition can occur with invalid bitstreams and + // *codebook_index == 2 + if (block_index >= s->codebooks[*codebook_index].size) + return (MacroBlock) { { 0 } }; + + return s->codebooks[*codebook_index].blocks[block_index]; +} + +static void insert_mb_into_sb(SuperBlock* sb, MacroBlock mb, unsigned index) { + // Formula: ((index / 4) * 16 + (index % 4) * 2) / 2 + uint32_t *dst = sb->pixels32 + index + (index & -4); + + // This technically violates C99 aliasing rules, but it should be safe. + dst[0] = mb.pixels32[0]; + dst[4] = mb.pixels32[1]; +} + +static void copy_superblock(uint16_t* dest, unsigned dest_stride, + uint16_t* src, unsigned src_stride) +{ + unsigned y; + if (src) + for (y = 0; y < 8; y++) + memcpy(dest + y * dest_stride, src + y * src_stride, + sizeof(uint16_t) * 8); + else + for (y = 0; y < 8; y++) + memset(dest + y * dest_stride, 0, sizeof(uint16_t) * 8); +} + +static const uint16_t mask_matrix[] = {0x1, 0x2, 0x10, 0x20, + 0x4, 0x8, 0x40, 0x80, + 0x100, 0x200, 0x1000, 0x2000, + 0x400, 0x800, 0x4000, 0x8000}; + +/** + * Decode a single frame + * @param avctx decoder context + * @param data decoded frame + * @param data_size size of the decoded frame + * @param buf input buffer + * @param buf_size input buffer size + * @return 0 success, -1 on error + */ +static int escape124_decode_frame(AVCodecContext *avctx, + void *data, int *data_size, + const uint8_t *buf, int buf_size) +{ + Escape124Context *s = avctx->priv_data; + + GetBitContext gb; + unsigned frame_flags, frame_size; + unsigned i; + + unsigned superblock_index, cb_index = 1, + superblock_col_index = 0, + superblocks_per_row = avctx->width / 8, skip = -1; + + uint16_t* old_frame_data, *new_frame_data; + unsigned old_stride, new_stride; + + AVFrame new_frame = { { 0 } }; + + init_get_bits(&gb, buf, buf_size * 8); + + // This call also guards the potential depth reads for the + // codebook unpacking. + if (!can_safely_read(&gb, 64)) + return -1; + + frame_flags = get_bits_long(&gb, 32); + frame_size = get_bits_long(&gb, 32); + + // Leave last frame unchanged + // FIXME: Is this necessary? I haven't seen it in any real samples + if (!(frame_flags & 0x114) || !(frame_flags & 0x7800000)) { + av_log(NULL, AV_LOG_DEBUG, "Skipping frame\n"); + + *data_size = sizeof(AVFrame); + *(AVFrame*)data = s->frame; + + return frame_size; + } + + for (i = 0; i < 3; i++) { + if (frame_flags & (1 << (17 + i))) { + unsigned cb_depth, cb_size; + if (i == 2) { + // This codebook can be cut off at places other than + // powers of 2, leaving some of the entries undefined. + cb_size = get_bits_long(&gb, 20); + cb_depth = av_log2(cb_size - 1) + 1; + } else { + cb_depth = get_bits(&gb, 4); + if (i == 0) { + // This is the most basic codebook: pow(2,depth) entries + // for a depth-length key + cb_size = 1 << cb_depth; + } else { + // This codebook varies per superblock + // FIXME: I don't think this handles integer overflow + // properly + cb_size = s->num_superblocks << cb_depth; + } + } + av_free(s->codebooks[i].blocks); + s->codebooks[i] = unpack_codebook(&gb, cb_depth, cb_size); + if (!s->codebooks[i].blocks) + return -1; + } + } + + new_frame.reference = 3; + if (avctx->get_buffer(avctx, &new_frame)) { + av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n"); + return -1; + } + + new_frame_data = (uint16_t*)new_frame.data[0]; + new_stride = new_frame.linesize[0] / 2; + old_frame_data = (uint16_t*)s->frame.data[0]; + old_stride = s->frame.linesize[0] / 2; + + for (superblock_index = 0; superblock_index < s->num_superblocks; + superblock_index++) { + MacroBlock mb; + SuperBlock sb; + unsigned multi_mask = 0; + + if (skip == -1) { + // Note that this call will make us skip the rest of the blocks + // if the frame prematurely ends + skip = decode_skip_count(&gb); + } + + if (skip) { + copy_superblock(new_frame_data, new_stride, + old_frame_data, old_stride); + } else { + copy_superblock(sb.pixels, 8, + old_frame_data, old_stride); + + while (can_safely_read(&gb, 1) && !get_bits1(&gb)) { + unsigned mask; + mb = decode_macroblock(s, &gb, &cb_index, superblock_index); + mask = get_bits(&gb, 16); + multi_mask |= mask; + for (i = 0; i < 16; i++) { + if (mask & mask_matrix[i]) { + insert_mb_into_sb(&sb, mb, i); + } + } + } + + if (can_safely_read(&gb, 1) && !get_bits1(&gb)) { + unsigned inv_mask = get_bits(&gb, 4); + for (i = 0; i < 4; i++) { + if (inv_mask & (1 << i)) { + multi_mask ^= 0xF << i*4; + } else { + multi_mask ^= get_bits(&gb, 4) << i*4; + } + } + + for (i = 0; i < 16; i++) { + if (multi_mask & mask_matrix[i]) { + if (!can_safely_read(&gb, 1)) + break; + mb = decode_macroblock(s, &gb, &cb_index, + superblock_index); + insert_mb_into_sb(&sb, mb, i); + } + } + } else if (frame_flags & (1 << 16)) { + while (can_safely_read(&gb, 1) && !get_bits1(&gb)) { + mb = decode_macroblock(s, &gb, &cb_index, superblock_index); + insert_mb_into_sb(&sb, mb, get_bits(&gb, 4)); + } + } + + copy_superblock(new_frame_data, new_stride, sb.pixels, 8); + } + + superblock_col_index++; + new_frame_data += 8; + if (old_frame_data) + old_frame_data += 8; + if (superblock_col_index == superblocks_per_row) { + new_frame_data += new_stride * 8 - superblocks_per_row * 8; + if (old_frame_data) + old_frame_data += old_stride * 8 - superblocks_per_row * 8; + superblock_col_index = 0; + } + skip--; + } + + av_log(NULL, AV_LOG_DEBUG, + "Escape sizes: %i, %i, %i\n", + frame_size, buf_size, get_bits_count(&gb) / 8); + + if (s->frame.data[0]) + avctx->release_buffer(avctx, &s->frame); + + *(AVFrame*)data = s->frame = new_frame; + *data_size = sizeof(AVFrame); + + return frame_size; +} + + +AVCodec escape124_decoder = { + "escape124", + CODEC_TYPE_VIDEO, + CODEC_ID_ESCAPE124, + sizeof(Escape124Context), + escape124_decode_init, + NULL, + escape124_decode_close, + escape124_decode_frame, + CODEC_CAP_DR1, + .long_name = NULL_IF_CONFIG_SMALL("Escape 124"), +}; + diff --git a/src/add-ons/media/plugins/avcodec/libavcodec/eval.c b/src/add-ons/media/plugins/avcodec/libavcodec/eval.c index 714ba046c5..ada353670c 100644 --- a/src/add-ons/media/plugins/avcodec/libavcodec/eval.c +++ b/src/add-ons/media/plugins/avcodec/libavcodec/eval.c @@ -1,22 +1,24 @@ /* * simple arithmetic expression evaluator * - * Copyright (c) 2002 Michael Niedermayer + * Copyright (c) 2002-2006 Michael Niedermayer + * Copyright (c) 2006 Oded Shimon * - * This library is free software; you can redistribute it and/or + * This file is part of FFmpeg. + * + * FFmpeg 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. + * version 2.1 of the License, or (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * FFmpeg 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 - * + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** @@ -27,7 +29,7 @@ */ #include "avcodec.h" -#include "mpegvideo.h" +#include "eval.h" #include #include @@ -35,17 +37,14 @@ #include #ifndef NAN - #define NAN 0 + #define NAN 0.0/0.0 #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif -#define STACK_SIZE 100 - typedef struct Parser{ - double stack[STACK_SIZE]; int stack_index; char *s; double *const_value; @@ -55,26 +54,68 @@ typedef struct Parser{ double (**func2)(void *, double a, double b); // NULL terminated char **func2_name; // NULL terminated void *opaque; + const char **error; +#define VARS 10 + double var[VARS]; } Parser; -static void evalExpression(Parser *p); +static const int8_t si_prefixes['z' - 'E' + 1]={ + ['y'-'E']= -24, + ['z'-'E']= -21, + ['a'-'E']= -18, + ['f'-'E']= -15, + ['p'-'E']= -12, + ['n'-'E']= - 9, + ['u'-'E']= - 6, + ['m'-'E']= - 3, + ['c'-'E']= - 2, + ['d'-'E']= - 1, + ['h'-'E']= 2, + ['k'-'E']= 3, + ['K'-'E']= 3, + ['M'-'E']= 6, + ['G'-'E']= 9, + ['T'-'E']= 12, + ['P'-'E']= 15, + ['E'-'E']= 18, + ['Z'-'E']= 21, + ['Y'-'E']= 24, +}; -static void push(Parser *p, double d){ - if(p->stack_index+1>= STACK_SIZE){ - av_log(NULL, AV_LOG_ERROR, "stack overflow in the parser\n"); - return; - } - p->stack[ p->stack_index++ ]= d; -//printf("push %f\n", d); fflush(stdout); -} +/** strtod() function extended with 'k', 'M', 'G', 'ki', 'Mi', 'Gi' and 'B' + * postfixes. This allows using f.e. kB, MiB, G and B as a postfix. This + * function assumes that the unit of numbers is bits not bytes. + */ +static double av_strtod(const char *name, char **tail) { + double d; + char *next; + d = strtod(name, &next); + /* if parsing succeeded, check for and interpret postfixes */ + if (next!=name) { -static double pop(Parser *p){ - if(p->stack_index<=0){ - av_log(NULL, AV_LOG_ERROR, "stack underflow in the parser\n"); - return NAN; + if(*next >= 'E' && *next <= 'z'){ + int e= si_prefixes[*next - 'E']; + if(e){ + if(next[1] == 'i'){ + d*= pow( 2, e/0.3); + next+=2; + }else{ + d*= pow(10, e); + next++; + } + } + } + + if(*next=='B') { + d*=8; + next++; + } } -//printf("pop\n"); fflush(stdout); - return p->stack[ --p->stack_index ]; + /* if requested, fill in tail with the position after the last parsed + character */ + if (tail) + *tail = next; + return d; } static int strmatch(const char *s, const char *prefix){ @@ -85,171 +126,340 @@ static int strmatch(const char *s, const char *prefix){ return 1; } -static void evalPrimary(Parser *p){ - double d, d2=NAN; +struct ff_expr_s { + enum { + e_value, e_const, e_func0, e_func1, e_func2, + e_squish, e_gauss, e_ld, + e_mod, e_max, e_min, e_eq, e_gt, e_gte, + e_pow, e_mul, e_div, e_add, + e_last, e_st, e_while, + } type; + double value; // is sign in other types + union { + int const_index; + double (*func0)(double); + double (*func1)(void *, double); + double (*func2)(void *, double, double); + } a; + AVEvalExpr * param[2]; +}; + +static double eval_expr(Parser * p, AVEvalExpr * e) { + switch (e->type) { + case e_value: return e->value; + case e_const: return e->value * p->const_value[e->a.const_index]; + case e_func0: return e->value * e->a.func0(eval_expr(p, e->param[0])); + case e_func1: return e->value * e->a.func1(p->opaque, eval_expr(p, e->param[0])); + case e_func2: return e->value * e->a.func2(p->opaque, eval_expr(p, e->param[0]), eval_expr(p, e->param[1])); + case e_squish: return 1/(1+exp(4*eval_expr(p, e->param[0]))); + case e_gauss: { double d = eval_expr(p, e->param[0]); return exp(-d*d/2)/sqrt(2*M_PI); } + case e_ld: return e->value * p->var[av_clip(eval_expr(p, e->param[0]), 0, VARS-1)]; + case e_while: { + double d = NAN; + while(eval_expr(p, e->param[0])) + d=eval_expr(p, e->param[1]); + return d; + } + default: { + double d = eval_expr(p, e->param[0]); + double d2 = eval_expr(p, e->param[1]); + switch (e->type) { + case e_mod: return e->value * (d - floor(d/d2)*d2); + case e_max: return e->value * (d > d2 ? d : d2); + case e_min: return e->value * (d < d2 ? d : d2); + case e_eq: return e->value * (d == d2 ? 1.0 : 0.0); + case e_gt: return e->value * (d > d2 ? 1.0 : 0.0); + case e_gte: return e->value * (d >= d2 ? 1.0 : 0.0); + case e_pow: return e->value * pow(d, d2); + case e_mul: return e->value * (d * d2); + case e_div: return e->value * (d / d2); + case e_add: return e->value * (d + d2); + case e_last:return e->value * d2; + case e_st : return e->value * (p->var[av_clip(d, 0, VARS-1)]= d2); + } + } + } + return NAN; +} + +static AVEvalExpr * parse_expr(Parser *p); + +void ff_eval_free(AVEvalExpr * e) { + if (!e) return; + ff_eval_free(e->param[0]); + ff_eval_free(e->param[1]); + av_freep(&e); +} + +static AVEvalExpr * parse_primary(Parser *p) { + AVEvalExpr * d = av_mallocz(sizeof(AVEvalExpr)); char *next= p->s; int i; /* number */ - d= strtod(p->s, &next); + d->value = av_strtod(p->s, &next); if(next != p->s){ - push(p, d); + d->type = e_value; p->s= next; - return; + return d; } - + d->value = 1; + /* named constants */ - for(i=0; p->const_name[i]; i++){ + for(i=0; p->const_name && p->const_name[i]; i++){ if(strmatch(p->s, p->const_name[i])){ - push(p, p->const_value[i]); p->s+= strlen(p->const_name[i]); - return; + d->type = e_const; + d->a.const_index = i; + return d; } } - + p->s= strchr(p->s, '('); if(p->s==NULL){ - av_log(NULL, AV_LOG_ERROR, "Parser: missing ( in \"%s\"\n", next); - return; + *p->error = "undefined constant or missing ("; + p->s= next; + ff_eval_free(d); + return NULL; } p->s++; // "(" - evalExpression(p); - d= pop(p); - p->s++; // ")" or "," - if(p->s[-1]== ','){ - evalExpression(p); - d2= pop(p); + if (*next == '(') { // special case do-nothing + av_freep(&d); + d = parse_expr(p); + if(p->s[0] != ')'){ + *p->error = "missing )"; + ff_eval_free(d); + return NULL; + } p->s++; // ")" + return d; } - - if( strmatch(next, "sinh" ) ) d= sinh(d); - else if( strmatch(next, "cosh" ) ) d= cosh(d); - else if( strmatch(next, "tanh" ) ) d= tanh(d); - else if( strmatch(next, "sin" ) ) d= sin(d); - else if( strmatch(next, "cos" ) ) d= cos(d); - else if( strmatch(next, "tan" ) ) d= tan(d); - else if( strmatch(next, "exp" ) ) d= exp(d); - else if( strmatch(next, "log" ) ) d= log(d); - else if( strmatch(next, "squish") ) d= 1/(1+exp(4*d)); - else if( strmatch(next, "gauss" ) ) d= exp(-d*d/2)/sqrt(2*M_PI); - else if( strmatch(next, "abs" ) ) d= fabs(d); - else if( strmatch(next, "max" ) ) d= d > d2 ? d : d2; - else if( strmatch(next, "min" ) ) d= d < d2 ? d : d2; - else if( strmatch(next, "gt" ) ) d= d > d2 ? 1.0 : 0.0; - else if( strmatch(next, "lt" ) ) d= d > d2 ? 0.0 : 1.0; - else if( strmatch(next, "eq" ) ) d= d == d2 ? 1.0 : 0.0; -// else if( strmatch(next, "l1" ) ) d= 1 + d2*(d - 1); -// else if( strmatch(next, "sq01" ) ) d= (d >= 0.0 && d <=1.0) ? 1.0 : 0.0; - else{ - int error=1; + d->param[0] = parse_expr(p); + if(p->s[0]== ','){ + p->s++; // "," + d->param[1] = parse_expr(p); + } + if(p->s[0] != ')'){ + *p->error = "missing )"; + ff_eval_free(d); + return NULL; + } + p->s++; // ")" + + d->type = e_func0; + if( strmatch(next, "sinh" ) ) d->a.func0 = sinh; + else if( strmatch(next, "cosh" ) ) d->a.func0 = cosh; + else if( strmatch(next, "tanh" ) ) d->a.func0 = tanh; + else if( strmatch(next, "sin" ) ) d->a.func0 = sin; + else if( strmatch(next, "cos" ) ) d->a.func0 = cos; + else if( strmatch(next, "tan" ) ) d->a.func0 = tan; + else if( strmatch(next, "atan" ) ) d->a.func0 = atan; + else if( strmatch(next, "asin" ) ) d->a.func0 = asin; + else if( strmatch(next, "acos" ) ) d->a.func0 = acos; + else if( strmatch(next, "exp" ) ) d->a.func0 = exp; + else if( strmatch(next, "log" ) ) d->a.func0 = log; + else if( strmatch(next, "abs" ) ) d->a.func0 = fabs; + else if( strmatch(next, "squish") ) d->type = e_squish; + else if( strmatch(next, "gauss" ) ) d->type = e_gauss; + else if( strmatch(next, "mod" ) ) d->type = e_mod; + else if( strmatch(next, "max" ) ) d->type = e_max; + else if( strmatch(next, "min" ) ) d->type = e_min; + else if( strmatch(next, "eq" ) ) d->type = e_eq; + else if( strmatch(next, "gte" ) ) d->type = e_gte; + else if( strmatch(next, "gt" ) ) d->type = e_gt; + else if( strmatch(next, "lte" ) ) { AVEvalExpr * tmp = d->param[1]; d->param[1] = d->param[0]; d->param[0] = tmp; d->type = e_gt; } + else if( strmatch(next, "lt" ) ) { AVEvalExpr * tmp = d->param[1]; d->param[1] = d->param[0]; d->param[0] = tmp; d->type = e_gte; } + else if( strmatch(next, "ld" ) ) d->type = e_ld; + else if( strmatch(next, "st" ) ) d->type = e_st; + else if( strmatch(next, "while" ) ) d->type = e_while; + else { for(i=0; p->func1_name && p->func1_name[i]; i++){ if(strmatch(next, p->func1_name[i])){ - d= p->func1[i](p->opaque, d); - error=0; - break; + d->a.func1 = p->func1[i]; + d->type = e_func1; + return d; } } for(i=0; p->func2_name && p->func2_name[i]; i++){ if(strmatch(next, p->func2_name[i])){ - d= p->func2[i](p->opaque, d, d2); - error=0; - break; + d->a.func2 = p->func2[i]; + d->type = e_func2; + return d; } } - if(error){ - av_log(NULL, AV_LOG_ERROR, "Parser: unknown function in \"%s\"\n", next); - return; - } + *p->error = "unknown function"; + ff_eval_free(d); + return NULL; } - - if(p->s[-1]!= ')'){ - av_log(NULL, AV_LOG_ERROR, "Parser: missing ) in \"%s\"\n", next); - return; - } - push(p, d); -} - -static void evalPow(Parser *p){ - int neg= 0; - if(p->s[0]=='+') p->s++; - - if(p->s[0]=='-'){ - neg= 1; - p->s++; - } - - if(p->s[0]=='('){ - p->s++;; - evalExpression(p); - if(p->s[0]!=')') - av_log(NULL, AV_LOG_ERROR, "Parser: missing )\n"); - p->s++; - }else{ - evalPrimary(p); - } - - if(neg) push(p, -pop(p)); + return d; } -static void evalFactor(Parser *p){ - evalPow(p); +static AVEvalExpr * new_eval_expr(int type, int value, AVEvalExpr *p0, AVEvalExpr *p1){ + AVEvalExpr * e = av_mallocz(sizeof(AVEvalExpr)); + e->type =type ; + e->value =value ; + e->param[0] =p0 ; + e->param[1] =p1 ; + return e; +} + +static AVEvalExpr * parse_pow(Parser *p, int *sign){ + *sign= (*p->s == '+') - (*p->s == '-'); + p->s += *sign&1; + return parse_primary(p); +} + +static AVEvalExpr * parse_factor(Parser *p){ + int sign, sign2; + AVEvalExpr * e = parse_pow(p, &sign); while(p->s[0]=='^'){ - double d; - p->s++; - evalPow(p); - d= pop(p); - push(p, pow(pop(p), d)); + e= new_eval_expr(e_pow, 1, e, parse_pow(p, &sign2)); + if (e->param[1]) e->param[1]->value *= (sign2|1); } + if (e) e->value *= (sign|1); + return e; } -static void evalTerm(Parser *p){ - evalFactor(p); +static AVEvalExpr * parse_term(Parser *p){ + AVEvalExpr * e = parse_factor(p); while(p->s[0]=='*' || p->s[0]=='/'){ - int inv= p->s[0]=='/'; - double d; + int c= *p->s++; + e= new_eval_expr(c == '*' ? e_mul : e_div, 1, e, parse_factor(p)); + } + return e; +} +static AVEvalExpr * parse_subexpr(Parser *p) { + AVEvalExpr * e = parse_term(p); + while(*p->s == '+' || *p->s == '-') { + e= new_eval_expr(e_add, 1, e, parse_term(p)); + }; + + return e; +} + +static AVEvalExpr * parse_expr(Parser *p) { + AVEvalExpr * e; + + if(p->stack_index <= 0) //protect against stack overflows + return NULL; + p->stack_index--; + + e = parse_subexpr(p); + + while(*p->s == ';') { p->s++; - evalFactor(p); - d= pop(p); - if(inv) d= 1.0/d; - push(p, d * pop(p)); + e= new_eval_expr(e_last, 1, e, parse_subexpr(p)); + }; + + p->stack_index++; + + return e; +} + +static int verify_expr(AVEvalExpr * e) { + if (!e) return 0; + switch (e->type) { + case e_value: + case e_const: return 1; + case e_func0: + case e_func1: + case e_squish: + case e_ld: + case e_gauss: return verify_expr(e->param[0]); + default: return verify_expr(e->param[0]) && verify_expr(e->param[1]); } } -static void evalExpression(Parser *p){ - evalTerm(p); - while(p->s[0]=='+' || p->s[0]=='-'){ - int sign= p->s[0]=='-'; - double d; - - p->s++; - evalTerm(p); - d= pop(p); - if(sign) d= -d; - push(p, d + pop(p)); - } -} - -double ff_eval(char *s, double *const_value, const char **const_name, +AVEvalExpr * ff_parse(const char *s, const char **const_name, double (**func1)(void *, double), const char **func1_name, double (**func2)(void *, double, double), char **func2_name, - void *opaque){ + const char **error){ Parser p; - - p.stack_index=0; - p.s= s; - p.const_value= const_value; + AVEvalExpr * e; + char w[strlen(s) + 1], * wp = w; + + while (*s) + if (!isspace(*s++)) *wp++ = s[-1]; + *wp++ = 0; + + p.stack_index=100; + p.s= w; p.const_name = const_name; p.func1 = func1; p.func1_name = func1_name; p.func2 = func2; p.func2_name = func2_name; - p.opaque = opaque; - - evalExpression(&p); - return pop(&p); + p.error= error; + + e = parse_expr(&p); + if (!verify_expr(e)) { + ff_eval_free(e); + return NULL; + } + return e; } + +double ff_parse_eval(AVEvalExpr * e, double *const_value, void *opaque) { + Parser p; + + p.const_value= const_value; + p.opaque = opaque; + return eval_expr(&p, e); +} + +double ff_eval2(const char *s, double *const_value, const char **const_name, + double (**func1)(void *, double), const char **func1_name, + double (**func2)(void *, double, double), char **func2_name, + void *opaque, const char **error){ + AVEvalExpr * e = ff_parse(s, const_name, func1, func1_name, func2, func2_name, error); + double d; + if (!e) return NAN; + d = ff_parse_eval(e, const_value, opaque); + ff_eval_free(e); + return d; +} + +#if LIBAVCODEC_VERSION_INT < ((52<<16)+(0<<8)+0) +attribute_deprecated double ff_eval(char *s, double *const_value, const char **const_name, + double (**func1)(void *, double), const char **func1_name, + double (**func2)(void *, double, double), char **func2_name, + void *opaque){ + const char *error=NULL; + double ret; + ret = ff_eval2(s, const_value, const_name, func1, func1_name, func2, func2_name, opaque, &error); + if (error) + av_log(NULL, AV_LOG_ERROR, "Error evaluating \"%s\": %s\n", s, error); + return ret; +} +#endif + +#ifdef TEST +#undef printf +static double const_values[]={ + M_PI, + M_E, + 0 +}; +static const char *const_names[]={ + "PI", + "E", + 0 +}; +int main(void){ + int i; + printf("%f == 12.7\n", ff_eval("1+(5-2)^(3-1)+1/2+sin(PI)-max(-2.2,-3.1)", const_values, const_names, NULL, NULL, NULL, NULL, NULL)); + printf("%f == 0.931322575\n", ff_eval("80G/80Gi", const_values, const_names, NULL, NULL, NULL, NULL, NULL)); + + for(i=0; i<1050; i++){ + START_TIMER + ff_eval("1+(5-2)^(3-1)+1/2+sin(PI)-max(-2.2,-3.1)", const_values, const_names, NULL, NULL, NULL, NULL, NULL); + STOP_TIMER("ff_eval") + } + return 0; +} +#endif diff --git a/src/add-ons/media/plugins/avcodec/libavcodec/eval.h b/src/add-ons/media/plugins/avcodec/libavcodec/eval.h new file mode 100644 index 0000000000..786e950c01 --- /dev/null +++ b/src/add-ons/media/plugins/avcodec/libavcodec/eval.h @@ -0,0 +1,87 @@ +/* + * simple arithmetic expression evaluator + * + * Copyright (c) 2002 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file eval.h + * eval header. + */ + +#ifndef FFMPEG_EVAL_H +#define FFMPEG_EVAL_H + +#if LIBAVCODEC_VERSION_INT < ((52<<16)+(0<<8)+0) +/** + * @deprecated Use ff_eval2 instead + */ +double ff_eval(char *s, double *const_value, const char **const_name, + double (**func1)(void *, double), const char **func1_name, + double (**func2)(void *, double, double), char **func2_name, + void *opaque); +#endif + +/** + * Parses and evaluates an expression. + * Note, this is significantly slower than ff_parse_eval() + * @param s expression as a zero terminated string for example "1+2^3+5*5+sin(2/3)" + * @param func1 NULL terminated array of function pointers for functions which take 1 argument + * @param func2 NULL terminated array of function pointers for functions which take 2 arguments + * @param const_name NULL terminated array of zero terminated strings of constant identifers for example {"PI", "E", 0} + * @param func1_name NULL terminated array of zero terminated strings of func1 identifers + * @param func2_name NULL terminated array of zero terminated strings of func2 identifers + * @param error pointer to a char* which is set to an error message if something goes wrong + * @param const_value a zero terminated array of values for the identifers from const_name + * @param opaque a pointer which will be passed to all functions from func1 and func2 + * @return the value of the expression + */ +double ff_eval2(const char *s, double *const_value, const char **const_name, + double (**func1)(void *, double), const char **func1_name, + double (**func2)(void *, double, double), char **func2_name, + void *opaque, const char **error); + +typedef struct ff_expr_s AVEvalExpr; + +/** + * Parses a expression. + * @param s expression as a zero terminated string for example "1+2^3+5*5+sin(2/3)" + * @param func1 NULL terminated array of function pointers for functions which take 1 argument + * @param func2 NULL terminated array of function pointers for functions which take 2 arguments + * @param const_name NULL terminated array of zero terminated strings of constant identifers for example {"PI", "E", 0} + * @param func1_name NULL terminated array of zero terminated strings of func1 identifers + * @param func2_name NULL terminated array of zero terminated strings of func2 identifers + * @param error pointer to a char* which is set to an error message if something goes wrong + * @return AVEvalExpr which must be freed with ff_eval_free by the user when it is not needed anymore + * NULL if anything went wrong + */ +AVEvalExpr * ff_parse(const char *s, const char **const_name, + double (**func1)(void *, double), const char **func1_name, + double (**func2)(void *, double, double), char **func2_name, + const char **error); +/** + * Evaluates a previously parsed expression. + * @param const_value a zero terminated array of values for the identifers from ff_parse const_name + * @param opaque a pointer which will be passed to all functions from func1 and func2 + * @return the value of the expression + */ +double ff_parse_eval(AVEvalExpr * e, double *const_value, void *opaque); +void ff_eval_free(AVEvalExpr * e); + +#endif /* FFMPEG_EVAL_H */