History

avcodec/asvenc: Don't waste bits encoding non-visible part

Up until now, the encoder replicated all the border pixels for incomplete 16x16 macroblocks. In case the available width or height is <= 8, some of the luma blocks of the MB do not correspond to actual input, so that we should encode them using the least amount of bits. Zeroing the block coefficients (as this commit does) achieves this, replicating the pixels and performing an FDCT does not. This commit also removes the frame copying code for insufficiently aligned dimensions. The vsynth3-asv[12] FATE tests use a 34x34 input file and are therefore affected by this. As the ref updates show, the size and checksum of the encoded changes, yet the decoded output stays the same.

Andreas Rheinhardt committed 1 year ago

Commit 0401ca71 parent ae0f71a3 files · +84 −55Browse files

Unified · Split

3 files
  1. libavcodec/asvenc.c +80 −51
  2. tests/ref/vsynth/vsynth3-asv1 +2 −2
  3. tests/ref/vsynth/vsynth3-asv2 +2 −2
libavcodec/asvenc.c+80−51
@@ -26,6 +26,7 @@ · 25 unmodified lines
26#include "config_components.h"26#include "config_components.h"
27 27
28#include "libavutil/attributes.h"28#include "libavutil/attributes.h"
29#include "libavutil/intreadwrite.h"
29#include "libavutil/mem.h"30#include "libavutil/mem.h"
30#include "libavutil/mem_internal.h"31#include "libavutil/mem_internal.h"
31 32
@@ -228,55 +229,65 @@ static inline void dct_get(ASVEncContext *a, const AVFrame *frame, · 196 unmodified lines
228 }229 }
229}230}
230 231
231static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,232static void handle_partial_mb(ASVEncContext *a, const uint8_t *const data[3],
232 const AVFrame *pict, int *got_packet)233 const int linesizes[3],
234 int valid_width, int valid_height)
233{235{
234 ASVEncContext *const a = avctx->priv_data;236 const int nb_blocks = a->c.avctx->flags & AV_CODEC_FLAG_GRAY ? 4 : 6;
235 const ASVCommonContext *const c = &a->c;237 static const struct Descriptor {
236 int size, ret;238 uint8_t x_offset, y_offset;
237 239 uint8_t component, subsampling;
238 if (pict->width % 16 || pict->height % 16) {240 } block_descriptor[] = {
239 AVFrame *clone = av_frame_alloc();241 { 0, 0, 0, 0 }, { 8, 0, 0, 0 }, { 0, 8, 0, 0 }, { 8, 8, 0, 0 },
240 int i;242 { 0, 0, 1, 1 }, { 0, 0, 2, 1 },
241 243 };
242 if (!clone)244
243 return AVERROR(ENOMEM);245 for (int i = 0; i < nb_blocks; ++i) {
244 clone->format = pict->format;246 const struct Descriptor *const desc = block_descriptor + i;
245 clone->width = FFALIGN(pict->width, 16);247 int width_avail = AV_CEIL_RSHIFT(valid_width, desc->subsampling) - desc->x_offset;
246 clone->height = FFALIGN(pict->height, 16);248 int height_avail = AV_CEIL_RSHIFT(valid_height, desc->subsampling) - desc->y_offset;
247 ret = av_frame_get_buffer(clone, 0);249
248 if (ret < 0) {250 if (width_avail <= 0 || height_avail <= 0) {
249 av_frame_free(&clone);251 // This block is outside of the visible part; don't replicate pixels,
250 return ret;252 // just zero the block, so that only the dc value will be coded.
253 memset(a->block[i], 0, sizeof(a->block[i]));
254 continue;
251 }255 }
252 256 width_avail = FFMIN(width_avail, 8);
253 ret = av_frame_copy(clone, pict);257 height_avail = FFMIN(height_avail, 8);
254 if (ret < 0) {258
255 av_frame_free(&clone);259 ptrdiff_t linesize = linesizes[desc->component];
256 return ret;260 const uint8_t *src = data[desc->component] + desc->y_offset * linesize + desc->x_offset;
261 int16_t *block = a->block[i];
262
263 for (int h = 0;; block += 8, src += linesize) {
264 int16_t last;
265 for (int w = 0; w < width_avail; ++w)
266 last = block[w] = src[w];
267 for (int w = width_avail; w < 8; ++w)
268 block[w] = last;
269 if (++h == height_avail)
270 break;
257 }271 }
258 272 const int16_t *const last_row = block;
259 for (i = 0; i<3; i++) {273 for (int h = height_avail; h < 8; ++h) {
260 int x, y;274 block += 8;
261 int w = AV_CEIL_RSHIFT(pict->width, !!i);275 AV_COPY128(block, last_row);
262 int h = AV_CEIL_RSHIFT(pict->height, !!i);
263 int w2 = AV_CEIL_RSHIFT(clone->width, !!i);
264 int h2 = AV_CEIL_RSHIFT(clone->height, !!i);
265 for (y=0; y<h; y++)
266 for (x=w; x<w2; x++)
267 clone->data[i][x + y*clone->linesize[i]] =
268 clone->data[i][w - 1 + y*clone->linesize[i]];
269 for (y=h; y<h2; y++)
270 for (x=0; x<w2; x++)
271 clone->data[i][x + y*clone->linesize[i]] =
272 clone->data[i][x + (h-1)*clone->linesize[i]];
273 }276 }
274 ret = encode_frame(avctx, pkt, clone, got_packet);
275 277
276 av_frame_free(&clone);278 a->fdsp.fdct(a->block[i]);
277 return ret;
278 }279 }
279 280
281 encode_mb(a, a->block);
282}
283
284static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
285 const AVFrame *pict, int *got_packet)
286{
287 ASVEncContext *const a = avctx->priv_data;
288 const ASVCommonContext *const c = &a->c;
289 int size, ret;
290
280 ret = ff_alloc_packet(avctx, pkt, c->mb_height * c->mb_width * MAX_MB_SIZE + 3);291 ret = ff_alloc_packet(avctx, pkt, c->mb_height * c->mb_width * MAX_MB_SIZE + 3);
281 if (ret < 0)292 if (ret < 0)
282 return ret;293 return ret;
@@ -290,19 +301,37 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, · 7 unmodified lines
290 }301 }
291 }302 }
292 303
293 if (c->mb_width2 != c->mb_width) {304 if (avctx->width & 15) {
294 int mb_x = c->mb_width2;305 const uint8_t *src[3] = {
306 pict->data[0] + c->mb_width2 * 16,
307 pict->data[1] + c->mb_width2 * 8,
308 pict->data[2] + c->mb_width2 * 8,
309 };
310 int available_width = avctx->width & 15;
311
295 for (int mb_y = 0; mb_y < c->mb_height2; mb_y++) {312 for (int mb_y = 0; mb_y < c->mb_height2; mb_y++) {
296 dct_get(a, pict, mb_x, mb_y);313 handle_partial_mb(a, src, pict->linesize, available_width, 16);
297 encode_mb(a, a->block);314 src[0] += 16 * pict->linesize[0];
315 src[1] += 8 * pict->linesize[1];
316 src[2] += 8 * pict->linesize[2];
298 }317 }
299 }318 }
300 319
301 if (c->mb_height2 != c->mb_height) {320 if (avctx->height & 15) {
302 int mb_y = c->mb_height2;321 const uint8_t *src[3] = {
303 for (int mb_x = 0; mb_x < c->mb_width; mb_x++) {322 pict->data[0] + c->mb_height2 * 16 * pict->linesize[0],
304 dct_get(a, pict, mb_x, mb_y);323 pict->data[1] + c->mb_height2 * 8 * pict->linesize[1],
305 encode_mb(a, a->block);324 pict->data[2] + c->mb_height2 * 8 * pict->linesize[2],
325 };
326 int available_height = avctx->height & 15;
327
328 for (int remaining = avctx->width;; remaining -= 16) {
329 handle_partial_mb(a, src, pict->linesize, remaining, available_height);
330 if (remaining <= 16)
331 break;
332 src[0] += 16;
333 src[1] += 8;
334 src[2] += 8;
306 }335 }
307 }336 }
308 337
tests/ref/vsynth/vsynth3-asv1+2−2
@@ -1,4 +1,4 @@
181eeea0d0e6219b2f381cf2100e9a12f *tests/data/fate/vsynth3-asv1.avi169ae6df10440e68c53bee4e713851199 *tests/data/fate/vsynth3-asv1.avi
234704 tests/data/fate/vsynth3-asv1.avi231524 tests/data/fate/vsynth3-asv1.avi
33c8636e22a96267451684f42d7a6f608 *tests/data/fate/vsynth3-asv1.out.rawvideo33c8636e22a96267451684f42d7a6f608 *tests/data/fate/vsynth3-asv1.out.rawvideo
4stddev: 13.16 PSNR: 25.74 MAXDIFF: 112 bytes: 86700/ 867004stddev: 13.16 PSNR: 25.74 MAXDIFF: 112 bytes: 86700/ 86700
tests/ref/vsynth/vsynth3-asv2+2−2
@@ -1,4 +1,4 @@
18402fb1112fb8119c019154a472b5cd0 *tests/data/fate/vsynth3-asv2.avi163000eaedeb60bede8baeb090f02881a *tests/data/fate/vsynth3-asv2.avi
236208 tests/data/fate/vsynth3-asv2.avi233696 tests/data/fate/vsynth3-asv2.avi
35469c0735b7c9279e5e8e3439fc6acab *tests/data/fate/vsynth3-asv2.out.rawvideo35469c0735b7c9279e5e8e3439fc6acab *tests/data/fate/vsynth3-asv2.out.rawvideo
4stddev: 9.07 PSNR: 28.97 MAXDIFF: 51 bytes: 86700/ 867004stddev: 9.07 PSNR: 28.97 MAXDIFF: 51 bytes: 86700/ 86700

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t