diff options
Diffstat (limited to 'libavcodec/roqvideoenc.c')
-rw-r--r-- | libavcodec/roqvideoenc.c | 119 |
1 files changed, 74 insertions, 45 deletions
diff --git a/libavcodec/roqvideoenc.c b/libavcodec/roqvideoenc.c index 872bee8..89879e8 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 Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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, + * FFmpeg 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 + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @file * id RoQ encoder by Vitor. Based on the Switchblade3 library and the - * Switchblade3 Libav glue by Eric Lasota. + * Switchblade3 FFmpeg glue by Eric Lasota. */ /* @@ -57,6 +57,7 @@ #include <string.h> #include "libavutil/attributes.h" +#include "libavutil/opt.h" #include "roqvideo.h" #include "bytestream.h" #include "elbg.h" @@ -69,7 +70,7 @@ * Maximum number of generated 4x4 codebooks. Can't be 256 to workaround a * Quake 3 bug. */ -#define MAX_CBS_4x4 255 +#define MAX_CBS_4x4 256 #define MAX_CBS_2x2 256 ///< Maximum number of 2x2 codebooks. @@ -245,7 +246,7 @@ 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)); + tempData->cel_evals = av_malloc_array(enc->width*enc->height/64, sizeof(CelEvaluation)); if (!tempData->cel_evals) return AVERROR(ENOMEM); @@ -541,7 +542,7 @@ static void remap_codebooks(RoqContext *enc, RoqTempdata *tempData) int i, j, idx=0; /* Make remaps for the final codebook usage */ - for (i=0; i<MAX_CBS_4x4; i++) { + for (i=0; i<(enc->quake3_compat ? MAX_CBS_4x4-1 : MAX_CBS_4x4); i++) { if (tempData->codebooks.usedCB4[i]) { tempData->i2f4[i] = idx; tempData->f2i4[idx] = i; @@ -798,14 +799,14 @@ static int generate_codebook(RoqContext *enc, RoqTempdata *tempdata, 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 *codebook = av_malloc_array(6*c_size, cbsize*sizeof(int)); int *closest_cb; if (!codebook) return AVERROR(ENOMEM); if (size == 4) { - closest_cb = av_malloc(6*c_size*inputCount*sizeof(int)); + closest_cb = av_malloc_array(6*c_size, inputCount*sizeof(int)); if (!closest_cb) { ret = AVERROR(ENOMEM); goto out; @@ -813,11 +814,11 @@ static int generate_codebook(RoqContext *enc, RoqTempdata *tempdata, } else closest_cb = tempdata->closest_cb2; - ret = ff_init_elbg(points, 6 * c_size, inputCount, codebook, + ret = avpriv_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, + ret = avpriv_do_elbg(points, 6 * c_size, inputCount, codebook, cbsize, 1, closest_cb, &enc->randctx); if (ret < 0) goto out; @@ -846,8 +847,8 @@ static int generate_new_codebooks(RoqContext *enc, RoqTempdata *tempData) int max = enc->width*enc->height/16; uint8_t mb2[3*4]; roq_cell *results4 = av_malloc(sizeof(roq_cell)*MAX_CBS_4x4*4); - uint8_t *yuvClusters=av_malloc(sizeof(int)*max*6*4); - int *points = av_malloc(max*6*4*sizeof(int)); + uint8_t *yuvClusters=av_malloc_array(max, sizeof(int)*6*4); + int *points = av_malloc_array(max, 6*4*sizeof(int)); int bias; if (!results4 || !yuvClusters || !points) { @@ -866,12 +867,12 @@ static int generate_new_codebooks(RoqContext *enc, RoqTempdata *tempData) /* Create 4x4 codebooks */ if ((ret = generate_codebook(enc, tempData, points, max, - results4, 4, MAX_CBS_4x4)) < 0) + results4, 4, (enc->quake3_compat ? MAX_CBS_4x4-1 : MAX_CBS_4x4))) < 0) goto out; - codebooks->numCB4 = MAX_CBS_4x4; + codebooks->numCB4 = (enc->quake3_compat ? MAX_CBS_4x4-1 : MAX_CBS_4x4); - tempData->closest_cb2 = av_malloc(max*4*sizeof(int)); + tempData->closest_cb2 = av_malloc_array(max, 4*sizeof(int)); if (!tempData->closest_cb2) { ret = AVERROR(ENOMEM); goto out; @@ -932,10 +933,14 @@ static int roq_encode_video(RoqContext *enc) 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) { + if (tempData->mainChunkSize/8 > 65535 && enc->quake3_compat) { + if (enc->lambda > 100000) { + av_log(enc->avctx, AV_LOG_ERROR, "Cannot encode video in Quake compatible form\n"); + return AVERROR(EINVAL); + } av_log(enc->avctx, AV_LOG_ERROR, - "Warning, generated a frame too big (%d > 65535), " - "try using a smaller qscale value.\n", + "Warning, generated a frame too big for Quake (%d > 65535), " + "now switching to a bigger qscale value.\n", tempData->mainChunkSize/8); enc->lambda *= 1.5; tempData->mainChunkSize = 0; @@ -955,15 +960,16 @@ static int roq_encode_video(RoqContext *enc) reconstruct_and_encode_image(enc, tempData, enc->width, enc->height, enc->width*enc->height/64); - enc->avctx->coded_frame = enc->current_frame; + av_frame_unref(enc->avctx->coded_frame); + av_frame_ref(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_freep(&tempData->cel_evals); + av_freep(&tempData->closest_cb2); enc->framesSinceKeyframe++; @@ -976,12 +982,13 @@ static av_cold int roq_encode_end(AVCodecContext *avctx) av_frame_free(&enc->current_frame); av_frame_free(&enc->last_frame); + av_frame_free(&enc->avctx->coded_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); + av_freep(&enc->tmpData); + av_freep(&enc->this_motion4); + av_freep(&enc->last_motion4); + av_freep(&enc->this_motion8); + av_freep(&enc->last_motion8); return 0; } @@ -997,11 +1004,16 @@ static av_cold int roq_encode_init(AVCodecContext *avctx) enc->framesSinceKeyframe = 0; if ((avctx->width & 0xf) || (avctx->height & 0xf)) { av_log(avctx, AV_LOG_ERROR, "Dimensions must be divisible by 16\n"); - return -1; + return AVERROR(EINVAL); + } + + if (avctx->width > 65535 || avctx->height > 65535) { + av_log(avctx, AV_LOG_ERROR, "Dimensions are max %d\n", enc->quake3_compat ? 32768 : 65535); + return AVERROR(EINVAL); } if (((avctx->width)&(avctx->width-1))||((avctx->height)&(avctx->height-1))) - av_log(avctx, AV_LOG_ERROR, "Warning: dimensions not power of two\n"); + av_log(avctx, AV_LOG_ERROR, "Warning: dimensions not power of two, this is not supported by quake\n"); enc->width = avctx->width; enc->height = avctx->height; @@ -1011,7 +1023,8 @@ static av_cold int roq_encode_init(AVCodecContext *avctx) enc->last_frame = av_frame_alloc(); enc->current_frame = av_frame_alloc(); - if (!enc->last_frame || !enc->current_frame) { + avctx->coded_frame = av_frame_alloc(); + if (!enc->last_frame || !enc->current_frame || !avctx->coded_frame) { roq_encode_end(avctx); return AVERROR(ENOMEM); } @@ -1019,16 +1032,22 @@ static av_cold int roq_encode_init(AVCodecContext *avctx) enc->tmpData = av_malloc(sizeof(RoqTempdata)); enc->this_motion4 = - av_mallocz((enc->width*enc->height/16)*sizeof(motion_vect)); + av_mallocz_array((enc->width*enc->height/16), sizeof(motion_vect)); enc->last_motion4 = - av_malloc ((enc->width*enc->height/16)*sizeof(motion_vect)); + av_malloc_array ((enc->width*enc->height/16), sizeof(motion_vect)); enc->this_motion8 = - av_mallocz((enc->width*enc->height/64)*sizeof(motion_vect)); + av_mallocz_array((enc->width*enc->height/64), sizeof(motion_vect)); enc->last_motion8 = - av_malloc ((enc->width*enc->height/64)*sizeof(motion_vect)); + av_malloc_array ((enc->width*enc->height/64), sizeof(motion_vect)); + + if (!enc->tmpData || !enc->this_motion4 || !enc->last_motion4 || + !enc->this_motion8 || !enc->last_motion8) { + roq_encode_end(avctx); + return AVERROR(ENOMEM); + } return 0; } @@ -1076,10 +1095,8 @@ static int roq_encode_frame(AVCodecContext *avctx, AVPacket *pkt, /* 138 bits max per 8x8 block + * 256 codebooks*(6 bytes 2x2 + 4 bytes 4x4) + 8 bytes frame header */ 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); + if ((ret = ff_alloc_packet2(avctx, pkt, size)) < 0) return ret; - } enc->out_buf = pkt->data; /* Check for I frame */ @@ -1089,11 +1106,9 @@ static int roq_encode_frame(AVCodecContext *avctx, AVPacket *pkt, if (enc->first_frame) { /* Alloc memory for the reconstruction data (we must know the stride for that) */ - 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; - } + if ((ret = ff_get_buffer(avctx, enc->current_frame, 0)) < 0 || + (ret = ff_get_buffer(avctx, enc->last_frame, 0)) < 0) + return ret; /* Before the first video frame, write a "video info" chunk */ roq_write_video_info_chunk(enc); @@ -1114,6 +1129,20 @@ static int roq_encode_frame(AVCodecContext *avctx, AVPacket *pkt, return 0; } +#define OFFSET(x) offsetof(RoqContext, x) +#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + { "quake3_compat", "Whether to respect known limitations in Quake 3 decoder", OFFSET(quake3_compat), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, VE }, + { NULL }, +}; + +static const AVClass roq_class = { + .class_name = "RoQ", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + AVCodec ff_roq_encoder = { .name = "roqvideo", .long_name = NULL_IF_CONFIG_SMALL("id RoQ video"), @@ -1123,7 +1152,7 @@ AVCodec ff_roq_encoder = { .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, + .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_NONE }, + .priv_class = &roq_class, }; |