diff --git a/libswscale/swscale.h b/libswscale/swscale.h
index 50c705ae06..4baef532b6 100644
--- a/libswscale/swscale.h
+++ b/libswscale/swscale.h
@@ -42,8 +42,6 @@
 #include "version.h"
 #endif
 
-typedef struct SwsContext SwsContext;
-
 /**
  * @defgroup libsws libswscale
  * Color conversion and scaling library.
@@ -65,17 +63,98 @@ const char *swscale_configuration(void);
 const char *swscale_license(void);
 
 /**
- * Get the AVClass for swsContext. It can be used in combination with
+ * Get the AVClass for SwsContext. It can be used in combination with
  * AV_OPT_SEARCH_FAKE_OBJ for examining options.
  *
  * @see av_opt_find().
  */
 const AVClass *sws_get_class(void);
 
-/**
- * Allocate an empty SwsContext. This must be filled and passed to
- * sws_init_context(). For filling see AVOptions, options.c and
- * sws_setColorspaceDetails().
+/******************************
+ * Flags and quality settings *
+ ******************************/
+
+typedef enum SwsDither {
+    SWS_DITHER_NONE = 0, /* disable dithering */
+    SWS_DITHER_AUTO,     /* auto-select from preset */
+    SWS_DITHER_BAYER,    /* ordered dither matrix */
+    SWS_DITHER_ED,       /* error diffusion */
+    SWS_DITHER_A_DITHER, /* arithmetic addition */
+    SWS_DITHER_X_DITHER, /* arithmetic xor */
+    SWS_DITHER_NB,       /* not part of the ABI */
+} SwsDither;
+
+typedef enum SwsAlphaBlend {
+    SWS_ALPHA_BLEND_NONE = 0,
+    SWS_ALPHA_BLEND_UNIFORM,
+    SWS_ALPHA_BLEND_CHECKERBOARD,
+    SWS_ALPHA_BLEND_NB,  /* not part of the ABI */
+} SwsAlphaBlend;
+
+/***********************************
+ * Context creation and management *
+ ***********************************/
+
+/**
+ * Main external API structure. New fields can be added to the end with
+ * minor version bumps. Removal, reordering and changes to existing fields
+ * require a major version bump. sizeof(SwsContext) is not part of the ABI.
+ */
+typedef struct SwsContext {
+    const AVClass *av_class;
+
+    /**
+     * Private data of the user, can be used to carry app specific stuff.
+     */
+    void *opaque;
+
+    /**
+     * Bitmask of SWS_*.
+     */
+    unsigned flags;
+
+    /**
+     * Extra parameters for fine-tuning certain scalers.
+     */
+    double scaler_params[2];
+
+    /**
+     * How many threads to use for processing, or 0 for automatic selection.
+     */
+    int threads;
+
+    /**
+     * Dither mode.
+     */
+    SwsDither dither;
+
+    /**
+     * Alpha blending mode. See `SwsAlphaBlend` for details.
+     */
+    SwsAlphaBlend alpha_blend;
+
+    /**
+     * Use gamma correct scaling.
+     */
+    int gamma_flag;
+
+    /**
+     * Frame property overrides.
+     */
+    int src_w, src_h;  ///< Width and height of the source frame
+    int dst_w, dst_h;  ///< Width and height of the destination frame
+    int src_format;    ///< Source pixel format
+    int dst_format;    ///< Destination pixel format
+    int src_range;     ///< Source is full range
+    int dst_range;     ///< Destination is full range
+    int src_v_chr_pos; ///< Source vertical chroma position in luma grid / 256
+    int src_h_chr_pos; ///< Source horizontal chroma position
+    int dst_v_chr_pos; ///< Destination vertical chroma position
+    int dst_h_chr_pos; ///< Destination horizontal chroma position
+} SwsContext;
+
+/**
+ * Allocate an empty SwsContext and set its fields to default values.
  */
 SwsContext *sws_alloc_context(void);
 
diff --git a/libswscale/swscale_internal.h b/libswscale/swscale_internal.h
index d459b79af3..12fa406e2c 100644
--- a/libswscale/swscale_internal.h
+++ b/libswscale/swscale_internal.h
@@ -73,23 +73,6 @@ static inline SwsInternal *sws_internal(const SwsContext *sws)
     return (SwsInternal *) sws;
 }
 
