Go2Meeting decoder

ELS and ePIC decoder courtesy of Maxim Poliakovski,
cleanup and integration by Diego Biurrun.

Signed-off-by: Diego Biurrun <diego@biurrun.de>
Signed-off-by: Luca Barbato <lu_zero@gentoo.org>
pull/267/head
Kostya Shishkov 10 years ago committed by Luca Barbato
parent e2bd03a14a
commit 08c2d8f0aa
  1. 1
      Changelog
  2. 2
      doc/general.texi
  3. 2
      libavcodec/Makefile
  4. 422
      libavcodec/elsdec.c
  5. 60
      libavcodec/elsdec.h
  6. 755
      libavcodec/g2meet.c
  7. 4
      libavcodec/version.h

@ -37,6 +37,7 @@ version <next>:
- Hap decoder and encoder
- DirectDraw Surface image/texture decoder
- rewritten ASF demuxer
- Go2Meeting decoding support
version 11:

@ -607,6 +607,8 @@ following image formats are supported:
@tab Sorenson H.263 used in Flash
@item Forward Uncompressed @tab @tab X
@item Fraps @tab @tab X
@item Go2Meeting @tab @tab X
@tab fourcc: G2M2, G2M3
@item Go2Webinar @tab @tab X
@tab fourcc: G2M4
@item H.261 @tab X @tab X

@ -214,7 +214,7 @@ OBJS-$(CONFIG_FLIC_DECODER) += flicvideo.o
OBJS-$(CONFIG_FOURXM_DECODER) += 4xm.o
OBJS-$(CONFIG_FRAPS_DECODER) += fraps.o
OBJS-$(CONFIG_FRWU_DECODER) += frwu.o
OBJS-$(CONFIG_G2M_DECODER) += g2meet.o
OBJS-$(CONFIG_G2M_DECODER) += g2meet.o elsdec.o
OBJS-$(CONFIG_G723_1_DECODER) += g723_1.o acelp_vectors.o \
celp_filters.o
OBJS-$(CONFIG_GIF_DECODER) += gifdec.o lzw.o

