summaryrefslogtreecommitdiffstats
path: root/video
diff options
context:
space:
mode:
Diffstat (limited to 'video')
-rw-r--r--video/mp_image.c22
-rw-r--r--video/mp_image.h9
-rw-r--r--video/sws_utils.c199
-rw-r--r--video/sws_utils.h30
4 files changed, 197 insertions, 63 deletions
diff --git a/video/mp_image.c b/video/mp_image.c
index 029c5fa91b..238f591858 100644
--- a/video/mp_image.c
+++ b/video/mp_image.c
@@ -405,22 +405,18 @@ void mp_image_vflip(struct mp_image *img)
}
}
-enum mp_csp mp_image_csp(struct mp_image *img)
+bool mp_image_params_equals(const struct mp_image_params *p1,
+ const struct mp_image_params *p2)
{
- if (img->colorspace != MP_CSP_AUTO)
- return img->colorspace;
- return (img->flags & MP_IMGFLAG_YUV) ? MP_CSP_BT_601 : MP_CSP_RGB;
+ return p1->imgfmt == p2->imgfmt &&
+ p1->w == p2->w && p1->h == p2->h &&
+ p1->d_w == p2->d_w && p1->d_h == p2->d_h &&
+ p1->colorspace == p2->colorspace &&
+ p1->colorlevels == p2->colorlevels;
}
-enum mp_csp_levels mp_image_levels(struct mp_image *img)
-{
- if (img->levels != MP_CSP_LEVELS_AUTO)
- return img->levels;
- return (img->flags & MP_IMGFLAG_YUV) ? MP_CSP_LEVELS_TV : MP_CSP_LEVELS_PC;
-}
-
-static void mp_image_params_from_image(struct mp_image_params *params,
- const struct mp_image *image)
+void mp_image_params_from_image(struct mp_image_params *params,
+ const struct mp_image *image)
{
// (Ideally mp_image should use mp_image_params directly instead)
*params = (struct mp_image_params) {
diff --git a/video/mp_image.h b/video/mp_image.h
index 69e3ae52b0..7324423967 100644
--- a/video/mp_image.h
+++ b/video/mp_image.h
@@ -131,15 +131,18 @@ struct mp_image *mp_image_new_external_ref(struct mp_image *img, void *arg,
bool (*is_unique)(void *arg),
void (*free)(void *arg));
-enum mp_csp mp_image_csp(struct mp_image *img);
-enum mp_csp_levels mp_image_levels(struct mp_image *img);
-
struct mp_csp_details;
void mp_image_set_colorspace_details(struct mp_image *image,
struct mp_csp_details *csp);
void mp_image_params_guess_csp(struct mp_image_params *params);
+bool mp_image_params_equals(const struct mp_image_params *p1,
+ const struct mp_image_params *p2);
+
+void mp_image_params_from_image(struct mp_image_params *params,
+ const struct mp_image *image);
+
struct AVFrame;
void mp_image_copy_fields_from_av_frame(struct mp_image *dst,
struct AVFrame *src);
diff --git a/video/sws_utils.c b/video/sws_utils.c
index 767aace541..a49f56397a 100644
--- a/video/sws_utils.c
+++ b/video/sws_utils.c
@@ -109,6 +109,33 @@ struct SwsContext *sws_getContextFromCmdLine(int srcW, int srcH,
srcFilterParam, dstFilterParam, NULL);
}
+// Set ctx parameters to global command line flags.
+void mp_sws_set_from_cmdline(struct mp_sws_context *ctx)
+{
+ sws_freeFilter(ctx->src_filter);
+ ctx->src_filter = sws_getDefaultFilter(sws_lum_gblur, sws_chr_gblur,
+ sws_lum_sharpen, sws_chr_sharpen,
+ sws_chr_hshift, sws_chr_vshift, 0);
+ ctx->force_reload = true;
+
+ ctx->flags = SWS_PRINT_INFO;
+
+ switch (sws_flags) {
+ case 0: ctx->flags |= SWS_FAST_BILINEAR; break;
+ case 1: ctx->flags |= SWS_BILINEAR; break;
+ case 2: ctx->flags |= SWS_BICUBIC; break;
+ case 3: ctx->flags |= SWS_X; break;
+ case 4: ctx->flags |= SWS_POINT; break;
+ case 5: ctx->flags |= SWS_AREA; break;
+ case 6: ctx->flags |= SWS_BICUBLIN; break;
+ case 7: ctx->flags |= SWS_GAUSS; break;
+ case 8: ctx->flags |= SWS_SINC; break;
+ case 9: ctx->flags |= SWS_LANCZOS; break;
+ case 10: ctx->flags |= SWS_SPLINE; break;
+ default: ctx->flags |= SWS_BILINEAR; break;
+ }
+}
+
bool mp_sws_supported_format(int imgfmt)
{
enum PixelFormat av_format = imgfmt2pixfmt(imgfmt);
@@ -146,8 +173,8 @@ static void planarize32(struct mp_image *dst, struct mp_image *src,
#define SET_COMPS(comp, r, g, b, a) \
{ (comp)[0] = (r); (comp)[1] = (g); (comp)[2] = (b); (comp)[3] = (a); }
-static void to_gbrp(struct mp_image *dst, struct mp_image *src,
- int my_sws_flags)
+static int to_gbrp(struct mp_image *dst, struct mp_image *src,
+ int my_sws_flags)
{
struct mp_image *temp = NULL;
int comp[4];
@@ -167,80 +194,158 @@ static void to_gbrp(struct mp_image *dst, struct mp_image *src,
planarize32(dst, src, comp);
talloc_free(temp);
+ return 0;
+}
+
+static bool cache_valid(struct mp_sws_context *ctx)
+{
+ struct mp_sws_context *old = ctx->cached;
+ if (ctx->force_reload)
+ return false;
+ return mp_image_params_equals(&ctx->src, &old->src) &&
+ mp_image_params_equals(&ctx->dst, &old->dst) &&
+ ctx->flags == old->flags &&
+ ctx->brightness == old->brightness &&
+ ctx->contrast == old->contrast &&
+ ctx->saturation == old->saturation;
}
+static int free_mp_sws(void *p)
+{
+ struct mp_sws_context *ctx = p;
+ sws_freeContext(ctx->sws);
+ sws_freeFilter(ctx->src_filter);
+ sws_freeFilter(ctx->dst_filter);
+ return 0;
+}
+
+// You're supposed to set your scaling parameters on the returned context.
+// Free the context with talloc_free().
+struct mp_sws_context *mp_sws_alloc(void *talloc_parent)
+{
+ struct mp_sws_context *ctx = talloc_ptrtype(talloc_parent, ctx);
+ *ctx = (struct mp_sws_context) {
+ .flags = SWS_BILINEAR,
+ .contrast = 1 << 16, // 1.0 in 16.16 fixed point
+ .saturation = 1 << 16,
+ .force_reload = true,
+ .cached = talloc_zero(ctx, struct mp_sws_context),
+ };
+ talloc_set_destructor(ctx, free_mp_sws);
+ return ctx;
+}
-static void mp_sws_set_conv(struct SwsContext *sws, struct mp_image *dst,
- struct mp_image *src, int my_sws_flags)
+// Reinitialize (if needed) - return error code.
+// Optional, but possibly useful to avoid having to handle mp_sws_scale errors.
+int mp_sws_reinit(struct mp_sws_context *ctx)
{
+ if (cache_valid(ctx))
+ return 0;
+
+ sws_freeContext(ctx->sws);
+ ctx->sws = sws_alloc_context();
+ if (!ctx->sws)
+ return -1;
+
+ struct mp_image_params *src = &ctx->src;
+ struct mp_image_params *dst = &ctx->dst;
+
+ mp_image_params_guess_csp(src); // sanitize colorspace/colorlevels
+ mp_image_params_guess_csp(dst);
+
+ struct mp_imgfmt_desc src_fmt = mp_imgfmt_get_desc(src->imgfmt);
+ struct mp_imgfmt_desc dst_fmt = mp_imgfmt_get_desc(dst->imgfmt);
+ if (!src_fmt.id || !dst_fmt.id)
+ return -1;
+
enum PixelFormat s_fmt = imgfmt2pixfmt(src->imgfmt);
- int s_csp = mp_csp_to_sws_colorspace(mp_image_csp(src));
- int s_range = mp_image_levels(src) == MP_CSP_LEVELS_PC;
+ if (s_fmt == PIX_FMT_NONE || sws_isSupportedInput(s_fmt) < 1)
+ return -1;
enum PixelFormat d_fmt = imgfmt2pixfmt(dst->imgfmt);
- int d_csp = mp_csp_to_sws_colorspace(mp_image_csp(dst));
- int d_range = mp_image_levels(dst) == MP_CSP_LEVELS_PC;
+ if (d_fmt == PIX_FMT_NONE || sws_isSupportedOutput(d_fmt) < 1)
+ return -1;
+
+ int s_csp = mp_csp_to_sws_colorspace(src->colorspace);
+ int s_range = src->colorlevels == MP_CSP_LEVELS_PC;
+
+ int d_csp = mp_csp_to_sws_colorspace(dst->colorspace);
+ int d_range = dst->colorlevels == MP_CSP_LEVELS_PC;
// Work around libswscale bug #1852 (fixed in ffmpeg commit 8edf9b1fa):
// setting range flags for RGB gives random bogus results.
// Newer libswscale always ignores range flags for RGB.
- bool s_yuv = src->flags & MP_IMGFLAG_YUV;
- bool d_yuv = dst->flags & MP_IMGFLAG_YUV;
- s_range = s_range && s_yuv;
- d_range = d_range && d_yuv;
+ s_range = s_range && (src_fmt.flags & MP_IMGFLAG_YUV);
+ d_range = d_range && (dst_fmt.flags & MP_IMGFLAG_YUV);
- av_opt_set_int(sws, "sws_flags", my_sws_flags, 0);
+ av_opt_set_int(ctx->sws, "sws_flags", ctx->flags, 0);
- av_opt_set_int(sws, "srcw", src->w, 0);
- av_opt_set_int(sws, "srch", src->h, 0);
- av_opt_set_int(sws, "src_format", s_fmt, 0);
+ av_opt_set_int(ctx->sws, "srcw", src->w, 0);
+ av_opt_set_int(ctx->sws, "srch", src->h, 0);
+ av_opt_set_int(ctx->sws, "src_format", s_fmt, 0);
- av_opt_set_int(sws, "dstw", dst->w, 0);
- av_opt_set_int(sws, "dsth", dst->h, 0);
- av_opt_set_int(sws, "dst_format", d_fmt, 0);
+ av_opt_set_int(ctx->sws, "dstw", dst->w, 0);
+ av_opt_set_int(ctx->sws, "dsth", dst->h, 0);
+ av_opt_set_int(ctx->sws, "dst_format", d_fmt, 0);
- sws_setColorspaceDetails(sws, sws_getCoefficients(s_csp), s_range,
+ // This can fail even with normal operation, e.g. if a conversion path
+ // simply does not support these settings.
+ sws_setColorspaceDetails(ctx->sws, sws_getCoefficients(s_csp), s_range,
sws_getCoefficients(d_csp), d_range,
- 0, 1 << 16, 1 << 16);
+ ctx->brightness, ctx->contrast, ctx->saturation);
+
+ if (sws_init_context(ctx->sws, ctx->src_filter, ctx->dst_filter) < 0)
+ return -1;
+
+ ctx->force_reload = false;
+ *ctx->cached = *ctx;
+ return 1;
}
-void mp_image_swscale(struct mp_image *dst, struct mp_image *src,
- int my_sws_flags)
+// Scale from src to dst - if src/dst have different parameters from previous
+// calls, the context is reinitialized. Return error code. (It can fail if
+// reinitialization was necessary, and swscale returned an error.)
+int mp_sws_scale(struct mp_sws_context *ctx, struct mp_image *dst,
+ struct mp_image *src)
{
+ // Hack for older swscale versions which don't support this.
+ // We absolutely need this in the OSD rendering path.
if (dst->imgfmt == IMGFMT_GBRP && !sws_isSupportedOutput(PIX_FMT_GBRP))
- return to_gbrp(dst, src, my_sws_flags);
+ return to_gbrp(dst, src, ctx->flags);
- struct SwsContext *sws = sws_alloc_context();
- mp_sws_set_conv(sws, dst, src, my_sws_flags);
+ mp_image_params_from_image(&ctx->src, src);
+ mp_image_params_from_image(&ctx->dst, dst);
- int res = sws_init_context(sws, NULL, NULL);
- assert(res >= 0);
+ int r = mp_sws_reinit(ctx);
+ if (r < 0) {
+ mp_msg(MSGT_VFILTER, MSGL_ERR, "libswscale initialization failed.\n");
+ return r;
+ }
- sws_scale(sws, (const uint8_t *const *) src->planes, src->stride,
+ sws_scale(ctx->sws, (const uint8_t *const *) src->planes, src->stride,
0, src->h, dst->planes, dst->stride);
- sws_freeContext(sws);
+ return 0;
+}
+
+void mp_image_swscale(struct mp_image *dst, struct mp_image *src,
+ int my_sws_flags)
+{
+ struct mp_sws_context *ctx = mp_sws_alloc(NULL);
+ ctx->flags = my_sws_flags;
+ mp_sws_scale(ctx, dst, src);
+ talloc_free(ctx);
}
void mp_image_sw_blur_scale(struct mp_image *dst, struct mp_image *src,
float gblur)
{
- struct SwsContext *sws = sws_alloc_context();
-
- int flags = SWS_LANCZOS | SWS_FULL_CHR_H_INT | SWS_FULL_CHR_H_INP |
- SWS_ACCURATE_RND | SWS_BITEXACT;
-
- mp_sws_set_conv(sws, dst, src, flags);
-
- SwsFilter *src_filter = sws_getDefaultFilter(gblur, gblur, 0, 0, 0, 0, 0);
-
- int res = sws_init_context(sws, src_filter, NULL);
- assert(res >= 0);
-
- sws_scale(sws, (const uint8_t *const *) src->planes, src->stride,
- 0, src->h, dst->planes, dst->stride);
- sws_freeContext(sws);
-
- sws_freeFilter(src_filter);
+ struct mp_sws_context *ctx = mp_sws_alloc(NULL);
+ ctx->flags = SWS_LANCZOS | SWS_FULL_CHR_H_INT | SWS_FULL_CHR_H_INP |
+ SWS_ACCURATE_RND | SWS_BITEXACT;
+ ctx->src_filter = sws_getDefaultFilter(gblur, gblur, 0, 0, 0, 0, 0);
+ ctx->force_reload = true;
+ mp_sws_scale(ctx, dst, src);
+ talloc_free(ctx);
}
// vim: ts=4 sw=4 et tw=80
diff --git a/video/sws_utils.h b/video/sws_utils.h
index 22d16edefb..bef8e465ce 100644
--- a/video/sws_utils.h
+++ b/video/sws_utils.h
@@ -4,6 +4,8 @@
#include <stdbool.h>
#include <libswscale/swscale.h>
+#include "mp_image.h"
+
struct mp_image;
struct mp_csp_details;
@@ -26,6 +28,34 @@ void mp_image_swscale(struct mp_image *dst, struct mp_image *src,
void mp_image_sw_blur_scale(struct mp_image *dst, struct mp_image *src,
float gblur);
+struct mp_sws_context {
+ // User configuration. These can be changed freely, at any time.
+ // mp_sws_scale() will handle the changes transparently.
+ int flags;
+ int brightness, contrast, saturation;
+ bool force_reload;
+ // These are also implicitly set by mp_sws_scale(), and thus optional.
+ // Setting them before that call makes sense when using mp_sws_reinit().
+ struct mp_image_params src, dst;
+
+ // Changing these requires setting force_reload=true.
+ // By default, they are NULL.
+ // Freeing the mp_sws_context will deallocate these if set.
+ struct SwsFilter *src_filter, *dst_filter;
+
+ // Cached context (if any)
+ struct SwsContext *sws;
+
+ // Contains parameters for which sws is valid
+ struct mp_sws_context *cached;
+};
+
+struct mp_sws_context *mp_sws_alloc(void *talloc_parent);
+int mp_sws_reinit(struct mp_sws_context *ctx);
+void mp_sws_set_from_cmdline(struct mp_sws_context *ctx);
+int mp_sws_scale(struct mp_sws_context *ctx, struct mp_image *dst,
+ struct mp_image *src);
+
#endif /* MP_SWS_UTILS_H */
// vim: ts=4 sw=4 et tw=80