[PATCH] Introduce the AV_PERM_NEG_LINESIZES flag, which allows to explicitely request a buffer which can have negative linesizes, so that if it is not specified in the permissions for the requested buffer the returned buffer must have positive linesizes.

Stefano Sabatini stefano.sabatini-lala
Sun Dec 5 03:37:06 CET 2010


This is required in particular with direct rendering as implemented in
ffply, as some codecs cannot process an image buffer with negative
linesizes and will crash.

Fix issue #1913.
---
 ffplay.c                 |    8 +++++
 libavfilter/allfilters.c |    1 +
 libavfilter/avfilter.c   |   11 +++++---
 libavfilter/avfilter.h   |    1 +
 libavfilter/vf_null.c    |   19 +++++++++++++
 libavfilter/vf_scale.c   |    2 +-
 libavfilter/vf_vflip.c   |   65 ++++++++++++++++++++++++++++++++++++++-------
 7 files changed, 92 insertions(+), 15 deletions(-)

diff --git a/ffplay.c b/ffplay.c
index 38a2fe1..169944b 100644
--- a/ffplay.c
+++ b/ffplay.c
@@ -1606,6 +1606,7 @@ typedef struct {
     VideoState *is;
     AVFrame *frame;
     int use_dr1;
+    int use_neg_linesizes;
 } FilterPriv;
 
 static int input_get_buffer(AVCodecContext *codec, AVFrame *pic)
@@ -1616,6 +1617,9 @@ static int input_get_buffer(AVCodecContext *codec, AVFrame *pic)
     int i, w, h, stride[4];
     unsigned edge;
 
+    if (codec->codec->capabilities & CODEC_CAP_NEG_LINESIZES)
+        perms |= AV_PERM_NEG_LINESIZES;
+
     if(pic->buffer_hints & FF_BUFFER_HINTS_VALID) {
         if(pic->buffer_hints & FF_BUFFER_HINTS_READABLE) perms |= AV_PERM_READ;
         if(pic->buffer_hints & FF_BUFFER_HINTS_PRESERVE) perms |= AV_PERM_PRESERVE;
@@ -1691,6 +1695,8 @@ static int input_init(AVFilterContext *ctx, const char *args, void *opaque)
         codec->get_buffer     = input_get_buffer;
         codec->release_buffer = input_release_buffer;
         codec->reget_buffer   = input_reget_buffer;
+        if (codec->codec->capabilities & CODEC_CAP_NEG_LINESIZES)
+            priv->use_neg_linesizes = 1;
     }
 
     priv->frame = avcodec_alloc_frame();
