[Ffmpeg-devel] Trellis Quantization Applied To (A)DPCM
Loren Merritt
lorenm
Wed Jun 7 01:09:38 CEST 2006
On Mon, 5 Jun 2006, Michael Niedermayer wrote:
> On Sun, Jun 04, 2006 at 10:01:01PM -0700, Loren Merritt wrote:
>>
>> Added some asymmetric tables. They helped only a little, but I don't
>> really know what to optimize for when writing a table by hand.
>>
>> OTOH, a brute-force iterative search isn't too ridiculously slow.
>> The next step would be to run a clustering algorithm like I proposed
>> before, but this time looking for N tables to jointly fit all the
>> frames of a training set, instead of clustering based on the statistics
>> of one frame at a time.
>
> the iterative search seems to get stuck in local minima ...
> [...]
And now with the clustering.
exponential tables
context=0 psnr:43.42 tables_tried:0
context=1 psnr:43.56 tables_tried:3326
context=2 psnr:44.43 tables_tried:57057
context=3 psnr:45.85 tables_tried:399119
clustered tables
context=0 psnr:46.20 tables_tried:0
context=1 psnr:47.11 tables_tried:2928
context=2 psnr:47.34 tables_tried:82576
context=3 psnr:47.45 tables_tried:414024
--Loren Merritt
-------------- next part --------------
Index: allcodecs.c
===================================================================
--- allcodecs.c (revision 5456)
+++ allcodecs.c (working copy)
@@ -172,6 +172,9 @@
#ifdef CONFIG_SNOW_ENCODER
register_avcodec(&snow_encoder);
#endif //CONFIG_SNOW_ENCODER
+#ifdef CONFIG_CYUV_ENCODER
+ register_avcodec(&cyuv_encoder);
+#endif //CONFIG_CYUV_ENCODER
#ifdef CONFIG_ZLIB_ENCODER
register_avcodec(&zlib_encoder);
#endif //CONFIG_ZLIB_ENCODER
Index: avcodec.h
===================================================================
--- avcodec.h (revision 5456)
+++ avcodec.h (working copy)
@@ -2098,6 +2098,7 @@
extern AVCodec vcr1_encoder;
extern AVCodec ffv1_encoder;
extern AVCodec snow_encoder;
+extern AVCodec cyuv_encoder;
extern AVCodec mdec_encoder;
extern AVCodec zlib_encoder;
extern AVCodec sonic_encoder;
Index: cyuv.c
===================================================================
--- cyuv.c (revision 5456)
+++ cyuv.c (working copy)
@@ -28,6 +28,10 @@
* Creative YUV (CYUV) Video Decoder.
*/
+#define DEBUG
+#undef NDEBUG
+#include <assert.h>
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -39,15 +43,25 @@
#include "mpegvideo.h"
-typedef struct CyuvDecodeContext {
+typedef struct CyuvContext {
AVCodecContext *avctx;
int width, height;
AVFrame frame;
-} CyuvDecodeContext;
+ int8_t deltas[3][16];
+ uint8_t **y_frames;
+ uint8_t **uv_frames;
+ int n_frames, n_alloced;
+} CyuvContext;
+#define swap(a,b) {typeof(a) t = a; a = b; b = t;}
+#define put_pack4to8(a,b) (*buf++ = (a) + ((b)<<4))
+#undef printf
+//#define printf(...) (printf(__VA_ARGS__), fflush(stdout))
+
+
static int cyuv_decode_init(AVCodecContext *avctx)
{
- CyuvDecodeContext *s = avctx->priv_data;
+ CyuvContext *s = avctx->priv_data;
s->avctx = avctx;
s->width = avctx->width;
@@ -65,7 +79,7 @@
void *data, int *data_size,
uint8_t *buf, int buf_size)
{
- CyuvDecodeContext *s=avctx->priv_data;
+ CyuvContext *s=avctx->priv_data;
unsigned char *y_plane;
unsigned char *u_plane;
@@ -168,16 +182,528 @@
static int cyuv_decode_end(AVCodecContext *avctx)
{
-/* CyuvDecodeContext *s = avctx->priv_data;*/
+/* CyuvContext *s = avctx->priv_data;*/
return 0;
}
+static int quantize_sample(int sample, int *pred, const int8_t *table, const uint8_t *itable)
+{
+ int p = *pred;
+ int bi = itable[(sample - p) & 0xff];
+ *pred = p + table[bi];
+ if(*pred & ~0xff){
+ int i, bscore=INT_MAX;
+ for(i=0; i<16; i++){
+ int score = abs(sample - ((p + table[i]) & 0xff));
+ if(score < bscore){
+ bscore = score;
+ bi = i;
+ }
+ }
+ *pred = (p + table[bi]) & 0xff;
+ }
+ return bi;
+}
+
+typedef struct TrellisPath {
+ int nibble;
+ int prev;
+} TrellisPath;
+
+typedef struct TrellisNode {
+ uint32_t ssd;
+ int path;
+ int pred;
+} TrellisNode;
+
+static int quantize_row_trellis(AVCodecContext *avctx, const uint8_t *samples,
+ uint8_t *dst, const int8_t *table, const uint8_t *itable, int n)
+{
+ const int frontier = 1 << avctx->trellis;
+ const int max_paths = frontier*n;
+ TrellisPath paths[max_paths], *p;
+ TrellisNode node_buf[2][frontier];
+ TrellisNode *nodep_buf[2][frontier];
+ TrellisNode **nodes = nodep_buf[0]; // nodes[] is always sorted by .ssd
+ TrellisNode **nodes_next = nodep_buf[1];
+ int pathn = 0, i, j, k, d;
+
+ memset(nodep_buf, 0, sizeof(nodep_buf));
+ nodes[0] = &node_buf[0][0];
+ paths[0].nibble = samples[0] >> 4;
+ nodes[0]->pred = paths[0].nibble << 4;
+ nodes[0]->path = pathn++;
+ d = samples[0] - nodes[0]->pred;
+ nodes[0]->ssd = d*d;
+ if(samples[0] < 0xf0) {
+ nodes[1] = &node_buf[0][1];
+ paths[1].nibble = (samples[0] >> 4) + 1;
+ nodes[1]->pred = paths[1].nibble << 4;
+ nodes[1]->path = pathn++;
+ d = samples[0] - nodes[1]->pred;
+ nodes[1]->ssd = d*d;
+ if(nodes[1]->ssd < nodes[0]->ssd)
+ swap(nodes[1], nodes[0]);
+ }
+
+ for(i=1; i<n; i++) {
+ TrellisNode *t = node_buf[i&1];
+ TrellisNode **u;
+ const int sample = samples[i];
+ int range = 2;
+ memset(nodes_next, 0, frontier*sizeof(TrellisNode*));
+ for(j=0; j<frontier && nodes[j]; j++) {
+ const int pred = nodes[j]->pred;
+ const int div = itable[(sample - pred) & 0xff];
+ const int nmin = FFMAX(div-range, 0);
+ const int nmax = FFMIN(div+range, 15);
+ int nibble;
+ range = 1;
+ for(nibble=nmin; nibble<=nmax; nibble++) {
+ int dec_sample = (pred + table[nibble]) & 0xff;
+ int d = sample - dec_sample;
+ uint32_t ssd = nodes[j]->ssd + d*d;
+ if(nodes_next[frontier-1] && ssd >= nodes_next[frontier-1]->ssd)
+ continue;
+ for(k=0; k<frontier && nodes_next[k]; k++) {
+ if(dec_sample == nodes_next[k]->pred) {
+ assert(ssd >= nodes_next[k]->ssd);
+ goto next_nibble;
+ }
+ }
+ for(k=0; k<frontier; k++) {
+ if(!nodes_next[k] || ssd < nodes_next[k]->ssd) {
+ TrellisNode *u = nodes_next[frontier-1];
+ if(!u) {
+ u = t++;
+ u->path = pathn++;
+ }
+ u->ssd = ssd;
+ u->pred = dec_sample;
+ paths[u->path].nibble = nibble;
+ paths[u->path].prev = nodes[j]->path;
+ memmove(&nodes_next[k+1], &nodes_next[k], (frontier-k-1)*sizeof(TrellisNode*));
+ nodes_next[k] = u;
+ break;
+ }
+ }
+ next_nibble:;
+ }
+ }
+
+ u = nodes;
+ nodes = nodes_next;
+ nodes_next = u;
+ }
+
+ p = &paths[nodes[0]->path];
+ for(i=n-1; i>=0; i--) {
+ dst[i] = p->nibble;
+ p = &paths[p->prev];
+ }
+ return nodes[0]->ssd;
+}
+
+static void build_inverse_table(uint8_t *itable, const int8_t *table)
+{
+ int d, i;
+ for(i=1; i<16; i++)
+ assert(table[i] > table[i-1]);
+ for(d=-128; d<128; d++){
+ int bscore = INT_MAX;
+ for(i=0; i<16; i++){
+ int score = abs(d - table[i]);
+ if(score > bscore)
+ break;
+ bscore = score;
+ }
+ itable[d&0xff] = i-1;
+ }
+}
+
+static int tables_tried = 0;
+
+static int try_row(const uint8_t *samples, const int8_t *table,
+ const uint8_t *itable, int n)
+{
+ // trellis doesn't make much difference here, so just use the fast method
+ int pred = clip_uint8(samples[0]+8) & -16;
+ int d = samples[0] - pred;
+ int ssd = d*d;
+ int i;
+ for(i=1; i<n; i++){
+ quantize_sample(samples[i], &pred, table, itable);
+ d = samples[i] - pred;
+ ssd += d*d;
+ }
+ return ssd;
+}
+
+static uint64_t try_table(const int8_t *table, int8_t *dst, uint64_t *score,
+ uint8_t **pix, int pixn, int w, int h, int stride)
+{
+ uint64_t ssd = 0;
+ uint8_t itable[256];
+ int y, i;
+ build_inverse_table(itable, table);
+ for(i=0; i<pixn; i++)
+ for(y=0; y<h; y++)
+ ssd += try_row(pix[i]+y*stride, table, itable, w);
+ if(ssd < *score){
+ *score = ssd;
+ memcpy(dst, table, 16);
+ }
+ tables_tried++;
+ return ssd;
+}
+
+#define N_TABLES 4
+static const int8_t some_tables[2][N_TABLES][16] = {
+ {{-120, -77, -51, -36, -22, -13, -8, -3, 0, 3, 8, 16, 28, 41, 60, 96},
+ {-104, -73, -48, -29, -16, -9, -4, -1, 0, 1, 4, 9, 16, 26, 42, 68},
+ { -47, -28, -17, -10, -5, -2, -1, 0, 1, 2, 5, 8, 13, 20, 31, 47},
+ { -30, -19, -12, -7, -4, -3, -2, -1, 0, 1, 2, 3, 6, 10, 17, 29},
+},{
+ { -25, -14, -9, -6, -3, -2, -1, 0, 1, 2, 3, 6, 9, 14, 21, 34},
+ { -26, -17, -12, -9, -6, -3, -2, -1, 0, 1, 2, 5, 8, 11, 16, 23},
+ { -43, -28, -19, -12, -7, -4, -1, 0, 1, 4, 7, 12, 17, 24, 33, 46},
+ { -52, -38, -27, -19, -12, -7, -4, -1, 0, 1, 4, 9, 16, 25, 36, 52},
+}};
+static const int8_t a_table[2][16] = {
+ {-116, -75, -48, -31, -18, -9, -4, -1, 0, 1, 4, 9, 18, 32, 54, 95},
+ { -39, -22, -13, -8, -5, -2, -1, 0, 1, 2, 5, 8, 13, 20, 31, 48},
+};
+
+static void dump_table(const int8_t *table, char name, uint64_t score)
+{
+ int i;
+ printf("%c %7"PRIx64" ", name, score);
+ for(i=0; i<16; i++)
+ printf(" %2x", table[i]&0xff);
+ printf("\n");
+}
+
+static uint64_t build_table(CyuvContext *s, int8_t *table, uint8_t **pix, int pixn, int plane, int w, int stride)
+{
+ const int h = s->height;
+ int i, j;
+ uint64_t ssd, bssd = 1ULL<<63;
+
+ for(i=0; i<N_TABLES; i++){
+ ssd = try_table(some_tables[!!plane][i], table, &bssd, pix, pixn, w, h, stride);
+ printf("%7"PRIx64" ", ssd);
+ if(ssd == 0){
+ printf("\n");
+ return ssd;
+ }
+ }
+ printf("\n");
+
+ // Iterative refinement:
+ // 16-dimensional diamond search, encoding the whole frame at each step
+ if(s->avctx->context_model >= 2){
+ int8_t tmp_buf[18];
+ int8_t *tmp = tmp_buf+1;
+ tmp[-1] = -128;
+ tmp[16] = 127;
+ try_table(s->deltas[plane], table, &bssd, pix, pixn, w, h, stride);
+
+ // modify 1 entry at a time
+ for(j=0; j<99; j++){
+ int bssd_bakj = bssd;
+ dump_table(table, "YUV"[plane], bssd);
+
+ for(i=0; i<16; i++){
+ int v = table[i];
+ int bssd_baki = bssd;
+ if(v>=-2 && v<=2)
+ continue;
+ memcpy(tmp, table, 16);
+
+ while(++tmp[i] < tmp[i+1]){
+ ssd = try_table(tmp, table, &bssd, pix, pixn, w, h, stride);
+ if(ssd > bssd)
+ break;
+ }
+ if(bssd < bssd_baki)
+ continue;
+
+ tmp[i] = v;
+ while(--tmp[i] > tmp[i-1]){
+ ssd = try_table(tmp, table, &bssd, pix, pixn, w, h, stride);
+ if(ssd > bssd)
+ break;
+ }
+ }
+ if(bssd == bssd_bakj)
+ break;
+ }
+
+ // modify 2 entries at a time
+ if(s->avctx->context_model >= 3){
+ for(j=0; j<99; j++){
+ int bssd_bakj = bssd;
+ dump_table(table, "YUV"[plane], bssd);
+ for(i=0; i<16*16*4; i++){
+ int i0= i&15;
+ int i1= (i>>4)&15;
+ int d0= ((i>>8)&1)*2-1;
+ int d1= ((i>>9)&1)*2-1;
+
+ if(i0>=i1)
+ continue;
+ memcpy(tmp, table, 16);
+ tmp[i0] += d0;
+ tmp[i1] += d1;
+ if( tmp[i0] >= tmp[i0+1] || tmp[i0] <= tmp[i0-1]
+ || tmp[i1] >= tmp[i1+1] || tmp[i1] <= tmp[i1-1])
+ continue;
+
+ try_table(tmp, table, &bssd, pix, pixn, w, h, stride);
+ }
+ if(bssd == bssd_bakj)
+ break;
+ }
+ }
+ memcpy(s->deltas[plane], table, 16);
+ }
+ return bssd;
+}
+
+static void train_tables(CyuvContext *s, int plane)
+{
+ const int k = 4;
+ const int w = s->width >> 2*plane;
+ const int h = s->height;
+ const int n = s->n_frames << plane;
+ uint8_t **all_frames = plane ? s->uv_frames : s->y_frames;
+ int i, j, pass;
+ int8_t tables[k][16];
+ memcpy(tables, some_tables[!!plane], sizeof(tables));
+
+ s->avctx->context_model = 3;
+
+ printf("training plane %d\n", plane);
+
+ for(pass=0; pass<99; pass++){
+ int8_t tables_bak[k][16], tmp[16];
+ uint8_t *pix[k][n];
+ int pixn[k], pixk[n];
+ uint64_t ssdj[k], ssdi[n], ssdsum=0;
+ memcpy(tables_bak, tables, sizeof(tables));
+ memset(pixn, 0, sizeof(pixn));
+ memset(ssdj, 0, sizeof(ssdj));
+ // assign each frame to the table that best codes it
+ printf("mapping ");
+ for(i=0; i<n; i++){
+ uint64_t bssd = 1ULL<<63;
+ int bj = 0;
+ for(j=0; j<k; j++){
+ uint64_t ssd = try_table(tables[j], tmp, &bssd, &all_frames[i], 1, w, h, w);
+ if(ssd == bssd)
+ bj = j;
+ }
+ ssdj[bj] += bssd;
+ ssdi[i] = bssd;
+ pixk[i] = bj;
+ pix[bj][pixn[bj]++] = all_frames[i];
+ printf("%d", bj);
+ }
+ printf("\n");
+ for(j=0; j<k; j++){
+ printf("T%d (%2d)", j, pixn[j]);
+ dump_table(tables[j], ' ', ssdj[j]);
+ }
+ // check for tables with no assigned frames,
+ // and assign them to the frames with the worst score
+ for(j=0; j<k; j++){
+ if(pixn[j] == 0){
+ uint64_t wssd = 0;
+ int wi = 0, wj, wn;
+ for(i=0; i<n; i++){
+ if(ssdi[i] > wssd && pixn[pixk[i]] > 1){
+ wssd = ssdi[i];
+ wi = i;
+ }
+ }
+ assert(wssd>0);
+ wj = pixk[wi];
+ wn = pixn[wj];
+ for(i=0; i<wn; i++){
+ if(pix[wj][i] == all_frames[wi]){
+ swap(pix[wj][i], pix[wj][wn-1]);
+ break;
+ }
+ }
+ assert(i<wn);
+ pix[j][0] = all_frames[wi];
+ pixn[j]++;
+ pixk[wi] = j;
+ pixn[wj]--;
+ memcpy(tables[j], tables[wj], 16); //dunno if this is good, but needed for guaranteed convergence
+ }
+ }
+ // modify each table to better code the frames assigned to it
+ for(j=0; j<k; j++){
+ memcpy(s->deltas[plane], tables[j], 16);
+ ssdsum += ssdj[j] = build_table(s, tables[j], pix[j], pixn[j], plane, w, w);
+ }
+ for(j=0; j<k; j++){
+ printf("S%d (%2d)", j, pixn[j]);
+ dump_table(tables[j], ' ', ssdj[j]);
+ }
+ printf("total: %7"PRIx64"\n", ssdsum);
+ fflush(stdout);
+ if(!memcmp(tables_bak, tables, sizeof(tables)))
+ break;
+ }
+}
+
+static int cyuv_encode_frame(AVCodecContext *avctx, unsigned char *buf, int buf_size, void *data)
+{
+ CyuvContext *s = avctx->priv_data;
+ AVFrame *pict = data;
+ int8_t *y_table = buf + 0;
+ int8_t *u_table = buf + 16;
+ int8_t *v_table = buf + 32;
+ uint8_t y_itable[256], u_itable[256], v_itable[256];
+ int x, y, p;
+ const int coded_size = 48 + s->height * s->width * 3/4;
+ assert(buf_size >= coded_size);
+ s->frame = *pict;
+ pict->error[0] = pict->error[1] = pict->error[2] = 0;
+
+ if(avctx->context_model > 0){
+ build_table(s, y_table, &s->frame.data[0], 1, 0, s->width, s->frame.linesize[0]);
+ build_table(s, u_table, &s->frame.data[1], 1, 1, s->width>>2, s->frame.linesize[1]);
+ build_table(s, v_table, &s->frame.data[2], 1, 2, s->width>>2, s->frame.linesize[2]);
+ {
+ static int fn = 0;
+ printf("fn %d tables_tried %d \n", fn, tables_tried);
+ fn++;
+ }
+ }else{
+ memcpy(y_table, a_table[0], 16);
+ memcpy(u_table, a_table[1], 16);
+ memcpy(v_table, a_table[1], 16);
+ }
+ build_inverse_table(y_itable, y_table);
+ build_inverse_table(u_itable, u_table);
+ build_inverse_table(v_itable, v_table);
+ buf += 48;
+
+ for(y=0; y<s->height; y++){
+ uint8_t *y_ptr = pict->data[0] + y*pict->linesize[0];
+ uint8_t *u_ptr = pict->data[1] + y*pict->linesize[1];
+ uint8_t *v_ptr = pict->data[2] + y*pict->linesize[2];
+ int y0, y1;
+
+ if(avctx->trellis > 0){
+ uint8_t y_buf[s->width];
+ uint8_t u_buf[s->width>>2];
+ uint8_t v_buf[s->width>>2];
+ pict->error[0] += quantize_row_trellis(avctx, y_ptr, y_buf, y_table, y_itable, s->width);
+ pict->error[1] += quantize_row_trellis(avctx, u_ptr, u_buf, u_table, u_itable, s->width>>2);
+ pict->error[2] += quantize_row_trellis(avctx, v_ptr, v_buf, v_table, v_itable, s->width>>2);
+ for(x=0; x<s->width; x+=4){
+ put_pack4to8(y_buf[x+0], u_buf[x>>2]);
+ put_pack4to8(y_buf[x+1], v_buf[x>>2]);
+ put_pack4to8(y_buf[x+2], y_buf[x+3]);
+ }
+ }else{
+ int y_pred = clip_uint8(y_ptr[0]+8) & -16;
+ int u_pred = clip_uint8(u_ptr[0]+8) & -16;
+ int v_pred = clip_uint8(v_ptr[0]+8) & -16;
+ put_pack4to8(y_pred>>4, u_pred>>4);
+ y0 = quantize_sample(y_ptr[1], &y_pred, y_table, y_itable);
+ put_pack4to8(y0, v_pred>>4);
+ y0 = quantize_sample(y_ptr[2], &y_pred, y_table, y_itable);
+ y1 = quantize_sample(y_ptr[3], &y_pred, y_table, y_itable);
+ put_pack4to8(y0, y1);
+ for(x=4; x<s->width; x+=4){
+ y_ptr+=4;
+ u_ptr++;
+ v_ptr++;
+ y0 = quantize_sample(y_ptr[0], &y_pred, y_table, y_itable);
+ y1 = quantize_sample(u_ptr[0], &u_pred, u_table, u_itable);
+ put_pack4to8(y0, y1);
+ y0 = quantize_sample(y_ptr[1], &y_pred, y_table, y_itable);
+ y1 = quantize_sample(v_ptr[0], &v_pred, v_table, v_itable);
+ put_pack4to8(y0, y1);
+ y0 = quantize_sample(y_ptr[2], &y_pred, y_table, y_itable);
+ y1 = quantize_sample(y_ptr[3], &y_pred, y_table, y_itable);
+ put_pack4to8(y0, y1);
+ }
+ }
+ }
+
+ if(avctx->flags & CODEC_FLAG_PASS1){
+ if(s->n_frames >= s->n_alloced){
+ s->n_alloced = 2*s->n_alloced+1;
+ s->y_frames = av_realloc(s->y_frames, s->n_alloced*sizeof(uint8_t*));
+ s->uv_frames = av_realloc(s->uv_frames, 2*s->n_alloced*sizeof(uint8_t*));
+ }
+ s->y_frames[s->n_frames] = av_malloc(s->width*s->height);
+ s->uv_frames[s->n_frames*2] = av_malloc(s->width*s->height/4);
+ s->uv_frames[s->n_frames*2+1] = av_malloc(s->width*s->height/4);
+ for(y=0; y<s->height; y++){
+ memcpy(s->y_frames[s->n_frames]+y*s->width,
+ pict->data[0] + y*pict->linesize[0], s->width);
+ memcpy(s->uv_frames[s->n_frames*2]+y*s->width/4,
+ pict->data[1] + y*pict->linesize[1], s->width/4);
+ memcpy(s->uv_frames[s->n_frames*2+1]+y*s->width/4,
+ pict->data[2] + y*pict->linesize[2], s->width/4);
+ }
+ s->n_frames++;
+ }
+
+ fflush(stdout);
+
+ s->frame = *pict;
+ avctx->coded_frame = &s->frame;
+ for(p=0; p<3; p++)
+ avctx->error[p] += pict->error[p];
+ return coded_size;
+}
+
+static int cyuv_encode_init(AVCodecContext *avctx)
+{
+ int i;
+ CyuvContext *s = avctx->priv_data;
+
+ s->avctx = avctx;
+ s->height = avctx->height;
+ s->width = avctx->width;
+ /* width needs to be divisible by 4 for this codec to work */
+ if((s->width & 0x3) || avctx->pix_fmt != PIX_FMT_YUV411P){
+ av_log(avctx, AV_LOG_ERROR, "Cyuv requires 411p input\n");
+ return -1;
+ }
+ avctx->bits_per_sample= 12;
+ avctx->coded_frame= &s->frame;
+
+ for(i=0; i<3; i++)
+ memcpy(s->deltas[i], some_tables[!!i][0], 16);
+
+ return 0;
+}
+
+static int cyuv_encode_end(AVCodecContext *avctx)
+{
+ if(avctx->flags & CODEC_FLAG_PASS1){
+ train_tables(avctx->priv_data, 0);
+ train_tables(avctx->priv_data, 1);
+ }
+ return 0;
+}
+
+
AVCodec cyuv_decoder = {
"cyuv",
CODEC_TYPE_VIDEO,
CODEC_ID_CYUV,
- sizeof(CyuvDecodeContext),
+ sizeof(CyuvContext),
cyuv_decode_init,
NULL,
cyuv_decode_end,
@@ -186,3 +712,12 @@
NULL
};
+AVCodec cyuv_encoder = {
+ "cyuv",
+ CODEC_TYPE_VIDEO,
+ CODEC_ID_CYUV,
+ sizeof(CyuvContext),
+ cyuv_encode_init,
+ cyuv_encode_frame,
+ cyuv_encode_end,
+};
More information about the ffmpeg-devel
mailing list