@ -0,0 +1,422 @@
/*
* ELS (Entropy Logarithmic-Scale) decoder
*
* Copyright (c) 2013 Maxim Poliakovski
*
* This file is part of Libav.
*
* Libav 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.
*
* Libav 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 Libav; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* Entropy Logarithmic-Scale binary arithmetic decoder
*/
#include <math.h>
#include <stdint.h>
#include "libavutil/common.h"
#include "libavutil/intreadwrite.h"
#include "avcodec.h"
#include "elsdec.h"
/* ELS coder constants and structures. */
#define ELS_JOTS_PER_BYTE 36
#define ELS_MAX (1 << 24)
#define RUNG_SPACE (64 * sizeof(ElsRungNode))
/* ELS coder tables. */
static const struct Ladder {
int8_t AMps;
int8_t ALps;
uint8_t next0;
uint8_t next1;
} Ladder[174] = {
{ -6, -5, 2, 1 },
{ -2, -12, 3, 6 },
{ -2, -12, 4, 6 },
{ -1, -16, 7, 5 },
{ -1, -16, 8, 10 },
{ -5, -6, 11, 9 },
{ -6, -5, 10, 5 },
{ -1, -18, 13, 11 },
{ -1, -18, 12, 14 },
{ -6, -5, 15, 18 },
{ -5, -6, 14, 9 },
{ -3, -8, 17, 15 },
{ -1, -20, 20, 16 },
{ -1, -20, 23, 17 },
{ -3, -8, 16, 18 },
{ -5, -6, 19, 26 },
{ -3, -9, 22, 24 },
{ -3, -9, 21, 19 },
{ -5, -6, 24, 26 },
{ -4, -7, 27, 25 },
{ -1, -22, 34, 28 },
{ -2, -11, 29, 27 },
{ -2, -11, 28, 30 },
{ -1, -22, 39, 29 },
{ -4, -7, 30, 32 },
{ -6, -5, 33, 31 },
{ -6, -5, 32, 25 },
{ -3, -8, 35, 33 },
{ -2, -12, 36, 38 },
{ -2, -12, 37, 35 },
{ -3, -8, 38, 40 },
{ -6, -5, 41, 48 },
{ -6, -5, 40, 31 },
{ -5, -6, 43, 41 },
{ -1, -24, 94, 42 },
{ -3, -8, 45, 43 },
{ -2, -12, 42, 44 },
{ -2, -12, 47, 45 },
{ -3, -8, 44, 46 },
{ -1, -24, 125, 47 },
{ -5, -6, 46, 48 },
{ -6, -5, 49, 49 },
{ -2, -13, 152, 164 },
{ -4, -7, 51, 49 },
{ -3, -9, 164, 168 },
{ -3, -9, 55, 51 },
{ -4, -7, 168, 170 },
{ -2, -13, 67, 55 },
{ -6, -5, 170, 49 },
{ -6, -5, 51, 170 },
{ -1, -72, 50, 74 },
{ -4, -7, 53, 49 },
{ -1, -61, 50, 74 },
{ -3, -8, 55, 49 },
{ -1, -51, 52, 76 },
{ -3, -9, 57, 51 },
{ -1, -46, 54, 76 },
{ -2, -10, 59, 53 },
{ -1, -43, 56, 78 },
{ -2, -11, 61, 53 },
{ -1, -41, 58, 80 },
{ -2, -12, 63, 55 },
{ -1, -39, 60, 82 },
{ -2, -12, 65, 55 },
{ -1, -37, 62, 84 },
{ -2, -13, 67, 57 },
{ -1, -36, 64, 86 },
{ -1, -14, 69, 59 },
{ -1, -35, 66, 88 },
{ -1, -14, 71, 59 },
{ -1, -34, 68, 90 },
{ -1, -15, 73, 61 },
{ -1, -33, 70, 92 },
{ -1, -15, 75, 61 },
{ -1, -32, 72, 94 },
{ -1, -15, 77, 63 },
{ -1, -31, 74, 96 },
{ -1, -16, 79, 65 },
{ -1, -31, 76, 98 },
{ -1, -16, 81, 67 },
{ -1, -30, 78, 100 },
{ -1, -17, 83, 67 },
{ -1, -29, 80, 102 },
{ -1, -17, 85, 69 },
{ -1, -29, 82, 104 },
{ -1, -18, 87, 71 },
{ -1, -28, 84, 104 },
{ -1, -18, 89, 73 },
{ -1, -28, 86, 108 },
{ -1, -18, 91, 73 },
{ -1, -27, 88, 108 },
{ -1, -19, 93, 75 },
{ -1, -27, 90, 112 },
{ -1, -19, 95, 77 },
{ -1, -26, 92, 112 },
{ -1, -20, 97, 79 },
{ -1, -26, 94, 114 },
{ -1, -20, 99, 81 },
{ -1, -25, 96, 116 },
{ -1, -20, 101, 83 },
{ -1, -25, 98, 118 },
{ -1, -21, 103, 83 },
{ -1, -24, 100, 120 },
{ -1, -21, 105, 85 },
{ -1, -24, 102, 122 },
{ -1, -22, 107, 87 },
{ -1, -23, 104, 124 },
{ -1, -22, 109, 89 },
{ -1, -23, 106, 126 },
{ -1, -22, 111, 91 },
{ -1, -22, 108, 128 },
{ -1, -23, 113, 93 },
{ -1, -22, 110, 130 },
{ -1, -23, 115, 95 },
{ -1, -22, 112, 132 },
{ -1, -24, 117, 97 },
{ -1, -21, 114, 134 },
{ -1, -24, 119, 99 },
{ -1, -21, 116, 136 },
{ -1, -25, 121, 101 },
{ -1, -20, 118, 136 },
{ -1, -25, 123, 103 },
{ -1, -20, 120, 138 },
{ -1, -26, 125, 105 },
{ -1, -20, 122, 140 },
{ -1, -26, 127, 107 },
{ -1, -19, 124, 142 },
{ -1, -27, 129, 107 },
{ -1, -19, 126, 144 },
{ -1, -27, 131, 111 },
{ -1, -18, 128, 146 },
{ -1, -28, 133, 111 },
{ -1, -18, 130, 146 },
{ -1, -28, 135, 115 },
{ -1, -18, 132, 148 },
{ -1, -29, 137, 115 },
{ -1, -17, 134, 150 },
{ -1, -29, 139, 117 },
{ -1, -17, 136, 152 },
{ -1, -30, 141, 119 },
{ -1, -16, 138, 152 },
{ -1, -31, 143, 121 },
{ -1, -16, 140, 154 },
{ -1, -31, 145, 123 },
{ -1, -15, 142, 156 },
{ -1, -32, 147, 125 },
{ -1, -15, 144, 158 },
{ -1, -33, 149, 127 },
{ -1, -15, 146, 158 },
{ -1, -34, 151, 129 },
{ -1, -14, 148, 160 },
{ -1, -35, 153, 131 },
{ -1, -14, 150, 160 },
{ -1, -36, 155, 133 },
{ -2, -13, 152, 162 },
{ -1, -37, 157, 135 },
{ -2, -12, 154, 164 },
{ -1, -39, 159, 137 },
{ -2, -12, 156, 164 },
{ -1, -41, 161, 139 },
{ -2, -11, 158, 166 },
{ -1, -43, 163, 141 },
{ -2, -10, 160, 166 },
{ -1, -46, 165, 143 },
{ -3, -9, 162, 168 },
{ -1, -51, 167, 143 },
{ -3, -8, 164, 170 },
{ -1, -61, 169, 145 },
{ -4, -7, 166, 170 },
{ -1, -72, 169, 145 },
{ -6, -5, 168, 49 },
{ 0, -108, 171, 171 },
{ 0, -108, 172, 172 },
{ -6, -5, 173, 173 },
};
static const uint32_t els_exp_tab[ELS_JOTS_PER_BYTE * 4 + 1] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 1, 1,
1, 2, 2, 2, 3, 4, 4, 5,
6, 7, 8, 10, 11, 13, 16, 18,
21, 25, 29, 34, 40, 47, 54, 64,
74, 87, 101, 118, 138, 161, 188, 219,
256, 298, 348, 406, 474, 552, 645, 752,
877, 1024, 1194, 1393, 1625, 1896, 2211, 2580,
3010, 3511, 4096, 4778, 5573, 6501, 7584, 8847,
10321, 12040, 14045, 16384, 19112, 22295, 26007, 30339,
35391, 41285, 48160, 56180, 65536, 76288, 89088, 103936,
121344, 141312, 165120, 192512, 224512, 262144, 305664, 356608,
416000, 485376, 566016, 660480, 770560, 898816, 1048576, 1223168,
1426688, 1664256, 1941504, 2264832, 2642176, 3082240, 3595520, 4194304,
4892672, 5707520, 6657792, 7766784, 9060096, 10568960, 12328960, 14382080,
16777216,
};
void ff_els_decoder_init(ElsDecCtx *ctx, const uint8_t *in, size_t data_size)
{
int nbytes;
/* consume up to 3 bytes from the input data */
if (data_size >= 3) {
ctx->x = AV_RB24(in);
nbytes = 3;
} else if (data_size == 2) {
ctx->x = AV_RB16(in);
nbytes = 2;
} else {
ctx->x = *in;
nbytes = 1;
}
ctx->in_buf = in + nbytes;
ctx->data_size = data_size - nbytes;
ctx->err = 0;
ctx->j = ELS_JOTS_PER_BYTE;
ctx->t = ELS_MAX;
ctx->diff = FFMIN(ELS_MAX - ctx->x,
ELS_MAX - els_exp_tab[ELS_JOTS_PER_BYTE * 4 - 1]);
}
void ff_els_decoder_uninit(ElsUnsignedRung *rung)
{
av_free(rung->rem_rung_list);
}
static int els_import_byte(ElsDecCtx *ctx)
{
if (!ctx->data_size) {
ctx->err = AVERROR_EOF;
return AVERROR_EOF;
}
ctx->x = (ctx->x << 8) | *ctx->in_buf++;
ctx->data_size--;
ctx->j += ELS_JOTS_PER_BYTE;
ctx->t <<= 8;
return 0;
}
int ff_els_decode_bit(ElsDecCtx *ctx, uint8_t *rung)
{
int z, bit, ret;
const uint32_t *pAllowable = &els_exp_tab[ELS_JOTS_PER_BYTE * 3];
if (ctx->err)
return 0;
z = pAllowable[ctx->j + Ladder[*rung].ALps];
ctx->t -= z;
ctx->diff -= z;
if (ctx->diff > 0)
return *rung & 1; /* shortcut for x < t > pAllowable[j - 1] */
if (ctx->t > ctx->x) { /* decode most probable symbol (MPS) */
ctx->j += Ladder[*rung].AMps;
while (ctx->t > pAllowable[ctx->j])
ctx->j++;
if (ctx->j <= 0) { /* MPS: import one byte from bytestream. */
ret = els_import_byte(ctx);
if (ret < 0)
return ret;
}
z = ctx->t;
bit = *rung & 1;
*rung = Ladder[*rung].next0;
} else { /* decode less probable symbol (LPS) */
ctx->x -= ctx->t;
ctx->t = z;
ctx->j += Ladder[*rung].ALps;
if (ctx->j <= 0) {
/* LPS: import one byte from bytestream. */
z <<= 8;
ret = els_import_byte(ctx);
if (ret < 0)
return ret;
if (ctx->j <= 0) {
/* LPS: import second byte from bytestream. */
z <<= 8;
ret = els_import_byte(ctx);
if (ret < 0)
return ret;
while (pAllowable[ctx->j - 1] >= z)
ctx->j--;
}
}
bit = !(*rung & 1);
*rung = Ladder[*rung].next1;
}
ctx->diff = FFMIN(z - ctx->x, z - pAllowable[ctx->j - 1]);
return bit;
}
unsigned ff_els_decode_unsigned(ElsDecCtx *ctx, ElsUnsignedRung *ur)
{
int i, n, r, bit;
ElsRungNode *rung_node;
if (ctx->err)
return 0;
/* decode unary prefix */
for (n = 0; n < ELS_EXPGOLOMB_LEN + 1; n++)
if (ff_els_decode_bit(ctx, &ur->prefix_rung[n]))
break;
/* handle the error/overflow case */
if (ctx->err || n >= ELS_EXPGOLOMB_LEN) {
ctx->err = AVERROR(EOVERFLOW);
return 0;
}
/* handle the zero case */
if (!n)
return 0;
/* initialize probability tree */
if (!ur->rem_rung_list) {
ur->rem_rung_list = av_realloc(NULL, RUNG_SPACE);
if (!ur->rem_rung_list) {
ctx->err = AVERROR(ENOMEM);
return 0;
}
memset(ur->rem_rung_list, 0, RUNG_SPACE);
ur->rung_list_size = RUNG_SPACE;
ur->avail_index = ELS_EXPGOLOMB_LEN;
}
/* decode the remainder */
for (i = 0, r = 0, bit = 0; i < n; i++) {
if (!i)
rung_node = &ur->rem_rung_list[n];
else {
if (!rung_node->next_index) {
if (ur->rung_list_size <= (ur->avail_index + 2) * sizeof(ElsRungNode)) {
// remember rung_node position
ptrdiff_t pos = rung_node - ur->rem_rung_list;
ur->rem_rung_list = av_realloc(ur->rem_rung_list,
ur->rung_list_size +
RUNG_SPACE);
if (!ur->rem_rung_list) {
av_free(ur->rem_rung_list);
ctx->err = AVERROR(ENOMEM);
return 0;
}
memset((uint8_t *) ur->rem_rung_list + ur->rung_list_size, 0,
RUNG_SPACE);
ur->rung_list_size += RUNG_SPACE;
// restore rung_node position in the new list
rung_node = &ur->rem_rung_list[pos];
}
rung_node->next_index = ur->avail_index;
ur->avail_index += 2;
}
rung_node = &ur->rem_rung_list[rung_node->next_index + bit];
}
bit = ff_els_decode_bit(ctx, &rung_node->rung);
if (ctx->err)
return bit;
r = (r << 1) + bit;
}
return (1 << n) - 1 + r; /* make value from exp golomb code */
}

