avfilter/vf_perspective: add av_assert0() to help gcc see that there are no uninitial...
[ffmpeg.git] / libavfilter / vf_perspective.c
index 3910b97..bebe37f 100644 (file)
@@ -19,6 +19,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include "libavutil/avassert.h"
 #include "libavutil/eval.h"
 #include "libavutil/imgutils.h"
 #include "libavutil/pixdesc.h"
@@ -46,16 +47,20 @@ typedef struct PerspectiveContext {
     int height[4];
     int hsub, vsub;
     int nb_planes;
+    int sense;
 
-    void (*perspective)(struct PerspectiveContext *s,
-                        uint8_t *dst, int dst_linesize,
-                        uint8_t *src, int src_linesize,
-                        int w, int h, int hsub, int vsub);
+    int (*perspective)(AVFilterContext *ctx,
+                       void *arg, int job, int nb_jobs);
 } PerspectiveContext;
 
 #define OFFSET(x) offsetof(PerspectiveContext, x)
 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
 
+enum PERSPECTIVESense {
+    PERSPECTIVE_SENSE_SOURCE      = 0, ///< coordinates give locations in source of corners of destination.
+    PERSPECTIVE_SENSE_DESTINATION = 1, ///< coordinates give locations in destination of corners of source.
+};
+
 static const AVOption perspective_options[] = {
     { "x0", "set top left x coordinate",     OFFSET(expr_str[0][0]), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS },
     { "y0", "set top left y coordinate",     OFFSET(expr_str[0][1]), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS },
@@ -68,6 +73,12 @@ static const AVOption perspective_options[] = {
     { "interpolation", "set interpolation", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=LINEAR}, 0, 1, FLAGS, "interpolation" },
     {      "linear", "", 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, "interpolation" },
     {       "cubic", "", 0, AV_OPT_TYPE_CONST, {.i64=CUBIC},  0, 0, FLAGS, "interpolation" },
+    { "sense",   "specify the sense of the coordinates", OFFSET(sense), AV_OPT_TYPE_INT, {.i64=PERSPECTIVE_SENSE_SOURCE}, 0, 1, FLAGS, "sense"},
+    {       "source", "specify locations in source to send to corners in destination",
+                0, AV_OPT_TYPE_CONST, {.i64=PERSPECTIVE_SENSE_SOURCE}, 0, 0, FLAGS, "sense"},
+    {       "destination", "specify locations in destination to send corners of source",
+                0, AV_OPT_TYPE_CONST, {.i64=PERSPECTIVE_SENSE_DESTINATION}, 0, 0, FLAGS, "sense"},
+
     { NULL }
 };
 
