Update avcodec to 20080825
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27546 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* E-AC-3 decoder
|
||||
* Copyright (c) 2007 Bartlomiej Wolowiec <[email protected]>
|
||||
* 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;
|
||||
}
|
||||
@@ -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<y+4;j++)
|
||||
for(i=x;i<x+4;i++)
|
||||
{
|
||||
if (i+xoffset>=0 && i+xoffset<width &&
|
||||
j+yoffset>=0 && j+yoffset<height) {
|
||||
dst[j*dst_stride + i] = src[(j+yoffset)*src_stride + i+xoffset];
|
||||
}else{
|
||||
dst[j*dst_stride + i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cmv_decode_inter(CmvContext * s, const uint8_t *buf, const uint8_t *buf_end){
|
||||
const uint8_t *raw = buf + (s->avctx->width*s->avctx->height/16);
|
||||
int x,y,i;
|
||||
|
||||
i = 0;
|
||||
for(y=0; y<s->avctx->height/4; y++)
|
||||
for(x=0; x<s->avctx->width/4 && buf+i<buf_end; x++) {
|
||||
if (buf[i]==0xFF) {
|
||||
unsigned char *dst = s->frame.data[0] + (y*4)*s->frame.linesize[0] + x*4;
|
||||
if (raw+16<buf_end && *raw==0xFF) { /* intra */
|
||||
raw++;
|
||||
memcpy(dst, raw, 4);
|
||||
memcpy(dst+s->frame.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<buf_end) { /* inter using second-last frame as reference */
|
||||
int xoffset = (*raw & 0xF) - 7;
|
||||
int yoffset = ((*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; i<pal_start+pal_count && i<AVPALETTE_COUNT && buf+2<buf_end; i++) {
|
||||
s->palette[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"),
|
||||
};
|
||||
@@ -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 <libavutil/lzo.h>
|
||||
|
||||
#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<src_end) {
|
||||
|
||||
/* determine size1 and size2 */
|
||||
size1 = (src[0] & 3);
|
||||
if ( src[0] & 0x80 ) { // 1
|
||||
if (src[0] & 0x40 ) { // 11
|
||||
if ( src[0] & 0x20 ) { // 111
|
||||
if ( src[0] < 0xFC ) // !(111111)
|
||||
size1 = (((src[0] & 31) + 1) << 2);
|
||||
src++;
|
||||
size2 = 0;
|
||||
} else { // 110
|
||||
offset = ((src[0] & 0x10) << 12) + AV_RB16(&src[1]) + 1;
|
||||
size2 = ((src[0] & 0xC) << 6) + src[3] + 5;
|
||||
src += 4;
|
||||
}
|
||||
} else { // 10
|
||||
size1 = ( ( src[1] & 0xC0) >> 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_start<offset)
|
||||
return 0;
|
||||
size -= size2;
|
||||
run = FFMIN(size2, dst_end-dst);
|
||||
av_memcpy_backptr(dst, offset, run);
|
||||
dst += run;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode inter-frame
|
||||
* @return 0 on success, -1 on critical buffer underflow
|
||||
*/
|
||||
static int tgv_decode_inter(TgvContext * s, const uint8_t *buf, const uint8_t *buf_end){
|
||||
unsigned char *frame0_end = s->last_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; i<num_mvs; i++) {
|
||||
s->mv_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; i<num_blocks_packed; i++) {
|
||||
int tmp[4];
|
||||
for(j=0; j<4; j++)
|
||||
tmp[j] = get_bits(&gb, 8);
|
||||
for(j=0; j<16; j++)
|
||||
s->block_codebook[i][15-j] = tmp[get_bits(&gb, 2)];
|
||||
}
|
||||
|
||||
/* read vectors and build frame */
|
||||
for(y=0; y<s->avctx->height/4; y++)
|
||||
for(x=0; x<s->avctx->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 (offset<num_blocks_raw)
|
||||
src = blocks_raw + 16*offset;
|
||||
else if (offset-num_blocks_raw<num_blocks_packed)
|
||||
src = s->block_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; i<pal_count && i<AVPALETTE_COUNT && buf+2<buf_end; i++) {
|
||||
s->palette[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"),
|
||||
};
|
||||
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
* Copyright (C) 2007 Vitor Sessak <[email protected]>
|
||||
*
|
||||
* 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 <string.h>
|
||||
|
||||
#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<dim; i++) {
|
||||
dist += (a[i] - b[i])*(a[i] - b[i]);
|
||||
if (dist > 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; i<dim; i++)
|
||||
res[i] = ROUNDED_DIV(vect[i],div);
|
||||
else if (res != vect)
|
||||
memcpy(res, vect, dim*sizeof(int));
|
||||
|
||||
}
|
||||
|
||||
static int eval_error_cell(elbg_data *elbg, int *centroid, cell *cells)
|
||||
{
|
||||
int error=0;
|
||||
for (; cells; cells=cells->next)
|
||||
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; i<elbg->numCB; 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; i<dim; i++)
|
||||
newcentroid[idx][i] += points[tempcell->index*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; i<elbg->dim; 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; i<elbg->dim; 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; j<elbg->dim; 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; i<numpoints/8; i++) {
|
||||
k = (i*BIG_PRIME) % numpoints;
|
||||
memcpy(temp_points + i*dim, points + k*dim, dim*sizeof(int));
|
||||
}
|
||||
|
||||
ff_init_elbg(temp_points, dim, numpoints/8, codebook, numCB, 2*max_steps, closest_cb, rand_state);
|
||||
ff_do_elbg(temp_points, dim, numpoints/8, codebook, numCB, 2*max_steps, closest_cb, rand_state);
|
||||
|
||||
av_free(temp_points);
|
||||
|
||||
} else // If not, initialize the codebook with random positions
|
||||
for (i=0; i < numCB; i++)
|
||||
memcpy(codebook + i*dim, points + ((i*BIG_PRIME)%numpoints)*dim,
|
||||
dim*sizeof(int));
|
||||
|
||||
}
|
||||
|
||||
void ff_do_elbg(int *points, int dim, int numpoints, int *codebook,
|
||||
int numCB, int max_steps, int *closest_cb,
|
||||
AVRandomState *rand_state)
|
||||
{
|
||||
int dist;
|
||||
elbg_data elbg_d;
|
||||
elbg_data *elbg = &elbg_d;
|
||||
int i, j, k, last_error, steps=0;
|
||||
int *dist_cb = av_malloc(numpoints*sizeof(int));
|
||||
int *size_part = av_malloc(numCB*sizeof(int));
|
||||
cell *list_buffer = av_malloc(numpoints*sizeof(cell));
|
||||
cell *free_cells;
|
||||
|
||||
elbg->error = 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);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2007 Vitor Sessak <[email protected]>
|
||||
*
|
||||
* 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 */
|
||||
@@ -3,19 +3,21 @@
|
||||
*
|
||||
* Copyright (c) 2002-2004 Michael Niedermayer <[email protected]>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -28,7 +30,6 @@
|
||||
#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;
|
||||
@@ -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;
|
||||
@@ -107,7 +108,7 @@ static void filter181(int16_t *data, int width, int height, int stride){
|
||||
}
|
||||
|
||||
/**
|
||||
* guess the dc of blocks which dont have a undamaged dc
|
||||
* 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
|
||||
*/
|
||||
@@ -197,7 +198,7 @@ static void guess_dc(MpegEncContext *s, int16_t *dc, int w, int h, int stride, i
|
||||
*/
|
||||
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_y<h; b_y++){
|
||||
for(b_x=0; b_x<w-1; b_x++){
|
||||
@@ -209,13 +210,13 @@ static void h_block_filter(MpegEncContext *s, uint8_t *dst, int w, int h, int st
|
||||
int left_damage = left_status&(DC_ERROR|AC_ERROR|MV_ERROR);
|
||||
int right_damage= right_status&(DC_ERROR|AC_ERROR|MV_ERROR);
|
||||
int offset= b_x*8 + b_y*stride*8;
|
||||
int16_t *left_mv= s->current_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;
|
||||
&& 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;
|
||||
@@ -224,7 +225,7 @@ static void h_block_filter(MpegEncContext *s, uint8_t *dst, int w, int h, int st
|
||||
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;
|
||||
|
||||
@@ -257,7 +258,7 @@ static void h_block_filter(MpegEncContext *s, uint8_t *dst, int w, int h, int st
|
||||
*/
|
||||
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_y<h-1; b_y++){
|
||||
for(b_x=0; b_x<w; b_x++){
|
||||
@@ -269,13 +270,13 @@ static void v_block_filter(MpegEncContext *s, uint8_t *dst, int w, int h, int st
|
||||
int top_damage = top_status&(DC_ERROR|AC_ERROR|MV_ERROR);
|
||||
int bottom_damage= bottom_status&(DC_ERROR|AC_ERROR|MV_ERROR);
|
||||
int offset= b_x*8 + b_y*stride*8;
|
||||
int16_t *top_mv= s->current_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;
|
||||
&& 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;
|
||||
@@ -284,7 +285,7 @@ static void v_block_filter(MpegEncContext *s, uint8_t *dst, int w, int h, int st
|
||||
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;
|
||||
|
||||
@@ -346,7 +347,7 @@ static void guess_mv(MpegEncContext *s){
|
||||
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]);
|
||||
|
||||
@@ -378,8 +379,8 @@ 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];
|
||||
|
||||
@@ -474,7 +475,7 @@ int score_sum=0;
|
||||
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]);
|
||||
|
||||
@@ -493,22 +494,22 @@ int score_sum=0;
|
||||
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+1<mb_width && fixed[mb_xy+1]){
|
||||
int k;
|
||||
for(k=0; k<16; k++)
|
||||
score += ABS(src[k*s->linesize+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+1<mb_height && fixed[mb_xy+mb_stride]){
|
||||
int k;
|
||||
for(k=0; k<16; k++)
|
||||
score += ABS(src[k+s->linesize*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
|
||||
@@ -560,7 +561,12 @@ static int is_intra_more_likely(MpegEncContext *s){
|
||||
undamaged_count++;
|
||||
}
|
||||
|
||||
if(undamaged_count < 5) return 0; //allmost all MBs damaged -> use temporal prediction
|
||||
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;
|
||||
@@ -578,7 +584,7 @@ static int is_intra_more_likely(MpegEncContext *s){
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
@@ -652,7 +663,7 @@ void ff_er_add_slice(MpegEncContext *s, int startx, int starty, int endx, int en
|
||||
|
||||
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;
|
||||
@@ -661,30 +672,34 @@ void ff_er_add_slice(MpegEncContext *s, int startx, int starty, int endx, int en
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
if(s->current_picture.motion_val[0] == NULL){
|
||||
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_y<s->mb_height; mb_y++){
|
||||
for(mb_x=0; mb_x<s->mb_width; mb_x++){
|
||||
@@ -754,7 +769,7 @@ void ff_er_frame_end(MpegEncContext *s){
|
||||
|
||||
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&AC_END) || (error1&DC_END) || (error1&MV_END))){ //end & uninit
|
||||
end_ok=0;
|
||||
}
|
||||
|
||||
@@ -815,6 +830,17 @@ void ff_er_frame_end(MpegEncContext *s){
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
dc_error= ac_error= mv_error=0;
|
||||
for(i=0; i<s->mb_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 */
|
||||
@@ -843,19 +869,19 @@ void ff_er_frame_end(MpegEncContext *s){
|
||||
|
||||
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]);
|
||||
@@ -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_y<s->mb_height; mb_y++){
|
||||
for(mb_x=0; mb_x<s->mb_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];
|
||||
@@ -882,7 +908,7 @@ void ff_er_frame_end(MpegEncContext *s){
|
||||
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;
|
||||
@@ -930,7 +956,7 @@ void ff_er_frame_end(MpegEncContext *s){
|
||||
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,18 +976,18 @@ 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);
|
||||
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 */
|
||||
@@ -1005,7 +1031,7 @@ ec_clean:
|
||||
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;
|
||||
|
||||
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
* Escape 124 Video Decoder
|
||||
* Copyright (C) 2008 Eli Friedman ([email protected])
|
||||
*
|
||||
* 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"),
|
||||
};
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
/*
|
||||
* simple arithmetic expression evaluator
|
||||
*
|
||||
* Copyright (c) 2002 Michael Niedermayer <[email protected]>
|
||||
* Copyright (c) 2002-2006 Michael Niedermayer <[email protected]>
|
||||
* Copyright (c) 2006 Oded Shimon <[email protected]>
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -35,17 +37,14 @@
|
||||
#include <math.h>
|
||||
|
||||
#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;
|
||||
/** 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) {
|
||||
|
||||
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++;
|
||||
}
|
||||
}
|
||||
p->stack[ p->stack_index++ ]= d;
|
||||
//printf("push %f\n", d); fflush(stdout);
|
||||
}
|
||||
|
||||
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=='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);
|
||||
p->s++; // ")"
|
||||
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;
|
||||
}
|
||||
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++; // ")"
|
||||
|
||||
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;
|
||||
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 {
|
||||
int error=1;
|
||||
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);
|
||||
return d;
|
||||
}
|
||||
|
||||
static void evalPow(Parser *p){
|
||||
int neg= 0;
|
||||
if(p->s[0]=='+') p->s++;
|
||||
|
||||
if(p->s[0]=='-'){
|
||||
neg= 1;
|
||||
p->s++;
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
static AVEvalExpr * parse_pow(Parser *p, int *sign){
|
||||
*sign= (*p->s == '+') - (*p->s == '-');
|
||||
p->s += *sign&1;
|
||||
return parse_primary(p);
|
||||
}
|
||||
|
||||
if(neg) push(p, -pop(p));
|
||||
}
|
||||
|
||||
static void evalFactor(Parser *p){
|
||||
evalPow(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;
|
||||
AVEvalExpr * e;
|
||||
char w[strlen(s) + 1], * wp = w;
|
||||
|
||||
p.stack_index=0;
|
||||
p.s= s;
|
||||
p.const_value= const_value;
|
||||
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;
|
||||
p.error= error;
|
||||
|
||||
evalExpression(&p);
|
||||
return pop(&p);
|
||||
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
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* simple arithmetic expression evaluator
|
||||
*
|
||||
* Copyright (c) 2002 Michael Niedermayer <[email protected]>
|
||||
*
|
||||
* 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 */
|
||||
Reference in New Issue
Block a user