X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Fmpegvideo_xvmc.c;h=159fe21b58edb9cbd68f0e3e0a4814b637671967;hb=5d42ac7ffb5471666136402a553454caf3a3c989;hp=edef2bcfd735d834add0b8597fd2cafb12dd3641;hpb=095edd3e6112078be8cb9e1b866b82707a6fbac8;p=ffmpeg diff --git a/libavcodec/mpegvideo_xvmc.c b/libavcodec/mpegvideo_xvmc.c index edef2bcfd73..159fe21b58e 100644 --- a/libavcodec/mpegvideo_xvmc.c +++ b/libavcodec/mpegvideo_xvmc.c @@ -2,24 +2,25 @@ * XVideo Motion Compensation * Copyright (c) 2003 Ivan Kalvachev * - * 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 */ #include +#include #include "avcodec.h" #include "dsputil.h" @@ -32,19 +33,24 @@ #include "xvmc_internal.h" /** - * Initializes the block field of the MpegEncContext pointer passed as + * Initialize the block field of the MpegEncContext pointer passed as * parameter after making sure that the data is not corrupted. + * In order to implement something like direct rendering instead of decoding + * coefficients in s->blocks and then copying them, copy them directly + * into the data_blocks array provided by xvmc. */ void ff_xvmc_init_block(MpegEncContext *s) { - struct xvmc_pixfmt_render *render = (struct xvmc_pixfmt_render*)s->current_picture.data[2]; - if (!render || render->magic_id != AV_XVMC_RENDER_MAGIC) { - assert(0); - return; // make sure that this is a render packet - } - s->block = (DCTELEM *)(render->data_blocks + render->next_free_data_block_num * 64); + struct xvmc_pix_fmt *render = (struct xvmc_pix_fmt*)s->current_picture.f.data[2]; + assert(render && render->xvmc_id == AV_XVMC_ID); + + s->block = (DCTELEM (*)[64])(render->data_blocks + render->next_free_data_block_num * 64); } +/** + * Fill individual block pointers, so there are no gaps in the data_block array + * in case not all blocks in the macroblock are coded. + */ void ff_xvmc_pack_pblocks(MpegEncContext *s, int cbp) { int i, j = 0; @@ -53,54 +59,72 @@ void ff_xvmc_pack_pblocks(MpegEncContext *s, int cbp) cbp <<= 12-mb_block_count; for (i = 0; i < mb_block_count; i++) { if (cbp & (1 << 11)) - s->pblocks[i] = (short *)(&s->block[j++]); + s->pblocks[i] = &s->block[j++]; else s->pblocks[i] = NULL; - cbp+=cbp; + cbp += cbp; } } /** + * Find and store the surfaces that are used as reference frames. * This function should be called for every new field and/or frame. * It should be safe to call the function a few times for the same field. */ -int ff_xvmc_field_start(MpegEncContext*s, AVCodecContext *avctx) +int ff_xvmc_field_start(MpegEncContext *s, AVCodecContext *avctx) { - struct xvmc_pixfmt_render *last, *next, *render = (struct xvmc_pixfmt_render*)s->current_picture.data[2]; + struct xvmc_pix_fmt *last, *next, *render = (struct xvmc_pix_fmt*)s->current_picture.f.data[2]; + const int mb_block_count = 4 + (1 << s->chroma_format); assert(avctx); - if (!render || render->magic_id != AV_XVMC_RENDER_MAGIC) + if (!render || render->xvmc_id != AV_XVMC_ID || + !render->data_blocks || !render->mv_blocks || + (unsigned int)render->allocated_mv_blocks > INT_MAX/(64*6) || + (unsigned int)render->allocated_data_blocks > INT_MAX/64 || + !render->p_surface) { + av_log(avctx, AV_LOG_ERROR, + "Render token doesn't look as expected.\n"); return -1; // make sure that this is a render packet - - render->picture_structure = s->picture_structure; - render->flags = s->first_field ? 0 : XVMC_SECOND_FIELD; + } if (render->filled_mv_blocks_num) { av_log(avctx, AV_LOG_ERROR, - "Rendering surface contains %i unprocessed blocks\n", + "Rendering surface contains %i unprocessed blocks.\n", render->filled_mv_blocks_num); return -1; } + if (render->allocated_mv_blocks < 1 || + render->allocated_data_blocks < render->allocated_mv_blocks*mb_block_count || + render->start_mv_blocks_num >= render->allocated_mv_blocks || + render->next_free_data_block_num > + render->allocated_data_blocks - + mb_block_count*(render->allocated_mv_blocks-render->start_mv_blocks_num)) { + av_log(avctx, AV_LOG_ERROR, + "Rendering surface doesn't provide enough block structures to work with.\n"); + return -1; + } - render->p_future_surface = NULL; - render->p_past_surface = NULL; + render->picture_structure = s->picture_structure; + render->flags = s->first_field ? 0 : XVMC_SECOND_FIELD; + render->p_future_surface = NULL; + render->p_past_surface = NULL; switch(s->pict_type) { - case FF_I_TYPE: + case AV_PICTURE_TYPE_I: return 0; // no prediction from other frames - case FF_B_TYPE: - next = (struct xvmc_pixfmt_render*)s->next_picture.data[2]; + case AV_PICTURE_TYPE_B: + next = (struct xvmc_pix_fmt*)s->next_picture.f.data[2]; if (!next) return -1; - if (next->magic_id != AV_XVMC_RENDER_MAGIC) + if (next->xvmc_id != AV_XVMC_ID) return -1; render->p_future_surface = next->p_surface; // no return here, going to set forward prediction - case FF_P_TYPE: - last = (struct xvmc_pixfmt_render*)s->last_picture.data[2]; + case AV_PICTURE_TYPE_P: + last = (struct xvmc_pix_fmt*)s->last_picture.f.data[2]; if (!last) last = render; // predict second field from the first - if (last->magic_id != AV_XVMC_RENDER_MAGIC) + if (last->xvmc_id != AV_XVMC_ID) return -1; render->p_past_surface = last->p_surface; return 0; @@ -110,22 +134,28 @@ return -1; } /** - * This function should be called for every new field and/or frame. + * Complete frame/field rendering by passing any remaining blocks. + * Normally ff_draw_horiz_band() is called for each slice, however, + * some leftover blocks, for example from error_resilience(), may remain. * It should be safe to call the function a few times for the same field. */ void ff_xvmc_field_end(MpegEncContext *s) { - struct xvmc_pixfmt_render *render = (struct xvmc_pixfmt_render*)s->current_picture.data[2]; + struct xvmc_pix_fmt *render = (struct xvmc_pix_fmt*)s->current_picture.f.data[2]; assert(render); if (render->filled_mv_blocks_num > 0) ff_draw_horiz_band(s, 0, 0); } +/** + * Synthesize the data needed by XvMC to render one macroblock of data. + * Fill all relevant fields, if necessary do IDCT. + */ void ff_xvmc_decode_mb(MpegEncContext *s) { XvMCMacroBlock *mv_block; - struct xvmc_pixfmt_render *render; + struct xvmc_pix_fmt *render; int i, cbp, blocks_per_mb; const int mb_xy = s->mb_y * s->mb_stride + s->mb_x; @@ -149,12 +179,12 @@ void ff_xvmc_decode_mb(MpegEncContext *s) // Do I need to export quant when I could not perform postprocessing? // Anyway, it doesn't hurt. - s->current_picture.qscale_table[mb_xy] = s->qscale; + s->current_picture.f.qscale_table[mb_xy] = s->qscale; // start of XVMC-specific code - render = (struct xvmc_pixfmt_render*)s->current_picture.data[2]; + render = (struct xvmc_pix_fmt*)s->current_picture.f.data[2]; assert(render); - assert(render->magic_id == AV_XVMC_RENDER_MAGIC); + assert(render->xvmc_id == AV_XVMC_ID); assert(render->mv_blocks); // take the next free macroblock @@ -255,9 +285,9 @@ void ff_xvmc_decode_mb(MpegEncContext *s) if (s->flags & CODEC_FLAG_GRAY) { if (s->mb_intra) { // intra frames are always full chroma blocks for (i = 4; i < blocks_per_mb; i++) { - memset(s->pblocks[i], 0, sizeof(short)*64); // so we need to clear them + memset(s->pblocks[i], 0, sizeof(*s->pblocks[i])); // so we need to clear them if (!render->unsigned_intra) - s->pblocks[i][0] = 1 << 10; + *s->pblocks[i][0] = 1 << 10; } } else { cbp &= 0xf << (blocks_per_mb - 4); @@ -271,10 +301,10 @@ void ff_xvmc_decode_mb(MpegEncContext *s) for (i = 0; i < blocks_per_mb; i++) { if (s->block_last_index[i] >= 0) { // I do not have unsigned_intra MOCO to test, hope it is OK. - if (s->mb_intra && (render->idct || (!render->idct && !render->unsigned_intra))) - s->pblocks[i][0] -= 1 << 10; + if (s->mb_intra && (render->idct || !render->unsigned_intra)) + *s->pblocks[i][0] -= 1 << 10; if (!render->idct) { - s->dsp.idct(s->pblocks[i]); + s->dsp.idct(*s->pblocks[i]); /* It is unclear if MC hardware requires pixel diff values to be * in the range [-255;255]. TODO: Clipping if such hardware is * ever found. As of now it would only be an unnecessary @@ -283,20 +313,20 @@ void ff_xvmc_decode_mb(MpegEncContext *s) // copy blocks only if the codec doesn't support pblocks reordering if (s->avctx->xvmc_acceleration == 1) { memcpy(&render->data_blocks[render->next_free_data_block_num*64], - s->pblocks[i], sizeof(short)*64); + s->pblocks[i], sizeof(*s->pblocks[i])); } render->next_free_data_block_num++; } } render->filled_mv_blocks_num++; - assert(render->filled_mv_blocks_num <= render->total_number_of_mv_blocks); - assert(render->next_free_data_block_num <= render->total_number_of_data_blocks); + assert(render->filled_mv_blocks_num <= render->allocated_mv_blocks); + assert(render->next_free_data_block_num <= render->allocated_data_blocks); /* The above conditions should not be able to fail as long as this function * is used and the following 'if ()' automatically calls a callback to free * blocks. */ - if (render->filled_mv_blocks_num >= render->total_number_of_mv_blocks) + if (render->filled_mv_blocks_num == render->allocated_mv_blocks) ff_draw_horiz_band(s, 0, 0); }