@@ -107,7 +118,8 @@ enum                                   { VAR_W, VAR_H, VAR_VARS_NB };
 
 static int config_input(AVFilterLink *inlink)
 {
-    double x0, x1, x2, x3, x4, x5, x6, x7, q;
+    double x0, x1, x2, x3, x4, x5, x6, x7, x8, q;
+    double t0, t1, t2, t3;
     AVFilterContext *ctx = inlink->dst;
     PerspectiveContext *s = ctx->priv;
     double (*ref)[2] = s->ref;
@@ -143,32 +155,66 @@ static int config_input(AVFilterLink *inlink)
     if (!s->pv)
         return AVERROR(ENOMEM);
 
-    x6 = ((ref[0][0] - ref[1][0] - ref[2][0] + ref[3][0]) *
-          (ref[2][1] - ref[3][1]) -
-         ( ref[0][1] - ref[1][1] - ref[2][1] + ref[3][1]) *
-          (ref[2][0] - ref[3][0])) * h;
-    x7 = ((ref[0][1] - ref[1][1] - ref[2][1] + ref[3][1]) *
-          (ref[1][0] - ref[3][0]) -
-         ( ref[0][0] - ref[1][0] - ref[2][0] + ref[3][0]) *
-          (ref[1][1] - ref[3][1])) * w;
-    q =  ( ref[1][0] - ref[3][0]) * (ref[2][1] - ref[3][1]) -
-         ( ref[2][0] - ref[3][0]) * (ref[1][1] - ref[3][1]);
-
-    x0 = q * (ref[1][0] - ref[0][0]) * h + x6 * ref[1][0];
-    x1 = q * (ref[2][0] - ref[0][0]) * w + x7 * ref[2][0];
-    x2 = q *  ref[0][0] * w * h;
-    x3 = q * (ref[1][1] - ref[0][1]) * h + x6 * ref[1][1];
-    x4 = q * (ref[2][1] - ref[0][1]) * w + x7 * ref[2][1];
-    x5 = q *  ref[0][1] * w * h;
+    switch (s->sense) {
+    case PERSPECTIVE_SENSE_SOURCE:
+        x6 = ((ref[0][0] - ref[1][0] - ref[2][0] + ref[3][0]) *
+              (ref[2][1] - ref[3][1]) -
+             ( ref[0][1] - ref[1][1] - ref[2][1] + ref[3][1]) *
+              (ref[2][0] - ref[3][0])) * h;
+        x7 = ((ref[0][1] - ref[1][1] - ref[2][1] + ref[3][1]) *
+              (ref[1][0] - ref[3][0]) -
+             ( ref[0][0] - ref[1][0] - ref[2][0] + ref[3][0]) *
+              (ref[1][1] - ref[3][1])) * w;
+        q =  ( ref[1][0] - ref[3][0]) * (ref[2][1] - ref[3][1]) -
+             ( ref[2][0] - ref[3][0]) * (ref[1][1] - ref[3][1]);
+
+        x0 = q * (ref[1][0] - ref[0][0]) * h + x6 * ref[1][0];
+        x1 = q * (ref[2][0] - ref[0][0]) * w + x7 * ref[2][0];
+        x2 = q *  ref[0][0] * w * h;
+        x3 = q * (ref[1][1] - ref[0][1]) * h + x6 * ref[1][1];
+        x4 = q * (ref[2][1] - ref[0][1]) * w + x7 * ref[2][1];
+        x5 = q *  ref[0][1] * w * h;
+        x8 = q * w * h;
+        break;
+    case PERSPECTIVE_SENSE_DESTINATION:
+        t0 = ref[0][0] * (ref[3][1] - ref[1][1]) +
+             ref[1][0] * (ref[0][1] - ref[3][1]) +
+             ref[3][0] * (ref[1][1] - ref[0][1]);
+        t1 = ref[1][0] * (ref[2][1] - ref[3][1]) +
+             ref[2][0] * (ref[3][1] - ref[1][1]) +
+             ref[3][0] * (ref[1][1] - ref[2][1]);
+        t2 = ref[0][0] * (ref[3][1] - ref[2][1]) +
+             ref[2][0] * (ref[0][1] - ref[3][1]) +
+             ref[3][0] * (ref[2][1] - ref[0][1]);
+        t3 = ref[0][0] * (ref[1][1] - ref[2][1]) +
+             ref[1][0] * (ref[2][1] - ref[0][1]) +
+             ref[2][0] * (ref[0][1] - ref[1][1]);
+
+        x0 = t0 * t1 * w * (ref[2][1] - ref[0][1]);
+        x1 = t0 * t1 * w * (ref[0][0] - ref[2][0]);
+        x2 = t0 * t1 * w * (ref[0][1] * ref[2][0] - ref[0][0] * ref[2][1]);
+        x3 = t1 * t2 * h * (ref[1][1] - ref[0][1]);
+        x4 = t1 * t2 * h * (ref[0][0] - ref[1][0]);
+        x5 = t1 * t2 * h * (ref[0][1] * ref[1][0] - ref[0][0] * ref[1][1]);
+        x6 = t1 * t2 * (ref[1][1] - ref[0][1]) +
+             t0 * t3 * (ref[2][1] - ref[3][1]);
+        x7 = t1 * t2 * (ref[0][0] - ref[1][0]) +
+             t0 * t3 * (ref[3][0] - ref[2][0]);
+        x8 = t1 * t2 * (ref[0][1] * ref[1][0] - ref[0][0] * ref[1][1]) +
+             t0 * t3 * (ref[2][0] * ref[3][1] - ref[2][1] * ref[3][0]);
+        break;
+    default:
+        av_assert0(0);
+    }
 
     for (y = 0; y < h; y++){
         for (x = 0; x < w; x++){
             int u, v;
 
             u = (int)floor(SUB_PIXELS * (x0 * x + x1 * y + x2) /
-                                        (x6 * x + x7 * y + q * w * h) + 0.5);
+                                        (x6 * x + x7 * y + x8) + 0.5);
             v = (int)floor(SUB_PIXELS * (x3 * x + x4 * y + x5) /
-                                        (x6 * x + x7 * y + q * w * h) + 0.5);
+                                        (x6 * x + x7 * y + x8) + 0.5);
 
             s->pv[x + y * w][0] = u;
             s->pv[x + y * w][1] = v;
@@ -193,15 +239,34 @@ static int config_input(AVFilterLink *inlink)
     return 0;
 }
 