@ -0,0 +1,60 @@
/*
* ELS (Entropy Logarithmic-Scale) decoder
*
* Copyright (c) 2013 Maxim Poliakovski
*
* This file is part of Libav.
*
* Libav 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.
*
* Libav 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 Libav; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* Entropy Logarithmic-Scale binary arithmetic coder
*/
#ifndef AVCODEC_ELSDEC_H
#define AVCODEC_ELSDEC_H
#include <stdint.h>
#include <sys/types.h>
#define ELS_EXPGOLOMB_LEN 10
typedef struct ElsDecCtx {
const uint8_t *in_buf;
unsigned x;
size_t data_size;
int j, t, diff, err;
} ElsDecCtx;
typedef struct ElsRungNode {
uint8_t rung;
uint16_t next_index;
} ElsRungNode;
typedef struct ElsUnsignedRung {
uint8_t prefix_rung[ELS_EXPGOLOMB_LEN + 1];
ElsRungNode *rem_rung_list;
size_t rung_list_size;
uint16_t avail_index;
} ElsUnsignedRung;
void ff_els_decoder_init(ElsDecCtx *ctx, const uint8_t *in, size_t data_size);
void ff_els_decoder_uninit(ElsUnsignedRung *rung);
int ff_els_decode_bit(ElsDecCtx *ctx, unsigned char *rung);
unsigned ff_els_decode_unsigned(ElsDecCtx *ctx, ElsUnsignedRung *ur);
#endif /* AVCODEC_ELSDEC_H */

