diff options
author | Andreas Rheinhardt <andreas.rheinhardt@outlook.com> | 2022-10-14 05:39:59 +0200 |
---|---|---|
committer | Andreas Rheinhardt <andreas.rheinhardt@outlook.com> | 2022-10-20 07:29:03 +0200 |
commit | 6fe4e8fab47089ff095d994a2a2b986cff0a50d4 (patch) | |
tree | 22d3f1365f31df626cc234049e4c1c77505373b4 /libavcodec/mpegvideo_dec.c | |
parent | 9ca312d8abd0d5d9364346392959f0a6d2061219 (diff) | |
download | ffmpeg-6fe4e8fab47089ff095d994a2a2b986cff0a50d4.tar.gz |
avcodec/mpegvideo: Split ff_mpv_reconstruct_mb() into de/encoder part
This has the advantage of not having to check for whether
a given MpegEncContext is actually a decoder or an encoder
context at runtime.
To do so, mpv_reconstruct_mb_internal() is moved into a new
template file that is included by both mpegvideo_enc.c
and mpegvideo_dec.c; the decoder-only code (mainly lowres)
are also moved to mpegvideo_dec.c. The is_encoder checks are
changed to #if IS_ENCODER in order to avoid having to include
headers for decoder-only functions in mpegvideo_enc.c.
This approach also has the advantage that it is easy to adapt
mpv_reconstruct_mb_internal() to using different structures
for decoders and encoders (e.g. the check for whether
a macroblock should be processed for the encoder or not
uses MpegEncContext elements that make no sense for decoders
and should not be part of their context).
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
Diffstat (limited to 'libavcodec/mpegvideo_dec.c')
-rw-r--r-- | libavcodec/mpegvideo_dec.c | 467 |
1 files changed, 467 insertions, 0 deletions
diff --git a/libavcodec/mpegvideo_dec.c b/libavcodec/mpegvideo_dec.c index 6d1edc027a..c2d6d8bdd7 100644 --- a/libavcodec/mpegvideo_dec.c +++ b/libavcodec/mpegvideo_dec.c @@ -22,17 +22,22 @@ #include <limits.h> +#include "config_components.h" + #include "libavutil/avassert.h" #include "libavutil/imgutils.h" #include "libavutil/internal.h" #include "libavutil/video_enc_params.h" #include "avcodec.h" +#include "h264chroma.h" #include "internal.h" #include "mpegutils.h" #include "mpegvideo.h" #include "mpegvideodec.h" +#include "mpeg4videodec.h" #include "threadframe.h" +#include "wmv2dec.h" void ff_mpv_decode_init(MpegEncContext *s, AVCodecContext *avctx) { @@ -563,3 +568,465 @@ void ff_mpv_report_decode_progress(MpegEncContext *s) if (s->pict_type != AV_PICTURE_TYPE_B && !s->partitioned_frame && !s->er.error_occurred) ff_thread_report_progress(&s->current_picture_ptr->tf, s->mb_y, 0); } + + +static inline int hpel_motion_lowres(MpegEncContext *s, + uint8_t *dest, const uint8_t *src, + int field_based, int field_select, + int src_x, int src_y, + int width, int height, ptrdiff_t stride, + int h_edge_pos, int v_edge_pos, + int w, int h, const h264_chroma_mc_func *pix_op, + int motion_x, int motion_y) +{ + const int lowres = s->avctx->lowres; + const int op_index = FFMIN(lowres, 3); + const int s_mask = (2 << lowres) - 1; + int emu = 0; + int sx, sy; + + if (s->quarter_sample) { + motion_x /= 2; + motion_y /= 2; + } + + sx = motion_x & s_mask; + sy = motion_y & s_mask; + src_x += motion_x >> lowres + 1; + src_y += motion_y >> lowres + 1; + + src += src_y * stride + src_x; + + if ((unsigned)src_x > FFMAX( h_edge_pos - (!!sx) - w, 0) || + (unsigned)src_y > FFMAX((v_edge_pos >> field_based) - (!!sy) - h, 0)) { + s->vdsp.emulated_edge_mc(s->sc.edge_emu_buffer, src, + s->linesize, s->linesize, + w + 1, (h + 1) << field_based, + src_x, src_y * (1 << field_based), + h_edge_pos, v_edge_pos); + src = s->sc.edge_emu_buffer; + emu = 1; + } + + sx = (sx << 2) >> lowres; + sy = (sy << 2) >> lowres; + if (field_select) + src += s->linesize; + pix_op[op_index](dest, src, stride, h, sx, sy); + return emu; +} + +/* apply one mpeg motion vector to the three components */ +static av_always_inline void mpeg_motion_lowres(MpegEncContext *s, + uint8_t *dest_y, + uint8_t *dest_cb, + uint8_t *dest_cr, + int field_based, + int bottom_field, + int field_select, + uint8_t *const *ref_picture, + const h264_chroma_mc_func *pix_op, + int motion_x, int motion_y, + int h, int mb_y) +{ + const uint8_t *ptr_y, *ptr_cb, *ptr_cr; + int mx, my, src_x, src_y, uvsrc_x, uvsrc_y, sx, sy, uvsx, uvsy; + ptrdiff_t uvlinesize, linesize; + const int lowres = s->avctx->lowres; + const int op_index = FFMIN(lowres - 1 + s->chroma_x_shift, 3); + const int block_s = 8 >> lowres; + const int s_mask = (2 << lowres) - 1; + const int h_edge_pos = s->h_edge_pos >> lowres; + const int v_edge_pos = s->v_edge_pos >> lowres; + linesize = s->current_picture.f->linesize[0] << field_based; + uvlinesize = s->current_picture.f->linesize[1] << field_based; + + // FIXME obviously not perfect but qpel will not work in lowres anyway + if (s->quarter_sample) { + motion_x /= 2; + motion_y /= 2; + } + + if (field_based) { + motion_y += (bottom_field - field_select)*((1 << lowres)-1); + } + + sx = motion_x & s_mask; + sy = motion_y & s_mask; + src_x = s->mb_x * 2 * block_s + (motion_x >> lowres + 1); + src_y = (mb_y * 2 * block_s >> field_based) + (motion_y >> lowres + 1); + + if (s->out_format == FMT_H263) { + uvsx = ((motion_x >> 1) & s_mask) | (sx & 1); + uvsy = ((motion_y >> 1) & s_mask) | (sy & 1); + uvsrc_x = src_x >> 1; + uvsrc_y = src_y >> 1; + } else if (s->out_format == FMT_H261) { + // even chroma mv's are full pel in H261 + mx = motion_x / 4; + my = motion_y / 4; + uvsx = (2 * mx) & s_mask; + uvsy = (2 * my) & s_mask; + uvsrc_x = s->mb_x * block_s + (mx >> lowres); + uvsrc_y = mb_y * block_s + (my >> lowres); + } else { + if (s->chroma_y_shift) { + mx = motion_x / 2; + my = motion_y / 2; + uvsx = mx & s_mask; + uvsy = my & s_mask; + uvsrc_x = s->mb_x * block_s + (mx >> lowres + 1); + uvsrc_y = (mb_y * block_s >> field_based) + (my >> lowres + 1); + } else { + if (s->chroma_x_shift) { + //Chroma422 + mx = motion_x / 2; + uvsx = mx & s_mask; + uvsy = motion_y & s_mask; + uvsrc_y = src_y; + uvsrc_x = s->mb_x*block_s + (mx >> (lowres+1)); + } else { + //Chroma444 + uvsx = motion_x & s_mask; + uvsy = motion_y & s_mask; + uvsrc_x = src_x; + uvsrc_y = src_y; + } + } + } + + ptr_y = ref_picture[0] + src_y * linesize + src_x; + ptr_cb = ref_picture[1] + uvsrc_y * uvlinesize + uvsrc_x; + ptr_cr = ref_picture[2] + uvsrc_y * uvlinesize + uvsrc_x; + + if ((unsigned) src_x > FFMAX( h_edge_pos - (!!sx) - 2 * block_s, 0) || uvsrc_y<0 || + (unsigned) src_y > FFMAX((v_edge_pos >> field_based) - (!!sy) - h, 0)) { + s->vdsp.emulated_edge_mc(s->sc.edge_emu_buffer, ptr_y, + linesize >> field_based, linesize >> field_based, + 17, 17 + field_based, + src_x, src_y * (1 << field_based), h_edge_pos, + v_edge_pos); + ptr_y = s->sc.edge_emu_buffer; + if (!CONFIG_GRAY || !(s->avctx->flags & AV_CODEC_FLAG_GRAY)) { + uint8_t *ubuf = s->sc.edge_emu_buffer + 18 * s->linesize; + uint8_t *vbuf =ubuf + 10 * s->uvlinesize; + if (s->workaround_bugs & FF_BUG_IEDGE) + vbuf -= s->uvlinesize; + s->vdsp.emulated_edge_mc(ubuf, ptr_cb, + uvlinesize >> field_based, uvlinesize >> field_based, + 9, 9 + field_based, + uvsrc_x, uvsrc_y * (1 << field_based), + h_edge_pos >> 1, v_edge_pos >> 1); + s->vdsp.emulated_edge_mc(vbuf, ptr_cr, + uvlinesize >> field_based,uvlinesize >> field_based, + 9, 9 + field_based, + uvsrc_x, uvsrc_y * (1 << field_based), + h_edge_pos >> 1, v_edge_pos >> 1); + ptr_cb = ubuf; + ptr_cr = vbuf; + } + } + + // FIXME use this for field pix too instead of the obnoxious hack which changes picture.f->data + if (bottom_field) { + dest_y += s->linesize; + dest_cb += s->uvlinesize; + dest_cr += s->uvlinesize; + } + + if (field_select) { + ptr_y += s->linesize; + ptr_cb += s->uvlinesize; + ptr_cr += s->uvlinesize; + } + + sx = (sx << 2) >> lowres; + sy = (sy << 2) >> lowres; + pix_op[lowres - 1](dest_y, ptr_y, linesize, h, sx, sy); + + if (!CONFIG_GRAY || !(s->avctx->flags & AV_CODEC_FLAG_GRAY)) { + int hc = s->chroma_y_shift ? (h+1-bottom_field)>>1 : h; + uvsx = (uvsx << 2) >> lowres; + uvsy = (uvsy << 2) >> lowres; + if (hc) { + pix_op[op_index](dest_cb, ptr_cb, uvlinesize, hc, uvsx, uvsy); + pix_op[op_index](dest_cr, ptr_cr, uvlinesize, hc, uvsx, uvsy); + } + } + // FIXME h261 lowres loop filter +} + +static inline void chroma_4mv_motion_lowres(MpegEncContext *s, + uint8_t *dest_cb, uint8_t *dest_cr, + uint8_t *const *ref_picture, + const h264_chroma_mc_func * pix_op, + int mx, int my) +{ + const int lowres = s->avctx->lowres; + const int op_index = FFMIN(lowres, 3); + const int block_s = 8 >> lowres; + const int s_mask = (2 << lowres) - 1; + const int h_edge_pos = s->h_edge_pos >> lowres + 1; + const int v_edge_pos = s->v_edge_pos >> lowres + 1; + int emu = 0, src_x, src_y, sx, sy; + ptrdiff_t offset; + const uint8_t *ptr; + + if (s->quarter_sample) { + mx /= 2; + my /= 2; + } + + /* In case of 8X8, we construct a single chroma motion vector + with a special rounding */ + mx = ff_h263_round_chroma(mx); + my = ff_h263_round_chroma(my); + + sx = mx & s_mask; + sy = my & s_mask; + src_x = s->mb_x * block_s + (mx >> lowres + 1); + src_y = s->mb_y * block_s + (my >> lowres + 1); + + offset = src_y * s->uvlinesize + src_x; + ptr = ref_picture[1] + offset; + if ((unsigned) src_x > FFMAX(h_edge_pos - (!!sx) - block_s, 0) || + (unsigned) src_y > FFMAX(v_edge_pos - (!!sy) - block_s, 0)) { + s->vdsp.emulated_edge_mc(s->sc.edge_emu_buffer, ptr, + s->uvlinesize, s->uvlinesize, + 9, 9, + src_x, src_y, h_edge_pos, v_edge_pos); + ptr = s->sc.edge_emu_buffer; + emu = 1; + } + sx = (sx << 2) >> lowres; + sy = (sy << 2) >> lowres; + pix_op[op_index](dest_cb, ptr, s->uvlinesize, block_s, sx, sy); + + ptr = ref_picture[2] + offset; + if (emu) { + s->vdsp.emulated_edge_mc(s->sc.edge_emu_buffer, ptr, + s->uvlinesize, s->uvlinesize, + 9, 9, + src_x, src_y, h_edge_pos, v_edge_pos); + ptr = s->sc.edge_emu_buffer; + } + pix_op[op_index](dest_cr, ptr, s->uvlinesize, block_s, sx, sy); +} + +/** + * motion compensation of a single macroblock + * @param s context + * @param dest_y luma destination pointer + * @param dest_cb chroma cb/u destination pointer + * @param dest_cr chroma cr/v destination pointer + * @param dir direction (0->forward, 1->backward) + * @param ref_picture array[3] of pointers to the 3 planes of the reference picture + * @param pix_op halfpel motion compensation function (average or put normally) + * the motion vectors are taken from s->mv and the MV type from s->mv_type + */ +static inline void MPV_motion_lowres(MpegEncContext *s, + uint8_t *dest_y, uint8_t *dest_cb, + uint8_t *dest_cr, + int dir, uint8_t *const *ref_picture, + const h264_chroma_mc_func *pix_op) +{ + int mx, my; + int mb_x, mb_y; + const int lowres = s->avctx->lowres; + const int block_s = 8 >>lowres; + + mb_x = s->mb_x; + mb_y = s->mb_y; + + switch (s->mv_type) { + case MV_TYPE_16X16: + mpeg_motion_lowres(s, dest_y, dest_cb, dest_cr, + 0, 0, 0, + ref_picture, pix_op, + s->mv[dir][0][0], s->mv[dir][0][1], + 2 * block_s, mb_y); + break; + case MV_TYPE_8X8: + mx = 0; + my = 0; + for (int i = 0; i < 4; i++) { + hpel_motion_lowres(s, dest_y + ((i & 1) + (i >> 1) * + s->linesize) * block_s, + ref_picture[0], 0, 0, + (2 * mb_x + (i & 1)) * block_s, + (2 * mb_y + (i >> 1)) * block_s, + s->width, s->height, s->linesize, + s->h_edge_pos >> lowres, s->v_edge_pos >> lowres, + block_s, block_s, pix_op, + s->mv[dir][i][0], s->mv[dir][i][1]); + + mx += s->mv[dir][i][0]; + my += s->mv[dir][i][1]; + } + + if (!CONFIG_GRAY || !(s->avctx->flags & AV_CODEC_FLAG_GRAY)) + chroma_4mv_motion_lowres(s, dest_cb, dest_cr, ref_picture, + pix_op, mx, my); + break; + case MV_TYPE_FIELD: + if (s->picture_structure == PICT_FRAME) { + /* top field */ + mpeg_motion_lowres(s, dest_y, dest_cb, dest_cr, + 1, 0, s->field_select[dir][0], + ref_picture, pix_op, + s->mv[dir][0][0], s->mv[dir][0][1], + block_s, mb_y); + /* bottom field */ + mpeg_motion_lowres(s, dest_y, dest_cb, dest_cr, + 1, 1, s->field_select[dir][1], + ref_picture, pix_op, + s->mv[dir][1][0], s->mv[dir][1][1], + block_s, mb_y); + } else { + if (s->picture_structure != s->field_select[dir][0] + 1 && + s->pict_type != AV_PICTURE_TYPE_B && !s->first_field) { + ref_picture = s->current_picture_ptr->f->data; + + } + mpeg_motion_lowres(s, dest_y, dest_cb, dest_cr, + 0, 0, s->field_select[dir][0], + ref_picture, pix_op, + s->mv[dir][0][0], + s->mv[dir][0][1], 2 * block_s, mb_y >> 1); + } + break; + case MV_TYPE_16X8: + for (int i = 0; i < 2; i++) { + uint8_t *const *ref2picture; + + if (s->picture_structure == s->field_select[dir][i] + 1 || + s->pict_type == AV_PICTURE_TYPE_B || s->first_field) { + ref2picture = ref_picture; + } else { + ref2picture = s->current_picture_ptr->f->data; + } + + mpeg_motion_lowres(s, dest_y, dest_cb, dest_cr, + 0, 0, s->field_select[dir][i], + ref2picture, pix_op, + s->mv[dir][i][0], s->mv[dir][i][1] + + 2 * block_s * i, block_s, mb_y >> 1); + + dest_y += 2 * block_s * s->linesize; + dest_cb += (2 * block_s >> s->chroma_y_shift) * s->uvlinesize; + dest_cr += (2 * block_s >> s->chroma_y_shift) * s->uvlinesize; + } + break; + case MV_TYPE_DMV: + if (s->picture_structure == PICT_FRAME) { + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + mpeg_motion_lowres(s, dest_y, dest_cb, dest_cr, + 1, j, j ^ i, + ref_picture, pix_op, + s->mv[dir][2 * i + j][0], + s->mv[dir][2 * i + j][1], + block_s, mb_y); + } + pix_op = s->h264chroma.avg_h264_chroma_pixels_tab; + } + } else { + for (int i = 0; i < 2; i++) { + mpeg_motion_lowres(s, dest_y, dest_cb, dest_cr, + 0, 0, s->picture_structure != i + 1, + ref_picture, pix_op, + s->mv[dir][2 * i][0],s->mv[dir][2 * i][1], + 2 * block_s, mb_y >> 1); + + // after put we make avg of the same block + pix_op = s->h264chroma.avg_h264_chroma_pixels_tab; + + // opposite parity is always in the same + // frame if this is second field + if (!s->first_field) { + ref_picture = s->current_picture_ptr->f->data; + } + } + } + break; + default: + av_assert2(0); + } +} + +/** + * find the lowest MB row referenced in the MVs + */ +static int lowest_referenced_row(MpegEncContext *s, int dir) +{ + int my_max = INT_MIN, my_min = INT_MAX, qpel_shift = !s->quarter_sample; + int off, mvs; + + if (s->picture_structure != PICT_FRAME || s->mcsel) + goto unhandled; + + switch (s->mv_type) { + case MV_TYPE_16X16: + mvs = 1; + break; + case MV_TYPE_16X8: + mvs = 2; + break; + case MV_TYPE_8X8: + mvs = 4; + break; + default: + goto unhandled; + } + + for (int i = 0; i < mvs; i++) { + int my = s->mv[dir][i][1]; + my_max = FFMAX(my_max, my); + my_min = FFMIN(my_min, my); + } + + off = ((FFMAX(-my_min, my_max) << qpel_shift) + 63) >> 6; + + return av_clip(s->mb_y + off, 0, s->mb_height - 1); +unhandled: + return s->mb_height - 1; +} + +/* add block[] to dest[] */ +static inline void add_dct(MpegEncContext *s, + int16_t *block, int i, uint8_t *dest, int line_size) +{ + if (s->block_last_index[i] >= 0) { + s->idsp.idct_add(dest, line_size, block); + } +} + +#define IS_ENCODER 0 +#include "mpv_reconstruct_mb_template.c" + +void ff_mpv_reconstruct_mb(MpegEncContext *s, int16_t block[12][64]) +{ + if (s->avctx->debug & FF_DEBUG_DCT_COEFF) { + /* print DCT coefficients */ + av_log(s->avctx, AV_LOG_DEBUG, "DCT coeffs of MB at %dx%d:\n", s->mb_x, s->mb_y); + for (int i = 0; i < 6; i++) { + for (int j = 0; j < 64; j++) { + av_log(s->avctx, AV_LOG_DEBUG, "%5d", + block[i][s->idsp.idct_permutation[j]]); + } + av_log(s->avctx, AV_LOG_DEBUG, "\n"); + } + } + + if (!s->avctx->lowres) { +#if !CONFIG_SMALL + if (s->out_format == FMT_MPEG1) + mpv_reconstruct_mb_internal(s, block, 0, DEFINITELY_MPEG12); + else + mpv_reconstruct_mb_internal(s, block, 0, NOT_MPEG12); +#else + mpv_reconstruct_mb_internal(s, block, 0, MAY_BE_MPEG12); +#endif + } else + mpv_reconstruct_mb_internal(s, block, 1, MAY_BE_MPEG12); +} |