-typedef enum SwsDither {
-    SWS_DITHER_NONE = 0,
-    SWS_DITHER_AUTO,
-    SWS_DITHER_BAYER,
-    SWS_DITHER_ED,
-    SWS_DITHER_A_DITHER,
-    SWS_DITHER_X_DITHER,
-    SWS_DITHER_NB,
-} SwsDither;
-
-typedef enum SwsAlphaBlend {
-    SWS_ALPHA_BLEND_NONE  = 0,
-    SWS_ALPHA_BLEND_UNIFORM,
-    SWS_ALPHA_BLEND_CHECKERBOARD,
-    SWS_ALPHA_BLEND_NB,
-} SwsAlphaBlend;
-
 typedef struct Range {
     unsigned int start;
     unsigned int len;
@@ -329,32 +312,10 @@ struct SwsFilterDescriptor;
 
 /* This struct should be aligned on at least a 32-byte boundary. */
 struct SwsInternal {
-    /* Currently active user-facing options. */
-    struct {
-        const AVClass *av_class;
-
-        double scaler_params[2];       ///< Input parameters for scaling algorithms that need them.
-        int flags;                     ///< Flags passed by the user to select scaler algorithm, optimizations, subsampling, etc...
-        int threads;                   ///< Number of threads used for scaling
-
-        int src_w;                     ///< Width  of source      luma/alpha planes.
-        int src_h;                     ///< Height of source      luma/alpha planes.
-        int dst_w;                     ///< Width  of destination luma/alpha planes.
-        int dst_h;                     ///< Height of destination luma/alpha planes.
-        enum AVPixelFormat src_format; ///< Source      pixel format.
-        enum AVPixelFormat dst_format; ///< Destination pixel format.
-        int src_range;                 ///< 0 = MPG YUV range, 1 = JPG YUV range (source      image).
-        int dst_range;                 ///< 0 = MPG YUV range, 1 = JPG YUV range (destination image).
-        int src_h_chr_pos;
-        int dst_h_chr_pos;
-        int src_v_chr_pos;
-        int dst_v_chr_pos;
-        int gamma_flag;
-
-        SwsDither dither;
-        SwsAlphaBlend alpha_blend;
-    } opts;
+    /* Currently active user-facing options. Also contains AVClass */
+    SwsContext opts;
 
+    /* Parent context (for slice contexts) */
     SwsContext *parent;
 
     AVSliceThread      *slicethread;
diff --git a/libswscale/utils.c b/libswscale/utils.c
index 4bddcb43d8..aee1153d4e 100644
--- a/libswscale/utils.c
+++ b/libswscale/utils.c
@@ -291,22 +291,20 @@ static SwsContext *alloc_set_opts(int srcW, int srcH, enum AVPixelFormat srcForm
                                   int flags, const double *param)
 {
     SwsContext *sws = sws_alloc_context();
-    SwsInternal *c = sws_internal(sws);
-
-    if (!c)
+    if (!sws)
         return NULL;
 
-    c->opts.flags = flags;
-    c->opts.src_w = srcW;
-    c->opts.src_h = srcH;
-    c->opts.dst_w = dstW;
-    c->opts.dst_h      = dstH;
-    c->opts.src_format = srcFormat;
-    c->opts.dst_format = dstFormat;
+    sws->flags = flags;
+    sws->src_w = srcW;
+    sws->src_h = srcH;
+    sws->dst_w = dstW;
+    sws->dst_h      = dstH;
+    sws->src_format = srcFormat;
+    sws->dst_format = dstFormat;
 
     if (param) {
-        c->opts.scaler_params[0] = param[0];
-        c->opts.scaler_params[1] = param[1];
+        sws->scaler_params[0] = param[0];
+        sws->scaler_params[1] = param[1];
     }
 
     return sws;
@@ -1234,16 +1232,16 @@ int sws_getColorspaceDetails(SwsContext *sws, int **inv_table,
 
 SwsContext *sws_alloc_context(void)
 {
-    SwsInternal *c = av_mallocz(sizeof(SwsInternal));
+    SwsInternal *c = (SwsInternal *) av_mallocz(sizeof(SwsInternal));
+    if (!c)
+        return NULL;
 
-    if (c) {
-        c->opts.av_class = &ff_sws_context_class;
-        av_opt_set_defaults(c);
-        atomic_init(&c->stride_unaligned_warned, 0);
-        atomic_init(&c->data_unaligned_warned,   0);
-    }
+    c->opts.av_class = &ff_sws_context_class;
+    av_opt_set_defaults(c);
+    atomic_init(&c->stride_unaligned_warned, 0);
+    atomic_init(&c->data_unaligned_warned,   0);
 
-    return (SwsContext *) c;
+    return &c->opts;
 }
 
 static uint16_t * alloc_gamma_tbl(double e)
@@ -2527,7 +2525,7 @@ void sws_freeContext(SwsContext *sws)
 
     ff_free_filters(c);
 
-    av_free(sws);
+    av_free(c);
 }
 
 void sws_free_context(SwsContext **pctx)
@@ -2540,7 +2538,7 @@ void sws_free_context(SwsContext **pctx)
     *pctx = NULL;
 }
 
-SwsContext *sws_getCachedContext(SwsContext *sws, int srcW,
+SwsContext *sws_getCachedContext(SwsContext *prev, int srcW,
                                  int srcH, enum AVPixelFormat srcFormat,
                                  int dstW, int dstH,
                                  enum AVPixelFormat dstFormat, int flags,
@@ -2548,59 +2546,48 @@ SwsContext *sws_getCachedContext(SwsContext *sws, int srcW,
                                  SwsFilter *dstFilter,
                                  const double *param)
 {
-    SwsInternal *context;
-
+    SwsContext *sws;
     static const double default_param[2] = { SWS_PARAM_DEFAULT,
                                              SWS_PARAM_DEFAULT };
-    int64_t src_h_chr_pos = -513, dst_h_chr_pos = -513,
-            src_v_chr_pos = -513, dst_v_chr_pos = -513;
 
     if (!param)
         param = default_param;
 
-    if ((context = sws_internal(sws)) &&
-        (context->opts.src_w      != srcW      ||
-         context->opts.src_h      != srcH      ||
-         context->opts.src_format != srcFormat ||
-         context->opts.dst_w      != dstW      ||
-         context->opts.dst_h      != dstH      ||
-         context->opts.dst_format != dstFormat ||
-         context->opts.flags      != flags     ||
-         context->opts.scaler_params[0]  != param[0]  ||
-         context->opts.scaler_params[1]  != param[1])) {
-
-        av_opt_get_int(context, "src_h_chr_pos", 0, &src_h_chr_pos);
-        av_opt_get_int(context, "src_v_chr_pos", 0, &src_v_chr_pos);
-        av_opt_get_int(context, "dst_h_chr_pos", 0, &dst_h_chr_pos);
-        av_opt_get_int(context, "dst_v_chr_pos", 0, &dst_v_chr_pos);
-        sws_freeContext(sws);
-        sws = NULL;
-    }
-
-    if (!sws) {
-        if (!(sws = sws_alloc_context()))
-            return NULL;
-        context            = sws_internal(sws);
-        context->opts.src_w      = srcW;
-        context->opts.src_h      = srcH;
-        context->opts.src_format = srcFormat;
-        context->opts.dst_w      = dstW;
-        context->opts.dst_h      = dstH;
-        context->opts.dst_format = dstFormat;
-        context->opts.flags      = flags;
-        context->opts.scaler_params[0]  = param[0];
-        context->opts.scaler_params[1]  = param[1];
-
-        av_opt_set_int(context, "src_h_chr_pos", src_h_chr_pos, 0);
-        av_opt_set_int(context, "src_v_chr_pos", src_v_chr_pos, 0);
-        av_opt_set_int(context, "dst_h_chr_pos", dst_h_chr_pos, 0);
-        av_opt_set_int(context, "dst_v_chr_pos", dst_v_chr_pos, 0);
-
-        if (sws_init_context(sws, srcFilter, dstFilter) < 0) {
-            sws_freeContext(sws);
-            return NULL;
-        }
+    if (prev && (prev->src_w            == srcW      ||
+                 prev->src_h            == srcH      ||
+                 prev->src_format       == srcFormat ||
+                 prev->dst_w            == dstW      ||
+                 prev->dst_h            == dstH      ||
+                 prev->dst_format       == dstFormat ||
+                 prev->flags            == flags     ||
+                 prev->scaler_params[0] == param[0]  ||
+                 prev->scaler_params[1] == param[1])) {
+        return prev;
     }
+
+    if (!(sws = sws_alloc_context())) {
+        sws_free_context(&prev);
+        return NULL;
+    }
+
+    if (prev) {
+        av_opt_copy(sws, prev);
+        sws_free_context(&prev);
+    }
+
+    sws->src_w            = srcW;
+    sws->src_h            = srcH;
+    sws->src_format       = srcFormat;
+    sws->dst_w            = dstW;
+    sws->dst_h            = dstH;
+    sws->dst_format       = dstFormat;
+    sws->flags            = flags;
+    sws->scaler_params[0] = param[0];
+    sws->scaler_params[1] = param[1];
+
+    if (sws_init_context(sws, srcFilter, dstFilter) < 0)
+        sws_free_context(&sws);
+
     return sws;
 }
 