@ -1,6 +1,7 @@
/*
* Go2Webinar decoder
* Go2Webinar / Go2Meeting decoder
* Copyright (c) 2012 Konstantin Shishkov
* Copyright (c) 2013 Maxim Poliakovski
*
* This file is part of Libav.
*
@ -21,7 +22,7 @@
/**
* @file
* Go2Webinar decoder
* Go2Webinar / Go2Meeting decoder
*/
#include <inttypes.h>
@ -32,12 +33,16 @@
#include "avcodec.h"
#include "blockdsp.h"
#include "bytestream.h"
#include "elsdec.h"
#include "get_bits.h"
#include "idctdsp.h"
#include "internal.h"
#include "jpegtables.h"
#include "mjpeg.h"
#define EPIC_PIX_STACK_SIZE 1024
#define EPIC_PIX_STACK_MAX (EPIC_PIX_STACK_SIZE - 1)
enum ChunkType {
DISPLAY_INFO = 0xC8,
TILE_DATA,
@ -74,6 +79,42 @@ static const uint8_t chroma_quant[64] = {
50, 50, 50, 50, 50, 50, 50, 50,
};
typedef struct ePICPixListElem {
struct ePICPixListElem *next;
uint32_t pixel;
uint8_t rung;
} ePICPixListElem;
typedef struct ePICPixHashElem {
uint32_t pix_id;
struct ePICPixListElem *list;
} ePICPixHashElem;
#define EPIC_HASH_SIZE 256
typedef struct ePICPixHash {
ePICPixHashElem *bucket[EPIC_HASH_SIZE];
int bucket_size[EPIC_HASH_SIZE];
int bucket_fill[EPIC_HASH_SIZE];
} ePICPixHash;
typedef struct ePICContext {
ElsDecCtx els_ctx;
int next_run_pos;
ElsUnsignedRung unsigned_rung;
uint8_t W_flag_rung;
uint8_t N_flag_rung;
uint8_t W_ctx_rung[256];
uint8_t N_ctx_rung[512];
uint8_t nw_pred_rung[256];
uint8_t ne_pred_rung[256];
uint8_t prev_row_rung[14];
uint8_t runlen_zeroes[14];
uint8_t runlen_one;
int stack_pos;
uint32_t stack[EPIC_PIX_STACK_SIZE];
ePICPixHash hash;
} ePICContext;
typedef struct JPGContext {
BlockDSPContext bdsp;
IDCTDSPContext idsp;
@ -87,7 +128,9 @@ typedef struct JPGContext {
} JPGContext;
typedef struct G2MContext {
ePICContext ec;
JPGContext jc;
int version;
int compression;
@ -101,8 +144,9 @@ typedef struct G2MContext {
uint8_t *framebuf;
int framebuf_stride, old_width, old_height;
uint8_t *synth_tile, *jpeg_tile;
int tile_stride, old_tile_w, old_tile_h;
uint8_t *synth_tile, *jpeg_tile, *epic_buf, *epic_buf_base;
int tile_stride, epic_buf_stride, old_tile_w, old_tile_h;
int swapuv;
uint8_t *kempf_buf, *kempf_flags;
@ -229,11 +273,11 @@ static int jpg_decode_block(JPGContext *c, GetBitContext *gb,
return 0;
}
static inline void yuv2rgb(uint8_t *out, int Y, int U, int V)
static inline void yuv2rgb(uint8_t *out, int ridx, int Y, int U, int V)
{
out[0] = av_clip_uint8(Y + ( 91881 * V + 32768 >> 16));
out[1] = av_clip_uint8(Y + (-22554 * U - 46802 * V + 32768 >> 16));
out[2] = av_clip_uint8(Y + (116130 * U + 32768 >> 16));
out[ridx] = av_clip_uint8(Y + (91881 * V + 32768 >> 16));
out[1] = av_clip_uint8(Y + (-22554 * U - 46802 * V + 32768 >> 16));
out[2 - ridx] = av_clip_uint8(Y + (116130 * U + 32768 >> 16));
}
static int jpg_decode_data(JPGContext *c, int width, int height,
@ -247,6 +291,7 @@ static int jpg_decode_data(JPGContext *c, int width, int height,
int bx, by;
int unesc_size;
int ret;
const int ridx = swapuv ? 2 : 0;
if ((ret = av_reallocp(&c->buf,
src_size + FF_INPUT_BUFFER_PADDING_SIZE)) < 0)
@ -298,9 +343,9 @@ static int jpg_decode_data(JPGContext *c, int width, int height,
int Y, U, V;
Y = c->block[(j >> 3) * 2 + (i >> 3)][(i & 7) + (j & 7) * 8];
U = c->block[4 ^ swapuv][(i >> 1) + (j >> 1) * 8] - 128;
V = c->block[5 ^ swapuv][(i >> 1) + (j >> 1) * 8] - 128;
yuv2rgb(out + i * 3, Y, U, V);
U = c->block[4][(i >> 1) + (j >> 1) * 8] - 128;
V = c->block[5][(i >> 1) + (j >> 1) * 8] - 128;
yuv2rgb(out + i * 3, ridx, Y, U, V);
}
}
@ -317,6 +362,659 @@ static int jpg_decode_data(JPGContext *c, int width, int height,
return 0;
}
#define LOAD_NEIGHBOURS(x) \
W = curr_row[(x) - 1]; \
N = above_row[(x)]; \
WW = curr_row[(x) - 2]; \
NW = above_row[(x) - 1]; \
NE = above_row[(x) + 1]; \
NN = above2_row[(x)]; \
NNW = above2_row[(x) - 1]; \
NWW = above_row[(x) - 2]; \
NNE = above2_row[(x) + 1]
#define UPDATE_NEIGHBOURS(x) \
NNW = NN; \
NN = NNE; \
NWW = NW; \
NW = N; \
N = NE; \
NE = above_row[(x) + 1]; \
NNE = above2_row[(x) + 1]
#define R_shift 16
#define G_shift 8
#define B_shift 0
static inline int log2_ceil(uint32_t x)
{
int c = 0;
for (--x; x > 0; x >>= 1)
c++;
return c;
}
/* improved djb2 hash from http://www.cse.yorku.ca/~oz/hash.html */
static int djb2_hash(uint32_t key)
{
int h = 5381;
h = (h * 33) ^ ((key >> 24) & 0xFF); // xxx: probably not needed at all
h = (h * 33) ^ ((key >> 16) & 0xFF);
h = (h * 33) ^ ((key >> 8) & 0xFF);
h = (h * 33) ^ (key & 0xFF);
return h & (EPIC_HASH_SIZE - 1);
}
static void epic_hash_init(ePICPixHash *hash)
{
memset(hash, 0, sizeof(*hash));
}
static ePICPixHashElem *epic_hash_find(const ePICPixHash *hash, uint32_t key)
{
int i, idx = djb2_hash(key);
ePICPixHashElem *bucket = hash->bucket[idx];
for (i = 0; i < hash->bucket_fill[idx]; i++)
if (bucket[i].pix_id == key)
return &bucket[i];
return NULL;
}
static ePICPixHashElem *epic_hash_add(ePICPixHash *hash, uint32_t key)
{
ePICPixHashElem *bucket, *ret;
int idx = djb2_hash(key);
if (hash->bucket_size[idx] > INT_MAX / sizeof(**hash->bucket))
return NULL;
if (!(hash->bucket_fill[idx] < hash->bucket_size[idx])) {
int new_size = hash->bucket_size[idx] + 16;
bucket = av_realloc(hash->bucket[idx], new_size * sizeof(*bucket));
if (!bucket)
return NULL;
hash->bucket[idx] = bucket;
hash->bucket_size[idx] = new_size;
}
ret = &hash->bucket[idx][hash->bucket_fill[idx]++];
memset(ret, 0, sizeof(*ret));
ret->pix_id = key;
return ret;
}
static int epic_add_pixel_to_cache(ePICPixHash *hash, uint32_t key, uint32_t pix)
{
ePICPixListElem *new_elem;
ePICPixHashElem *hash_elem = epic_hash_find(hash, key);
if (!hash_elem) {
if (!(hash_elem = epic_hash_add(hash, key)))
return AVERROR(ENOMEM);
}
new_elem = av_mallocz(sizeof(*new_elem));
if (!new_elem)
return AVERROR(ENOMEM);
new_elem->pixel = pix;
new_elem->next = hash_elem->list;
hash_elem->list = new_elem;
return 0;
}
static inline int epic_cache_entries_for_pixel(const ePICPixHash *hash,
uint32_t pix)
{
ePICPixHashElem *hash_elem = epic_hash_find(hash, pix);
if (hash_elem != NULL && hash_elem->list != NULL)
return 1;
return 0;
}
static void epic_free_pixel_cache(ePICPixHash *hash)
{
int i, j;
for (i = 0; i < EPIC_HASH_SIZE; i++) {
for (j = 0; j < hash->bucket_fill[i]; j++) {
ePICPixListElem *list_elem = hash->bucket[i][j].list;
while (list_elem) {
ePICPixListElem *tmp = list_elem->next;
av_free(list_elem);
list_elem = tmp;
}
}
av_freep(&hash->bucket[i]);
hash->bucket_size[i] =
hash->bucket_fill[i] = 0;
}
}
static inline int is_pixel_on_stack(const ePICContext *dc, uint32_t pix)
{
int i;
for (i = 0; i < dc->stack_pos; i++)
if (dc->stack[i] == pix)
break;
return i != dc->stack_pos;
}
#define TOSIGNED(val) (((val) >> 1) ^ -((val) & 1))
static inline int epic_decode_component_pred(ePICContext *dc,
int N, int W, int NW)
{
unsigned delta = ff_els_decode_unsigned(&dc->els_ctx, &dc->unsigned_rung);
return mid_pred(N, N + W - NW, W) - TOSIGNED(delta);
}
static uint32_t epic_decode_pixel_pred(ePICContext *dc, int x, int y,
const uint32_t *curr_row,
const uint32_t *above_row)
{
uint32_t N, W, NW, pred;
unsigned delta;
int GN, GW, GNW, R, G, B;
if (x && y) {
W = curr_row[x - 1];
N = above_row[x];
NW = above_row[x - 1];
GN = (N >> G_shift) & 0xFF;
GW = (W >> G_shift) & 0xFF;
GNW = (NW >> G_shift) & 0xFF;
G = epic_decode_component_pred(dc, GN, GW, GNW);
R = G + epic_decode_component_pred(dc,
((N >> R_shift) & 0xFF) - GN,
((W >> R_shift) & 0xFF) - GW,
((NW >> R_shift) & 0xFF) - GNW);
B = G + epic_decode_component_pred(dc,
((N >> B_shift) & 0xFF) - GN,
((W >> B_shift) & 0xFF) - GW,
((NW >> B_shift) & 0xFF) - GNW);
} else {
if (x)
pred = curr_row[x - 1];
else
pred = above_row[x];
delta = ff_els_decode_unsigned(&dc->els_ctx, &dc->unsigned_rung);
R = ((pred >> R_shift) & 0xFF) - TOSIGNED(delta);
delta = ff_els_decode_unsigned(&dc->els_ctx, &dc->unsigned_rung);
G = ((pred >> G_shift) & 0xFF) - TOSIGNED(delta);
delta = ff_els_decode_unsigned(&dc->els_ctx, &dc->unsigned_rung);
B = ((pred >> B_shift) & 0xFF) - TOSIGNED(delta);
}
return (R << R_shift) | (G << G_shift) | (B << B_shift);
}
static int epic_predict_pixel(ePICContext *dc, uint8_t *rung,
uint32_t *pPix, uint32_t pix)
{
if (!ff_els_decode_bit(&dc->els_ctx, rung)) {
*pPix = pix;
return 1;
}
dc->stack[dc->stack_pos++ & EPIC_PIX_STACK_MAX] = pix;
return 0;
}
static int epic_handle_edges(ePICContext *dc, int x, int y,
const uint32_t *curr_row,
const uint32_t *above_row, uint32_t *pPix)
{
uint32_t pix;
if (!x && !y) { /* special case: top-left pixel */
/* the top-left pixel is coded independently with 3 unsigned numbers */
*pPix = (ff_els_decode_unsigned(&dc->els_ctx, &dc->unsigned_rung) << R_shift) |
(ff_els_decode_unsigned(&dc->els_ctx, &dc->unsigned_rung) << G_shift) |
(ff_els_decode_unsigned(&dc->els_ctx, &dc->unsigned_rung) << B_shift);
return 1;
}
if (x) { /* predict from W first */
pix = curr_row[x - 1];
if (epic_predict_pixel(dc, &dc->W_flag_rung, pPix, pix))
return 1;
}
if (y) { /* then try to predict from N */
pix = above_row[x];
if (!dc->stack_pos || dc->stack[0] != pix) {
if (epic_predict_pixel(dc, &dc->N_flag_rung, pPix, pix))
return 1;
}
}
return 0;
}
static int epic_decode_run_length(ePICContext *dc, int x, int y, int tile_width,
const uint32_t *curr_row,
const uint32_t *above_row,
const uint32_t *above2_row,
uint32_t *pPix, int *pRun)
{
int idx, got_pixel = 0, WWneW, old_WWneW = 0;
uint32_t W, WW, N, NN, NW, NE, NWW, NNW, NNE;
*pRun = 0;
LOAD_NEIGHBOURS(x);
if (dc->next_run_pos == x) {
/* can't reuse W for the new pixel in this case */
WWneW = 1;
} else {
idx = (WW != W) << 7 |
(NW != W) << 6 |
(N != NE) << 5 |
(NW != N) << 4 |
(NWW != NW) << 3 |
(NNE != NE) << 2 |
(NN != N) << 1 |
(NNW != NW);
WWneW = ff_els_decode_bit(&dc->els_ctx, &dc->W_ctx_rung[idx]);
}
if (WWneW)
dc->stack[dc->stack_pos++ & EPIC_PIX_STACK_MAX] = W;
else {
*pPix = W;
got_pixel = 1;
}
do {
int NWneW = 1;
if (got_pixel) // pixel value already known (derived from either W or N)
NWneW = *pPix != N;
else { // pixel value is unknown and will be decoded later
NWneW = *pRun ? NWneW : NW != W;
/* TODO: RFC this mess! */
switch (((NW != N) << 2) | (NWneW << 1) | WWneW) {
case 0:
break; // do nothing here
case 3:
case 5:
case 6:
case 7:
if (!is_pixel_on_stack(dc, N)) {
idx = WWneW << 8 |
(*pRun ? old_WWneW : WW != W) << 7 |
NWneW << 6 |
(N != NE) << 5 |
(NW != N) << 4 |
(NWW != NW) << 3 |
(NNE != NE) << 2 |
(NN != N) << 1 |
(NNW != NW);
if (!ff_els_decode_bit(&dc->els_ctx, &dc->N_ctx_rung[idx])) {
NWneW = 0;
*pPix = N;
got_pixel = 1;
break;
}
}
/* fall through */
default:
NWneW = 1;
old_WWneW = WWneW;
if (!is_pixel_on_stack(dc, N))
dc->stack[dc->stack_pos++ & EPIC_PIX_STACK_MAX] = N;
}
}
(*pRun)++;
if (x + *pRun >= tile_width - 1)
break;
UPDATE_NEIGHBOURS(x + *pRun);
if (!NWneW && NW == N && N == NE) {
int pos, run, rle;
int start_pos = x + *pRun;
/* scan for a run of pix in the line above */
uint32_t pix = above_row[start_pos + 1];
for (pos = start_pos + 2; pos < tile_width; pos++)
if (!(above_row[pos] == pix))
break;
run = pos - start_pos - 1;
idx = log2_ceil(run);
if (ff_els_decode_bit(&dc->els_ctx, &dc->prev_row_rung[idx]))
*pRun += run;
else {
int flag;
/* run-length is coded as plain binary number of idx - 1 bits */
for (pos = idx - 1, rle = 0, flag = 0; pos >= 0; pos--) {
if ((1 << pos) + rle < run &&
ff_els_decode_bit(&dc->els_ctx,
flag ? &dc->runlen_one
: &dc->runlen_zeroes[pos])) {
flag = 1;
rle |= 1 << pos;
}
}
*pRun += rle;
break; // return immediately
}
if (x + *pRun >= tile_width - 1)
break;
LOAD_NEIGHBOURS(x + *pRun);
WWneW = 0;
NWneW = 0;
}
idx = WWneW << 7 |
NWneW << 6 |
(N != NE) << 5 |
(NW != N) << 4 |
(NWW != NW) << 3 |
(NNE != NE) << 2 |
(NN != N) << 1 |
(NNW != NW);
WWneW = ff_els_decode_bit(&dc->els_ctx, &dc->W_ctx_rung[idx]);
} while (!WWneW);
dc->next_run_pos = x + *pRun;
return got_pixel;
}
static int epic_predict_pixel2(ePICContext *dc, uint8_t *rung,
uint32_t *pPix, uint32_t pix)
{
if (ff_els_decode_bit(&dc->els_ctx, rung)) {
*pPix = pix;
return 1;
}
dc->stack[dc->stack_pos++ & EPIC_PIX_STACK_MAX] = pix;
return 0;
}
static int epic_predict_from_NW_NE(ePICContext *dc, int x, int y, int run,
int tile_width, const uint32_t *curr_row,
const uint32_t *above_row, uint32_t *pPix)
{
int pos;
/* try to reuse the NW pixel first */
if (x && y) {
uint32_t NW = above_row[x - 1];
if (NW != curr_row[x - 1] && NW != above_row[x] && !is_pixel_on_stack(dc, NW)) {
if (epic_predict_pixel2(dc, &dc->nw_pred_rung[NW & 0xFF], pPix, NW))
return 1;
}
}
/* try to reuse the NE[x + run, y] pixel */
pos = x + run - 1;
if (pos < tile_width - 1 && y) {
uint32_t NE = above_row[pos + 1];
if (NE != above_row[pos] && !is_pixel_on_stack(dc, NE)) {
if (epic_predict_pixel2(dc, &dc->ne_pred_rung[NE & 0xFF], pPix, NE))
return 1;
}
}
return 0;
}
static int epic_decode_from_cache(ePICContext *dc, uint32_t W, uint32_t *pPix)
{
ePICPixListElem *list, *prev = NULL;
ePICPixHashElem *hash_elem = epic_hash_find(&dc->hash, W);
if (!hash_elem || !hash_elem->list)
return 0;
list = hash_elem->list;
while (list) {
if (!is_pixel_on_stack(dc, list->pixel)) {
if (ff_els_decode_bit(&dc->els_ctx, &list->rung)) {
*pPix = list->pixel;
if (list != hash_elem->list) {
prev->next = list->next;
list->next = hash_elem->list;
hash_elem->list = list;
}
return 1;
}
dc->stack[dc->stack_pos++ & EPIC_PIX_STACK_MAX] = list->pixel;
}
prev = list;
list = list->next;
}
return 0;
}
static int epic_decode_tile(ePICContext *dc, uint8_t *out, int tile_height,
int tile_width, int stride)
{
int x, y;
uint32_t pix;
uint32_t *curr_row = NULL, *above_row = NULL, *above2_row;
for (y = 0; y < tile_height; y++, out += stride) {
above2_row = above_row;
above_row = curr_row;
curr_row = (uint32_t *) out;
for (x = 0, dc->next_run_pos = 0; x < tile_width;) {
if (dc->els_ctx.err)
return AVERROR_INVALIDDATA; // bail out in the case of ELS overflow
pix = curr_row[x - 1]; // get W pixel
if (y >= 1 && x >= 2 &&
pix != curr_row[x - 2] && pix != above_row[x - 1] &&
pix != above_row[x - 2] && pix != above_row[x] &&
!epic_cache_entries_for_pixel(&dc->hash, pix)) {
curr_row[x] = epic_decode_pixel_pred(dc, x, y, curr_row, above_row);
x++;
} else {
int got_pixel, run;
dc->stack_pos = 0; // empty stack
if (y < 2 || x < 2 || x == tile_width - 1) {
run = 1;
got_pixel = epic_handle_edges(dc, x, y, curr_row, above_row, &pix);
} else
got_pixel = epic_decode_run_length(dc, x, y, tile_width,
curr_row, above_row,
above2_row, &pix, &run);
if (!got_pixel && !epic_predict_from_NW_NE(dc, x, y, run,
tile_width, curr_row,
above_row, &pix)) {
uint32_t ref_pix = curr_row[x - 1];
if (!x || !epic_decode_from_cache(dc, ref_pix, &pix)) {
pix = epic_decode_pixel_pred(dc, x, y, curr_row, above_row);
if (x) {
int ret = epic_add_pixel_to_cache(&dc->hash,
ref_pix,
pix);
if (ret)
return ret;
}
}
}
for (; run > 0; x++, run--)
curr_row[x] = pix;
}
}
}
return 0;
}
static int epic_jb_decode_tile(G2MContext *c, int tile_x, int tile_y,
const uint8_t *src, size_t src_size,
AVCodecContext *avctx)
{
uint8_t prefix, mask = 0x80;
int extrabytes, tile_width, tile_height, awidth, aheight;
size_t els_dsize;
uint8_t *dst;
if (!src_size)
return 0;
/* get data size of the ELS partition as unsigned variable-length integer */
prefix = *src++;
src_size--;
for (extrabytes = 0; (prefix & mask) && (extrabytes < 7); extrabytes++)
mask >>= 1;
if (extrabytes > 3 || src_size < extrabytes) {
av_log(avctx, AV_LOG_ERROR, "ePIC: invalid data size VLI\n");
return AVERROR_INVALIDDATA;
}
els_dsize = prefix & ((0x80 >> extrabytes) - 1); // mask out the length prefix
while (extrabytes-- > 0) {
els_dsize = (els_dsize << 8) | *src++;
src_size--;
}
if (src_size < els_dsize) {
av_log(avctx, AV_LOG_ERROR, "ePIC: data too short, needed %zu, got %zu\n",
els_dsize, src_size);
return AVERROR_INVALIDDATA;
}
tile_width = FFMIN(c->width - tile_x * c->tile_width, c->tile_width);
tile_height = FFMIN(c->height - tile_y * c->tile_height, c->tile_height);
awidth = FFALIGN(tile_width, 16);
aheight = FFALIGN(tile_height, 16);
if (els_dsize) {
int ret, i, j, k;
uint8_t tr_r, tr_g, tr_b, *buf;
uint32_t *in;
/* ELS decoder initializations */
memset(&c->ec, 0, sizeof(c->ec));
ff_els_decoder_init(&c->ec.els_ctx, src, els_dsize);
epic_hash_init(&c->ec.hash);
/* decode transparent pixel value */
tr_r = ff_els_decode_unsigned(&c->ec.els_ctx, &c->ec.unsigned_rung);
tr_g = ff_els_decode_unsigned(&c->ec.els_ctx, &c->ec.unsigned_rung);
tr_b = ff_els_decode_unsigned(&c->ec.els_ctx, &c->ec.unsigned_rung);
if (c->ec.els_ctx.err != 0) {
av_log(avctx, AV_LOG_ERROR,
"ePIC: couldn't decode transparency pixel!\n");
return AVERROR_INVALIDDATA;
}
ret = epic_decode_tile(&c->ec, c->epic_buf, tile_height, tile_width,
c->epic_buf_stride);
epic_free_pixel_cache(&c->ec.hash);
ff_els_decoder_uninit(&c->ec.unsigned_rung);
if (ret) {
av_log(avctx, AV_LOG_ERROR,
"ePIC: tile decoding failed, frame=%d, tile_x=%d, tile_y=%d\n",
avctx->frame_number, tile_x, tile_y);
return AVERROR_INVALIDDATA;
}
buf = c->epic_buf;
dst = c->framebuf + tile_x * c->tile_width * 3 +
tile_y * c->tile_height * c->framebuf_stride;
for (j = 0; j < tile_height; j++) {
uint8_t *out = dst;
in = (uint32_t *) buf;
for (i = 0; i < tile_width; i++) {
out[0] = (in[i] >> R_shift) & 0xFF;
out[1] = (in[i] >> G_shift) & 0xFF;
out[2] = (in[i] >> B_shift) & 0xFF;
out += 3;
}
buf += c->epic_buf_stride;
dst += c->framebuf_stride;
}
if (src_size > els_dsize) {
uint8_t *jpg;
uint32_t tr;
int bstride = FFALIGN(tile_width, 16) >> 3;
int nblocks = 0;
int estride = c->epic_buf_stride >> 2;
src += els_dsize;
src_size -= els_dsize;
in = (uint32_t *) c->epic_buf;
tr = (tr_r << R_shift) | (tr_g << G_shift) | (tr_b << B_shift);
memset(c->kempf_flags, 0,
(aheight >> 3) * bstride * sizeof(*c->kempf_flags));
for (j = 0; j < tile_height; j += 8) {
for (i = 0; i < tile_width; i += 8) {
c->kempf_flags[(i >> 3) + (j >> 3) * bstride] = 0;
for (k = 0; k < 8 * 8; k++) {
if (in[i + (k & 7) + (k >> 3) * estride] == tr) {
c->kempf_flags[(i >> 3) + (j >> 3) * bstride] = 1;
nblocks++;
break;
}
}
}
in += 8 * estride;
}
memset(c->jpeg_tile, 0, c->tile_stride * aheight);
jpg_decode_data(&c->jc, awidth, aheight, src, src_size,
c->jpeg_tile, c->tile_stride,
c->kempf_flags, bstride, nblocks, c->swapuv);
in = (uint32_t *) c->epic_buf;
dst = c->framebuf + tile_x * c->tile_width * 3 +
tile_y * c->tile_height * c->framebuf_stride;
jpg = c->jpeg_tile;
for (j = 0; j < tile_height; j++) {
for (i = 0; i < tile_width; i++)
if (in[i] == tr)
memcpy(dst + i * 3, jpg + i * 3, 3);
in += c->epic_buf_stride >> 2;
dst += c->framebuf_stride;
jpg += c->tile_stride;
}
}
} else {
dst = c->framebuf + tile_x * c->tile_width * 3 +
tile_y * c->tile_height * c->framebuf_stride;
return jpg_decode_data(&c->jc, tile_width, tile_height, src, src_size,
dst, c->framebuf_stride, NULL, 0, 0, c->swapuv);
}
return 0;
}
static void kempf_restore_buf(const uint8_t *src, int len,
uint8_t *dst, int stride,
const uint8_t *jpeg_tile, int tile_stride,
@ -325,6 +1023,7 @@ static void kempf_restore_buf(const uint8_t *src, int len,
{
GetBitContext gb;
int i, j, nb, col;
int align_width = FFALIGN(width, 16);
init_get_bits(&gb, src, len * 8);
@ -343,6 +1042,7 @@ static void kempf_restore_buf(const uint8_t *src, int len,
else
memcpy(dst + i * 3, jpeg_tile + i * 3, 3);
}
skip_bits_long(&gb, nb * (align_width - width));
}
}
@ -466,22 +1166,31 @@ static int g2m_init_buffers(G2MContext *c)
return AVERROR(ENOMEM);
}
if (!c->synth_tile || !c->jpeg_tile ||
(c->compression == 2 && !c->epic_buf_base) ||
c->old_tile_w < c->tile_width ||
c->old_tile_h < c->tile_height) {
c->tile_stride = FFALIGN(c->tile_width * 3, 16);
aligned_height = FFALIGN(c->tile_height, 16);
c->tile_stride = FFALIGN(c->tile_width * 3, 16);
c->epic_buf_stride = FFALIGN(c->tile_width * 4, 16);
aligned_height = FFALIGN(c->tile_height, 16);
av_free(c->synth_tile);
av_free(c->jpeg_tile);
av_free(c->kempf_buf);
av_free(c->kempf_flags);
av_free(c->epic_buf_base);
c->synth_tile = av_mallocz(c->tile_stride * aligned_height);
c->jpeg_tile = av_mallocz(c->tile_stride * aligned_height);
c->kempf_buf = av_mallocz((c->tile_width + 1) * aligned_height
+ FF_INPUT_BUFFER_PADDING_SIZE);
c->kempf_flags = av_mallocz( c->tile_width * aligned_height);
c->kempf_buf = av_mallocz((c->tile_width + 1) * aligned_height +
FF_INPUT_BUFFER_PADDING_SIZE);
c->kempf_flags = av_mallocz(c->tile_width * aligned_height);
if (!c->synth_tile || !c->jpeg_tile ||
!c->kempf_buf || !c->kempf_flags)
return AVERROR(ENOMEM);
if (c->compression == 2) {
c->epic_buf_base = av_mallocz(c->epic_buf_stride * aligned_height + 4);
if (!c->epic_buf_base)
return AVERROR(ENOMEM);
c->epic_buf = c->epic_buf_base + 4;
}
}
return 0;
@ -690,10 +1399,7 @@ static int g2m_decode_frame(AVCodecContext *avctx, void *data,
return AVERROR_INVALIDDATA;
}
if ((magic & 0xF) < 4) {
av_log(avctx, AV_LOG_ERROR, "G2M2 and G2M3 are not yet supported\n");
return AVERROR(ENOSYS);
}
c->swapuv = magic == MKBETAG('G', '2', 'M', '2');
while (bytestream2_get_bytes_left(&bc) > 5) {
chunk_size = bytestream2_get_le32(&bc) - 1;
@ -795,9 +1501,10 @@ static int g2m_decode_frame(AVCodecContext *avctx, void *data,
ret = 0;
switch (c->compression) {
case COMPR_EPIC_J_B:
av_log(avctx, AV_LOG_ERROR,
"ePIC j-b compression is not implemented yet\n");
return AVERROR(ENOSYS);
ret = epic_jb_decode_tile(c, c->tile_x, c->tile_y,
buf + bytestream2_tell(&bc),
chunk_size - 2, avctx);
break;
case COMPR_KEMPF_J_B:
ret = kempf_decode_tile(c, c->tile_x, c->tile_y,
buf + bytestream2_tell(&bc),
@ -895,6 +1602,7 @@ static av_cold int g2m_decode_end(AVCodecContext *avctx)
jpg_free_context(&c->jc);
av_freep(&c->epic_buf_base);
av_freep(&c->kempf_buf);
av_freep(&c->kempf_flags);
av_freep(&c->synth_tile);
@ -915,4 +1623,5 @@ AVCodec ff_g2m_decoder = {
.close = g2m_decode_end,
.decode = g2m_decode_frame,
.capabilities = CODEC_CAP_DR1,
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
};

@ -29,8 +29,8 @@
#include "libavutil/version.h"
#define LIBAVCODEC_VERSION_MAJOR 56
#define LIBAVCODEC_VERSION_MINOR 29
#define LIBAVCODEC_VERSION_MICRO 1
#define LIBAVCODEC_VERSION_MINOR 31
#define LIBAVCODEC_VERSION_MICRO 0
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
LIBAVCODEC_VERSION_MINOR, \

Loading…
Cancel
Save