summaryrefslogtreecommitdiffstats
path: root/video
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-07-18 13:17:56 +0200
committerwm4 <wm4@nowhere>2013-07-18 13:31:01 +0200
commitd8659c9aa0ab74a3dabb321f34e93dd7f3890b03 (patch)
tree5116f8710d67cd9409dc54dee2e3cf5ef5e99bc6 /video
parent48789b354648b9892ec63dc91b91f7a3d5b588e4 (diff)
downloadmpv-d8659c9aa0ab74a3dabb321f34e93dd7f3890b03.tar.bz2
mpv-d8659c9aa0ab74a3dabb321f34e93dd7f3890b03.tar.xz
sws_utils: refactor swscale wrapper code
This splits the monolithic mp_image_swscale() function into a bunch of functions and a context struct. This means it's possible to set arbitrary parameters (e.g. even obscure ones without getting in the way), and you don't have to create the context on every call. This code is preparation for removing duplicated libswscale API usage from other parts of the code.
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