summaryrefslogtreecommitdiffstats
path: root/video/mp_image.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/mp_image.c')
-rw-r--r--video/mp_image.c453
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(&params->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;
}
}