-static void resample_cubic(PerspectiveContext *s,
-                           uint8_t *dst, int dst_linesize,
-                           uint8_t *src, int src_linesize,
-                           int w, int h, int hsub, int vsub)
+typedef struct ThreadData {
+    uint8_t *dst;
+    int dst_linesize;
+    uint8_t *src;
+    int src_linesize;
+    int w, h;
+    int hsub, vsub;
+} ThreadData;
+
+static int resample_cubic(AVFilterContext *ctx, void *arg,
+                          int job, int nb_jobs)
 {
+    PerspectiveContext *s = ctx->priv;
+    ThreadData *td = arg;
+    uint8_t *dst = td->dst;
+    int dst_linesize = td->dst_linesize;
+    uint8_t *src = td->src;
+    int src_linesize = td->src_linesize;
+    int w = td->w;
+    int h = td->h;
+    int hsub = td->hsub;
+    int vsub = td->vsub;
+    int start = (h * job) / nb_jobs;
+    int end   = (h * (job+1)) / nb_jobs;
     const int linesize = s->linesize[0];
     int x, y;
 
-    for (y = 0; y < h; y++) {
+    for (y = start; y < end; y++) {
         int sy = y << vsub;
         for (x = 0; x < w; x++) {
             int u, v, subU, subV, sum, sx;
@@ -259,17 +324,28 @@ static void resample_cubic(PerspectiveContext *s,
             dst[x + y * dst_linesize] = sum;
         }
     }
+    return 0;
 }
 
-static void resample_linear(PerspectiveContext *s,
-                            uint8_t *dst, int dst_linesize,
-                            uint8_t *src, int src_linesize,
-                            int w, int h, int hsub, int vsub)
+static int resample_linear(AVFilterContext *ctx, void *arg,
+                           int job, int nb_jobs)
 {
+    PerspectiveContext *s = ctx->priv;
+    ThreadData *td = arg;
+    uint8_t *dst = td->dst;
+    int dst_linesize = td->dst_linesize;
+    uint8_t *src = td->src;
+    int src_linesize = td->src_linesize;
+    int w = td->w;
+    int h = td->h;
+    int hsub = td->hsub;
+    int vsub = td->vsub;
+    int start = (h * job) / nb_jobs;
+    int end   = (h * (job+1)) / nb_jobs;
     const int linesize = s->linesize[0];
     int x, y;
 
-    for (y = 0; y < h; y++){
+    for (y = start; y < end; y++){
         int sy = y << vsub;
         for (x = 0; x < w; x++){
             int u, v, subU, subV, sum, sx, index, subUI, subVI;
@@ -323,6 +399,7 @@ static void resample_linear(PerspectiveContext *s,
             dst[x + y * dst_linesize] = sum;
         }
     }
+    return 0;
 }
 
 static av_cold int init(AVFilterContext *ctx)
@@ -355,9 +432,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
     for (plane = 0; plane < s->nb_planes; plane++) {
         int hsub = plane == 1 || plane == 2 ? s->hsub : 0;
         int vsub = plane == 1 || plane == 2 ? s->vsub : 0;
-        s->perspective(s, out->data[plane], out->linesize[plane],
-                       frame->data[plane], frame->linesize[plane],
-                       s->linesize[plane], s->height[plane], hsub, vsub);
+        ThreadData td = {.dst = out->data[plane],
+                         .dst_linesize = out->linesize[plane],
+                         .src = frame->data[plane],
+                         .src_linesize = frame->linesize[plane],
+                         .w = s->linesize[plane],
+                         .h = s->height[plane],
+                         .hsub = hsub,
+                         .vsub = vsub };
+        ctx->internal->execute(ctx, s->perspective, &td, NULL, FFMIN(td.h, ctx->graph->nb_threads));
     }
 
     av_frame_free(&frame);
@@ -389,7 +472,7 @@ static const AVFilterPad perspective_outputs[] = {
     { NULL }
 };
 
-AVFilter avfilter_vf_perspective = {
+AVFilter ff_vf_perspective = {
     .name          = "perspective",
     .description   = NULL_IF_CONFIG_SMALL("Correct the perspective of video."),
     .priv_size     = sizeof(PerspectiveContext),
@@ -399,5 +482,5 @@ AVFilter avfilter_vf_perspective = {
     .inputs        = perspective_inputs,
     .outputs       = perspective_outputs,
     .priv_class    = &perspective_class,
-    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
 };