X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Froqvideoenc.c;h=0ae6190bb8c5b064f953e678dd0cebc17a99698a;hb=ddcf67c8a51c67b122a826d8b5819e96d591d813;hp=ca939b9c490cbfbfef0a760ed8f69cee95bbb515;hpb=3a66812fc859b0fa374a9a72e36d8cef27838b08;p=ffmpeg diff --git a/libavcodec/roqvideoenc.c b/libavcodec/roqvideoenc.c index ca939b9c490..0ae6190bb8c 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 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,12 @@ */ #include -#include #include "roqvideo.h" #include "bytestream.h" #include "elbg.h" +#include "internal.h" +#include "mathops.h" #define CHROMA_BIAS 1 @@ -112,7 +113,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 +125,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 +166,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) { @@ -190,20 +191,20 @@ typedef struct int subCels[4]; motion_vect motion; int cbEntry; -} subcel_evaluation_t; +} SubcelEvaluation; typedef struct { 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 { @@ -214,14 +215,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]; @@ -233,20 +234,20 @@ 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 void 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)); /* Map to the ROQ quadtree order */ for (y=0; yheight; y+=16) @@ -260,7 +261,7 @@ static void create_cel_evals(RoqContext *enc, roq_tempdata_t *tempData) /** * 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,10 +394,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]; @@ -457,10 +458,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; @@ -533,7 +534,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; @@ -565,7 +566,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; @@ -620,7 +621,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; @@ -628,7 +629,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; @@ -754,8 +755,8 @@ static void reconstruct_and_encode_image(RoqContext *enc, roq_tempdata_t *tempDa /** * 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 +774,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,13 +790,13 @@ static void create_clusters(AVFrame *frame, int w, int h, uint8_t *yuvClusters) } } -static void generate_codebook(RoqContext *enc, roq_tempdata_t *tempdata, +static void generate_codebook(RoqContext *enc, RoqTempdata *tempdata, int *points, int inputCount, roq_cell *results, int size, int cbsize) { int i, j, k; int c_size = size*size/4; - int *buf = points; + int *buf; int *codebook = av_malloc(6*c_size*cbsize*sizeof(int)); int *closest_cb; @@ -824,10 +825,10 @@ static void generate_codebook(RoqContext *enc, roq_tempdata_t *tempdata, av_free(codebook); } -static void generate_new_codebooks(RoqContext *enc, roq_tempdata_t *tempData) +static void generate_new_codebooks(RoqContext *enc, RoqTempdata *tempData) { int i,j; - roq_codebooks_t *codebooks = &tempData->codebooks; + 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); @@ -880,14 +881,14 @@ static void generate_new_codebooks(RoqContext *enc, roq_tempdata_t *tempData) static void roq_encode_video(RoqContext *enc) { - roq_tempdata_t tempData; + RoqTempdata *tempData = enc->tmpData; int i; - memset(&tempData, 0, sizeof(tempData)); + memset(tempData, 0, sizeof(*tempData)); - create_cel_evals(enc, &tempData); + create_cel_evals(enc, tempData); - generate_new_codebooks(enc, &tempData); + generate_new_codebooks(enc, tempData); if (enc->framesSinceKeyframe >= 1) { motion_search(enc, 8); @@ -896,28 +897,41 @@ 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); + enc->avctx->coded_frame = enc->current_frame; + /* Rotate frame history */ FFSWAP(AVFrame *, enc->current_frame, enc->last_frame); 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++; } @@ -926,7 +940,7 @@ static int roq_encode_init(AVCodecContext *avctx) { RoqContext *enc = avctx->priv_data; - av_init_random(1, &enc->randctx); + av_lfg_init(&enc->randctx, 1); enc->framesSinceKeyframe = 0; if ((avctx->width & 0xf) || (avctx->height & 0xf)) { @@ -937,12 +951,6 @@ 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; @@ -952,6 +960,8 @@ static int roq_encode_init(AVCodecContext *avctx) enc->last_frame = &enc->frames[0]; enc->current_frame = &enc->frames[1]; + enc->tmpData = av_malloc(sizeof(RoqTempdata)); + enc->this_motion4 = av_mallocz((enc->width*enc->height/16)*sizeof(motion_vect)); @@ -992,13 +1002,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; @@ -1010,10 +1019,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) @@ -1037,7 +1048,12 @@ 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; + pkt->size = enc->out_buf - pkt->data; + if (enc->framesSinceKeyframe == 1) + pkt->flags |= AV_PKT_FLAG_KEY; + *got_packet = 1; + + return 0; } static int roq_encode_end(AVCodecContext *avctx) @@ -1047,6 +1063,7 @@ static int roq_encode_end(AVCodecContext *avctx) 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); @@ -1055,15 +1072,15 @@ static int roq_encode_end(AVCodecContext *avctx) 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", + .type = AVMEDIA_TYPE_VIDEO, + .id = 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 PixelFormat[]){PIX_FMT_YUV444P, PIX_FMT_NONE}, + .long_name = NULL_IF_CONFIG_SMALL("id RoQ video"), };