X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Froqvideoenc.c;h=fd651e9552607c47ef4ba181e28e2cbde4b69016;hb=3a651f599a18b023602370b67a77eb0efa309b20;hp=e7cfcb703603a1b946957e07836a39841c554f4f;hpb=3aab27b4599911aad2fd9dc14cdc66414bf27eb3;p=ffmpeg diff --git a/libavcodec/roqvideoenc.c b/libavcodec/roqvideoenc.c index e7cfcb70360..fd651e95526 100644 --- a/libavcodec/roqvideoenc.c +++ b/libavcodec/roqvideoenc.c @@ -5,27 +5,27 @@ * 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 libavcodec/roqvideoenc.c + * @file * id RoQ encoder by Vitor. Based on the Switchblade3 library and the - * Switchblade3 FFmpeg glue by Eric Lasota. + * Switchblade3 Libav glue by Eric Lasota. */ /* @@ -56,9 +56,11 @@ #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 *stride1, int *stride2, 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; @@ -165,7 +167,7 @@ static int eval_motion_dist(RoqContext *enc, int x, int y, motion_vect vect, } /** - * Returns distortion between two macroblocks + * @return distortion between two macroblocks */ static inline int squared_diff_macroblock(uint8_t a[], uint8_t b[], int size) { @@ -181,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; @@ -192,8 +193,7 @@ typedef struct int cbEntry; } SubcelEvaluation; -typedef struct -{ +typedef struct CelEvaluation { int eval_dist[4]; int best_coding; @@ -205,8 +205,7 @@ typedef struct int sourceX, sourceY; } CelEvaluation; -typedef struct -{ +typedef struct RoqCodebooks { int numCB4; int numCB2; int usedCB2[MAX_CBS_2x2]; @@ -240,13 +239,15 @@ typedef struct RoqTempData } RoqTempdata; /** - * Initializes cel evaluators and sets their source coordinates + * Initialize cel evaluators and set their source coordinates */ -static void create_cel_evals(RoqContext *enc, RoqTempdata *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(CelEvaluation)); + if (!tempData->cel_evals) + return AVERROR(ENOMEM); /* Map to the ROQ quadtree order */ for (y=0; yheight; y+=16) @@ -255,12 +256,14 @@ static void create_cel_evals(RoqContext *enc, RoqTempdata *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; @@ -393,7 +396,7 @@ 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(SubcelEvaluation *subcel, int x, int y, RoqContext *enc, RoqTempdata *tempData) @@ -457,7 +460,7 @@ static void gather_data_for_subcel(SubcelEvaluation *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(CelEvaluation *cel, RoqContext *enc, RoqTempdata *tempData) @@ -596,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]; @@ -754,8 +756,8 @@ static void reconstruct_and_encode_image(RoqContext *enc, RoqTempdata *tempData, /** * 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; @@ -773,9 +775,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; @@ -789,26 +791,36 @@ static void create_clusters(AVFrame *frame, int w, int h, uint8_t *yuvClusters) } } -static void generate_codebook(RoqContext *enc, RoqTempdata *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; 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, RoqTempdata *tempData) +static int generate_new_codebooks(RoqContext *enc, RoqTempdata *tempData) { - int i,j; + int i, j, ret = 0; RoqCodebooks *codebooks = &tempData->codebooks; int max = enc->width*enc->height/16; uint8_t mb2[3*4]; @@ -835,6 +850,11 @@ static void generate_new_codebooks(RoqContext *enc, RoqTempdata *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); @@ -845,14 +865,22 @@ static void generate_new_codebooks(RoqContext *enc, RoqTempdata *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; @@ -872,22 +900,27 @@ static void generate_new_codebooks(RoqContext *enc, RoqTempdata *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) { RoqTempdata *tempData = enc->tmpData; - int i; + int i, ret; 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); @@ -898,9 +931,20 @@ static void roq_encode_video(RoqContext *enc) for (i=0; iwidth*enc->height/64; i++) gather_data_for_cel(tempData->cel_evals + i, enc, tempData); - /* Quake 3 can't handle chunks bigger than 65536 bytes */ - if (tempData->mainChunkSize/8 > 65536) { - enc->lambda *= .8; + /* 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)); + goto retry_encode; } @@ -922,9 +966,27 @@ static void roq_encode_video(RoqContext *enc) av_free(tempData->closest_cb2); enc->framesSinceKeyframe++; + + return 0; } -static int roq_encode_init(AVCodecContext *avctx) +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 av_cold int roq_encode_init(AVCodecContext *avctx) { RoqContext *enc = avctx->priv_data; @@ -939,20 +1001,18 @@ 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)); @@ -996,13 +1056,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->avctx = avctx; enc->frame_to_enc = frame; @@ -1014,10 +1073,12 @@ 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 */ if (enc->framesSinceKeyframe == avctx->gop_size) @@ -1026,8 +1087,8 @@ static int roq_encode_frame(AVCodecContext *avctx, unsigned char *buf, int buf_s 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; } @@ -1039,37 +1100,28 @@ static int roq_encode_frame(AVCodecContext *avctx, unsigned char *buf, int buf_s } /* Encode the actual frame */ - roq_encode_video(enc); + ret = roq_encode_video(enc); + if (ret < 0) + return ret; - return enc->out_buf - buf_start; -} - -static int roq_encode_end(AVCodecContext *avctx) -{ - RoqContext *enc = avctx->priv_data; - - avctx->release_buffer(avctx, enc->last_frame); - avctx->release_buffer(avctx, enc->current_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); + 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, PIX_FMT_NONE}, - .long_name = NULL_IF_CONFIG_SMALL("id RoQ video"), +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 }, };