diff options
Diffstat (limited to 'video/mp_image.c')
-rw-r--r-- | video/mp_image.c | 453 |
1 files changed, 296 insertions, 157 deletions
diff --git a/video/mp_image.c b/video/mp_image.c index 0e95a59808..609fb17bf0 100644 --- a/video/mp_image.c +++ b/video/mp_image.c @@ -16,27 +16,33 @@ */ #include <limits.h> -#include <pthread.h> #include <assert.h> #include <libavutil/mem.h> #include <libavutil/common.h> +#include <libavutil/display.h> #include <libavutil/bswap.h> #include <libavutil/hwcontext.h> #include <libavutil/intreadwrite.h> #include <libavutil/rational.h> #include <libavcodec/avcodec.h> #include <libavutil/mastering_display_metadata.h> +#include <libplacebo/utils/libav.h> + +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 16, 100) +# include <libavutil/dovi_meta.h> +#endif #include "mpv_talloc.h" -#include "config.h" #include "common/av_common.h" #include "common/common.h" +#include "fmt-conversion.h" #include "hwdec.h" #include "mp_image.h" +#include "osdep/threads.h" #include "sws_utils.h" -#include "fmt-conversion.h" +#include "out/placebo/utils.h" // Determine strides, plane sizes, and total required size for an image // allocation. Returns total size on success, <0 on error. Unused planes @@ -65,7 +71,7 @@ static int mp_image_layout(int imgfmt, int w, int h, int stride_align, int alloc_w = mp_chroma_div_up(w, desc.xs[n]); int alloc_h = MP_ALIGN_UP(h, 32) >> desc.ys[n]; int line_bytes = (alloc_w * desc.bpp[n] + 7) / 8; - out_stride[n] = MP_ALIGN_UP(line_bytes, stride_align); + out_stride[n] = MP_ALIGN_NPOT(line_bytes, stride_align); out_plane_size[n] = out_stride[n] * alloc_h; } if (desc.flags & MP_IMGFLAG_PAL) @@ -132,7 +138,7 @@ static bool mp_image_fill_alloc(struct mp_image *mpi, int stride_align, // The allocated size of buffer must be given by buffer_size. buffer_size should // be at least the value returned by mp_image_get_alloc_size(). If buffer is not // already aligned to stride_align, the function will attempt to align the -// pointer itself by incrementing the buffer pointer until ther alignment is +// pointer itself by incrementing the buffer pointer until their alignment is // achieved (if buffer_size is not large enough to allow aligning the buffer // safely, the function fails). To be safe, you may want to overallocate the // buffer by stride_align bytes, and include the overallocation in buffer_size. @@ -186,13 +192,11 @@ static bool mp_image_alloc_planes(struct mp_image *mpi) void mp_image_setfmt(struct mp_image *mpi, int out_fmt) { - struct mp_image_params params = mpi->params; struct mp_imgfmt_desc fmt = mp_imgfmt_get_desc(out_fmt); - params.imgfmt = fmt.id; + mpi->params.imgfmt = fmt.id; mpi->fmt = fmt; mpi->imgfmt = fmt.id; mpi->num_planes = fmt.num_planes; - mpi->params = params; } static void mp_image_destructor(void *ptr) @@ -203,6 +207,8 @@ static void mp_image_destructor(void *ptr) av_buffer_unref(&mpi->hwctx); av_buffer_unref(&mpi->icc_profile); av_buffer_unref(&mpi->a53_cc); + av_buffer_unref(&mpi->dovi); + av_buffer_unref(&mpi->film_grain); for (int n = 0; n < mpi->num_ff_side_data; n++) av_buffer_unref(&mpi->ff_side_data[n].buf); talloc_free(mpi->ff_side_data); @@ -216,15 +222,13 @@ int mp_chroma_div_up(int size, int shift) // Return the storage width in pixels of the given plane. int mp_image_plane_w(struct mp_image *mpi, int plane) { - return mp_chroma_div_up(MP_ALIGN_UP(mpi->w, mpi->fmt.align_x), - mpi->fmt.xs[plane]); + return mp_chroma_div_up(mpi->w, mpi->fmt.xs[plane]); } // Return the storage height in pixels of the given plane. int mp_image_plane_h(struct mp_image *mpi, int plane) { - return mp_chroma_div_up(MP_ALIGN_UP(mpi->h, mpi->fmt.align_y), - mpi->fmt.ys[plane]); + return mp_chroma_div_up(mpi->h, mpi->fmt.ys[plane]); } // Caller has to make sure this doesn't exceed the allocated plane data/strides. @@ -309,12 +313,11 @@ void mp_image_unref_data(struct mp_image *img) } } -static void ref_buffer(bool *ok, AVBufferRef **dst) +static void ref_buffer(AVBufferRef **dst) { if (*dst) { *dst = av_buffer_ref(*dst); - if (!*dst) - *ok = false; + MP_HANDLE_OOM(*dst); } } @@ -332,26 +335,21 @@ struct mp_image *mp_image_new_ref(struct mp_image *img) talloc_set_destructor(new, mp_image_destructor); *new = *img; - bool ok = true; for (int p = 0; p < MP_MAX_PLANES; p++) - ref_buffer(&ok, &new->bufs[p]); + ref_buffer(&new->bufs[p]); - ref_buffer(&ok, &new->hwctx); - ref_buffer(&ok, &new->icc_profile); - ref_buffer(&ok, &new->a53_cc); + ref_buffer(&new->hwctx); + ref_buffer(&new->icc_profile); + ref_buffer(&new->a53_cc); + ref_buffer(&new->dovi); + ref_buffer(&new->film_grain); new->ff_side_data = talloc_memdup(NULL, new->ff_side_data, new->num_ff_side_data * sizeof(new->ff_side_data[0])); for (int n = 0; n < new->num_ff_side_data; n++) - ref_buffer(&ok, &new->ff_side_data[n].buf); + ref_buffer(&new->ff_side_data[n].buf); - if (ok) - return new; - - // Do this after _all_ bufs were changed; we don't want it to free bufs - // from the original image if this fails. - talloc_free(new); - return NULL; + return new; } struct free_args { @@ -380,6 +378,8 @@ struct mp_image *mp_image_new_dummy_ref(struct mp_image *img) new->hwctx = NULL; new->icc_profile = NULL; new->a53_cc = NULL; + new->dovi = NULL; + new->film_grain = NULL; new->num_ff_side_data = 0; new->ff_side_data = NULL; return new; @@ -488,7 +488,7 @@ void mp_image_copy(struct mp_image *dst, struct mp_image *src) memcpy(dst->planes[1], src->planes[1], AVPALETTE_SIZE); } -static enum mp_csp mp_image_params_get_forced_csp(struct mp_image_params *params) +static enum pl_color_system mp_image_params_get_forced_csp(struct mp_image_params *params) { int imgfmt = params->hw_subfmt ? params->hw_subfmt : params->imgfmt; return mp_imgfmt_get_forced_csp(imgfmt); @@ -505,6 +505,8 @@ static void assign_bufref(AVBufferRef **dst, AVBufferRef *new) void mp_image_copy_attributes(struct mp_image *dst, struct mp_image *src) { + assert(dst != src); + dst->pict_type = src->pict_type; dst->fields = src->fields; dst->pts = src->pts; @@ -515,13 +517,20 @@ void mp_image_copy_attributes(struct mp_image *dst, struct mp_image *src) dst->params.p_w = src->params.p_w; dst->params.p_h = src->params.p_h; dst->params.color = src->params.color; + dst->params.repr = src->params.repr; + dst->params.light = src->params.light; dst->params.chroma_location = src->params.chroma_location; - dst->params.alpha = src->params.alpha; + dst->params.crop = src->params.crop; dst->nominal_fps = src->nominal_fps; + // ensure colorspace consistency - if (mp_image_params_get_forced_csp(&dst->params) != - mp_image_params_get_forced_csp(&src->params)) - dst->params.color = (struct mp_colorspace){0}; + enum pl_color_system dst_forced_csp = mp_image_params_get_forced_csp(&dst->params); + if (mp_image_params_get_forced_csp(&src->params) != dst_forced_csp) { + dst->params.repr.sys = dst_forced_csp != PL_COLOR_SYSTEM_UNKNOWN ? + dst_forced_csp : + mp_csp_guess_colorspace(src->w, src->h); + } + if ((dst->fmt.flags & MP_IMGFLAG_PAL) && (src->fmt.flags & MP_IMGFLAG_PAL)) { if (dst->planes[1] && src->planes[1]) { if (mp_image_make_writeable(dst)) @@ -529,7 +538,21 @@ void mp_image_copy_attributes(struct mp_image *dst, struct mp_image *src) } } assign_bufref(&dst->icc_profile, src->icc_profile); + assign_bufref(&dst->dovi, src->dovi); + assign_bufref(&dst->film_grain, src->film_grain); assign_bufref(&dst->a53_cc, src->a53_cc); + + for (int n = 0; n < dst->num_ff_side_data; n++) + av_buffer_unref(&dst->ff_side_data[n].buf); + + MP_RESIZE_ARRAY(NULL, dst->ff_side_data, src->num_ff_side_data); + dst->num_ff_side_data = src->num_ff_side_data; + + for (int n = 0; n < dst->num_ff_side_data; n++) { + dst->ff_side_data[n].type = src->ff_side_data[n].type; + dst->ff_side_data[n].buf = av_buffer_ref(src->ff_side_data[n].buf); + MP_HANDLE_OOM(dst->ff_side_data[n].buf); + } } // Crop the given image to (x0, y0)-(x1, y1) (bottom/right border exclusive) @@ -593,7 +616,7 @@ static bool endian_swap_bytes(void *d, size_t bytes, size_t word_size) AV_WL32(ud + x * 2, AV_RB32(ud + x * 2)); break; default: - assert(0); + MP_ASSERT_UNREACHABLE(); } return true; @@ -642,8 +665,8 @@ void mp_image_clear(struct mp_image *img, int x0, int y0, int x1, int y1) plane_size[cd->plane] = plane_bits / 8u; int depth = cd->size + MPMIN(cd->pad, 0); double m, o; - mp_get_csp_uint_mul(area.params.color.space, - area.params.color.levels, + mp_get_csp_uint_mul(area.params.repr.sys, + area.params.repr.levels, depth, c + 1, &m, &o); uint64_t val = MPCLAMP(lrint((0 - o) / m), 0, 1ull << depth); plane_clear_i[cd->plane] |= val << cd->offset; @@ -669,7 +692,7 @@ void mp_image_clear(struct mp_image *img, int x0, int y0, int x1, int y1) int p_w = mp_image_plane_w(&area, p); for (int y = 0; y < p_h; y++) { void *ptr = area.planes[p] + (ptrdiff_t)area.stride[p] * y; - if (plane_size[p] && plane_clear[p]) { + if (plane_size[p]) { memset_pattern(ptr, p_w / misery, plane_clear[p], plane_size[p]); } else { memset(ptr, 0, mp_image_plane_bytes(&area, p, 0, area.w)); @@ -701,12 +724,26 @@ void mp_image_vflip(struct mp_image *img) } } +bool mp_image_crop_valid(const struct mp_image_params *p) +{ + return p->crop.x1 > p->crop.x0 && p->crop.y1 > p->crop.y0 && + p->crop.x0 >= 0 && p->crop.y0 >= 0 && + p->crop.x1 <= p->w && p->crop.y1 <= p->h; +} + // Display size derived from image size and pixel aspect ratio. void mp_image_params_get_dsize(const struct mp_image_params *p, int *d_w, int *d_h) { - *d_w = p->w; - *d_h = p->h; + if (mp_image_crop_valid(p)) + { + *d_w = mp_rect_w(p->crop); + *d_h = mp_rect_h(p->crop); + } else { + *d_w = p->w; + *d_h = p->h; + } + if (p->p_w > p->p_h && p->p_h >= 1) *d_w = MPCLAMP(*d_w * (int64_t)p->p_w / p->p_h, 1, INT_MAX); if (p->p_h > p->p_w && p->p_w >= 1) @@ -731,24 +768,26 @@ char *mp_image_params_to_str_buf(char *b, size_t bs, if (p->hw_subfmt) mp_snprintf_cat(b, bs, "[%s]", mp_imgfmt_to_name(p->hw_subfmt)); mp_snprintf_cat(b, bs, " %s/%s/%s/%s/%s", - m_opt_choice_str(mp_csp_names, p->color.space), - m_opt_choice_str(mp_csp_prim_names, p->color.primaries), - m_opt_choice_str(mp_csp_trc_names, p->color.gamma), - m_opt_choice_str(mp_csp_levels_names, p->color.levels), - m_opt_choice_str(mp_csp_light_names, p->color.light)); - if (p->color.sig_peak) - mp_snprintf_cat(b, bs, " SP=%f", p->color.sig_peak); + m_opt_choice_str(pl_csp_names, p->repr.sys), + m_opt_choice_str(pl_csp_prim_names, p->color.primaries), + m_opt_choice_str(pl_csp_trc_names, p->color.transfer), + m_opt_choice_str(pl_csp_levels_names, p->repr.levels), + m_opt_choice_str(mp_csp_light_names, p->light)); mp_snprintf_cat(b, bs, " CL=%s", - m_opt_choice_str(mp_chroma_names, p->chroma_location)); + m_opt_choice_str(pl_chroma_names, p->chroma_location)); + if (mp_image_crop_valid(p)) { + mp_snprintf_cat(b, bs, " crop=%dx%d+%d+%d", mp_rect_w(p->crop), + mp_rect_h(p->crop), p->crop.x0, p->crop.y0); + } if (p->rotate) mp_snprintf_cat(b, bs, " rot=%d", p->rotate); if (p->stereo3d > 0) { mp_snprintf_cat(b, bs, " stereo=%s", MP_STEREO3D_NAME_DEF(p->stereo3d, "?")); } - if (p->alpha) { + if (p->repr.alpha) { mp_snprintf_cat(b, bs, " A=%s", - m_opt_choice_str(mp_alpha_names, p->alpha)); + m_opt_choice_str(pl_alpha_names, p->repr.alpha)); } } else { snprintf(b, bs, "???"); @@ -791,11 +830,25 @@ bool mp_image_params_equal(const struct mp_image_params *p1, p1->hw_subfmt == p2->hw_subfmt && p1->w == p2->w && p1->h == p2->h && p1->p_w == p2->p_w && p1->p_h == p2->p_h && - mp_colorspace_equal(p1->color, p2->color) && + p1->force_window == p2->force_window && + pl_color_space_equal(&p1->color, &p2->color) && + pl_color_repr_equal(&p1->repr, &p2->repr) && + p1->light == p2->light && p1->chroma_location == p2->chroma_location && p1->rotate == p2->rotate && p1->stereo3d == p2->stereo3d && - p1->alpha == p2->alpha; + mp_rect_equals(&p1->crop, &p2->crop); +} + +bool mp_image_params_static_equal(const struct mp_image_params *p1, + const struct mp_image_params *p2) +{ + // Compare only static video parameters, excluding dynamic metadata. + struct mp_image_params a = *p1; + struct mp_image_params b = *p2; + a.repr.dovi = b.repr.dovi = NULL; + a.color.hdr = b.color.hdr = (struct pl_hdr_metadata){0}; + return mp_image_params_equal(&a, &b); } // Set most image parameters, but not image format or size. @@ -807,118 +860,143 @@ void mp_image_set_attributes(struct mp_image *image, nparams.imgfmt = image->imgfmt; nparams.w = image->w; nparams.h = image->h; - if (nparams.imgfmt != params->imgfmt) - nparams.color = (struct mp_colorspace){0}; + if (nparams.imgfmt != params->imgfmt) { + nparams.repr = (struct pl_color_repr){0}; + nparams.color = (struct pl_color_space){0}; + } mp_image_set_params(image, &nparams); } +static enum pl_color_levels infer_levels(enum mp_imgfmt imgfmt) +{ + switch (imgfmt2pixfmt(imgfmt)) { + case AV_PIX_FMT_YUVJ420P: + case AV_PIX_FMT_YUVJ411P: + case AV_PIX_FMT_YUVJ422P: + case AV_PIX_FMT_YUVJ444P: + case AV_PIX_FMT_YUVJ440P: + case AV_PIX_FMT_GRAY8: + case AV_PIX_FMT_YA8: + case AV_PIX_FMT_GRAY9LE: + case AV_PIX_FMT_GRAY9BE: + case AV_PIX_FMT_GRAY10LE: + case AV_PIX_FMT_GRAY10BE: + case AV_PIX_FMT_GRAY12LE: + case AV_PIX_FMT_GRAY12BE: + case AV_PIX_FMT_GRAY14LE: + case AV_PIX_FMT_GRAY14BE: + case AV_PIX_FMT_GRAY16LE: + case AV_PIX_FMT_GRAY16BE: + case AV_PIX_FMT_YA16BE: + case AV_PIX_FMT_YA16LE: + return PL_COLOR_LEVELS_FULL; + default: + return PL_COLOR_LEVELS_LIMITED; + } +} + // If details like params->colorspace/colorlevels are missing, guess them from // the other settings. Also, even if they are set, make them consistent with // the colorspace as implied by the pixel format. void mp_image_params_guess_csp(struct mp_image_params *params) { - enum mp_csp forced_csp = mp_image_params_get_forced_csp(params); - if (forced_csp == MP_CSP_AUTO) { // YUV/other - if (params->color.space != MP_CSP_BT_601 && - params->color.space != MP_CSP_BT_709 && - params->color.space != MP_CSP_BT_2020_NC && - params->color.space != MP_CSP_BT_2020_C && - params->color.space != MP_CSP_SMPTE_240M && - params->color.space != MP_CSP_YCGCO) + enum pl_color_system forced_csp = mp_image_params_get_forced_csp(params); + if (forced_csp == PL_COLOR_SYSTEM_UNKNOWN) { // YUV/other + if (params->repr.sys != PL_COLOR_SYSTEM_BT_601 && + params->repr.sys != PL_COLOR_SYSTEM_BT_709 && + params->repr.sys != PL_COLOR_SYSTEM_BT_2020_NC && + params->repr.sys != PL_COLOR_SYSTEM_BT_2020_C && + params->repr.sys != PL_COLOR_SYSTEM_BT_2100_PQ && + params->repr.sys != PL_COLOR_SYSTEM_BT_2100_HLG && + params->repr.sys != PL_COLOR_SYSTEM_DOLBYVISION && + params->repr.sys != PL_COLOR_SYSTEM_SMPTE_240M && + params->repr.sys != PL_COLOR_SYSTEM_YCGCO) { // Makes no sense, so guess instead // YCGCO should be separate, but libavcodec disagrees - params->color.space = MP_CSP_AUTO; + params->repr.sys = PL_COLOR_SYSTEM_UNKNOWN; } - if (params->color.space == MP_CSP_AUTO) - params->color.space = mp_csp_guess_colorspace(params->w, params->h); - if (params->color.levels == MP_CSP_LEVELS_AUTO) { - if (params->color.gamma == MP_CSP_TRC_V_LOG) { - params->color.levels = MP_CSP_LEVELS_PC; + if (params->repr.sys == PL_COLOR_SYSTEM_UNKNOWN) + params->repr.sys = mp_csp_guess_colorspace(params->w, params->h); + if (params->repr.levels == PL_COLOR_LEVELS_UNKNOWN) { + if (params->color.transfer == PL_COLOR_TRC_V_LOG) { + params->repr.levels = PL_COLOR_LEVELS_FULL; } else { - params->color.levels = MP_CSP_LEVELS_TV; + params->repr.levels = infer_levels(params->imgfmt); } } - if (params->color.primaries == MP_CSP_PRIM_AUTO) { + if (params->color.primaries == PL_COLOR_PRIM_UNKNOWN) { // Guess based on the colormatrix as a first priority - if (params->color.space == MP_CSP_BT_2020_NC || - params->color.space == MP_CSP_BT_2020_C) { - params->color.primaries = MP_CSP_PRIM_BT_2020; - } else if (params->color.space == MP_CSP_BT_709) { - params->color.primaries = MP_CSP_PRIM_BT_709; + if (params->repr.sys == PL_COLOR_SYSTEM_BT_2020_NC || + params->repr.sys == PL_COLOR_SYSTEM_BT_2020_C) { + params->color.primaries = PL_COLOR_PRIM_BT_2020; + } else if (params->repr.sys == PL_COLOR_SYSTEM_BT_709) { + params->color.primaries = PL_COLOR_PRIM_BT_709; } else { // Ambiguous colormatrix for BT.601, guess based on res params->color.primaries = mp_csp_guess_primaries(params->w, params->h); } } - if (params->color.gamma == MP_CSP_TRC_AUTO) - params->color.gamma = MP_CSP_TRC_BT_1886; - } else if (forced_csp == MP_CSP_RGB) { - params->color.space = MP_CSP_RGB; - params->color.levels = MP_CSP_LEVELS_PC; + if (params->color.transfer == PL_COLOR_TRC_UNKNOWN) + params->color.transfer = PL_COLOR_TRC_BT_1886; + } else if (forced_csp == PL_COLOR_SYSTEM_RGB) { + params->repr.sys = PL_COLOR_SYSTEM_RGB; + params->repr.levels = PL_COLOR_LEVELS_FULL; // The majority of RGB content is either sRGB or (rarely) some other // color space which we don't even handle, like AdobeRGB or // ProPhotoRGB. The only reasonable thing we can do is assume it's // sRGB and hope for the best, which should usually just work out fine. // Note: sRGB primaries = BT.709 primaries - if (params->color.primaries == MP_CSP_PRIM_AUTO) - params->color.primaries = MP_CSP_PRIM_BT_709; - if (params->color.gamma == MP_CSP_TRC_AUTO) - params->color.gamma = MP_CSP_TRC_SRGB; - } else if (forced_csp == MP_CSP_XYZ) { - params->color.space = MP_CSP_XYZ; - params->color.levels = MP_CSP_LEVELS_PC; - - // In theory, XYZ data does not really need a concept of 'primaries' to - // function, but this field can still be relevant for guiding gamut - // mapping optimizations, and it's also used by `mp_get_csp_matrix` - // when deciding what RGB space to map XYZ to for VOs that don't want - // to directly ingest XYZ into their color pipeline. BT.709 would be a - // sane default here, but it runs the risk of clipping any wide gamut - // content, so we pick BT.2020 instead to be on the safer side. - if (params->color.primaries == MP_CSP_PRIM_AUTO) - params->color.primaries = MP_CSP_PRIM_BT_2020; - if (params->color.gamma == MP_CSP_TRC_AUTO) - params->color.gamma = MP_CSP_TRC_LINEAR; + if (params->color.primaries == PL_COLOR_PRIM_UNKNOWN) + params->color.primaries = PL_COLOR_PRIM_BT_709; + if (params->color.transfer == PL_COLOR_TRC_UNKNOWN) + params->color.transfer = PL_COLOR_TRC_SRGB; + } else if (forced_csp == PL_COLOR_SYSTEM_XYZ) { + params->repr.sys = PL_COLOR_SYSTEM_XYZ; + params->repr.levels = PL_COLOR_LEVELS_FULL; + // Force gamma to ST428 as this is the only correct for DCDM X'Y'Z' + params->color.transfer = PL_COLOR_TRC_ST428; + // Don't care about primaries, they shouldn't be used, or if anything + // MP_CSP_PRIM_ST428 should be defined. } else { // We have no clue. - params->color.space = MP_CSP_AUTO; - params->color.levels = MP_CSP_LEVELS_AUTO; - params->color.primaries = MP_CSP_PRIM_AUTO; - params->color.gamma = MP_CSP_TRC_AUTO; + params->repr.sys = PL_COLOR_SYSTEM_UNKNOWN; + params->repr.levels = PL_COLOR_LEVELS_UNKNOWN; + params->color.primaries = PL_COLOR_PRIM_UNKNOWN; + params->color.transfer = PL_COLOR_TRC_UNKNOWN; } - if (!params->color.sig_peak) { - if (params->color.gamma == MP_CSP_TRC_HLG) { - params->color.sig_peak = 1000 / MP_REF_WHITE; // reference display + if (!params->color.hdr.max_luma) { + if (params->color.transfer == PL_COLOR_TRC_HLG) { + params->color.hdr.max_luma = 1000; // reference display } else { // If the signal peak is unknown, we're forced to pick the TRC's // nominal range as the signal peak to prevent clipping - params->color.sig_peak = mp_trc_nom_peak(params->color.gamma); + params->color.hdr.max_luma = pl_color_transfer_nominal_peak(params->color.transfer) * MP_REF_WHITE; } } - if (!mp_trc_is_hdr(params->color.gamma)) { + if (!pl_color_space_is_hdr(¶ms->color)) { // Some clips have leftover HDR metadata after conversion to SDR, so to // avoid blowing up the tone mapping code, strip/sanitize it - params->color.sig_peak = 1.0; + params->color.hdr = pl_hdr_metadata_empty; } - if (params->chroma_location == MP_CHROMA_AUTO) { - if (params->color.levels == MP_CSP_LEVELS_TV) - params->chroma_location = MP_CHROMA_LEFT; - if (params->color.levels == MP_CSP_LEVELS_PC) - params->chroma_location = MP_CHROMA_CENTER; + if (params->chroma_location == PL_CHROMA_UNKNOWN) { + if (params->repr.levels == PL_COLOR_LEVELS_LIMITED) + params->chroma_location = PL_CHROMA_LEFT; + if (params->repr.levels == PL_COLOR_LEVELS_FULL) + params->chroma_location = PL_CHROMA_CENTER; } - if (params->color.light == MP_CSP_LIGHT_AUTO) { + if (params->light == MP_CSP_LIGHT_AUTO) { // HLG is always scene-referred (using its own OOTF), everything else - // we assume is display-refered by default. - if (params->color.gamma == MP_CSP_TRC_HLG) { - params->color.light = MP_CSP_LIGHT_SCENE_HLG; + // we assume is display-referred by default. + if (params->color.transfer == PL_COLOR_TRC_HLG) { + params->light = MP_CSP_LIGHT_SCENE_HLG; } else { - params->color.light = MP_CSP_LIGHT_DISPLAY; + params->light = MP_CSP_LIGHT_DISPLAY; } } } @@ -947,54 +1025,107 @@ struct mp_image *mp_image_from_av_frame(struct AVFrame *src) dst->pict_type = src->pict_type; + dst->params.crop.x0 = src->crop_left; + dst->params.crop.y0 = src->crop_top; + dst->params.crop.x1 = src->width - src->crop_right; + dst->params.crop.y1 = src->height - src->crop_bottom; + dst->fields = 0; +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(58, 7, 100) + if (src->flags & AV_FRAME_FLAG_INTERLACED) + dst->fields |= MP_IMGFIELD_INTERLACED; + if (src->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) + dst->fields |= MP_IMGFIELD_TOP_FIRST; +#else if (src->interlaced_frame) dst->fields |= MP_IMGFIELD_INTERLACED; if (src->top_field_first) dst->fields |= MP_IMGFIELD_TOP_FIRST; +#endif if (src->repeat_pict == 1) dst->fields |= MP_IMGFIELD_REPEAT_FIRST; - dst->params.color = (struct mp_colorspace){ - .space = avcol_spc_to_mp_csp(src->colorspace), - .levels = avcol_range_to_mp_csp_levels(src->color_range), - .primaries = avcol_pri_to_mp_csp_prim(src->color_primaries), - .gamma = avcol_trc_to_mp_csp_trc(src->color_trc), + dst->params.repr = (struct pl_color_repr){ + .sys = pl_system_from_av(src->colorspace), + .levels = pl_levels_from_av(src->color_range), + }; + + dst->params.color = (struct pl_color_space){ + .primaries = pl_primaries_from_av(src->color_primaries), + .transfer = pl_transfer_from_av(src->color_trc), }; - dst->params.chroma_location = avchroma_location_to_mp(src->chroma_location); + dst->params.chroma_location = pl_chroma_from_av(src->chroma_location); if (src->opaque_ref) { struct mp_image_params *p = (void *)src->opaque_ref->data; - dst->params.rotate = p->rotate; dst->params.stereo3d = p->stereo3d; // Might be incorrect if colorspace changes. - dst->params.color.light = p->color.light; - dst->params.alpha = p->alpha; + dst->params.light = p->light; + dst->params.repr.alpha = p->repr.alpha; + } + + sd = av_frame_get_side_data(src, AV_FRAME_DATA_DISPLAYMATRIX); + if (sd) { + double r = av_display_rotation_get((int32_t *)(sd->data)); + if (!isnan(r)) + dst->params.rotate = (((int)(-r) % 360) + 360) % 360; } sd = av_frame_get_side_data(src, AV_FRAME_DATA_ICC_PROFILE); if (sd) dst->icc_profile = sd->buf; - // Get the content light metadata if available - sd = av_frame_get_side_data(src, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + AVFrameSideData *mdm = av_frame_get_side_data(src, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + AVFrameSideData *clm = av_frame_get_side_data(src, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + AVFrameSideData *dhp = av_frame_get_side_data(src, AV_FRAME_DATA_DYNAMIC_HDR_PLUS); + pl_map_hdr_metadata(&dst->params.color.hdr, &(struct pl_av_hdr_metadata) { + .mdm = (void *)(mdm ? mdm->data : NULL), + .clm = (void *)(clm ? clm->data : NULL), + .dhp = (void *)(dhp ? dhp->data : NULL), + }); + + sd = av_frame_get_side_data(src, AV_FRAME_DATA_A53_CC); + if (sd) + dst->a53_cc = sd->buf; + + AVBufferRef *dovi = NULL; +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 16, 100) + sd = av_frame_get_side_data(src, AV_FRAME_DATA_DOVI_METADATA); if (sd) { - AVContentLightMetadata *clm = (AVContentLightMetadata *)sd->data; - dst->params.color.sig_peak = clm->MaxCLL / MP_REF_WHITE; +#ifdef PL_HAVE_LAV_DOLBY_VISION + const AVDOVIMetadata *metadata = (const AVDOVIMetadata *)sd->buf->data; + const AVDOVIRpuDataHeader *header = av_dovi_get_header(metadata); + if (header->disable_residual_flag) { + dst->dovi = dovi = av_buffer_alloc(sizeof(struct pl_dovi_metadata)); + MP_HANDLE_OOM(dovi); +#if PL_API_VER >= 343 + pl_map_avdovi_metadata(&dst->params.color, &dst->params.repr, + (void *)dst->dovi->data, metadata); +#else + struct pl_frame frame; + frame.repr = dst->params.repr; + frame.color = dst->params.color; + pl_frame_map_avdovi_metadata(&frame, (void *)dst->dovi->data, metadata); + dst->params.repr = frame.repr; + dst->params.color = frame.color; +#endif + } +#endif } - // Otherwise, try getting the mastering metadata if available - sd = av_frame_get_side_data(src, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); - if (!dst->params.color.sig_peak && sd) { - AVMasteringDisplayMetadata *mdm = (AVMasteringDisplayMetadata *)sd->data; - if (mdm->has_luminance) - dst->params.color.sig_peak = av_q2d(mdm->max_luminance) / MP_REF_WHITE; + sd = av_frame_get_side_data(src, AV_FRAME_DATA_DOVI_RPU_BUFFER); + if (sd) { +#ifdef PL_HAVE_LIBDOVI + pl_hdr_metadata_from_dovi_rpu(&dst->params.color.hdr, sd->buf->data, + sd->buf->size); +#endif } +#endif - sd = av_frame_get_side_data(src, AV_FRAME_DATA_A53_CC); + sd = av_frame_get_side_data(src, AV_FRAME_DATA_FILM_GRAIN_PARAMS); if (sd) - dst->a53_cc = sd->buf; + dst->film_grain = sd->buf; for (int n = 0; n < src->nb_side_data; n++) { sd = src->side_data[n]; @@ -1014,6 +1145,7 @@ struct mp_image *mp_image_from_av_frame(struct AVFrame *src) // Allocated, but non-refcounted data. talloc_free(dst->ff_side_data); + av_buffer_unref(&dovi); return res; } @@ -1042,6 +1174,11 @@ struct AVFrame *mp_image_to_av_frame(struct mp_image *src) dst->width = src->w; dst->height = src->h; + dst->crop_left = src->params.crop.x0; + dst->crop_top = src->params.crop.y0; + dst->crop_right = dst->width - src->params.crop.x1; + dst->crop_bottom = dst->height - src->params.crop.y1; + dst->sample_aspect_ratio.num = src->params.p_w; dst->sample_aspect_ratio.den = src->params.p_h; @@ -1052,41 +1189,44 @@ struct AVFrame *mp_image_to_av_frame(struct mp_image *src) dst->extended_data = dst->data; dst->pict_type = src->pict_type; +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(58, 7, 100) + if (src->fields & MP_IMGFIELD_INTERLACED) + dst->flags |= AV_FRAME_FLAG_INTERLACED; + if (src->fields & MP_IMGFIELD_TOP_FIRST) + dst->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; +#else if (src->fields & MP_IMGFIELD_INTERLACED) dst->interlaced_frame = 1; if (src->fields & MP_IMGFIELD_TOP_FIRST) dst->top_field_first = 1; +#endif if (src->fields & MP_IMGFIELD_REPEAT_FIRST) dst->repeat_pict = 1; - dst->colorspace = mp_csp_to_avcol_spc(src->params.color.space); - dst->color_range = mp_csp_levels_to_avcol_range(src->params.color.levels); - dst->color_primaries = - mp_csp_prim_to_avcol_pri(src->params.color.primaries); - dst->color_trc = mp_csp_trc_to_avcol_trc(src->params.color.gamma); + pl_avframe_set_repr(dst, src->params.repr); - dst->chroma_location = mp_chroma_location_to_av(src->params.chroma_location); + dst->chroma_location = pl_chroma_to_av(src->params.chroma_location); dst->opaque_ref = av_buffer_alloc(sizeof(struct mp_image_params)); - if (!dst->opaque_ref) - abort(); + MP_HANDLE_OOM(dst->opaque_ref); *(struct mp_image_params *)dst->opaque_ref->data = src->params; if (src->icc_profile) { AVFrameSideData *sd = av_frame_new_side_data_from_buf(dst, AV_FRAME_DATA_ICC_PROFILE, new_ref->icc_profile); - if (!sd) - abort(); + MP_HANDLE_OOM(sd); new_ref->icc_profile = NULL; } - if (src->params.color.sig_peak) { - AVContentLightMetadata *clm = - av_content_light_metadata_create_side_data(dst); - if (!clm) - abort(); - clm->MaxCLL = src->params.color.sig_peak * MP_REF_WHITE; + pl_avframe_set_color(dst, src->params.color); + + { + AVFrameSideData *sd = av_frame_new_side_data(dst, + AV_FRAME_DATA_DISPLAYMATRIX, + sizeof(int32_t) * 9); + MP_HANDLE_OOM(sd); + av_display_rotation_set((int32_t *)sd->data, src->params.rotate); } // Add back side data, but only for types which are not specially handled @@ -1096,8 +1236,7 @@ struct AVFrame *mp_image_to_av_frame(struct mp_image *src) if (!av_frame_get_side_data(dst, mpsd->type)) { AVFrameSideData *sd = av_frame_new_side_data_from_buf(dst, mpsd->type, mpsd->buf); - if (!sd) - abort(); + MP_HANDLE_OOM(sd); mpsd->buf = NULL; } } |