@@ -1719,6 +1725,8 @@ static int input_request_frame(AVFilterLink *link)
 
     if(priv->use_dr1) {
         picref = avfilter_ref_buffer(priv->frame->opaque, ~0);
+        if (priv->use_neg_linesizes)
+            picref->perms |= AV_PERM_NEG_LINESIZES;
     } else {
         picref = avfilter_get_video_buffer(link, AV_PERM_WRITE, link->w, link->h);
         av_image_copy(picref->data, picref->linesize,
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 7f7e46d..292bd21 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -56,6 +56,7 @@ void avfilter_register_all(void)
     REGISTER_FILTER (OVERLAY,     overlay,     vf);
     REGISTER_FILTER (PAD,         pad,         vf);
     REGISTER_FILTER (PIXDESCTEST, pixdesctest, vf);
+    REGISTER_FILTER (POSLS,       posls,       vf);
     REGISTER_FILTER (SCALE,       scale,       vf);
     REGISTER_FILTER (SETDAR,      setdar,      vf);
     REGISTER_FILTER (SETPTS,      setpts,      vf);
diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index db254d6..04cb020 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -197,12 +197,13 @@ int avfilter_config_links(AVFilterContext *filter)
 
 char *ff_get_ref_perms_string(char *buf, size_t buf_size, int perms)
 {
-    snprintf(buf, buf_size, "%s%s%s%s%s",
+    snprintf(buf, buf_size, "%s%s%s%s%s%s",
              perms & AV_PERM_READ      ? "r" : "",
              perms & AV_PERM_WRITE     ? "w" : "",
              perms & AV_PERM_PRESERVE  ? "p" : "",
              perms & AV_PERM_REUSE     ? "u" : "",
-             perms & AV_PERM_REUSE2    ? "U" : "");
+             perms & AV_PERM_REUSE2    ? "U" : "",
+             perms & AV_PERM_NEG_LINESIZES ? "n" : "");
     return buf;
 }
 
@@ -360,15 +361,17 @@ void avfilter_start_frame(AVFilterLink *link, AVFilterBufferRef *picref)
 {
     void (*start_frame)(AVFilterLink *, AVFilterBufferRef *);
     AVFilterPad *dst = link->dstpad;
+    int perms = picref->perms;
 
     FF_DPRINTF_START(NULL, start_frame); ff_dprintf_link(NULL, link, 0); dprintf(NULL, " "); ff_dprintf_ref(NULL, picref, 1);
 
     if (!(start_frame = dst->start_frame))
         start_frame = avfilter_default_start_frame;
 
+    if (picref->linesize[0] < 0)
+        perms |= AV_PERM_NEG_LINESIZES;
     /* prepare to copy the picture if it has insufficient permissions */
-    if ((dst->min_perms & picref->perms) != dst->min_perms ||
-         dst->rej_perms & picref->perms) {
+    if ((dst->min_perms & perms) != dst->min_perms || dst->rej_perms & perms) {
         av_log(link->dst, AV_LOG_DEBUG,
                 "frame copy needed (have perms %x, need %x, reject %x)\n",
                 picref->perms,
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index a4bc978..fccf5ff 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@ -87,6 +87,7 @@ typedef struct AVFilterBuffer {
 #define AV_PERM_PRESERVE 0x04   ///< nobody else can overwrite the buffer
 #define AV_PERM_REUSE    0x08   ///< can output the buffer multiple times, with the same contents each time
 #define AV_PERM_REUSE2   0x10   ///< can output the buffer multiple times, modified each time
+#define AV_PERM_NEG_LINESIZES 0x20  ///< the buffer requested can have negative linesizes
 
 /**
  * Audio specific properties in a reference to an AVFilterBuffer. Since
diff --git a/libavfilter/vf_null.c b/libavfilter/vf_null.c
index 989cd86..8db939b 100644
--- a/libavfilter/vf_null.c
+++ b/libavfilter/vf_null.c
@@ -40,3 +40,22 @@ AVFilter avfilter_vf_null = {
                                     .type             = AVMEDIA_TYPE_VIDEO, },
                                   { .name = NULL}},
 };
+
+AVFilter avfilter_vf_posls = {
+    .name      = "posls",
+    .description = NULL_IF_CONFIG_SMALL("Make the output buffer have positive linesizes."),
+
+    .priv_size = 0,
+
+    .inputs    = (AVFilterPad[]) {{ .name             = "default",
+                                    .type             = AVMEDIA_TYPE_VIDEO,
+                                    .get_video_buffer = avfilter_null_get_video_buffer,
+                                    .start_frame      = avfilter_null_start_frame,
+                                    .end_frame        = avfilter_null_end_frame,
+                                    .rej_perms        = AV_PERM_NEG_LINESIZES, },
+                                  { .name = NULL}},
+
+    .outputs   = (AVFilterPad[]) {{ .name             = "default",
+                                    .type             = AVMEDIA_TYPE_VIDEO, },
+                                  { .name = NULL}},
+};
diff --git a/libavfilter/vf_scale.c b/libavfilter/vf_scale.c
index 100ba2b..7b0efd5 100644
--- a/libavfilter/vf_scale.c
+++ b/libavfilter/vf_scale.c
@@ -151,7 +151,7 @@ static void start_frame(AVFilterLink *link, AVFilterBufferRef *picref)
     scale->hsub = av_pix_fmt_descriptors[link->format].log2_chroma_w;
     scale->vsub = av_pix_fmt_descriptors[link->format].log2_chroma_h;
 
-    outpicref = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
+    outpicref = avfilter_get_video_buffer(outlink, AV_PERM_WRITE|AV_PERM_NEG_LINESIZES, outlink->w, outlink->h);
     avfilter_copy_buffer_ref_props(outpicref, picref);
     outpicref->video->w = outlink->w;
     outpicref->video->h = outlink->h;
diff --git a/libavfilter/vf_vflip.c b/libavfilter/vf_vflip.c
index 95d90d3..b0cd370 100644
--- a/libavfilter/vf_vflip.c
+++ b/libavfilter/vf_vflip.c
@@ -24,10 +24,12 @@
  */
 
 #include "libavutil/pixdesc.h"
+#include "libavcore/imgutils.h"
 #include "avfilter.h"
 
 typedef struct {
     int vsub;   ///< vertical chroma subsampling
+    int linesizes[4];
 } FlipContext;
 
 static int config_input(AVFilterLink *link)
@@ -35,6 +37,7 @@ static int config_input(AVFilterLink *link)
     FlipContext *flip = link->dst->priv;
 
     flip->vsub = av_pix_fmt_descriptors[link->format].log2_chroma_h;
+    av_image_fill_linesizes(flip->linesizes, link->format, link->w);
 
     return 0;
 }
@@ -43,11 +46,13 @@ static AVFilterBufferRef *get_video_buffer(AVFilterLink *link, int perms,
                                         int w, int h)
 {
     FlipContext *flip = link->dst->priv;
+    AVFilterBufferRef *picref;
     int i;
 
-    AVFilterBufferRef *picref = avfilter_get_video_buffer(link->dst->outputs[0],
-                                                       perms, w, h);
+    if (!(perms & AV_PERM_NEG_LINESIZES))
+        return avfilter_default_get_video_buffer(link, perms, w, h);
 
+    picref = avfilter_get_video_buffer(link->dst->outputs[0], perms, w, h);
     for (i = 0; i < 4; i ++) {
         int vsub = i == 1 || i == 2 ? flip->vsub : 0;
 
@@ -63,25 +68,65 @@ static AVFilterBufferRef *get_video_buffer(AVFilterLink *link, int perms,
 static void start_frame(AVFilterLink *link, AVFilterBufferRef *picref)
 {
     FlipContext *flip = link->dst->priv;
+    AVFilterBufferRef *picref2 = avfilter_ref_buffer(picref, ~0);
     int i;
 
+    if (!(picref->perms & AV_PERM_NEG_LINESIZES)) {
+        picref2->perms |= AV_PERM_NEG_LINESIZES;
+        avfilter_default_start_frame(link, picref2);
+        return;
+    }
+
     for (i = 0; i < 4; i ++) {
         int vsub = i == 1 || i == 2 ? flip->vsub : 0;
 
-        if (picref->data[i]) {
-            picref->data[i] += ((link->h >> vsub)-1) * picref->linesize[i];
-            picref->linesize[i] = -picref->linesize[i];
+        if (picref2->data[i]) {
+            picref2->data[i] += ((link->h >> vsub)-1) * picref->linesize[i];
+            picref2->linesize[i] = -picref->linesize[i];
         }
     }
 
-    avfilter_start_frame(link->dst->outputs[0], picref);
+    avfilter_start_frame(link->dst->outputs[0], picref2);
 }
 
-static void draw_slice(AVFilterLink *link, int y, int h, int slice_dir)
+static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
 {
-    AVFilterContext *ctx = link->dst;
+    AVFilterContext *ctx = inlink->dst;
+    FlipContext *flip = ctx->priv;
+    AVFilterLink *outlink = ctx->outputs[0];
+
+    if (!(inlink->cur_buf->perms & AV_PERM_NEG_LINESIZES)) {
+        AVFilterBufferRef *inpic  = inlink ->cur_buf;
+        AVFilterBufferRef *outpic = outlink->out_buf;
+        uint8_t *inrow, *outrow;
+        int plane, i;
+
+        /* vflip slice */
+        for (plane = 0; plane < 4 && inpic->data[plane]; plane++) {
+            int frame_h = plane == 1 || plane == 2 ? (inlink->h)>>flip->vsub : inlink->h;
+            int y1      = plane == 1 || plane == 2 ? y>>flip->vsub : y;
+            int h1      = plane == 1 || plane == 2 ? h>>flip->vsub : h;
+
+            inrow  = inpic ->data[plane] + inpic ->linesize[plane] * y1;
+            outrow = outpic->data[plane] + outpic->linesize[plane] * (frame_h -y1-1);
+
+            for (i = 0; i < h1; i++) {
+                memcpy(outrow, inrow, flip->linesizes[plane]);
+                inrow  += inpic ->linesize[plane];
+                outrow -= outpic->linesize[plane];
+            }
+        }
+    }
+
+    avfilter_draw_slice(outlink, inlink->h - (y+h), h, -1 * slice_dir);
+}
 
-    avfilter_draw_slice(ctx->outputs[0], link->h - (y+h), h, -1 * slice_dir);
+static void end_frame(AVFilterLink *inlink)
+{
+    if (!(inlink->cur_buf->perms & AV_PERM_NEG_LINESIZES)) {
+        avfilter_default_end_frame(inlink);
+    } else
+        avfilter_null_end_frame(inlink);
 }
 
 AVFilter avfilter_vf_vflip = {
@@ -95,7 +140,7 @@ AVFilter avfilter_vf_vflip = {
                                     .get_video_buffer = get_video_buffer,
                                     .start_frame      = start_frame,
                                     .draw_slice       = draw_slice,
-                                    .end_frame        = avfilter_null_end_frame,
+                                    .end_frame        = end_frame,
                                     .config_props     = config_input, },
                                   { .name = NULL}},
     .outputs   = (AVFilterPad[]) {{ .name             = "default",
-- 
1.7.2.3


--AhhlLboLdkugWU4S--



More information about the ffmpeg-devel mailing list