X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Froqvideoenc.c;h=6421ccc8561363bd0beb270d6d9b92b665480444;hb=1d12a545ce828eaf4fb37295400008ea37635ab8;hp=4bd9602b400c18c3cc6b942457e18c03dea96e0e;hpb=670e47e196294f962c549cb24955b955fc48eda5;p=ffmpeg diff --git a/libavcodec/roqvideoenc.c b/libavcodec/roqvideoenc.c index 4bd9602b400..6421ccc8561 100644 --- a/libavcodec/roqvideoenc.c +++ b/libavcodec/roqvideoenc.c @@ -1,31 +1,31 @@ /* * RoQ Video Encoder. * - * Copyright (C) 2007 Vitor + * Copyright (C) 2007 Vitor Sessak * Copyright (C) 2004-2007 Eric Lasota * Based on RoQ specs (C) 2001 Tim Ferguson * - * This file is part of FFmpeg. + * This file is part of Libav. * - * FFmpeg is free software; you can redistribute it and/or + * 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. * - * FFmpeg is distributed in the hope that it will be useful, + * 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 FFmpeg; if not, write to the Free Software + * License along with Libav; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** - * @file roqvideoenc.c - * Id RoQ encoder by Vitor. Based on the Switchblade3 library and the - * Switchblade3 FFmpeg glue by Eric Lasota. + * @file + * id RoQ encoder by Vitor. Based on the Switchblade3 library and the + * Switchblade3 Libav glue by Eric Lasota. */ /* @@ -55,11 +55,13 @@ */ #include -#include +#include "libavutil/attributes.h" #include "roqvideo.h" #include "bytestream.h" #include "elbg.h" +#include "internal.h" +#include "mathops.h" #define CHROMA_BIAS 1 @@ -112,7 +114,7 @@ static inline int square(int x) return x*x; } -static inline int eval_sse(uint8_t *a, uint8_t *b, int count) +static inline int eval_sse(const uint8_t *a, const uint8_t *b, int count) { int diff=0; @@ -124,8 +126,8 @@ static inline int eval_sse(uint8_t *a, uint8_t *b, int count) // FIXME Could use DSPContext.sse, but it is not so speed critical (used // just for motion estimation). -static int block_sse(uint8_t **buf1, uint8_t **buf2, int x1, int y1, int x2, - int y2, int *stride, int size) +static int block_sse(uint8_t * const *buf1, uint8_t * const *buf2, int x1, int y1, + int x2, int y2, const int *stride1, const int *stride2, int size) { int i, k; int sse=0; @@ -133,8 +135,8 @@ static int block_sse(uint8_t **buf1, uint8_t **buf2, int x1, int y1, int x2, for (k=0; k<3; k++) { int bias = (k ? CHROMA_BIAS : 4); for (i=0; iframe_to_enc->data, enc->last_frame->data, x, y, - mx, my, enc->frame_to_enc->linesize, size); + mx, my, + enc->frame_to_enc->linesize, enc->last_frame->linesize, + size); } /** - * Returns distortion between two macroblocks + * @return distortion between two macroblocks */ static inline int squared_diff_macroblock(uint8_t a[], uint8_t b[], int size) { @@ -179,8 +183,7 @@ static inline int squared_diff_macroblock(uint8_t a[], uint8_t b[], int size) return sdiff; } -typedef struct -{ +typedef struct SubcelEvaluation { int eval_dist[4]; int best_bit_use; int best_coding; @@ -188,23 +191,21 @@ typedef struct int subCels[4]; motion_vect motion; int cbEntry; -} subcel_evaluation_t; +} SubcelEvaluation; -typedef struct -{ +typedef struct CelEvaluation { int eval_dist[4]; int best_coding; - subcel_evaluation_t subCels[4]; + SubcelEvaluation subCels[4]; motion_vect motion; int cbEntry; int sourceX, sourceY; -} cel_evaluation_t; +} CelEvaluation; -typedef struct -{ +typedef struct RoqCodebooks { int numCB4; int numCB2; int usedCB2[MAX_CBS_2x2]; @@ -212,14 +213,14 @@ typedef struct uint8_t unpacked_cb2[MAX_CBS_2x2*2*2*3]; uint8_t unpacked_cb4[MAX_CBS_4x4*4*4*3]; uint8_t unpacked_cb4_enlarged[MAX_CBS_4x4*8*8*3]; -} roq_codebooks_t; +} RoqCodebooks; /** * Temporary vars */ -typedef struct +typedef struct RoqTempData { - cel_evaluation_t *cel_evals; + CelEvaluation *cel_evals; int f2i4[MAX_CBS_4x4]; int i2f4[MAX_CBS_4x4]; @@ -231,20 +232,22 @@ typedef struct int numCB4; int numCB2; - roq_codebooks_t codebooks; + RoqCodebooks codebooks; int *closest_cb2; int used_option[4]; -} roq_tempdata_t; +} RoqTempdata; /** - * Initializes cel evaluators and sets their source coordinates + * Initialize cel evaluators and set their source coordinates */ -static void create_cel_evals(RoqContext *enc, roq_tempdata_t *tempData) +static int create_cel_evals(RoqContext *enc, RoqTempdata *tempData) { int n=0, x, y, i; - tempData->cel_evals = av_malloc(enc->width*enc->height/64 * sizeof(cel_evaluation_t)); + tempData->cel_evals = av_malloc(enc->width*enc->height/64 * sizeof(CelEvaluation)); + if (!tempData->cel_evals) + return AVERROR(ENOMEM); /* Map to the ROQ quadtree order */ for (y=0; yheight; y+=16) @@ -253,12 +256,14 @@ static void create_cel_evals(RoqContext *enc, roq_tempdata_t *tempData) tempData->cel_evals[n ].sourceX = x + (i&1)*8; tempData->cel_evals[n++].sourceY = y + (i&2)*4; } + + return 0; } /** * Get macroblocks from parts of the image */ -static void get_frame_mb(AVFrame *frame, int x, int y, uint8_t mb[], int dim) +static void get_frame_mb(const AVFrame *frame, int x, int y, uint8_t mb[], int dim) { int i, j, cp; @@ -391,10 +396,10 @@ static void motion_search(RoqContext *enc, int blocksize) } /** - * Gets distortion for all options available to a subcel + * Get distortion for all options available to a subcel */ -static void gather_data_for_subcel(subcel_evaluation_t *subcel, int x, - int y, RoqContext *enc, roq_tempdata_t *tempData) +static void gather_data_for_subcel(SubcelEvaluation *subcel, int x, + int y, RoqContext *enc, RoqTempdata *tempData) { uint8_t mb4[4*4*3]; uint8_t mb2[2*2*3]; @@ -417,6 +422,7 @@ static void gather_data_for_subcel(subcel_evaluation_t *subcel, int x, enc->current_frame->data, x, y, x, y, enc->frame_to_enc->linesize, + enc->current_frame->linesize, 4); else subcel->eval_dist[RoQ_ID_MOT] = INT_MAX; @@ -454,10 +460,10 @@ static void gather_data_for_subcel(subcel_evaluation_t *subcel, int x, } /** - * Gets distortion for all options available to a cel + * Get distortion for all options available to a cel */ -static void gather_data_for_cel(cel_evaluation_t *cel, RoqContext *enc, - roq_tempdata_t *tempData) +static void gather_data_for_cel(CelEvaluation *cel, RoqContext *enc, + RoqTempdata *tempData) { uint8_t mb8[8*8*3]; int index = cel->sourceY*enc->width/64 + cel->sourceX/8; @@ -479,7 +485,8 @@ static void gather_data_for_cel(cel_evaluation_t *cel, RoqContext *enc, enc->current_frame->data, cel->sourceX, cel->sourceY, cel->sourceX, cel->sourceY, - enc->frame_to_enc->linesize, 8); + enc->frame_to_enc->linesize, + enc->current_frame->linesize,8); else cel->eval_dist[RoQ_ID_MOT] = INT_MAX; @@ -529,7 +536,7 @@ static void gather_data_for_cel(cel_evaluation_t *cel, RoqContext *enc, } } -static void remap_codebooks(RoqContext *enc, roq_tempdata_t *tempData) +static void remap_codebooks(RoqContext *enc, RoqTempdata *tempData) { int i, j, idx=0; @@ -561,7 +568,7 @@ static void remap_codebooks(RoqContext *enc, roq_tempdata_t *tempData) /** * Write codebook chunk */ -static void write_codebooks(RoqContext *enc, roq_tempdata_t *tempData) +static void write_codebooks(RoqContext *enc, RoqTempdata *tempData) { int i, j; uint8_t **outp= &enc->out_buf; @@ -592,8 +599,7 @@ static inline uint8_t motion_arg(motion_vect mot) return ((ax&15)<<4) | (ay&15); } -typedef struct -{ +typedef struct CodingSpool { int typeSpool; int typeSpoolLength; uint8_t argumentSpool[64]; @@ -604,7 +610,7 @@ typedef struct /* NOTE: Typecodes must be spooled AFTER arguments!! */ static void write_typecode(CodingSpool *s, uint8_t type) { - s->typeSpool |= ((type) & 3) << (14 - s->typeSpoolLength); + s->typeSpool |= (type & 3) << (14 - s->typeSpoolLength); s->typeSpoolLength += 2; if (s->typeSpoolLength == 16) { bytestream_put_le16(s->pout, s->typeSpool); @@ -616,7 +622,7 @@ static void write_typecode(CodingSpool *s, uint8_t type) } } -static void reconstruct_and_encode_image(RoqContext *enc, roq_tempdata_t *tempData, int w, int h, int numBlocks) +static void reconstruct_and_encode_image(RoqContext *enc, RoqTempdata *tempData, int w, int h, int numBlocks) { int i, j, k; int x, y; @@ -624,7 +630,7 @@ static void reconstruct_and_encode_image(RoqContext *enc, roq_tempdata_t *tempDa int dist=0; roq_qcell *qcell; - cel_evaluation_t *eval; + CelEvaluation *eval; CodingSpool spool; @@ -729,27 +735,14 @@ static void reconstruct_and_encode_image(RoqContext *enc, roq_tempdata_t *tempDa /* Flush the remainder of the argument/type spool */ while (spool.typeSpoolLength) write_typecode(&spool, 0x0); - -#if 0 - uint8_t *fdata[3] = {enc->frame_to_enc->data[0], - enc->frame_to_enc->data[1], - enc->frame_to_enc->data[2]}; - uint8_t *cdata[3] = {enc->current_frame->data[0], - enc->current_frame->data[1], - enc->current_frame->data[2]}; - av_log(enc->avctx, AV_LOG_ERROR, "Expected distortion: %i Actual: %i\n", - dist, - block_sse(fdata, cdata, 0, 0, 0, 0, - enc->frame_to_enc->linesize, enc->width)); //WARNING: Square dimensions implied... -#endif } /** * Create a single YUV cell from a 2x2 section of the image */ -static inline void frame_block_to_cell(uint8_t *block, uint8_t **data, - int top, int left, int *stride) +static inline void frame_block_to_cell(uint8_t *block, uint8_t * const *data, + int top, int left, const int *stride) { int i, j, u=0, v=0; @@ -767,9 +760,9 @@ static inline void frame_block_to_cell(uint8_t *block, uint8_t **data, } /** - * Creates YUV clusters for the entire image + * Create YUV clusters for the entire image */ -static void create_clusters(AVFrame *frame, int w, int h, uint8_t *yuvClusters) +static void create_clusters(const AVFrame *frame, int w, int h, uint8_t *yuvClusters) { int i, j, k, l; @@ -783,26 +776,36 @@ static void create_clusters(AVFrame *frame, int w, int h, uint8_t *yuvClusters) } } -static void generate_codebook(RoqContext *enc, roq_tempdata_t *tempdata, - int *points, int inputCount, roq_cell *results, - int size, int cbsize) +static int generate_codebook(RoqContext *enc, RoqTempdata *tempdata, + int *points, int inputCount, roq_cell *results, + int size, int cbsize) { - int i, j, k; + int i, j, k, ret = 0; int c_size = size*size/4; - int *buf = points; + int *buf; int *codebook = av_malloc(6*c_size*cbsize*sizeof(int)); int *closest_cb; - if (size == 4) + if (!codebook) + return AVERROR(ENOMEM); + + if (size == 4) { closest_cb = av_malloc(6*c_size*inputCount*sizeof(int)); - else + if (!closest_cb) { + ret = AVERROR(ENOMEM); + goto out; + } + } else closest_cb = tempdata->closest_cb2; - ff_init_elbg(points, 6*c_size, inputCount, codebook, cbsize, 1, closest_cb, &enc->randctx); - ff_do_elbg(points, 6*c_size, inputCount, codebook, cbsize, 1, closest_cb, &enc->randctx); - - if (size == 4) - av_free(closest_cb); + ret = ff_init_elbg(points, 6 * c_size, inputCount, codebook, + cbsize, 1, closest_cb, &enc->randctx); + if (ret < 0) + goto out; + ret = ff_do_elbg(points, 6 * c_size, inputCount, codebook, + cbsize, 1, closest_cb, &enc->randctx); + if (ret < 0) + goto out; buf = codebook; for (i=0; iv = (*buf++ + CHROMA_BIAS/2)/CHROMA_BIAS; results++; } - +out: + if (size == 4) + av_free(closest_cb); av_free(codebook); + return ret; } -static void generate_new_codebooks(RoqContext *enc, roq_tempdata_t *tempData) +static int generate_new_codebooks(RoqContext *enc, RoqTempdata *tempData) { - int i,j; - roq_codebooks_t *codebooks = &tempData->codebooks; + int i, j, ret = 0; + RoqCodebooks *codebooks = &tempData->codebooks; int max = enc->width*enc->height/16; uint8_t mb2[3*4]; roq_cell *results4 = av_malloc(sizeof(roq_cell)*MAX_CBS_4x4*4); @@ -829,6 +835,11 @@ static void generate_new_codebooks(RoqContext *enc, roq_tempdata_t *tempData) int *points = av_malloc(max*6*4*sizeof(int)); int bias; + if (!results4 || !yuvClusters || !points) { + ret = AVERROR(ENOMEM); + goto out; + } + /* Subsample YUV data */ create_clusters(enc->frame_to_enc, enc->width, enc->height, yuvClusters); @@ -839,14 +850,22 @@ static void generate_new_codebooks(RoqContext *enc, roq_tempdata_t *tempData) } /* Create 4x4 codebooks */ - generate_codebook(enc, tempData, points, max, results4, 4, MAX_CBS_4x4); + if ((ret = generate_codebook(enc, tempData, points, max, + results4, 4, MAX_CBS_4x4)) < 0) + goto out; codebooks->numCB4 = MAX_CBS_4x4; tempData->closest_cb2 = av_malloc(max*4*sizeof(int)); + if (!tempData->closest_cb2) { + ret = AVERROR(ENOMEM); + goto out; + } /* Create 2x2 codebooks */ - generate_codebook(enc, tempData, points, max*4, enc->cb2x2, 2, MAX_CBS_2x2); + if ((ret = generate_codebook(enc, tempData, points, max * 4, + enc->cb2x2, 2, MAX_CBS_2x2)) < 0) + goto out; codebooks->numCB2 = MAX_CBS_2x2; @@ -866,22 +885,27 @@ static void generate_new_codebooks(RoqContext *enc, roq_tempdata_t *tempData) enlarge_roq_mb4(codebooks->unpacked_cb4 + i*4*4*3, codebooks->unpacked_cb4_enlarged + i*8*8*3); } - +out: av_free(yuvClusters); av_free(points); av_free(results4); + return ret; } -static void roq_encode_video(RoqContext *enc) +static int roq_encode_video(RoqContext *enc) { - roq_tempdata_t tempData; - int i; + RoqTempdata *tempData = enc->tmpData; + int i, ret; - memset(&tempData, 0, sizeof(tempData)); + memset(tempData, 0, sizeof(*tempData)); - create_cel_evals(enc, &tempData); + ret = create_cel_evals(enc, tempData); + if (ret < 0) + return ret; - generate_new_codebooks(enc, &tempData); + ret = generate_new_codebooks(enc, tempData); + if (ret < 0) + return ret; if (enc->framesSinceKeyframe >= 1) { motion_search(enc, 8); @@ -890,19 +914,30 @@ static void roq_encode_video(RoqContext *enc) retry_encode: for (i=0; iwidth*enc->height/64; i++) - gather_data_for_cel(tempData.cel_evals + i, enc, &tempData); + gather_data_for_cel(tempData->cel_evals + i, enc, tempData); + + /* Quake 3 can't handle chunks bigger than 65535 bytes */ + if (tempData->mainChunkSize/8 > 65535) { + av_log(enc->avctx, AV_LOG_ERROR, + "Warning, generated a frame too big (%d > 65535), " + "try using a smaller qscale value.\n", + tempData->mainChunkSize/8); + enc->lambda *= 1.5; + tempData->mainChunkSize = 0; + memset(tempData->used_option, 0, sizeof(tempData->used_option)); + memset(tempData->codebooks.usedCB4, 0, + sizeof(tempData->codebooks.usedCB4)); + memset(tempData->codebooks.usedCB2, 0, + sizeof(tempData->codebooks.usedCB2)); - /* Quake 3 can't handle chunks bigger than 65536 bytes */ - if (tempData.mainChunkSize/8 > 65536) { - enc->lambda *= .8; goto retry_encode; } - remap_codebooks(enc, &tempData); + remap_codebooks(enc, tempData); - write_codebooks(enc, &tempData); + write_codebooks(enc, tempData); - reconstruct_and_encode_image(enc, &tempData, enc->width, enc->height, + reconstruct_and_encode_image(enc, tempData, enc->width, enc->height, enc->width*enc->height/64); /* Rotate frame history */ @@ -910,17 +945,37 @@ static void roq_encode_video(RoqContext *enc) FFSWAP(motion_vect *, enc->last_motion4, enc->this_motion4); FFSWAP(motion_vect *, enc->last_motion8, enc->this_motion8); - av_free(tempData.cel_evals); - av_free(tempData.closest_cb2); + av_free(tempData->cel_evals); + av_free(tempData->closest_cb2); enc->framesSinceKeyframe++; + + return 0; +} + +static av_cold int roq_encode_end(AVCodecContext *avctx) +{ + RoqContext *enc = avctx->priv_data; + + av_frame_free(&enc->current_frame); + av_frame_free(&enc->last_frame); + + av_free(enc->tmpData); + av_free(enc->this_motion4); + av_free(enc->last_motion4); + av_free(enc->this_motion8); + av_free(enc->last_motion8); + + return 0; } -static int roq_encode_init(AVCodecContext *avctx) +static av_cold int roq_encode_init(AVCodecContext *avctx) { RoqContext *enc = avctx->priv_data; - av_init_random(1, &enc->randctx); + av_lfg_init(&enc->randctx, 1); + + enc->avctx = avctx; enc->framesSinceKeyframe = 0; if ((avctx->width & 0xf) || (avctx->height & 0xf)) { @@ -931,20 +986,20 @@ static int roq_encode_init(AVCodecContext *avctx) if (((avctx->width)&(avctx->width-1))||((avctx->height)&(avctx->height-1))) av_log(avctx, AV_LOG_ERROR, "Warning: dimensions not power of two\n"); - if (avcodec_check_dimensions(avctx, avctx->width, avctx->height)) { - av_log(avctx, AV_LOG_ERROR, "Invalid dimensions (%dx%d)\n", - avctx->width, avctx->height); - return -1; - } - enc->width = avctx->width; enc->height = avctx->height; enc->framesSinceKeyframe = 0; enc->first_frame = 1; - enc->last_frame = &enc->frames[0]; - enc->current_frame = &enc->frames[1]; + enc->last_frame = av_frame_alloc(); + enc->current_frame = av_frame_alloc(); + if (!enc->last_frame || !enc->current_frame) { + roq_encode_end(avctx); + return AVERROR(ENOMEM); + } + + enc->tmpData = av_malloc(sizeof(RoqTempdata)); enc->this_motion4 = av_mallocz((enc->width*enc->height/16)*sizeof(motion_vect)); @@ -986,15 +1041,12 @@ static void roq_write_video_info_chunk(RoqContext *enc) bytestream_put_byte(&enc->out_buf, 0x00); } -static int roq_encode_frame(AVCodecContext *avctx, unsigned char *buf, int buf_size, void *data) +static int roq_encode_frame(AVCodecContext *avctx, AVPacket *pkt, + const AVFrame *frame, int *got_packet) { RoqContext *enc = avctx->priv_data; - AVFrame *frame= data; - uint8_t *buf_start = buf; + int size, ret; - enc->out_buf = buf; - enc->y_stride = frame->linesize[0]; - enc->c_stride = frame->linesize[1]; enc->avctx = avctx; enc->frame_to_enc = frame; @@ -1006,20 +1058,22 @@ static int roq_encode_frame(AVCodecContext *avctx, unsigned char *buf, int buf_s /* 138 bits max per 8x8 block + * 256 codebooks*(6 bytes 2x2 + 4 bytes 4x4) + 8 bytes frame header */ - if (((enc->width*enc->height/64)*138+7)/8 + 256*(6+4) + 8 > buf_size) { - av_log(avctx, AV_LOG_ERROR, " RoQ: Output buffer too small!\n"); - return -1; + size = ((enc->width * enc->height / 64) * 138 + 7) / 8 + 256 * (6 + 4) + 8; + if ((ret = ff_alloc_packet(pkt, size)) < 0) { + av_log(avctx, AV_LOG_ERROR, "Error getting output packet with size %d.\n", size); + return ret; } + enc->out_buf = pkt->data; - /* Check for I frame */ + /* Check for I-frame */ if (enc->framesSinceKeyframe == avctx->gop_size) enc->framesSinceKeyframe = 0; if (enc->first_frame) { /* Alloc memory for the reconstruction data (we must know the stride for that) */ - if (avctx->get_buffer(avctx, enc->current_frame) || - avctx->get_buffer(avctx, enc->last_frame)) { + if (ff_get_buffer(avctx, enc->current_frame, 0) || + ff_get_buffer(avctx, enc->last_frame, 0)) { av_log(avctx, AV_LOG_ERROR, " RoQ: get_buffer() failed\n"); return -1; } @@ -1031,35 +1085,28 @@ static int roq_encode_frame(AVCodecContext *avctx, unsigned char *buf, int buf_s } /* Encode the actual frame */ - roq_encode_video(enc); - - return enc->out_buf - buf_start; -} - -static int roq_encode_end(AVCodecContext *avctx) -{ - RoqContext *enc = avctx->priv_data; + ret = roq_encode_video(enc); + if (ret < 0) + return ret; - avctx->release_buffer(avctx, enc->last_frame); - avctx->release_buffer(avctx, enc->current_frame); - - av_free(enc->this_motion4); - av_free(enc->last_motion4); - av_free(enc->this_motion8); - av_free(enc->last_motion8); + pkt->size = enc->out_buf - pkt->data; + if (enc->framesSinceKeyframe == 1) + pkt->flags |= AV_PKT_FLAG_KEY; + *got_packet = 1; return 0; } -AVCodec roq_encoder = -{ - "roqvideo", - CODEC_TYPE_VIDEO, - CODEC_ID_ROQ, - sizeof(RoqContext), - roq_encode_init, - roq_encode_frame, - roq_encode_end, - .supported_framerates = (AVRational[]){{30,1}, {0,0}}, - .pix_fmts = (enum PixelFormat[]){PIX_FMT_YUV444P, -1}, +AVCodec ff_roq_encoder = { + .name = "roqvideo", + .long_name = NULL_IF_CONFIG_SMALL("id RoQ video"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_ROQ, + .priv_data_size = sizeof(RoqContext), + .init = roq_encode_init, + .encode2 = roq_encode_frame, + .close = roq_encode_end, + .supported_framerates = (const AVRational[]){ {30,1}, {0,0} }, + .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV444P, + AV_PIX_FMT_NONE }, };