diff mbox

[FFmpeg-devel,v10,1/2] lavf/vf_transpose: add exif orientation support

Message ID 20190609212835.17650-1-junli1026@gmail.com
State Superseded
Headers show

Commit Message

Jun Li June 9, 2019, 9:28 p.m. UTC
Add exif orientation support and expose an option.
---
 libavfilter/hflip.h        |   2 +
 libavfilter/transpose.h    |  14 ++++
 libavfilter/vf_hflip.c     |  40 ++++++---
 libavfilter/vf_transpose.c | 168 ++++++++++++++++++++++++++++++++-----
 4 files changed, 192 insertions(+), 32 deletions(-)

Comments

Jun Li June 12, 2019, 2:05 a.m. UTC | #1
On Sun, Jun 9, 2019 at 2:28 PM Jun Li <junli1026@gmail.com> wrote:

> Add exif orientation support and expose an option.
> ---
>  libavfilter/hflip.h        |   2 +
>  libavfilter/transpose.h    |  14 ++++
>  libavfilter/vf_hflip.c     |  40 ++++++---
>  libavfilter/vf_transpose.c | 168 ++++++++++++++++++++++++++++++++-----
>  4 files changed, 192 insertions(+), 32 deletions(-)
>
> diff --git a/libavfilter/hflip.h b/libavfilter/hflip.h
> index 204090dbb4..4e89bae3fc 100644
> --- a/libavfilter/hflip.h
> +++ b/libavfilter/hflip.h
> @@ -35,5 +35,7 @@ typedef struct FlipContext {
>
>  int ff_hflip_init(FlipContext *s, int step[4], int nb_planes);
>  void ff_hflip_init_x86(FlipContext *s, int step[4], int nb_planes);
> +int ff_hflip_config_props(FlipContext* s, AVFilterLink *inlink);
> +int ff_hflip_filter_slice(FlipContext *s, AVFrame *in, AVFrame *out, int
> job, int nb_jobs, int vlifp);
>
>  #endif /* AVFILTER_HFLIP_H */
> diff --git a/libavfilter/transpose.h b/libavfilter/transpose.h
> index aa262b9487..5da08bddc0 100644
> --- a/libavfilter/transpose.h
> +++ b/libavfilter/transpose.h
> @@ -34,4 +34,18 @@ enum TransposeDir {
>      TRANSPOSE_VFLIP,
>  };
>
> +enum OrientationType {
> +    ORIENTATION_AUTO_TRANSPOSE = -2,
> +    ORIENTATION_AUTO_FLIP = -1,
> +    ORIENTATION_NONE = 0,
> +    ORIENTATION_NORMAL,
> +    ORIENTATION_HFLIP,
> +    ORIENTATION_ROTATE180,
> +    ORIENTATION_VFLIP,
> +    ORIENTATION_HFLIP_ROTATE270CW,
> +    ORIENTATION_ROTATE90CW,
> +    ORIENTATION_HFLIP_ROTATE90CW,
> +    ORIENTATION_ROTATE270CW
> +};
> +
>  #endif
> diff --git a/libavfilter/vf_hflip.c b/libavfilter/vf_hflip.c
> index b77afc77fc..d24ca5c2e7 100644
> --- a/libavfilter/vf_hflip.c
> +++ b/libavfilter/vf_hflip.c
> @@ -125,9 +125,8 @@ static void hflip_qword_c(const uint8_t *ssrc, uint8_t
> *ddst, int w)
>          dst[j] = src[-j];
>  }
>
> -static int config_props(AVFilterLink *inlink)
> +int ff_hflip_config_props(FlipContext* s, AVFilterLink *inlink)
>  {
> -    FlipContext *s = inlink->dst->priv;
>      const AVPixFmtDescriptor *pix_desc =
> av_pix_fmt_desc_get(inlink->format);
>      const int hsub = pix_desc->log2_chroma_w;
>      const int vsub = pix_desc->log2_chroma_h;
> @@ -144,6 +143,12 @@ static int config_props(AVFilterLink *inlink)
>      return ff_hflip_init(s, s->max_step, nb_planes);
>  }
>
> +static int config_props(AVFilterLink *inlink)
> +{
> +    FlipContext *s = inlink->dst->priv;
> +    return ff_hflip_config_props(s, inlink);
> +}
> +
>  int ff_hflip_init(FlipContext *s, int step[4], int nb_planes)
>  {
>      int i;
> @@ -170,14 +175,10 @@ typedef struct ThreadData {
>      AVFrame *in, *out;
>  } ThreadData;
>
> -static int filter_slice(AVFilterContext *ctx, void *arg, int job, int
> nb_jobs)
> +int ff_hflip_filter_slice(FlipContext *s, AVFrame *in, AVFrame *out, int
> job, int nb_jobs, int vflip)
>  {
> -    FlipContext *s = ctx->priv;
> -    ThreadData *td = arg;
> -    AVFrame *in = td->in;
> -    AVFrame *out = td->out;
>      uint8_t *inrow, *outrow;
> -    int i, plane, step;
> +    int i, plane, step, outlinesize;
>
>      for (plane = 0; plane < 4 && in->data[plane] && in->linesize[plane];
> plane++) {
>          const int width  = s->planewidth[plane];
> @@ -187,19 +188,36 @@ static int filter_slice(AVFilterContext *ctx, void
> *arg, int job, int nb_jobs)
>
>          step = s->max_step[plane];
>
> -        outrow = out->data[plane] + start * out->linesize[plane];
> -        inrow  = in ->data[plane] + start * in->linesize[plane] + (width
> - 1) * step;
> +        if (vflip) {
> +            outrow = out->data[plane] + (height - start - 1)*
> out->linesize[plane];
> +            outlinesize = -out->linesize[plane];
> +        } else {
> +            outrow = out->data[plane] + start * out->linesize[plane];
> +            outlinesize = out->linesize[plane];
> +        }
> +
> +        inrow = in->data[plane] + start * in->linesize[plane] +  (width -
> 1) * step;
> +
>          for (i = start; i < end; i++) {
>              s->flip_line[plane](inrow, outrow, width);
>
>              inrow  += in ->linesize[plane];
> -            outrow += out->linesize[plane];
> +            outrow += outlinesize;
>          }
>      }
>
>      return 0;
>  }
>
> +static int filter_slice(AVFilterContext *ctx, void *arg, int job, int
> nb_jobs)
> +{
> +    FlipContext *s = ctx->priv;
> +    ThreadData *td = arg;
> +    AVFrame *in = td->in;
> +    AVFrame *out = td->out;
> +    return ff_hflip_filter_slice(s, in, out, job, nb_jobs, 0);
> +}
> +
>  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
>  {
>      AVFilterContext *ctx  = inlink->dst;
> diff --git a/libavfilter/vf_transpose.c b/libavfilter/vf_transpose.c
> index dd54947bd9..05dc04a89f 100644
> --- a/libavfilter/vf_transpose.c
> +++ b/libavfilter/vf_transpose.c
> @@ -39,6 +39,7 @@
>  #include "internal.h"
>  #include "video.h"
>  #include "transpose.h"
> +#include "hflip.h"
>
>  typedef struct TransVtable {
>      void (*transpose_8x8)(uint8_t *src, ptrdiff_t src_linesize,
> @@ -48,16 +49,22 @@ typedef struct TransVtable {
>                              int w, int h);
>  } TransVtable;
>
> -typedef struct TransContext {
> -    const AVClass *class;
> +typedef struct TransContextData {
>      int hsub, vsub;
>      int planes;
>      int pixsteps[4];
> +    TransVtable vtables[4];
> +} TransContextData;
>
> +typedef struct TransContext {
> +    const AVClass *class;
>      int passthrough;    ///< PassthroughType, landscape passthrough mode
> enabled
>      int dir;            ///< TransposeDir
> -
> -    TransVtable vtables[4];
> +    int orientation;    ///< OrientationType
> +    union {
> +        TransContextData transpose;
> +        FlipContext flip;
> +    } ctx;
>  } TransContext;
>
>  static int query_formats(AVFilterContext *ctx)
> @@ -207,14 +214,18 @@ static int config_props_output(AVFilterLink *outlink)
>          s->passthrough = TRANSPOSE_PT_TYPE_NONE;
>      }
>
> -    s->hsub = desc_in->log2_chroma_w;
> -    s->vsub = desc_in->log2_chroma_h;
> -    s->planes = av_pix_fmt_count_planes(outlink->format);
> +    if (s->orientation == ORIENTATION_AUTO_FLIP ||
> +       (s->orientation > ORIENTATION_NONE && s->orientation <=
> ORIENTATION_VFLIP))
> +        return ff_hflip_config_props(&(s->ctx.flip), inlink);
> +
> +    s->ctx.transpose.hsub = desc_in->log2_chroma_w;
> +    s->ctx.transpose.vsub = desc_in->log2_chroma_h;
> +    s->ctx.transpose.planes = av_pix_fmt_count_planes(outlink->format);
>
>      av_assert0(desc_in->nb_components == desc_out->nb_components);
>
>
> -    av_image_fill_max_pixsteps(s->pixsteps, NULL, desc_out);
> +    av_image_fill_max_pixsteps(s->ctx.transpose.pixsteps, NULL, desc_out);
>
>      outlink->w = inlink->h;
>      outlink->h = inlink->w;
> @@ -226,10 +237,10 @@ static int config_props_output(AVFilterLink *outlink)
>          outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
>
>      for (int i = 0; i < 4; i++) {
> -        TransVtable *v = &s->vtables[i];
> -        switch (s->pixsteps[i]) {
> +        TransVtable *v = &s->ctx.transpose.vtables[i];
> +        switch (s->ctx.transpose.pixsteps[i]) {
>          case 1: v->transpose_block = transpose_block_8_c;
> -                v->transpose_8x8   = transpose_8x8_8_c;  break;
> +                v->transpose_8x8   = transpose_8x8_8_c; break;
>          case 2: v->transpose_block = transpose_block_16_c;
>                  v->transpose_8x8   = transpose_8x8_16_c; break;
>          case 3: v->transpose_block = transpose_block_24_c;
> @@ -262,21 +273,69 @@ static AVFrame *get_video_buffer(AVFilterLink
> *inlink, int w, int h)
>
>  typedef struct ThreadData {
>      AVFrame *in, *out;
> +    int orientation;
>  } ThreadData;
>
> -static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
> -                        int nb_jobs)
> +static int get_frame_orientation(AVFilterContext *ctx, const AVFrame
> *frame)
> +{
> +    TransContext *s = ctx->priv;
> +    AVDictionaryEntry *entry = NULL;
> +    int orientation = ORIENTATION_NONE;
> +
> +    if (s->orientation >= ORIENTATION_NONE)
> +        return s->orientation;
> +
> +    // read exif orientation data
> +    entry = av_dict_get(frame->metadata, "Orientation", NULL, 0);
> +    if (entry && entry->value)
> +        orientation = atoi(entry->value);
> +
> +    // check frame orientation validity
> +    if (orientation < ORIENTATION_NORMAL ||
> +        orientation > ORIENTATION_ROTATE270CW) {
> +        // frame orientation is invalid or empty, we have to guess one.
> +        av_log(ctx, AV_LOG_WARNING, "Frame orientation is either empty or
> invalid.\n");
> +        return s->orientation == ORIENTATION_AUTO_FLIP ?
> +               ORIENTATION_NORMAL : ORIENTATION_HFLIP_ROTATE270CW;
> +    }
> +
> +    // check conflict between frame orientation and passed-in orientation
> option
> +    if (s->orientation == ORIENTATION_AUTO_FLIP &&
> +        orientation > ORIENTATION_VFLIP) {
> +        av_log(ctx, AV_LOG_WARNING,
> +               "Found valid orientation value: %i, but auto-flip only
> cover range [1, 4]. Set to default value 1(ORIENTATION_NONE).\n",
> +               orientation);
> +        // conflict happens, return the min value of orientation range
> +        return ORIENTATION_NORMAL;
> +    }
> +
> +    // check conflict between frame orientation and passed-in orientation
> option
> +    if (s->orientation == ORIENTATION_AUTO_TRANSPOSE &&
> +        orientation <= ORIENTATION_VFLIP) {
> +        av_log(ctx, AV_LOG_WARNING,
> +               "Found valid orientation value: %i, but auto-transpose
> only cover range [5, 8]. Set to default value
> 5(ORIENTATION_HFLIP_ROTATE270CW).\n",
> +               orientation);
> +        // conflict happens, return the min value of orientation range,
> same effect as dir=0
> +        return ORIENTATION_HFLIP_ROTATE270CW;
> +    }
> +
> +    return orientation;
> +}
> +
> +static int transpose_filter_slice(AVFilterContext *ctx, void *arg, int
> jobnr, int nb_jobs)
>  {
>      TransContext *s = ctx->priv;
>      ThreadData *td = arg;
>      AVFrame *out = td->out;
>      AVFrame *in = td->in;
> +    TransContextData *c = &(s->ctx.transpose);
> +    int orientation = td->orientation;
>      int plane;
>
> -    for (plane = 0; plane < s->planes; plane++) {
> -        int hsub    = plane == 1 || plane == 2 ? s->hsub : 0;
> -        int vsub    = plane == 1 || plane == 2 ? s->vsub : 0;
> -        int pixstep = s->pixsteps[plane];
> +    for (plane = 0; plane < c->planes; plane++) {
> +        int hsub    = plane == 1 || plane == 2 ? c->hsub : 0;
> +        int vsub    = plane == 1 || plane == 2 ? c->vsub : 0;
> +        int pixstep = c->pixsteps[plane];
>          int inh     = AV_CEIL_RSHIFT(in->height, vsub);
>          int outw    = AV_CEIL_RSHIFT(out->width,  hsub);
>          int outh    = AV_CEIL_RSHIFT(out->height, vsub);
> @@ -285,19 +344,36 @@ static int filter_slice(AVFilterContext *ctx, void
> *arg, int jobnr,
>          uint8_t *dst, *src;
>          int dstlinesize, srclinesize;
>          int x, y;
> -        TransVtable *v = &s->vtables[plane];
> +        int dir = s->dir;
> +        TransVtable *v = &c->vtables[plane];
>
>          dstlinesize = out->linesize[plane];
>          dst         = out->data[plane] + start * dstlinesize;
>          src         = in->data[plane];
>          srclinesize = in->linesize[plane];
> +        switch (orientation) {
> +            case ORIENTATION_HFLIP_ROTATE270CW:
> +                dir = 0; // mirror horizontal and rotate 270 CW, same as
> dir=cclock_flip
> +                break;
> +            case ORIENTATION_ROTATE90CW:
> +                dir = 1; // rotate 90 CW, same as dir=clock
> +                break;
> +            case ORIENTATION_HFLIP_ROTATE90CW:
> +                dir = 3; // mirror horizontal and rotate 90 CW, same as
> dir=clock_flip
> +                break;
> +            case ORIENTATION_ROTATE270CW:
> +                dir = 2; // rotate 270 CW, same as dir=cclock
> +                break;
> +            default:
> +                break;
> +        }
>
> -        if (s->dir & 1) {
> +        if (dir & 1) {
>              src         += in->linesize[plane] * (inh - 1);
>              srclinesize *= -1;
>          }
>
> -        if (s->dir & 2) {
> +        if (dir & 2) {
>              dst          = out->data[plane] + dstlinesize * (outh - start
> - 1);
>              dstlinesize *= -1;
>          }
> @@ -326,6 +402,22 @@ static int filter_slice(AVFilterContext *ctx, void
> *arg, int jobnr,
>      return 0;
>  }
>
> +static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
> +                        int nb_jobs)
> +{
> +    TransContext *s = ctx->priv;
> +    ThreadData *td = arg;
> +    AVFrame *out = td->out;
> +    AVFrame *in = td->in;
> +    int orientation = td->orientation;
> +
> +    if (orientation > ORIENTATION_NONE && orientation <=
> ORIENTATION_VFLIP) {
> +        return ff_hflip_filter_slice(&s->ctx.flip, in, out, jobnr,
> nb_jobs,
> +                                     orientation ==
> ORIENTATION_ROTATE180);
> +    }
> +    return transpose_filter_slice(ctx, arg, jobnr, nb_jobs);
> +}
> +
>  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
>  {
>      AVFilterContext *ctx = inlink->dst;
> @@ -333,10 +425,29 @@ static int filter_frame(AVFilterLink *inlink,
> AVFrame *in)
>      AVFilterLink *outlink = ctx->outputs[0];
>      ThreadData td;
>      AVFrame *out;
> +    int orientation = ORIENTATION_NONE;
>
>      if (s->passthrough)
>          return ff_filter_frame(outlink, in);
>
> +    if (s->orientation) {
> +        orientation = get_frame_orientation(ctx, in);
> +        if (orientation == ORIENTATION_NORMAL) // orientation 'Normal',
> do nothing
> +            return ff_filter_frame(outlink, in);
> +
> +        if (orientation == ORIENTATION_VFLIP) {
> +            int i;
> +            for (i = 0; i < 4; i ++) {
> +                int height = s->ctx.flip.planeheight[i];
> +                if (in->data[i]) {
> +                    in->data[i] += (height - 1) * in->linesize[i];
> +                    in->linesize[i] = -in->linesize[i];
> +                }
> +            }
> +            return ff_filter_frame(outlink, in);
> +        }
> +    }
> +
>      out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
>      if (!out) {
>          av_frame_free(&in);
> @@ -346,12 +457,14 @@ static int filter_frame(AVFilterLink *inlink,
> AVFrame *in)
>
>      if (in->sample_aspect_ratio.num == 0) {
>          out->sample_aspect_ratio = in->sample_aspect_ratio;
> -    } else {
> +    } else if (s->orientation == ORIENTATION_AUTO_TRANSPOSE ||
> +               orientation > ORIENTATION_VFLIP) { // transpose
>          out->sample_aspect_ratio.num = in->sample_aspect_ratio.den;
>          out->sample_aspect_ratio.den = in->sample_aspect_ratio.num;
>      }
>
>      td.in = in, td.out = out;
> +    td.orientation = orientation;
>      ctx->internal->execute(ctx, filter_slice, &td, NULL,
> FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
>      av_frame_free(&in);
>      return ff_filter_frame(outlink, out);
> @@ -373,6 +486,19 @@ static const AVOption transpose_options[] = {
>          { "portrait",  "preserve portrait geometry",   0,
> AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT},  INT_MIN, INT_MAX,
> FLAGS, "passthrough" },
>          { "landscape", "preserve landscape geometry",  0,
> AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX,
> FLAGS, "passthrough" },
>
> +    { "orientation", "apply frame exif orientation",
> +      OFFSET(orientation), AV_OPT_TYPE_INT, {.i64 = ORIENTATION_NONE },
> ORIENTATION_AUTO_TRANSPOSE, ORIENTATION_ROTATE270CW, FLAGS, "orientation" },
> +        { "auto_transpose",    "apply transposition if frame's
> orientation value is in range [5,8]",                     0,
> AV_OPT_TYPE_CONST, {.i64=ORIENTATION_AUTO_TRANSPOSE},    INT_MIN, INT_MAX,
> FLAGS, "orientation" },
> +        { "auto_flip",         "apply flip if frame's orientation value
> is in range [1,4]",                              0, AV_OPT_TYPE_CONST,
> {.i64=ORIENTATION_AUTO_FLIP},         INT_MIN, INT_MAX, FLAGS,
> "orientation" },
> +        { "none",              "apply default transposition, same as
> dir=cclock_flip",                                   0, AV_OPT_TYPE_CONST,
> {.i64=ORIENTATION_NONE},              INT_MIN, INT_MAX, FLAGS,
> "orientation" },
> +        { "normal",            "exif orientation 'Normal', do not apply
> any flip or transposition",                      0, AV_OPT_TYPE_CONST,
> {.i64=ORIENTATION_NORMAL},            INT_MIN, INT_MAX, FLAGS,
> "orientation" },
> +        { "hflip",             "exif orientation 'Mirror horizontal',
> apply horizontal flip",                            0, AV_OPT_TYPE_CONST,
> {.i64=ORIENTATION_HFLIP},             INT_MIN, INT_MAX, FLAGS,
> "orientation" },
> +        { "rotate180",         "exif orientation 'Rotate 180', apply
> rotation 180",                                      0, AV_OPT_TYPE_CONST,
> {.i64=ORIENTATION_ROTATE180},         INT_MIN, INT_MAX, FLAGS,
> "orientation" },
> +        { "vflip",             "exif orientation 'Mirror vertical', apply
> vertical flip",                                0, AV_OPT_TYPE_CONST,
> {.i64=ORIENTATION_VFLIP},             INT_MIN, INT_MAX, FLAGS,
> "orientation" },
> +        { "hflip_rotate270cw", "exif orientation 'Mirror horizontal and
> rotate 270 CW', same effect as dir=cclock_flip", 0, AV_OPT_TYPE_CONST,
> {.i64=ORIENTATION_HFLIP_ROTATE270CW}, INT_MIN, INT_MAX, FLAGS,
> "orientation" },
> +        { "rotate90cw",        "exif orientation 'Rotate 90 CW', same as
> dir=clock",                                     0, AV_OPT_TYPE_CONST,
> {.i64=ORIENTATION_ROTATE90CW},        INT_MIN, INT_MAX, FLAGS,
> "orientation" },
> +        { "hflip_rotate90cw",  "exif orientation 'Mirror horizontal and
> rotate 90 CW', same effect as dir=clock_flip",   0, AV_OPT_TYPE_CONST,
> {.i64=ORIENTATION_HFLIP_ROTATE90CW},  INT_MIN, INT_MAX, FLAGS,
> "orientation" },
> +        { "rotate270",         "exif orientation 'Rotate 270 CW', same as
> dir=cclock",                                   0, AV_OPT_TYPE_CONST,
> {.i64=ORIENTATION_ROTATE270CW},       INT_MIN, INT_MAX, FLAGS,
> "orientation" },
>      { NULL }
>  };
>
> --
> 2.17.1
>
>
Ping :)
Jun Li June 16, 2019, 2:09 a.m. UTC | #2
On Tue, Jun 11, 2019 at 7:05 PM Jun Li <junli1026@gmail.com> wrote:

>
> On Sun, Jun 9, 2019 at 2:28 PM Jun Li <junli1026@gmail.com> wrote:
>
>> Add exif orientation support and expose an option.
>> ---
>>  libavfilter/hflip.h        |   2 +
>>  libavfilter/transpose.h    |  14 ++++
>>  libavfilter/vf_hflip.c     |  40 ++++++---
>>  libavfilter/vf_transpose.c | 168 ++++++++++++++++++++++++++++++++-----
>>  4 files changed, 192 insertions(+), 32 deletions(-)
>>
>> diff --git a/libavfilter/hflip.h b/libavfilter/hflip.h
>> index 204090dbb4..4e89bae3fc 100644
>> --- a/libavfilter/hflip.h
>> +++ b/libavfilter/hflip.h
>> @@ -35,5 +35,7 @@ typedef struct FlipContext {
>>
>>  int ff_hflip_init(FlipContext *s, int step[4], int nb_planes);
>>  void ff_hflip_init_x86(FlipContext *s, int step[4], int nb_planes);
>> +int ff_hflip_config_props(FlipContext* s, AVFilterLink *inlink);
>> +int ff_hflip_filter_slice(FlipContext *s, AVFrame *in, AVFrame *out, int
>> job, int nb_jobs, int vlifp);
>>
>>  #endif /* AVFILTER_HFLIP_H */
>> diff --git a/libavfilter/transpose.h b/libavfilter/transpose.h
>> index aa262b9487..5da08bddc0 100644
>> --- a/libavfilter/transpose.h
>> +++ b/libavfilter/transpose.h
>> @@ -34,4 +34,18 @@ enum TransposeDir {
>>      TRANSPOSE_VFLIP,
>>  };
>>
>> +enum OrientationType {
>> +    ORIENTATION_AUTO_TRANSPOSE = -2,
>> +    ORIENTATION_AUTO_FLIP = -1,
>> +    ORIENTATION_NONE = 0,
>> +    ORIENTATION_NORMAL,
>> +    ORIENTATION_HFLIP,
>> +    ORIENTATION_ROTATE180,
>> +    ORIENTATION_VFLIP,
>> +    ORIENTATION_HFLIP_ROTATE270CW,
>> +    ORIENTATION_ROTATE90CW,
>> +    ORIENTATION_HFLIP_ROTATE90CW,
>> +    ORIENTATION_ROTATE270CW
>> +};
>> +
>>  #endif
>> diff --git a/libavfilter/vf_hflip.c b/libavfilter/vf_hflip.c
>> index b77afc77fc..d24ca5c2e7 100644
>> --- a/libavfilter/vf_hflip.c
>> +++ b/libavfilter/vf_hflip.c
>> @@ -125,9 +125,8 @@ static void hflip_qword_c(const uint8_t *ssrc,
>> uint8_t *ddst, int w)
>>          dst[j] = src[-j];
>>  }
>>
>> -static int config_props(AVFilterLink *inlink)
>> +int ff_hflip_config_props(FlipContext* s, AVFilterLink *inlink)
>>  {
>> -    FlipContext *s = inlink->dst->priv;
>>      const AVPixFmtDescriptor *pix_desc =
>> av_pix_fmt_desc_get(inlink->format);
>>      const int hsub = pix_desc->log2_chroma_w;
>>      const int vsub = pix_desc->log2_chroma_h;
>> @@ -144,6 +143,12 @@ static int config_props(AVFilterLink *inlink)
>>      return ff_hflip_init(s, s->max_step, nb_planes);
>>  }
>>
>> +static int config_props(AVFilterLink *inlink)
>> +{
>> +    FlipContext *s = inlink->dst->priv;
>> +    return ff_hflip_config_props(s, inlink);
>> +}
>> +
>>  int ff_hflip_init(FlipContext *s, int step[4], int nb_planes)
>>  {
>>      int i;
>> @@ -170,14 +175,10 @@ typedef struct ThreadData {
>>      AVFrame *in, *out;
>>  } ThreadData;
>>
>> -static int filter_slice(AVFilterContext *ctx, void *arg, int job, int
>> nb_jobs)
>> +int ff_hflip_filter_slice(FlipContext *s, AVFrame *in, AVFrame *out, int
>> job, int nb_jobs, int vflip)
>>  {
>> -    FlipContext *s = ctx->priv;
>> -    ThreadData *td = arg;
>> -    AVFrame *in = td->in;
>> -    AVFrame *out = td->out;
>>      uint8_t *inrow, *outrow;
>> -    int i, plane, step;
>> +    int i, plane, step, outlinesize;
>>
>>      for (plane = 0; plane < 4 && in->data[plane] && in->linesize[plane];
>> plane++) {
>>          const int width  = s->planewidth[plane];
>> @@ -187,19 +188,36 @@ static int filter_slice(AVFilterContext *ctx, void
>> *arg, int job, int nb_jobs)
>>
>>          step = s->max_step[plane];
>>
>> -        outrow = out->data[plane] + start * out->linesize[plane];
>> -        inrow  = in ->data[plane] + start * in->linesize[plane] + (width
>> - 1) * step;
>> +        if (vflip) {
>> +            outrow = out->data[plane] + (height - start - 1)*
>> out->linesize[plane];
>> +            outlinesize = -out->linesize[plane];
>> +        } else {
>> +            outrow = out->data[plane] + start * out->linesize[plane];
>> +            outlinesize = out->linesize[plane];
>> +        }
>> +
>> +        inrow = in->data[plane] + start * in->linesize[plane] +  (width
>> - 1) * step;
>> +
>>          for (i = start; i < end; i++) {
>>              s->flip_line[plane](inrow, outrow, width);
>>
>>              inrow  += in ->linesize[plane];
>> -            outrow += out->linesize[plane];
>> +            outrow += outlinesize;
>>          }
>>      }
>>
>>      return 0;
>>  }
>>
>> +static int filter_slice(AVFilterContext *ctx, void *arg, int job, int
>> nb_jobs)
>> +{
>> +    FlipContext *s = ctx->priv;
>> +    ThreadData *td = arg;
>> +    AVFrame *in = td->in;
>> +    AVFrame *out = td->out;
>> +    return ff_hflip_filter_slice(s, in, out, job, nb_jobs, 0);
>> +}
>> +
>>  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
>>  {
>>      AVFilterContext *ctx  = inlink->dst;
>> diff --git a/libavfilter/vf_transpose.c b/libavfilter/vf_transpose.c
>> index dd54947bd9..05dc04a89f 100644
>> --- a/libavfilter/vf_transpose.c
>> +++ b/libavfilter/vf_transpose.c
>> @@ -39,6 +39,7 @@
>>  #include "internal.h"
>>  #include "video.h"
>>  #include "transpose.h"
>> +#include "hflip.h"
>>
>>  typedef struct TransVtable {
>>      void (*transpose_8x8)(uint8_t *src, ptrdiff_t src_linesize,
>> @@ -48,16 +49,22 @@ typedef struct TransVtable {
>>                              int w, int h);
>>  } TransVtable;
>>
>> -typedef struct TransContext {
>> -    const AVClass *class;
>> +typedef struct TransContextData {
>>      int hsub, vsub;
>>      int planes;
>>      int pixsteps[4];
>> +    TransVtable vtables[4];
>> +} TransContextData;
>>
>> +typedef struct TransContext {
>> +    const AVClass *class;
>>      int passthrough;    ///< PassthroughType, landscape passthrough mode
>> enabled
>>      int dir;            ///< TransposeDir
>> -
>> -    TransVtable vtables[4];
>> +    int orientation;    ///< OrientationType
>> +    union {
>> +        TransContextData transpose;
>> +        FlipContext flip;
>> +    } ctx;
>>  } TransContext;
>>
>>  static int query_formats(AVFilterContext *ctx)
>> @@ -207,14 +214,18 @@ static int config_props_output(AVFilterLink
>> *outlink)
>>          s->passthrough = TRANSPOSE_PT_TYPE_NONE;
>>      }
>>
>> -    s->hsub = desc_in->log2_chroma_w;
>> -    s->vsub = desc_in->log2_chroma_h;
>> -    s->planes = av_pix_fmt_count_planes(outlink->format);
>> +    if (s->orientation == ORIENTATION_AUTO_FLIP ||
>> +       (s->orientation > ORIENTATION_NONE && s->orientation <=
>> ORIENTATION_VFLIP))
>> +        return ff_hflip_config_props(&(s->ctx.flip), inlink);
>> +
>> +    s->ctx.transpose.hsub = desc_in->log2_chroma_w;
>> +    s->ctx.transpose.vsub = desc_in->log2_chroma_h;
>> +    s->ctx.transpose.planes = av_pix_fmt_count_planes(outlink->format);
>>
>>      av_assert0(desc_in->nb_components == desc_out->nb_components);
>>
>>
>> -    av_image_fill_max_pixsteps(s->pixsteps, NULL, desc_out);
>> +    av_image_fill_max_pixsteps(s->ctx.transpose.pixsteps, NULL,
>> desc_out);
>>
>>      outlink->w = inlink->h;
>>      outlink->h = inlink->w;
>> @@ -226,10 +237,10 @@ static int config_props_output(AVFilterLink
>> *outlink)
>>          outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
>>
>>      for (int i = 0; i < 4; i++) {
>> -        TransVtable *v = &s->vtables[i];
>> -        switch (s->pixsteps[i]) {
>> +        TransVtable *v = &s->ctx.transpose.vtables[i];
>> +        switch (s->ctx.transpose.pixsteps[i]) {
>>          case 1: v->transpose_block = transpose_block_8_c;
>> -                v->transpose_8x8   = transpose_8x8_8_c;  break;
>> +                v->transpose_8x8   = transpose_8x8_8_c; break;
>>          case 2: v->transpose_block = transpose_block_16_c;
>>                  v->transpose_8x8   = transpose_8x8_16_c; break;
>>          case 3: v->transpose_block = transpose_block_24_c;
>> @@ -262,21 +273,69 @@ static AVFrame *get_video_buffer(AVFilterLink
>> *inlink, int w, int h)
>>
>>  typedef struct ThreadData {
>>      AVFrame *in, *out;
>> +    int orientation;
>>  } ThreadData;
>>
>> -static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
>> -                        int nb_jobs)
>> +static int get_frame_orientation(AVFilterContext *ctx, const AVFrame
>> *frame)
>> +{
>> +    TransContext *s = ctx->priv;
>> +    AVDictionaryEntry *entry = NULL;
>> +    int orientation = ORIENTATION_NONE;
>> +
>> +    if (s->orientation >= ORIENTATION_NONE)
>> +        return s->orientation;
>> +
>> +    // read exif orientation data
>> +    entry = av_dict_get(frame->metadata, "Orientation", NULL, 0);
>> +    if (entry && entry->value)
>> +        orientation = atoi(entry->value);
>> +
>> +    // check frame orientation validity
>> +    if (orientation < ORIENTATION_NORMAL ||
>> +        orientation > ORIENTATION_ROTATE270CW) {
>> +        // frame orientation is invalid or empty, we have to guess one.
>> +        av_log(ctx, AV_LOG_WARNING, "Frame orientation is either empty
>> or invalid.\n");
>> +        return s->orientation == ORIENTATION_AUTO_FLIP ?
>> +               ORIENTATION_NORMAL : ORIENTATION_HFLIP_ROTATE270CW;
>> +    }
>> +
>> +    // check conflict between frame orientation and passed-in
>> orientation option
>> +    if (s->orientation == ORIENTATION_AUTO_FLIP &&
>> +        orientation > ORIENTATION_VFLIP) {
>> +        av_log(ctx, AV_LOG_WARNING,
>> +               "Found valid orientation value: %i, but auto-flip only
>> cover range [1, 4]. Set to default value 1(ORIENTATION_NONE).\n",
>> +               orientation);
>> +        // conflict happens, return the min value of orientation range
>> +        return ORIENTATION_NORMAL;
>> +    }
>> +
>> +    // check conflict between frame orientation and passed-in
>> orientation option
>> +    if (s->orientation == ORIENTATION_AUTO_TRANSPOSE &&
>> +        orientation <= ORIENTATION_VFLIP) {
>> +        av_log(ctx, AV_LOG_WARNING,
>> +               "Found valid orientation value: %i, but auto-transpose
>> only cover range [5, 8]. Set to default value
>> 5(ORIENTATION_HFLIP_ROTATE270CW).\n",
>> +               orientation);
>> +        // conflict happens, return the min value of orientation range,
>> same effect as dir=0
>> +        return ORIENTATION_HFLIP_ROTATE270CW;
>> +    }
>> +
>> +    return orientation;
>> +}
>> +
>> +static int transpose_filter_slice(AVFilterContext *ctx, void *arg, int
>> jobnr, int nb_jobs)
>>  {
>>      TransContext *s = ctx->priv;
>>      ThreadData *td = arg;
>>      AVFrame *out = td->out;
>>      AVFrame *in = td->in;
>> +    TransContextData *c = &(s->ctx.transpose);
>> +    int orientation = td->orientation;
>>      int plane;
>>
>> -    for (plane = 0; plane < s->planes; plane++) {
>> -        int hsub    = plane == 1 || plane == 2 ? s->hsub : 0;
>> -        int vsub    = plane == 1 || plane == 2 ? s->vsub : 0;
>> -        int pixstep = s->pixsteps[plane];
>> +    for (plane = 0; plane < c->planes; plane++) {
>> +        int hsub    = plane == 1 || plane == 2 ? c->hsub : 0;
>> +        int vsub    = plane == 1 || plane == 2 ? c->vsub : 0;
>> +        int pixstep = c->pixsteps[plane];
>>          int inh     = AV_CEIL_RSHIFT(in->height, vsub);
>>          int outw    = AV_CEIL_RSHIFT(out->width,  hsub);
>>          int outh    = AV_CEIL_RSHIFT(out->height, vsub);
>> @@ -285,19 +344,36 @@ static int filter_slice(AVFilterContext *ctx, void
>> *arg, int jobnr,
>>          uint8_t *dst, *src;
>>          int dstlinesize, srclinesize;
>>          int x, y;
>> -        TransVtable *v = &s->vtables[plane];
>> +        int dir = s->dir;
>> +        TransVtable *v = &c->vtables[plane];
>>
>>          dstlinesize = out->linesize[plane];
>>          dst         = out->data[plane] + start * dstlinesize;
>>          src         = in->data[plane];
>>          srclinesize = in->linesize[plane];
>> +        switch (orientation) {
>> +            case ORIENTATION_HFLIP_ROTATE270CW:
>> +                dir = 0; // mirror horizontal and rotate 270 CW, same as
>> dir=cclock_flip
>> +                break;
>> +            case ORIENTATION_ROTATE90CW:
>> +                dir = 1; // rotate 90 CW, same as dir=clock
>> +                break;
>> +            case ORIENTATION_HFLIP_ROTATE90CW:
>> +                dir = 3; // mirror horizontal and rotate 90 CW, same as
>> dir=clock_flip
>> +                break;
>> +            case ORIENTATION_ROTATE270CW:
>> +                dir = 2; // rotate 270 CW, same as dir=cclock
>> +                break;
>> +            default:
>> +                break;
>> +        }
>>
>> -        if (s->dir & 1) {
>> +        if (dir & 1) {
>>              src         += in->linesize[plane] * (inh - 1);
>>              srclinesize *= -1;
>>          }
>>
>> -        if (s->dir & 2) {
>> +        if (dir & 2) {
>>              dst          = out->data[plane] + dstlinesize * (outh -
>> start - 1);
>>              dstlinesize *= -1;
>>          }
>> @@ -326,6 +402,22 @@ static int filter_slice(AVFilterContext *ctx, void
>> *arg, int jobnr,
>>      return 0;
>>  }
>>
>> +static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
>> +                        int nb_jobs)
>> +{
>> +    TransContext *s = ctx->priv;
>> +    ThreadData *td = arg;
>> +    AVFrame *out = td->out;
>> +    AVFrame *in = td->in;
>> +    int orientation = td->orientation;
>> +
>> +    if (orientation > ORIENTATION_NONE && orientation <=
>> ORIENTATION_VFLIP) {
>> +        return ff_hflip_filter_slice(&s->ctx.flip, in, out, jobnr,
>> nb_jobs,
>> +                                     orientation ==
>> ORIENTATION_ROTATE180);
>> +    }
>> +    return transpose_filter_slice(ctx, arg, jobnr, nb_jobs);
>> +}
>> +
>>  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
>>  {
>>      AVFilterContext *ctx = inlink->dst;
>> @@ -333,10 +425,29 @@ static int filter_frame(AVFilterLink *inlink,
>> AVFrame *in)
>>      AVFilterLink *outlink = ctx->outputs[0];
>>      ThreadData td;
>>      AVFrame *out;
>> +    int orientation = ORIENTATION_NONE;
>>
>>      if (s->passthrough)
>>          return ff_filter_frame(outlink, in);
>>
>> +    if (s->orientation) {
>> +        orientation = get_frame_orientation(ctx, in);
>> +        if (orientation == ORIENTATION_NORMAL) // orientation 'Normal',
>> do nothing
>> +            return ff_filter_frame(outlink, in);
>> +
>> +        if (orientation == ORIENTATION_VFLIP) {
>> +            int i;
>> +            for (i = 0; i < 4; i ++) {
>> +                int height = s->ctx.flip.planeheight[i];
>> +                if (in->data[i]) {
>> +                    in->data[i] += (height - 1) * in->linesize[i];
>> +                    in->linesize[i] = -in->linesize[i];
>> +                }
>> +            }
>> +            return ff_filter_frame(outlink, in);
>> +        }
>> +    }
>> +
>>      out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
>>      if (!out) {
>>          av_frame_free(&in);
>> @@ -346,12 +457,14 @@ static int filter_frame(AVFilterLink *inlink,
>> AVFrame *in)
>>
>>      if (in->sample_aspect_ratio.num == 0) {
>>          out->sample_aspect_ratio = in->sample_aspect_ratio;
>> -    } else {
>> +    } else if (s->orientation == ORIENTATION_AUTO_TRANSPOSE ||
>> +               orientation > ORIENTATION_VFLIP) { // transpose
>>          out->sample_aspect_ratio.num = in->sample_aspect_ratio.den;
>>          out->sample_aspect_ratio.den = in->sample_aspect_ratio.num;
>>      }
>>
>>      td.in = in, td.out = out;
>> +    td.orientation = orientation;
>>      ctx->internal->execute(ctx, filter_slice, &td, NULL,
>> FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
>>      av_frame_free(&in);
>>      return ff_filter_frame(outlink, out);
>> @@ -373,6 +486,19 @@ static const AVOption transpose_options[] = {
>>          { "portrait",  "preserve portrait geometry",   0,
>> AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT},  INT_MIN, INT_MAX,
>> FLAGS, "passthrough" },
>>          { "landscape", "preserve landscape geometry",  0,
>> AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX,
>> FLAGS, "passthrough" },
>>
>> +    { "orientation", "apply frame exif orientation",
>> +      OFFSET(orientation), AV_OPT_TYPE_INT, {.i64 = ORIENTATION_NONE },
>> ORIENTATION_AUTO_TRANSPOSE, ORIENTATION_ROTATE270CW, FLAGS, "orientation" },
>> +        { "auto_transpose",    "apply transposition if frame's
>> orientation value is in range [5,8]",                     0,
>> AV_OPT_TYPE_CONST, {.i64=ORIENTATION_AUTO_TRANSPOSE},    INT_MIN, INT_MAX,
>> FLAGS, "orientation" },
>> +        { "auto_flip",         "apply flip if frame's orientation value
>> is in range [1,4]",                              0, AV_OPT_TYPE_CONST,
>> {.i64=ORIENTATION_AUTO_FLIP},         INT_MIN, INT_MAX, FLAGS,
>> "orientation" },
>> +        { "none",              "apply default transposition, same as
>> dir=cclock_flip",                                   0, AV_OPT_TYPE_CONST,
>> {.i64=ORIENTATION_NONE},              INT_MIN, INT_MAX, FLAGS,
>> "orientation" },
>> +        { "normal",            "exif orientation 'Normal', do not apply
>> any flip or transposition",                      0, AV_OPT_TYPE_CONST,
>> {.i64=ORIENTATION_NORMAL},            INT_MIN, INT_MAX, FLAGS,
>> "orientation" },
>> +        { "hflip",             "exif orientation 'Mirror horizontal',
>> apply horizontal flip",                            0, AV_OPT_TYPE_CONST,
>> {.i64=ORIENTATION_HFLIP},             INT_MIN, INT_MAX, FLAGS,
>> "orientation" },
>> +        { "rotate180",         "exif orientation 'Rotate 180', apply
>> rotation 180",                                      0, AV_OPT_TYPE_CONST,
>> {.i64=ORIENTATION_ROTATE180},         INT_MIN, INT_MAX, FLAGS,
>> "orientation" },
>> +        { "vflip",             "exif orientation 'Mirror vertical',
>> apply vertical flip",                                0, AV_OPT_TYPE_CONST,
>> {.i64=ORIENTATION_VFLIP},             INT_MIN, INT_MAX, FLAGS,
>> "orientation" },
>> +        { "hflip_rotate270cw", "exif orientation 'Mirror horizontal and
>> rotate 270 CW', same effect as dir=cclock_flip", 0, AV_OPT_TYPE_CONST,
>> {.i64=ORIENTATION_HFLIP_ROTATE270CW}, INT_MIN, INT_MAX, FLAGS,
>> "orientation" },
>> +        { "rotate90cw",        "exif orientation 'Rotate 90 CW', same as
>> dir=clock",                                     0, AV_OPT_TYPE_CONST,
>> {.i64=ORIENTATION_ROTATE90CW},        INT_MIN, INT_MAX, FLAGS,
>> "orientation" },
>> +        { "hflip_rotate90cw",  "exif orientation 'Mirror horizontal and
>> rotate 90 CW', same effect as dir=clock_flip",   0, AV_OPT_TYPE_CONST,
>> {.i64=ORIENTATION_HFLIP_ROTATE90CW},  INT_MIN, INT_MAX, FLAGS,
>> "orientation" },
>> +        { "rotate270",         "exif orientation 'Rotate 270 CW', same
>> as dir=cclock",                                   0, AV_OPT_TYPE_CONST,
>> {.i64=ORIENTATION_ROTATE270CW},       INT_MIN, INT_MAX, FLAGS,
>> "orientation" },
>>      { NULL }
>>  };
>>
>> --
>> 2.17.1
>>
>>
> Ping :)
>

Ping x 2
Jun Li June 18, 2019, 12:31 a.m. UTC | #3
On Sat, Jun 15, 2019 at 7:09 PM Jun Li <junli1026@gmail.com> wrote:

>
>
> On Tue, Jun 11, 2019 at 7:05 PM Jun Li <junli1026@gmail.com> wrote:
>
>>
>> On Sun, Jun 9, 2019 at 2:28 PM Jun Li <junli1026@gmail.com> wrote:
>>
>>> Add exif orientation support and expose an option.
>>> ---
>>>  libavfilter/hflip.h        |   2 +
>>>  libavfilter/transpose.h    |  14 ++++
>>>  libavfilter/vf_hflip.c     |  40 ++++++---
>>>  libavfilter/vf_transpose.c | 168 ++++++++++++++++++++++++++++++++-----
>>>  4 files changed, 192 insertions(+), 32 deletions(-)
>>>
>>> diff --git a/libavfilter/hflip.h b/libavfilter/hflip.h
>>> index 204090dbb4..4e89bae3fc 100644
>>> --- a/libavfilter/hflip.h
>>> +++ b/libavfilter/hflip.h
>>> @@ -35,5 +35,7 @@ typedef struct FlipContext {
>>>
>>>  int ff_hflip_init(FlipContext *s, int step[4], int nb_planes);
>>>  void ff_hflip_init_x86(FlipContext *s, int step[4], int nb_planes);
>>> +int ff_hflip_config_props(FlipContext* s, AVFilterLink *inlink);
>>> +int ff_hflip_filter_slice(FlipContext *s, AVFrame *in, AVFrame *out,
>>> int job, int nb_jobs, int vlifp);
>>>
>>>  #endif /* AVFILTER_HFLIP_H */
>>> diff --git a/libavfilter/transpose.h b/libavfilter/transpose.h
>>> index aa262b9487..5da08bddc0 100644
>>> --- a/libavfilter/transpose.h
>>> +++ b/libavfilter/transpose.h
>>> @@ -34,4 +34,18 @@ enum TransposeDir {
>>>      TRANSPOSE_VFLIP,
>>>  };
>>>
>>> +enum OrientationType {
>>> +    ORIENTATION_AUTO_TRANSPOSE = -2,
>>> +    ORIENTATION_AUTO_FLIP = -1,
>>> +    ORIENTATION_NONE = 0,
>>> +    ORIENTATION_NORMAL,
>>> +    ORIENTATION_HFLIP,
>>> +    ORIENTATION_ROTATE180,
>>> +    ORIENTATION_VFLIP,
>>> +    ORIENTATION_HFLIP_ROTATE270CW,
>>> +    ORIENTATION_ROTATE90CW,
>>> +    ORIENTATION_HFLIP_ROTATE90CW,
>>> +    ORIENTATION_ROTATE270CW
>>> +};
>>> +
>>>  #endif
>>> diff --git a/libavfilter/vf_hflip.c b/libavfilter/vf_hflip.c
>>> index b77afc77fc..d24ca5c2e7 100644
>>> --- a/libavfilter/vf_hflip.c
>>> +++ b/libavfilter/vf_hflip.c
>>> @@ -125,9 +125,8 @@ static void hflip_qword_c(const uint8_t *ssrc,
>>> uint8_t *ddst, int w)
>>>          dst[j] = src[-j];
>>>  }
>>>
>>> -static int config_props(AVFilterLink *inlink)
>>> +int ff_hflip_config_props(FlipContext* s, AVFilterLink *inlink)
>>>  {
>>> -    FlipContext *s = inlink->dst->priv;
>>>      const AVPixFmtDescriptor *pix_desc =
>>> av_pix_fmt_desc_get(inlink->format);
>>>      const int hsub = pix_desc->log2_chroma_w;
>>>      const int vsub = pix_desc->log2_chroma_h;
>>> @@ -144,6 +143,12 @@ static int config_props(AVFilterLink *inlink)
>>>      return ff_hflip_init(s, s->max_step, nb_planes);
>>>  }
>>>
>>> +static int config_props(AVFilterLink *inlink)
>>> +{
>>> +    FlipContext *s = inlink->dst->priv;
>>> +    return ff_hflip_config_props(s, inlink);
>>> +}
>>> +
>>>  int ff_hflip_init(FlipContext *s, int step[4], int nb_planes)
>>>  {
>>>      int i;
>>> @@ -170,14 +175,10 @@ typedef struct ThreadData {
>>>      AVFrame *in, *out;
>>>  } ThreadData;
>>>
>>> -static int filter_slice(AVFilterContext *ctx, void *arg, int job, int
>>> nb_jobs)
>>> +int ff_hflip_filter_slice(FlipContext *s, AVFrame *in, AVFrame *out,
>>> int job, int nb_jobs, int vflip)
>>>  {
>>> -    FlipContext *s = ctx->priv;
>>> -    ThreadData *td = arg;
>>> -    AVFrame *in = td->in;
>>> -    AVFrame *out = td->out;
>>>      uint8_t *inrow, *outrow;
>>> -    int i, plane, step;
>>> +    int i, plane, step, outlinesize;
>>>
>>>      for (plane = 0; plane < 4 && in->data[plane] &&
>>> in->linesize[plane]; plane++) {
>>>          const int width  = s->planewidth[plane];
>>> @@ -187,19 +188,36 @@ static int filter_slice(AVFilterContext *ctx, void
>>> *arg, int job, int nb_jobs)
>>>
>>>          step = s->max_step[plane];
>>>
>>> -        outrow = out->data[plane] + start * out->linesize[plane];
>>> -        inrow  = in ->data[plane] + start * in->linesize[plane] +
>>> (width - 1) * step;
>>> +        if (vflip) {
>>> +            outrow = out->data[plane] + (height - start - 1)*
>>> out->linesize[plane];
>>> +            outlinesize = -out->linesize[plane];
>>> +        } else {
>>> +            outrow = out->data[plane] + start * out->linesize[plane];
>>> +            outlinesize = out->linesize[plane];
>>> +        }
>>> +
>>> +        inrow = in->data[plane] + start * in->linesize[plane] +  (width
>>> - 1) * step;
>>> +
>>>          for (i = start; i < end; i++) {
>>>              s->flip_line[plane](inrow, outrow, width);
>>>
>>>              inrow  += in ->linesize[plane];
>>> -            outrow += out->linesize[plane];
>>> +            outrow += outlinesize;
>>>          }
>>>      }
>>>
>>>      return 0;
>>>  }
>>>
>>> +static int filter_slice(AVFilterContext *ctx, void *arg, int job, int
>>> nb_jobs)
>>> +{
>>> +    FlipContext *s = ctx->priv;
>>> +    ThreadData *td = arg;
>>> +    AVFrame *in = td->in;
>>> +    AVFrame *out = td->out;
>>> +    return ff_hflip_filter_slice(s, in, out, job, nb_jobs, 0);
>>> +}
>>> +
>>>  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
>>>  {
>>>      AVFilterContext *ctx  = inlink->dst;
>>> diff --git a/libavfilter/vf_transpose.c b/libavfilter/vf_transpose.c
>>> index dd54947bd9..05dc04a89f 100644
>>> --- a/libavfilter/vf_transpose.c
>>> +++ b/libavfilter/vf_transpose.c
>>> @@ -39,6 +39,7 @@
>>>  #include "internal.h"
>>>  #include "video.h"
>>>  #include "transpose.h"
>>> +#include "hflip.h"
>>>
>>>  typedef struct TransVtable {
>>>      void (*transpose_8x8)(uint8_t *src, ptrdiff_t src_linesize,
>>> @@ -48,16 +49,22 @@ typedef struct TransVtable {
>>>                              int w, int h);
>>>  } TransVtable;
>>>
>>> -typedef struct TransContext {
>>> -    const AVClass *class;
>>> +typedef struct TransContextData {
>>>      int hsub, vsub;
>>>      int planes;
>>>      int pixsteps[4];
>>> +    TransVtable vtables[4];
>>> +} TransContextData;
>>>
>>> +typedef struct TransContext {
>>> +    const AVClass *class;
>>>      int passthrough;    ///< PassthroughType, landscape passthrough
>>> mode enabled
>>>      int dir;            ///< TransposeDir
>>> -
>>> -    TransVtable vtables[4];
>>> +    int orientation;    ///< OrientationType
>>> +    union {
>>> +        TransContextData transpose;
>>> +        FlipContext flip;
>>> +    } ctx;
>>>  } TransContext;
>>>
>>>  static int query_formats(AVFilterContext *ctx)
>>> @@ -207,14 +214,18 @@ static int config_props_output(AVFilterLink
>>> *outlink)
>>>          s->passthrough = TRANSPOSE_PT_TYPE_NONE;
>>>      }
>>>
>>> -    s->hsub = desc_in->log2_chroma_w;
>>> -    s->vsub = desc_in->log2_chroma_h;
>>> -    s->planes = av_pix_fmt_count_planes(outlink->format);
>>> +    if (s->orientation == ORIENTATION_AUTO_FLIP ||
>>> +       (s->orientation > ORIENTATION_NONE && s->orientation <=
>>> ORIENTATION_VFLIP))
>>> +        return ff_hflip_config_props(&(s->ctx.flip), inlink);
>>> +
>>> +    s->ctx.transpose.hsub = desc_in->log2_chroma_w;
>>> +    s->ctx.transpose.vsub = desc_in->log2_chroma_h;
>>> +    s->ctx.transpose.planes = av_pix_fmt_count_planes(outlink->format);
>>>
>>>      av_assert0(desc_in->nb_components == desc_out->nb_components);
>>>
>>>
>>> -    av_image_fill_max_pixsteps(s->pixsteps, NULL, desc_out);
>>> +    av_image_fill_max_pixsteps(s->ctx.transpose.pixsteps, NULL,
>>> desc_out);
>>>
>>>      outlink->w = inlink->h;
>>>      outlink->h = inlink->w;
>>> @@ -226,10 +237,10 @@ static int config_props_output(AVFilterLink
>>> *outlink)
>>>          outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
>>>
>>>      for (int i = 0; i < 4; i++) {
>>> -        TransVtable *v = &s->vtables[i];
>>> -        switch (s->pixsteps[i]) {
>>> +        TransVtable *v = &s->ctx.transpose.vtables[i];
>>> +        switch (s->ctx.transpose.pixsteps[i]) {
>>>          case 1: v->transpose_block = transpose_block_8_c;
>>> -                v->transpose_8x8   = transpose_8x8_8_c;  break;
>>> +                v->transpose_8x8   = transpose_8x8_8_c; break;
>>>          case 2: v->transpose_block = transpose_block_16_c;
>>>                  v->transpose_8x8   = transpose_8x8_16_c; break;
>>>          case 3: v->transpose_block = transpose_block_24_c;
>>> @@ -262,21 +273,69 @@ static AVFrame *get_video_buffer(AVFilterLink
>>> *inlink, int w, int h)
>>>
>>>  typedef struct ThreadData {
>>>      AVFrame *in, *out;
>>> +    int orientation;
>>>  } ThreadData;
>>>
>>> -static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
>>> -                        int nb_jobs)
>>> +static int get_frame_orientation(AVFilterContext *ctx, const AVFrame
>>> *frame)
>>> +{
>>> +    TransContext *s = ctx->priv;
>>> +    AVDictionaryEntry *entry = NULL;
>>> +    int orientation = ORIENTATION_NONE;
>>> +
>>> +    if (s->orientation >= ORIENTATION_NONE)
>>> +        return s->orientation;
>>> +
>>> +    // read exif orientation data
>>> +    entry = av_dict_get(frame->metadata, "Orientation", NULL, 0);
>>> +    if (entry && entry->value)
>>> +        orientation = atoi(entry->value);
>>> +
>>> +    // check frame orientation validity
>>> +    if (orientation < ORIENTATION_NORMAL ||
>>> +        orientation > ORIENTATION_ROTATE270CW) {
>>> +        // frame orientation is invalid or empty, we have to guess one.
>>> +        av_log(ctx, AV_LOG_WARNING, "Frame orientation is either empty
>>> or invalid.\n");
>>> +        return s->orientation == ORIENTATION_AUTO_FLIP ?
>>> +               ORIENTATION_NORMAL : ORIENTATION_HFLIP_ROTATE270CW;
>>> +    }
>>> +
>>> +    // check conflict between frame orientation and passed-in
>>> orientation option
>>> +    if (s->orientation == ORIENTATION_AUTO_FLIP &&
>>> +        orientation > ORIENTATION_VFLIP) {
>>> +        av_log(ctx, AV_LOG_WARNING,
>>> +               "Found valid orientation value: %i, but auto-flip only
>>> cover range [1, 4]. Set to default value 1(ORIENTATION_NONE).\n",
>>> +               orientation);
>>> +        // conflict happens, return the min value of orientation range
>>> +        return ORIENTATION_NORMAL;
>>> +    }
>>> +
>>> +    // check conflict between frame orientation and passed-in
>>> orientation option
>>> +    if (s->orientation == ORIENTATION_AUTO_TRANSPOSE &&
>>> +        orientation <= ORIENTATION_VFLIP) {
>>> +        av_log(ctx, AV_LOG_WARNING,
>>> +               "Found valid orientation value: %i, but auto-transpose
>>> only cover range [5, 8]. Set to default value
>>> 5(ORIENTATION_HFLIP_ROTATE270CW).\n",
>>> +               orientation);
>>> +        // conflict happens, return the min value of orientation range,
>>> same effect as dir=0
>>> +        return ORIENTATION_HFLIP_ROTATE270CW;
>>> +    }
>>> +
>>> +    return orientation;
>>> +}
>>> +
>>> +static int transpose_filter_slice(AVFilterContext *ctx, void *arg, int
>>> jobnr, int nb_jobs)
>>>  {
>>>      TransContext *s = ctx->priv;
>>>      ThreadData *td = arg;
>>>      AVFrame *out = td->out;
>>>      AVFrame *in = td->in;
>>> +    TransContextData *c = &(s->ctx.transpose);
>>> +    int orientation = td->orientation;
>>>      int plane;
>>>
>>> -    for (plane = 0; plane < s->planes; plane++) {
>>> -        int hsub    = plane == 1 || plane == 2 ? s->hsub : 0;
>>> -        int vsub    = plane == 1 || plane == 2 ? s->vsub : 0;
>>> -        int pixstep = s->pixsteps[plane];
>>> +    for (plane = 0; plane < c->planes; plane++) {
>>> +        int hsub    = plane == 1 || plane == 2 ? c->hsub : 0;
>>> +        int vsub    = plane == 1 || plane == 2 ? c->vsub : 0;
>>> +        int pixstep = c->pixsteps[plane];
>>>          int inh     = AV_CEIL_RSHIFT(in->height, vsub);
>>>          int outw    = AV_CEIL_RSHIFT(out->width,  hsub);
>>>          int outh    = AV_CEIL_RSHIFT(out->height, vsub);
>>> @@ -285,19 +344,36 @@ static int filter_slice(AVFilterContext *ctx, void
>>> *arg, int jobnr,
>>>          uint8_t *dst, *src;
>>>          int dstlinesize, srclinesize;
>>>          int x, y;
>>> -        TransVtable *v = &s->vtables[plane];
>>> +        int dir = s->dir;
>>> +        TransVtable *v = &c->vtables[plane];
>>>
>>>          dstlinesize = out->linesize[plane];
>>>          dst         = out->data[plane] + start * dstlinesize;
>>>          src         = in->data[plane];
>>>          srclinesize = in->linesize[plane];
>>> +        switch (orientation) {
>>> +            case ORIENTATION_HFLIP_ROTATE270CW:
>>> +                dir = 0; // mirror horizontal and rotate 270 CW, same
>>> as dir=cclock_flip
>>> +                break;
>>> +            case ORIENTATION_ROTATE90CW:
>>> +                dir = 1; // rotate 90 CW, same as dir=clock
>>> +                break;
>>> +            case ORIENTATION_HFLIP_ROTATE90CW:
>>> +                dir = 3; // mirror horizontal and rotate 90 CW, same as
>>> dir=clock_flip
>>> +                break;
>>> +            case ORIENTATION_ROTATE270CW:
>>> +                dir = 2; // rotate 270 CW, same as dir=cclock
>>> +                break;
>>> +            default:
>>> +                break;
>>> +        }
>>>
>>> -        if (s->dir & 1) {
>>> +        if (dir & 1) {
>>>              src         += in->linesize[plane] * (inh - 1);
>>>              srclinesize *= -1;
>>>          }
>>>
>>> -        if (s->dir & 2) {
>>> +        if (dir & 2) {
>>>              dst          = out->data[plane] + dstlinesize * (outh -
>>> start - 1);
>>>              dstlinesize *= -1;
>>>          }
>>> @@ -326,6 +402,22 @@ static int filter_slice(AVFilterContext *ctx, void
>>> *arg, int jobnr,
>>>      return 0;
>>>  }
>>>
>>> +static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
>>> +                        int nb_jobs)
>>> +{
>>> +    TransContext *s = ctx->priv;
>>> +    ThreadData *td = arg;
>>> +    AVFrame *out = td->out;
>>> +    AVFrame *in = td->in;
>>> +    int orientation = td->orientation;
>>> +
>>> +    if (orientation > ORIENTATION_NONE && orientation <=
>>> ORIENTATION_VFLIP) {
>>> +        return ff_hflip_filter_slice(&s->ctx.flip, in, out, jobnr,
>>> nb_jobs,
>>> +                                     orientation ==
>>> ORIENTATION_ROTATE180);
>>> +    }
>>> +    return transpose_filter_slice(ctx, arg, jobnr, nb_jobs);
>>> +}
>>> +
>>>  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
>>>  {
>>>      AVFilterContext *ctx = inlink->dst;
>>> @@ -333,10 +425,29 @@ static int filter_frame(AVFilterLink *inlink,
>>> AVFrame *in)
>>>      AVFilterLink *outlink = ctx->outputs[0];
>>>      ThreadData td;
>>>      AVFrame *out;
>>> +    int orientation = ORIENTATION_NONE;
>>>
>>>      if (s->passthrough)
>>>          return ff_filter_frame(outlink, in);
>>>
>>> +    if (s->orientation) {
>>> +        orientation = get_frame_orientation(ctx, in);
>>> +        if (orientation == ORIENTATION_NORMAL) // orientation 'Normal',
>>> do nothing
>>> +            return ff_filter_frame(outlink, in);
>>> +
>>> +        if (orientation == ORIENTATION_VFLIP) {
>>> +            int i;
>>> +            for (i = 0; i < 4; i ++) {
>>> +                int height = s->ctx.flip.planeheight[i];
>>> +                if (in->data[i]) {
>>> +                    in->data[i] += (height - 1) * in->linesize[i];
>>> +                    in->linesize[i] = -in->linesize[i];
>>> +                }
>>> +            }
>>> +            return ff_filter_frame(outlink, in);
>>> +        }
>>> +    }
>>> +
>>>      out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
>>>      if (!out) {
>>>          av_frame_free(&in);
>>> @@ -346,12 +457,14 @@ static int filter_frame(AVFilterLink *inlink,
>>> AVFrame *in)
>>>
>>>      if (in->sample_aspect_ratio.num == 0) {
>>>          out->sample_aspect_ratio = in->sample_aspect_ratio;
>>> -    } else {
>>> +    } else if (s->orientation == ORIENTATION_AUTO_TRANSPOSE ||
>>> +               orientation > ORIENTATION_VFLIP) { // transpose
>>>          out->sample_aspect_ratio.num = in->sample_aspect_ratio.den;
>>>          out->sample_aspect_ratio.den = in->sample_aspect_ratio.num;
>>>      }
>>>
>>>      td.in = in, td.out = out;
>>> +    td.orientation = orientation;
>>>      ctx->internal->execute(ctx, filter_slice, &td, NULL,
>>> FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
>>>      av_frame_free(&in);
>>>      return ff_filter_frame(outlink, out);
>>> @@ -373,6 +486,19 @@ static const AVOption transpose_options[] = {
>>>          { "portrait",  "preserve portrait geometry",   0,
>>> AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT},  INT_MIN, INT_MAX,
>>> FLAGS, "passthrough" },
>>>          { "landscape", "preserve landscape geometry",  0,
>>> AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX,
>>> FLAGS, "passthrough" },
>>>
>>> +    { "orientation", "apply frame exif orientation",
>>> +      OFFSET(orientation), AV_OPT_TYPE_INT, {.i64 = ORIENTATION_NONE
>>> },  ORIENTATION_AUTO_TRANSPOSE, ORIENTATION_ROTATE270CW, FLAGS,
>>> "orientation" },
>>> +        { "auto_transpose",    "apply transposition if frame's
>>> orientation value is in range [5,8]",                     0,
>>> AV_OPT_TYPE_CONST, {.i64=ORIENTATION_AUTO_TRANSPOSE},    INT_MIN, INT_MAX,
>>> FLAGS, "orientation" },
>>> +        { "auto_flip",         "apply flip if frame's orientation value
>>> is in range [1,4]",                              0, AV_OPT_TYPE_CONST,
>>> {.i64=ORIENTATION_AUTO_FLIP},         INT_MIN, INT_MAX, FLAGS,
>>> "orientation" },
>>> +        { "none",              "apply default transposition, same as
>>> dir=cclock_flip",                                   0, AV_OPT_TYPE_CONST,
>>> {.i64=ORIENTATION_NONE},              INT_MIN, INT_MAX, FLAGS,
>>> "orientation" },
>>> +        { "normal",            "exif orientation 'Normal', do not apply
>>> any flip or transposition",                      0, AV_OPT_TYPE_CONST,
>>> {.i64=ORIENTATION_NORMAL},            INT_MIN, INT_MAX, FLAGS,
>>> "orientation" },
>>> +        { "hflip",             "exif orientation 'Mirror horizontal',
>>> apply horizontal flip",                            0, AV_OPT_TYPE_CONST,
>>> {.i64=ORIENTATION_HFLIP},             INT_MIN, INT_MAX, FLAGS,
>>> "orientation" },
>>> +        { "rotate180",         "exif orientation 'Rotate 180', apply
>>> rotation 180",                                      0, AV_OPT_TYPE_CONST,
>>> {.i64=ORIENTATION_ROTATE180},         INT_MIN, INT_MAX, FLAGS,
>>> "orientation" },
>>> +        { "vflip",             "exif orientation 'Mirror vertical',
>>> apply vertical flip",                                0, AV_OPT_TYPE_CONST,
>>> {.i64=ORIENTATION_VFLIP},             INT_MIN, INT_MAX, FLAGS,
>>> "orientation" },
>>> +        { "hflip_rotate270cw", "exif orientation 'Mirror horizontal and
>>> rotate 270 CW', same effect as dir=cclock_flip", 0, AV_OPT_TYPE_CONST,
>>> {.i64=ORIENTATION_HFLIP_ROTATE270CW}, INT_MIN, INT_MAX, FLAGS,
>>> "orientation" },
>>> +        { "rotate90cw",        "exif orientation 'Rotate 90 CW', same
>>> as dir=clock",                                     0, AV_OPT_TYPE_CONST,
>>> {.i64=ORIENTATION_ROTATE90CW},        INT_MIN, INT_MAX, FLAGS,
>>> "orientation" },
>>> +        { "hflip_rotate90cw",  "exif orientation 'Mirror horizontal and
>>> rotate 90 CW', same effect as dir=clock_flip",   0, AV_OPT_TYPE_CONST,
>>> {.i64=ORIENTATION_HFLIP_ROTATE90CW},  INT_MIN, INT_MAX, FLAGS,
>>> "orientation" },
>>> +        { "rotate270",         "exif orientation 'Rotate 270 CW', same
>>> as dir=cclock",                                   0, AV_OPT_TYPE_CONST,
>>> {.i64=ORIENTATION_ROTATE270CW},       INT_MIN, INT_MAX, FLAGS,
>>> "orientation" },
>>>      { NULL }
>>>  };
>>>
>>> --
>>> 2.17.1
>>>
>>>
>> Ping :)
>>
>
> Ping x 2
>

Ping x3, could someone please help to take a look ? Really appreciate that.

-Jun
Jun Li June 25, 2019, 1:19 a.m. UTC | #4
On Mon, Jun 17, 2019 at 5:31 PM Jun Li <junli1026@gmail.com> wrote:

>
>
> On Sat, Jun 15, 2019 at 7:09 PM Jun Li <junli1026@gmail.com> wrote:
>
>>
>>
>> On Tue, Jun 11, 2019 at 7:05 PM Jun Li <junli1026@gmail.com> wrote:
>>
>>>
>>> On Sun, Jun 9, 2019 at 2:28 PM Jun Li <junli1026@gmail.com> wrote:
>>>
>>>> Add exif orientation support and expose an option.
>>>> ---
>>>>  libavfilter/hflip.h        |   2 +
>>>>  libavfilter/transpose.h    |  14 ++++
>>>>  libavfilter/vf_hflip.c     |  40 ++++++---
>>>>  libavfilter/vf_transpose.c | 168 ++++++++++++++++++++++++++++++++-----
>>>>  4 files changed, 192 insertions(+), 32 deletions(-)
>>>>
>>>> diff --git a/libavfilter/hflip.h b/libavfilter/hflip.h
>>>> index 204090dbb4..4e89bae3fc 100644
>>>> --- a/libavfilter/hflip.h
>>>> +++ b/libavfilter/hflip.h
>>>> @@ -35,5 +35,7 @@ typedef struct FlipContext {
>>>>
>>>>  int ff_hflip_init(FlipContext *s, int step[4], int nb_planes);
>>>>  void ff_hflip_init_x86(FlipContext *s, int step[4], int nb_planes);
>>>> +int ff_hflip_config_props(FlipContext* s, AVFilterLink *inlink);
>>>> +int ff_hflip_filter_slice(FlipContext *s, AVFrame *in, AVFrame *out,
>>>> int job, int nb_jobs, int vlifp);
>>>>
>>>>  #endif /* AVFILTER_HFLIP_H */
>>>> diff --git a/libavfilter/transpose.h b/libavfilter/transpose.h
>>>> index aa262b9487..5da08bddc0 100644
>>>> --- a/libavfilter/transpose.h
>>>> +++ b/libavfilter/transpose.h
>>>> @@ -34,4 +34,18 @@ enum TransposeDir {
>>>>      TRANSPOSE_VFLIP,
>>>>  };
>>>>
>>>> +enum OrientationType {
>>>> +    ORIENTATION_AUTO_TRANSPOSE = -2,
>>>> +    ORIENTATION_AUTO_FLIP = -1,
>>>> +    ORIENTATION_NONE = 0,
>>>> +    ORIENTATION_NORMAL,
>>>> +    ORIENTATION_HFLIP,
>>>> +    ORIENTATION_ROTATE180,
>>>> +    ORIENTATION_VFLIP,
>>>> +    ORIENTATION_HFLIP_ROTATE270CW,
>>>> +    ORIENTATION_ROTATE90CW,
>>>> +    ORIENTATION_HFLIP_ROTATE90CW,
>>>> +    ORIENTATION_ROTATE270CW
>>>> +};
>>>> +
>>>>  #endif
>>>> diff --git a/libavfilter/vf_hflip.c b/libavfilter/vf_hflip.c
>>>> index b77afc77fc..d24ca5c2e7 100644
>>>> --- a/libavfilter/vf_hflip.c
>>>> +++ b/libavfilter/vf_hflip.c
>>>> @@ -125,9 +125,8 @@ static void hflip_qword_c(const uint8_t *ssrc,
>>>> uint8_t *ddst, int w)
>>>>          dst[j] = src[-j];
>>>>  }
>>>>
>>>> -static int config_props(AVFilterLink *inlink)
>>>> +int ff_hflip_config_props(FlipContext* s, AVFilterLink *inlink)
>>>>  {
>>>> -    FlipContext *s = inlink->dst->priv;
>>>>      const AVPixFmtDescriptor *pix_desc =
>>>> av_pix_fmt_desc_get(inlink->format);
>>>>      const int hsub = pix_desc->log2_chroma_w;
>>>>      const int vsub = pix_desc->log2_chroma_h;
>>>> @@ -144,6 +143,12 @@ static int config_props(AVFilterLink *inlink)
>>>>      return ff_hflip_init(s, s->max_step, nb_planes);
>>>>  }
>>>>
>>>> +static int config_props(AVFilterLink *inlink)
>>>> +{
>>>> +    FlipContext *s = inlink->dst->priv;
>>>> +    return ff_hflip_config_props(s, inlink);
>>>> +}
>>>> +
>>>>  int ff_hflip_init(FlipContext *s, int step[4], int nb_planes)
>>>>  {
>>>>      int i;
>>>> @@ -170,14 +175,10 @@ typedef struct ThreadData {
>>>>      AVFrame *in, *out;
>>>>  } ThreadData;
>>>>
>>>> -static int filter_slice(AVFilterContext *ctx, void *arg, int job, int
>>>> nb_jobs)
>>>> +int ff_hflip_filter_slice(FlipContext *s, AVFrame *in, AVFrame *out,
>>>> int job, int nb_jobs, int vflip)
>>>>  {
>>>> -    FlipContext *s = ctx->priv;
>>>> -    ThreadData *td = arg;
>>>> -    AVFrame *in = td->in;
>>>> -    AVFrame *out = td->out;
>>>>      uint8_t *inrow, *outrow;
>>>> -    int i, plane, step;
>>>> +    int i, plane, step, outlinesize;
>>>>
>>>>      for (plane = 0; plane < 4 && in->data[plane] &&
>>>> in->linesize[plane]; plane++) {
>>>>          const int width  = s->planewidth[plane];
>>>> @@ -187,19 +188,36 @@ static int filter_slice(AVFilterContext *ctx,
>>>> void *arg, int job, int nb_jobs)
>>>>
>>>>          step = s->max_step[plane];
>>>>
>>>> -        outrow = out->data[plane] + start * out->linesize[plane];
>>>> -        inrow  = in ->data[plane] + start * in->linesize[plane] +
>>>> (width - 1) * step;
>>>> +        if (vflip) {
>>>> +            outrow = out->data[plane] + (height - start - 1)*
>>>> out->linesize[plane];
>>>> +            outlinesize = -out->linesize[plane];
>>>> +        } else {
>>>> +            outrow = out->data[plane] + start * out->linesize[plane];
>>>> +            outlinesize = out->linesize[plane];
>>>> +        }
>>>> +
>>>> +        inrow = in->data[plane] + start * in->linesize[plane] +
>>>> (width - 1) * step;
>>>> +
>>>>          for (i = start; i < end; i++) {
>>>>              s->flip_line[plane](inrow, outrow, width);
>>>>
>>>>              inrow  += in ->linesize[plane];
>>>> -            outrow += out->linesize[plane];
>>>> +            outrow += outlinesize;
>>>>          }
>>>>      }
>>>>
>>>>      return 0;
>>>>  }
>>>>
>>>> +static int filter_slice(AVFilterContext *ctx, void *arg, int job, int
>>>> nb_jobs)
>>>> +{
>>>> +    FlipContext *s = ctx->priv;
>>>> +    ThreadData *td = arg;
>>>> +    AVFrame *in = td->in;
>>>> +    AVFrame *out = td->out;
>>>> +    return ff_hflip_filter_slice(s, in, out, job, nb_jobs, 0);
>>>> +}
>>>> +
>>>>  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
>>>>  {
>>>>      AVFilterContext *ctx  = inlink->dst;
>>>> diff --git a/libavfilter/vf_transpose.c b/libavfilter/vf_transpose.c
>>>> index dd54947bd9..05dc04a89f 100644
>>>> --- a/libavfilter/vf_transpose.c
>>>> +++ b/libavfilter/vf_transpose.c
>>>> @@ -39,6 +39,7 @@
>>>>  #include "internal.h"
>>>>  #include "video.h"
>>>>  #include "transpose.h"
>>>> +#include "hflip.h"
>>>>
>>>>  typedef struct TransVtable {
>>>>      void (*transpose_8x8)(uint8_t *src, ptrdiff_t src_linesize,
>>>> @@ -48,16 +49,22 @@ typedef struct TransVtable {
>>>>                              int w, int h);
>>>>  } TransVtable;
>>>>
>>>> -typedef struct TransContext {
>>>> -    const AVClass *class;
>>>> +typedef struct TransContextData {
>>>>      int hsub, vsub;
>>>>      int planes;
>>>>      int pixsteps[4];
>>>> +    TransVtable vtables[4];
>>>> +} TransContextData;
>>>>
>>>> +typedef struct TransContext {
>>>> +    const AVClass *class;
>>>>      int passthrough;    ///< PassthroughType, landscape passthrough
>>>> mode enabled
>>>>      int dir;            ///< TransposeDir
>>>> -
>>>> -    TransVtable vtables[4];
>>>> +    int orientation;    ///< OrientationType
>>>> +    union {
>>>> +        TransContextData transpose;
>>>> +        FlipContext flip;
>>>> +    } ctx;
>>>>  } TransContext;
>>>>
>>>>  static int query_formats(AVFilterContext *ctx)
>>>> @@ -207,14 +214,18 @@ static int config_props_output(AVFilterLink
>>>> *outlink)
>>>>          s->passthrough = TRANSPOSE_PT_TYPE_NONE;
>>>>      }
>>>>
>>>> -    s->hsub = desc_in->log2_chroma_w;
>>>> -    s->vsub = desc_in->log2_chroma_h;
>>>> -    s->planes = av_pix_fmt_count_planes(outlink->format);
>>>> +    if (s->orientation == ORIENTATION_AUTO_FLIP ||
>>>> +       (s->orientation > ORIENTATION_NONE && s->orientation <=
>>>> ORIENTATION_VFLIP))
>>>> +        return ff_hflip_config_props(&(s->ctx.flip), inlink);
>>>> +
>>>> +    s->ctx.transpose.hsub = desc_in->log2_chroma_w;
>>>> +    s->ctx.transpose.vsub = desc_in->log2_chroma_h;
>>>> +    s->ctx.transpose.planes = av_pix_fmt_count_planes(outlink->format);
>>>>
>>>>      av_assert0(desc_in->nb_components == desc_out->nb_components);
>>>>
>>>>
>>>> -    av_image_fill_max_pixsteps(s->pixsteps, NULL, desc_out);
>>>> +    av_image_fill_max_pixsteps(s->ctx.transpose.pixsteps, NULL,
>>>> desc_out);
>>>>
>>>>      outlink->w = inlink->h;
>>>>      outlink->h = inlink->w;
>>>> @@ -226,10 +237,10 @@ static int config_props_output(AVFilterLink
>>>> *outlink)
>>>>          outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
>>>>
>>>>      for (int i = 0; i < 4; i++) {
>>>> -        TransVtable *v = &s->vtables[i];
>>>> -        switch (s->pixsteps[i]) {
>>>> +        TransVtable *v = &s->ctx.transpose.vtables[i];
>>>> +        switch (s->ctx.transpose.pixsteps[i]) {
>>>>          case 1: v->transpose_block = transpose_block_8_c;
>>>> -                v->transpose_8x8   = transpose_8x8_8_c;  break;
>>>> +                v->transpose_8x8   = transpose_8x8_8_c; break;
>>>>          case 2: v->transpose_block = transpose_block_16_c;
>>>>                  v->transpose_8x8   = transpose_8x8_16_c; break;
>>>>          case 3: v->transpose_block = transpose_block_24_c;
>>>> @@ -262,21 +273,69 @@ static AVFrame *get_video_buffer(AVFilterLink
>>>> *inlink, int w, int h)
>>>>
>>>>  typedef struct ThreadData {
>>>>      AVFrame *in, *out;
>>>> +    int orientation;
>>>>  } ThreadData;
>>>>
>>>> -static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
>>>> -                        int nb_jobs)
>>>> +static int get_frame_orientation(AVFilterContext *ctx, const AVFrame
>>>> *frame)
>>>> +{
>>>> +    TransContext *s = ctx->priv;
>>>> +    AVDictionaryEntry *entry = NULL;
>>>> +    int orientation = ORIENTATION_NONE;
>>>> +
>>>> +    if (s->orientation >= ORIENTATION_NONE)
>>>> +        return s->orientation;
>>>> +
>>>> +    // read exif orientation data
>>>> +    entry = av_dict_get(frame->metadata, "Orientation", NULL, 0);
>>>> +    if (entry && entry->value)
>>>> +        orientation = atoi(entry->value);
>>>> +
>>>> +    // check frame orientation validity
>>>> +    if (orientation < ORIENTATION_NORMAL ||
>>>> +        orientation > ORIENTATION_ROTATE270CW) {
>>>> +        // frame orientation is invalid or empty, we have to guess one.
>>>> +        av_log(ctx, AV_LOG_WARNING, "Frame orientation is either empty
>>>> or invalid.\n");
>>>> +        return s->orientation == ORIENTATION_AUTO_FLIP ?
>>>> +               ORIENTATION_NORMAL : ORIENTATION_HFLIP_ROTATE270CW;
>>>> +    }
>>>> +
>>>> +    // check conflict between frame orientation and passed-in
>>>> orientation option
>>>> +    if (s->orientation == ORIENTATION_AUTO_FLIP &&
>>>> +        orientation > ORIENTATION_VFLIP) {
>>>> +        av_log(ctx, AV_LOG_WARNING,
>>>> +               "Found valid orientation value: %i, but auto-flip only
>>>> cover range [1, 4]. Set to default value 1(ORIENTATION_NONE).\n",
>>>> +               orientation);
>>>> +        // conflict happens, return the min value of orientation range
>>>> +        return ORIENTATION_NORMAL;
>>>> +    }
>>>> +
>>>> +    // check conflict between frame orientation and passed-in
>>>> orientation option
>>>> +    if (s->orientation == ORIENTATION_AUTO_TRANSPOSE &&
>>>> +        orientation <= ORIENTATION_VFLIP) {
>>>> +        av_log(ctx, AV_LOG_WARNING,
>>>> +               "Found valid orientation value: %i, but auto-transpose
>>>> only cover range [5, 8]. Set to default value
>>>> 5(ORIENTATION_HFLIP_ROTATE270CW).\n",
>>>> +               orientation);
>>>> +        // conflict happens, return the min value of orientation
>>>> range, same effect as dir=0
>>>> +        return ORIENTATION_HFLIP_ROTATE270CW;
>>>> +    }
>>>> +
>>>> +    return orientation;
>>>> +}
>>>> +
>>>> +static int transpose_filter_slice(AVFilterContext *ctx, void *arg, int
>>>> jobnr, int nb_jobs)
>>>>  {
>>>>      TransContext *s = ctx->priv;
>>>>      ThreadData *td = arg;
>>>>      AVFrame *out = td->out;
>>>>      AVFrame *in = td->in;
>>>> +    TransContextData *c = &(s->ctx.transpose);
>>>> +    int orientation = td->orientation;
>>>>      int plane;
>>>>
>>>> -    for (plane = 0; plane < s->planes; plane++) {
>>>> -        int hsub    = plane == 1 || plane == 2 ? s->hsub : 0;
>>>> -        int vsub    = plane == 1 || plane == 2 ? s->vsub : 0;
>>>> -        int pixstep = s->pixsteps[plane];
>>>> +    for (plane = 0; plane < c->planes; plane++) {
>>>> +        int hsub    = plane == 1 || plane == 2 ? c->hsub : 0;
>>>> +        int vsub    = plane == 1 || plane == 2 ? c->vsub : 0;
>>>> +        int pixstep = c->pixsteps[plane];
>>>>          int inh     = AV_CEIL_RSHIFT(in->height, vsub);
>>>>          int outw    = AV_CEIL_RSHIFT(out->width,  hsub);
>>>>          int outh    = AV_CEIL_RSHIFT(out->height, vsub);
>>>> @@ -285,19 +344,36 @@ static int filter_slice(AVFilterContext *ctx,
>>>> void *arg, int jobnr,
>>>>          uint8_t *dst, *src;
>>>>          int dstlinesize, srclinesize;
>>>>          int x, y;
>>>> -        TransVtable *v = &s->vtables[plane];
>>>> +        int dir = s->dir;
>>>> +        TransVtable *v = &c->vtables[plane];
>>>>
>>>>          dstlinesize = out->linesize[plane];
>>>>          dst         = out->data[plane] + start * dstlinesize;
>>>>          src         = in->data[plane];
>>>>          srclinesize = in->linesize[plane];
>>>> +        switch (orientation) {
>>>> +            case ORIENTATION_HFLIP_ROTATE270CW:
>>>> +                dir = 0; // mirror horizontal and rotate 270 CW, same
>>>> as dir=cclock_flip
>>>> +                break;
>>>> +            case ORIENTATION_ROTATE90CW:
>>>> +                dir = 1; // rotate 90 CW, same as dir=clock
>>>> +                break;
>>>> +            case ORIENTATION_HFLIP_ROTATE90CW:
>>>> +                dir = 3; // mirror horizontal and rotate 90 CW, same
>>>> as dir=clock_flip
>>>> +                break;
>>>> +            case ORIENTATION_ROTATE270CW:
>>>> +                dir = 2; // rotate 270 CW, same as dir=cclock
>>>> +                break;
>>>> +            default:
>>>> +                break;
>>>> +        }
>>>>
>>>> -        if (s->dir & 1) {
>>>> +        if (dir & 1) {
>>>>              src         += in->linesize[plane] * (inh - 1);
>>>>              srclinesize *= -1;
>>>>          }
>>>>
>>>> -        if (s->dir & 2) {
>>>> +        if (dir & 2) {
>>>>              dst          = out->data[plane] + dstlinesize * (outh -
>>>> start - 1);
>>>>              dstlinesize *= -1;
>>>>          }
>>>> @@ -326,6 +402,22 @@ static int filter_slice(AVFilterContext *ctx, void
>>>> *arg, int jobnr,
>>>>      return 0;
>>>>  }
>>>>
>>>> +static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
>>>> +                        int nb_jobs)
>>>> +{
>>>> +    TransContext *s = ctx->priv;
>>>> +    ThreadData *td = arg;
>>>> +    AVFrame *out = td->out;
>>>> +    AVFrame *in = td->in;
>>>> +    int orientation = td->orientation;
>>>> +
>>>> +    if (orientation > ORIENTATION_NONE && orientation <=
>>>> ORIENTATION_VFLIP) {
>>>> +        return ff_hflip_filter_slice(&s->ctx.flip, in, out, jobnr,
>>>> nb_jobs,
>>>> +                                     orientation ==
>>>> ORIENTATION_ROTATE180);
>>>> +    }
>>>> +    return transpose_filter_slice(ctx, arg, jobnr, nb_jobs);
>>>> +}
>>>> +
>>>>  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
>>>>  {
>>>>      AVFilterContext *ctx = inlink->dst;
>>>> @@ -333,10 +425,29 @@ static int filter_frame(AVFilterLink *inlink,
>>>> AVFrame *in)
>>>>      AVFilterLink *outlink = ctx->outputs[0];
>>>>      ThreadData td;
>>>>      AVFrame *out;
>>>> +    int orientation = ORIENTATION_NONE;
>>>>
>>>>      if (s->passthrough)
>>>>          return ff_filter_frame(outlink, in);
>>>>
>>>> +    if (s->orientation) {
>>>> +        orientation = get_frame_orientation(ctx, in);
>>>> +        if (orientation == ORIENTATION_NORMAL) // orientation
>>>> 'Normal', do nothing
>>>> +            return ff_filter_frame(outlink, in);
>>>> +
>>>> +        if (orientation == ORIENTATION_VFLIP) {
>>>> +            int i;
>>>> +            for (i = 0; i < 4; i ++) {
>>>> +                int height = s->ctx.flip.planeheight[i];
>>>> +                if (in->data[i]) {
>>>> +                    in->data[i] += (height - 1) * in->linesize[i];
>>>> +                    in->linesize[i] = -in->linesize[i];
>>>> +                }
>>>> +            }
>>>> +            return ff_filter_frame(outlink, in);
>>>> +        }
>>>> +    }
>>>> +
>>>>      out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
>>>>      if (!out) {
>>>>          av_frame_free(&in);
>>>> @@ -346,12 +457,14 @@ static int filter_frame(AVFilterLink *inlink,
>>>> AVFrame *in)
>>>>
>>>>      if (in->sample_aspect_ratio.num == 0) {
>>>>          out->sample_aspect_ratio = in->sample_aspect_ratio;
>>>> -    } else {
>>>> +    } else if (s->orientation == ORIENTATION_AUTO_TRANSPOSE ||
>>>> +               orientation > ORIENTATION_VFLIP) { // transpose
>>>>          out->sample_aspect_ratio.num = in->sample_aspect_ratio.den;
>>>>          out->sample_aspect_ratio.den = in->sample_aspect_ratio.num;
>>>>      }
>>>>
>>>>      td.in = in, td.out = out;
>>>> +    td.orientation = orientation;
>>>>      ctx->internal->execute(ctx, filter_slice, &td, NULL,
>>>> FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
>>>>      av_frame_free(&in);
>>>>      return ff_filter_frame(outlink, out);
>>>> @@ -373,6 +486,19 @@ static const AVOption transpose_options[] = {
>>>>          { "portrait",  "preserve portrait geometry",   0,
>>>> AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT},  INT_MIN, INT_MAX,
>>>> FLAGS, "passthrough" },
>>>>          { "landscape", "preserve landscape geometry",  0,
>>>> AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX,
>>>> FLAGS, "passthrough" },
>>>>
>>>> +    { "orientation", "apply frame exif orientation",
>>>> +      OFFSET(orientation), AV_OPT_TYPE_INT, {.i64 = ORIENTATION_NONE
>>>> },  ORIENTATION_AUTO_TRANSPOSE, ORIENTATION_ROTATE270CW, FLAGS,
>>>> "orientation" },
>>>> +        { "auto_transpose",    "apply transposition if frame's
>>>> orientation value is in range [5,8]",                     0,
>>>> AV_OPT_TYPE_CONST, {.i64=ORIENTATION_AUTO_TRANSPOSE},    INT_MIN, INT_MAX,
>>>> FLAGS, "orientation" },
>>>> +        { "auto_flip",         "apply flip if frame's orientation
>>>> value is in range [1,4]",                              0,
>>>> AV_OPT_TYPE_CONST, {.i64=ORIENTATION_AUTO_FLIP},         INT_MIN, INT_MAX,
>>>> FLAGS, "orientation" },
>>>> +        { "none",              "apply default transposition, same as
>>>> dir=cclock_flip",                                   0, AV_OPT_TYPE_CONST,
>>>> {.i64=ORIENTATION_NONE},              INT_MIN, INT_MAX, FLAGS,
>>>> "orientation" },
>>>> +        { "normal",            "exif orientation 'Normal', do not
>>>> apply any flip or transposition",                      0,
>>>> AV_OPT_TYPE_CONST, {.i64=ORIENTATION_NORMAL},            INT_MIN, INT_MAX,
>>>> FLAGS, "orientation" },
>>>> +        { "hflip",             "exif orientation 'Mirror horizontal',
>>>> apply horizontal flip",                            0, AV_OPT_TYPE_CONST,
>>>> {.i64=ORIENTATION_HFLIP},             INT_MIN, INT_MAX, FLAGS,
>>>> "orientation" },
>>>> +        { "rotate180",         "exif orientation 'Rotate 180', apply
>>>> rotation 180",                                      0, AV_OPT_TYPE_CONST,
>>>> {.i64=ORIENTATION_ROTATE180},         INT_MIN, INT_MAX, FLAGS,
>>>> "orientation" },
>>>> +        { "vflip",             "exif orientation 'Mirror vertical',
>>>> apply vertical flip",                                0, AV_OPT_TYPE_CONST,
>>>> {.i64=ORIENTATION_VFLIP},             INT_MIN, INT_MAX, FLAGS,
>>>> "orientation" },
>>>> +        { "hflip_rotate270cw", "exif orientation 'Mirror horizontal
>>>> and rotate 270 CW', same effect as dir=cclock_flip", 0, AV_OPT_TYPE_CONST,
>>>> {.i64=ORIENTATION_HFLIP_ROTATE270CW}, INT_MIN, INT_MAX, FLAGS,
>>>> "orientation" },
>>>> +        { "rotate90cw",        "exif orientation 'Rotate 90 CW', same
>>>> as dir=clock",                                     0, AV_OPT_TYPE_CONST,
>>>> {.i64=ORIENTATION_ROTATE90CW},        INT_MIN, INT_MAX, FLAGS,
>>>> "orientation" },
>>>> +        { "hflip_rotate90cw",  "exif orientation 'Mirror horizontal
>>>> and rotate 90 CW', same effect as dir=clock_flip",   0, AV_OPT_TYPE_CONST,
>>>> {.i64=ORIENTATION_HFLIP_ROTATE90CW},  INT_MIN, INT_MAX, FLAGS,
>>>> "orientation" },
>>>> +        { "rotate270",         "exif orientation 'Rotate 270 CW', same
>>>> as dir=cclock",                                   0, AV_OPT_TYPE_CONST,
>>>> {.i64=ORIENTATION_ROTATE270CW},       INT_MIN, INT_MAX, FLAGS,
>>>> "orientation" },
>>>>      { NULL }
>>>>  };
>>>>
>>>> --
>>>> 2.17.1
>>>>
>>>>
>>> Ping :)
>>>
>>
>> Ping x 2
>>
>
> Ping x3, could someone please help to take a look ? Really appreciate that.
>
>
Ping x4. This is to address exif auto-rotate, make fate test passed.

> -Jun
>
Steven Liu June 26, 2019, 11:32 a.m. UTC | #5
Jun Li <junli1026@gmail.com> 于2019年6月25日周二 上午9:26写道:
>
> On Mon, Jun 17, 2019 at 5:31 PM Jun Li <junli1026@gmail.com> wrote:
>
> >
> >
> > On Sat, Jun 15, 2019 at 7:09 PM Jun Li <junli1026@gmail.com> wrote:
> >
> >>
> >>
> >> On Tue, Jun 11, 2019 at 7:05 PM Jun Li <junli1026@gmail.com> wrote:
> >>
> >>>
> >>> On Sun, Jun 9, 2019 at 2:28 PM Jun Li <junli1026@gmail.com> wrote:
> >>>
> >>>> Add exif orientation support and expose an option.
> >>>> ---
> >>>>  libavfilter/hflip.h        |   2 +
> >>>>  libavfilter/transpose.h    |  14 ++++
> >>>>  libavfilter/vf_hflip.c     |  40 ++++++---
> >>>>  libavfilter/vf_transpose.c | 168 ++++++++++++++++++++++++++++++++-----
> >>>>  4 files changed, 192 insertions(+), 32 deletions(-)
> >>>>
> >>>> diff --git a/libavfilter/hflip.h b/libavfilter/hflip.h
> >>>> index 204090dbb4..4e89bae3fc 100644
> >>>> --- a/libavfilter/hflip.h
> >>>> +++ b/libavfilter/hflip.h
> >>>> @@ -35,5 +35,7 @@ typedef struct FlipContext {
> >>>>
> >>>>  int ff_hflip_init(FlipContext *s, int step[4], int nb_planes);
> >>>>  void ff_hflip_init_x86(FlipContext *s, int step[4], int nb_planes);
> >>>> +int ff_hflip_config_props(FlipContext* s, AVFilterLink *inlink);
> >>>> +int ff_hflip_filter_slice(FlipContext *s, AVFrame *in, AVFrame *out,
> >>>> int job, int nb_jobs, int vlifp);
> >>>>
> >>>>  #endif /* AVFILTER_HFLIP_H */
> >>>> diff --git a/libavfilter/transpose.h b/libavfilter/transpose.h
> >>>> index aa262b9487..5da08bddc0 100644
> >>>> --- a/libavfilter/transpose.h
> >>>> +++ b/libavfilter/transpose.h
> >>>> @@ -34,4 +34,18 @@ enum TransposeDir {
> >>>>      TRANSPOSE_VFLIP,
> >>>>  };
> >>>>
> >>>> +enum OrientationType {
> >>>> +    ORIENTATION_AUTO_TRANSPOSE = -2,
> >>>> +    ORIENTATION_AUTO_FLIP = -1,
> >>>> +    ORIENTATION_NONE = 0,
> >>>> +    ORIENTATION_NORMAL,
> >>>> +    ORIENTATION_HFLIP,
> >>>> +    ORIENTATION_ROTATE180,
> >>>> +    ORIENTATION_VFLIP,
> >>>> +    ORIENTATION_HFLIP_ROTATE270CW,
> >>>> +    ORIENTATION_ROTATE90CW,
> >>>> +    ORIENTATION_HFLIP_ROTATE90CW,
> >>>> +    ORIENTATION_ROTATE270CW
> >>>> +};
> >>>> +
> >>>>  #endif
> >>>> diff --git a/libavfilter/vf_hflip.c b/libavfilter/vf_hflip.c
> >>>> index b77afc77fc..d24ca5c2e7 100644
> >>>> --- a/libavfilter/vf_hflip.c
> >>>> +++ b/libavfilter/vf_hflip.c
> >>>> @@ -125,9 +125,8 @@ static void hflip_qword_c(const uint8_t *ssrc,
> >>>> uint8_t *ddst, int w)
> >>>>          dst[j] = src[-j];
> >>>>  }
> >>>>
> >>>> -static int config_props(AVFilterLink *inlink)
> >>>> +int ff_hflip_config_props(FlipContext* s, AVFilterLink *inlink)
> >>>>  {
> >>>> -    FlipContext *s = inlink->dst->priv;
> >>>>      const AVPixFmtDescriptor *pix_desc =
> >>>> av_pix_fmt_desc_get(inlink->format);
> >>>>      const int hsub = pix_desc->log2_chroma_w;
> >>>>      const int vsub = pix_desc->log2_chroma_h;
> >>>> @@ -144,6 +143,12 @@ static int config_props(AVFilterLink *inlink)
> >>>>      return ff_hflip_init(s, s->max_step, nb_planes);
> >>>>  }
> >>>>
> >>>> +static int config_props(AVFilterLink *inlink)
> >>>> +{
> >>>> +    FlipContext *s = inlink->dst->priv;
> >>>> +    return ff_hflip_config_props(s, inlink);
> >>>> +}
> >>>> +
> >>>>  int ff_hflip_init(FlipContext *s, int step[4], int nb_planes)
> >>>>  {
> >>>>      int i;
> >>>> @@ -170,14 +175,10 @@ typedef struct ThreadData {
> >>>>      AVFrame *in, *out;
> >>>>  } ThreadData;
> >>>>
> >>>> -static int filter_slice(AVFilterContext *ctx, void *arg, int job, int
> >>>> nb_jobs)
> >>>> +int ff_hflip_filter_slice(FlipContext *s, AVFrame *in, AVFrame *out,
> >>>> int job, int nb_jobs, int vflip)
> >>>>  {
> >>>> -    FlipContext *s = ctx->priv;
> >>>> -    ThreadData *td = arg;
> >>>> -    AVFrame *in = td->in;
> >>>> -    AVFrame *out = td->out;
> >>>>      uint8_t *inrow, *outrow;
> >>>> -    int i, plane, step;
> >>>> +    int i, plane, step, outlinesize;
> >>>>
> >>>>      for (plane = 0; plane < 4 && in->data[plane] &&
> >>>> in->linesize[plane]; plane++) {
> >>>>          const int width  = s->planewidth[plane];
> >>>> @@ -187,19 +188,36 @@ static int filter_slice(AVFilterContext *ctx,
> >>>> void *arg, int job, int nb_jobs)
> >>>>
> >>>>          step = s->max_step[plane];
> >>>>
> >>>> -        outrow = out->data[plane] + start * out->linesize[plane];
> >>>> -        inrow  = in ->data[plane] + start * in->linesize[plane] +
> >>>> (width - 1) * step;
> >>>> +        if (vflip) {
> >>>> +            outrow = out->data[plane] + (height - start - 1)*
> >>>> out->linesize[plane];
> >>>> +            outlinesize = -out->linesize[plane];
> >>>> +        } else {
> >>>> +            outrow = out->data[plane] + start * out->linesize[plane];
> >>>> +            outlinesize = out->linesize[plane];
> >>>> +        }
> >>>> +
> >>>> +        inrow = in->data[plane] + start * in->linesize[plane] +
> >>>> (width - 1) * step;
> >>>> +
> >>>>          for (i = start; i < end; i++) {
> >>>>              s->flip_line[plane](inrow, outrow, width);
> >>>>
> >>>>              inrow  += in ->linesize[plane];
> >>>> -            outrow += out->linesize[plane];
> >>>> +            outrow += outlinesize;
> >>>>          }
> >>>>      }
> >>>>
> >>>>      return 0;
> >>>>  }
> >>>>
> >>>> +static int filter_slice(AVFilterContext *ctx, void *arg, int job, int
> >>>> nb_jobs)
> >>>> +{
> >>>> +    FlipContext *s = ctx->priv;
> >>>> +    ThreadData *td = arg;
> >>>> +    AVFrame *in = td->in;
> >>>> +    AVFrame *out = td->out;
> >>>> +    return ff_hflip_filter_slice(s, in, out, job, nb_jobs, 0);
> >>>> +}
> >>>> +
> >>>>  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
> >>>>  {
> >>>>      AVFilterContext *ctx  = inlink->dst;
> >>>> diff --git a/libavfilter/vf_transpose.c b/libavfilter/vf_transpose.c
> >>>> index dd54947bd9..05dc04a89f 100644
> >>>> --- a/libavfilter/vf_transpose.c
> >>>> +++ b/libavfilter/vf_transpose.c
> >>>> @@ -39,6 +39,7 @@
> >>>>  #include "internal.h"
> >>>>  #include "video.h"
> >>>>  #include "transpose.h"
> >>>> +#include "hflip.h"
> >>>>
> >>>>  typedef struct TransVtable {
> >>>>      void (*transpose_8x8)(uint8_t *src, ptrdiff_t src_linesize,
> >>>> @@ -48,16 +49,22 @@ typedef struct TransVtable {
> >>>>                              int w, int h);
> >>>>  } TransVtable;
> >>>>
> >>>> -typedef struct TransContext {
> >>>> -    const AVClass *class;
> >>>> +typedef struct TransContextData {
> >>>>      int hsub, vsub;
> >>>>      int planes;
> >>>>      int pixsteps[4];
> >>>> +    TransVtable vtables[4];
> >>>> +} TransContextData;
> >>>>
> >>>> +typedef struct TransContext {
> >>>> +    const AVClass *class;
> >>>>      int passthrough;    ///< PassthroughType, landscape passthrough
> >>>> mode enabled
> >>>>      int dir;            ///< TransposeDir
> >>>> -
> >>>> -    TransVtable vtables[4];
> >>>> +    int orientation;    ///< OrientationType
> >>>> +    union {
> >>>> +        TransContextData transpose;
> >>>> +        FlipContext flip;
> >>>> +    } ctx;
> >>>>  } TransContext;
> >>>>
> >>>>  static int query_formats(AVFilterContext *ctx)
> >>>> @@ -207,14 +214,18 @@ static int config_props_output(AVFilterLink
> >>>> *outlink)
> >>>>          s->passthrough = TRANSPOSE_PT_TYPE_NONE;
> >>>>      }
> >>>>
> >>>> -    s->hsub = desc_in->log2_chroma_w;
> >>>> -    s->vsub = desc_in->log2_chroma_h;
> >>>> -    s->planes = av_pix_fmt_count_planes(outlink->format);
> >>>> +    if (s->orientation == ORIENTATION_AUTO_FLIP ||
> >>>> +       (s->orientation > ORIENTATION_NONE && s->orientation <=
> >>>> ORIENTATION_VFLIP))
> >>>> +        return ff_hflip_config_props(&(s->ctx.flip), inlink);
> >>>> +
> >>>> +    s->ctx.transpose.hsub = desc_in->log2_chroma_w;
> >>>> +    s->ctx.transpose.vsub = desc_in->log2_chroma_h;
> >>>> +    s->ctx.transpose.planes = av_pix_fmt_count_planes(outlink->format);
> >>>>
> >>>>      av_assert0(desc_in->nb_components == desc_out->nb_components);
> >>>>
> >>>>
> >>>> -    av_image_fill_max_pixsteps(s->pixsteps, NULL, desc_out);
> >>>> +    av_image_fill_max_pixsteps(s->ctx.transpose.pixsteps, NULL,
> >>>> desc_out);
> >>>>
> >>>>      outlink->w = inlink->h;
> >>>>      outlink->h = inlink->w;
> >>>> @@ -226,10 +237,10 @@ static int config_props_output(AVFilterLink
> >>>> *outlink)
> >>>>          outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
> >>>>
> >>>>      for (int i = 0; i < 4; i++) {
> >>>> -        TransVtable *v = &s->vtables[i];
> >>>> -        switch (s->pixsteps[i]) {
> >>>> +        TransVtable *v = &s->ctx.transpose.vtables[i];
> >>>> +        switch (s->ctx.transpose.pixsteps[i]) {
> >>>>          case 1: v->transpose_block = transpose_block_8_c;
> >>>> -                v->transpose_8x8   = transpose_8x8_8_c;  break;
> >>>> +                v->transpose_8x8   = transpose_8x8_8_c; break;
> >>>>          case 2: v->transpose_block = transpose_block_16_c;
> >>>>                  v->transpose_8x8   = transpose_8x8_16_c; break;
> >>>>          case 3: v->transpose_block = transpose_block_24_c;
> >>>> @@ -262,21 +273,69 @@ static AVFrame *get_video_buffer(AVFilterLink
> >>>> *inlink, int w, int h)
> >>>>
> >>>>  typedef struct ThreadData {
> >>>>      AVFrame *in, *out;
> >>>> +    int orientation;
> >>>>  } ThreadData;
> >>>>
> >>>> -static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
> >>>> -                        int nb_jobs)
> >>>> +static int get_frame_orientation(AVFilterContext *ctx, const AVFrame
> >>>> *frame)
> >>>> +{
> >>>> +    TransContext *s = ctx->priv;
> >>>> +    AVDictionaryEntry *entry = NULL;
> >>>> +    int orientation = ORIENTATION_NONE;
> >>>> +
> >>>> +    if (s->orientation >= ORIENTATION_NONE)
> >>>> +        return s->orientation;
> >>>> +
> >>>> +    // read exif orientation data
> >>>> +    entry = av_dict_get(frame->metadata, "Orientation", NULL, 0);
> >>>> +    if (entry && entry->value)
> >>>> +        orientation = atoi(entry->value);
> >>>> +
> >>>> +    // check frame orientation validity
> >>>> +    if (orientation < ORIENTATION_NORMAL ||
> >>>> +        orientation > ORIENTATION_ROTATE270CW) {
> >>>> +        // frame orientation is invalid or empty, we have to guess one.
> >>>> +        av_log(ctx, AV_LOG_WARNING, "Frame orientation is either empty
> >>>> or invalid.\n");
> >>>> +        return s->orientation == ORIENTATION_AUTO_FLIP ?
> >>>> +               ORIENTATION_NORMAL : ORIENTATION_HFLIP_ROTATE270CW;
> >>>> +    }
> >>>> +
> >>>> +    // check conflict between frame orientation and passed-in
> >>>> orientation option
> >>>> +    if (s->orientation == ORIENTATION_AUTO_FLIP &&
> >>>> +        orientation > ORIENTATION_VFLIP) {
> >>>> +        av_log(ctx, AV_LOG_WARNING,
> >>>> +               "Found valid orientation value: %i, but auto-flip only
> >>>> cover range [1, 4]. Set to default value 1(ORIENTATION_NONE).\n",
> >>>> +               orientation);
> >>>> +        // conflict happens, return the min value of orientation range
> >>>> +        return ORIENTATION_NORMAL;
> >>>> +    }
> >>>> +
> >>>> +    // check conflict between frame orientation and passed-in
> >>>> orientation option
> >>>> +    if (s->orientation == ORIENTATION_AUTO_TRANSPOSE &&
> >>>> +        orientation <= ORIENTATION_VFLIP) {
> >>>> +        av_log(ctx, AV_LOG_WARNING,
> >>>> +               "Found valid orientation value: %i, but auto-transpose
> >>>> only cover range [5, 8]. Set to default value
> >>>> 5(ORIENTATION_HFLIP_ROTATE270CW).\n",
> >>>> +               orientation);
> >>>> +        // conflict happens, return the min value of orientation
> >>>> range, same effect as dir=0
> >>>> +        return ORIENTATION_HFLIP_ROTATE270CW;
> >>>> +    }
> >>>> +
> >>>> +    return orientation;
> >>>> +}
> >>>> +
> >>>> +static int transpose_filter_slice(AVFilterContext *ctx, void *arg, int
> >>>> jobnr, int nb_jobs)
> >>>>  {
> >>>>      TransContext *s = ctx->priv;
> >>>>      ThreadData *td = arg;
> >>>>      AVFrame *out = td->out;
> >>>>      AVFrame *in = td->in;
> >>>> +    TransContextData *c = &(s->ctx.transpose);
> >>>> +    int orientation = td->orientation;
> >>>>      int plane;
> >>>>
> >>>> -    for (plane = 0; plane < s->planes; plane++) {
> >>>> -        int hsub    = plane == 1 || plane == 2 ? s->hsub : 0;
> >>>> -        int vsub    = plane == 1 || plane == 2 ? s->vsub : 0;
> >>>> -        int pixstep = s->pixsteps[plane];
> >>>> +    for (plane = 0; plane < c->planes; plane++) {
> >>>> +        int hsub    = plane == 1 || plane == 2 ? c->hsub : 0;
> >>>> +        int vsub    = plane == 1 || plane == 2 ? c->vsub : 0;
> >>>> +        int pixstep = c->pixsteps[plane];
> >>>>          int inh     = AV_CEIL_RSHIFT(in->height, vsub);
> >>>>          int outw    = AV_CEIL_RSHIFT(out->width,  hsub);
> >>>>          int outh    = AV_CEIL_RSHIFT(out->height, vsub);
> >>>> @@ -285,19 +344,36 @@ static int filter_slice(AVFilterContext *ctx,
> >>>> void *arg, int jobnr,
> >>>>          uint8_t *dst, *src;
> >>>>          int dstlinesize, srclinesize;
> >>>>          int x, y;
> >>>> -        TransVtable *v = &s->vtables[plane];
> >>>> +        int dir = s->dir;
> >>>> +        TransVtable *v = &c->vtables[plane];
> >>>>
> >>>>          dstlinesize = out->linesize[plane];
> >>>>          dst         = out->data[plane] + start * dstlinesize;
> >>>>          src         = in->data[plane];
> >>>>          srclinesize = in->linesize[plane];
> >>>> +        switch (orientation) {
> >>>> +            case ORIENTATION_HFLIP_ROTATE270CW:
> >>>> +                dir = 0; // mirror horizontal and rotate 270 CW, same
> >>>> as dir=cclock_flip
> >>>> +                break;
> >>>> +            case ORIENTATION_ROTATE90CW:
> >>>> +                dir = 1; // rotate 90 CW, same as dir=clock
> >>>> +                break;
> >>>> +            case ORIENTATION_HFLIP_ROTATE90CW:
> >>>> +                dir = 3; // mirror horizontal and rotate 90 CW, same
> >>>> as dir=clock_flip
> >>>> +                break;
> >>>> +            case ORIENTATION_ROTATE270CW:
> >>>> +                dir = 2; // rotate 270 CW, same as dir=cclock
> >>>> +                break;
> >>>> +            default:
> >>>> +                break;
> >>>> +        }
> >>>>
> >>>> -        if (s->dir & 1) {
> >>>> +        if (dir & 1) {
> >>>>              src         += in->linesize[plane] * (inh - 1);
> >>>>              srclinesize *= -1;
> >>>>          }
> >>>>
> >>>> -        if (s->dir & 2) {
> >>>> +        if (dir & 2) {
> >>>>              dst          = out->data[plane] + dstlinesize * (outh -
> >>>> start - 1);
> >>>>              dstlinesize *= -1;
> >>>>          }
> >>>> @@ -326,6 +402,22 @@ static int filter_slice(AVFilterContext *ctx, void
> >>>> *arg, int jobnr,
> >>>>      return 0;
> >>>>  }
> >>>>
> >>>> +static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
> >>>> +                        int nb_jobs)
> >>>> +{
> >>>> +    TransContext *s = ctx->priv;
> >>>> +    ThreadData *td = arg;
> >>>> +    AVFrame *out = td->out;
> >>>> +    AVFrame *in = td->in;
> >>>> +    int orientation = td->orientation;
> >>>> +
> >>>> +    if (orientation > ORIENTATION_NONE && orientation <=
> >>>> ORIENTATION_VFLIP) {
> >>>> +        return ff_hflip_filter_slice(&s->ctx.flip, in, out, jobnr,
> >>>> nb_jobs,
> >>>> +                                     orientation ==
> >>>> ORIENTATION_ROTATE180);
> >>>> +    }
> >>>> +    return transpose_filter_slice(ctx, arg, jobnr, nb_jobs);
> >>>> +}
> >>>> +
> >>>>  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
> >>>>  {
> >>>>      AVFilterContext *ctx = inlink->dst;
> >>>> @@ -333,10 +425,29 @@ static int filter_frame(AVFilterLink *inlink,
> >>>> AVFrame *in)
> >>>>      AVFilterLink *outlink = ctx->outputs[0];
> >>>>      ThreadData td;
> >>>>      AVFrame *out;
> >>>> +    int orientation = ORIENTATION_NONE;
> >>>>
> >>>>      if (s->passthrough)
> >>>>          return ff_filter_frame(outlink, in);
> >>>>
> >>>> +    if (s->orientation) {
> >>>> +        orientation = get_frame_orientation(ctx, in);
> >>>> +        if (orientation == ORIENTATION_NORMAL) // orientation
> >>>> 'Normal', do nothing
> >>>> +            return ff_filter_frame(outlink, in);
> >>>> +
> >>>> +        if (orientation == ORIENTATION_VFLIP) {
> >>>> +            int i;
> >>>> +            for (i = 0; i < 4; i ++) {
> >>>> +                int height = s->ctx.flip.planeheight[i];
> >>>> +                if (in->data[i]) {
> >>>> +                    in->data[i] += (height - 1) * in->linesize[i];
> >>>> +                    in->linesize[i] = -in->linesize[i];
> >>>> +                }
> >>>> +            }
> >>>> +            return ff_filter_frame(outlink, in);
> >>>> +        }
> >>>> +    }
> >>>> +
> >>>>      out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
> >>>>      if (!out) {
> >>>>          av_frame_free(&in);
> >>>> @@ -346,12 +457,14 @@ static int filter_frame(AVFilterLink *inlink,
> >>>> AVFrame *in)
> >>>>
> >>>>      if (in->sample_aspect_ratio.num == 0) {
> >>>>          out->sample_aspect_ratio = in->sample_aspect_ratio;
> >>>> -    } else {
> >>>> +    } else if (s->orientation == ORIENTATION_AUTO_TRANSPOSE ||
> >>>> +               orientation > ORIENTATION_VFLIP) { // transpose
> >>>>          out->sample_aspect_ratio.num = in->sample_aspect_ratio.den;
> >>>>          out->sample_aspect_ratio.den = in->sample_aspect_ratio.num;
> >>>>      }
> >>>>
> >>>>      td.in = in, td.out = out;
> >>>> +    td.orientation = orientation;
> >>>>      ctx->internal->execute(ctx, filter_slice, &td, NULL,
> >>>> FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
> >>>>      av_frame_free(&in);
> >>>>      return ff_filter_frame(outlink, out);
> >>>> @@ -373,6 +486,19 @@ static const AVOption transpose_options[] = {
> >>>>          { "portrait",  "preserve portrait geometry",   0,
> >>>> AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT},  INT_MIN, INT_MAX,
> >>>> FLAGS, "passthrough" },
> >>>>          { "landscape", "preserve landscape geometry",  0,
> >>>> AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX,
> >>>> FLAGS, "passthrough" },
> >>>>
> >>>> +    { "orientation", "apply frame exif orientation",
> >>>> +      OFFSET(orientation), AV_OPT_TYPE_INT, {.i64 = ORIENTATION_NONE
> >>>> },  ORIENTATION_AUTO_TRANSPOSE, ORIENTATION_ROTATE270CW, FLAGS,
> >>>> "orientation" },
> >>>> +        { "auto_transpose",    "apply transposition if frame's
> >>>> orientation value is in range [5,8]",                     0,
> >>>> AV_OPT_TYPE_CONST, {.i64=ORIENTATION_AUTO_TRANSPOSE},    INT_MIN, INT_MAX,
> >>>> FLAGS, "orientation" },
> >>>> +        { "auto_flip",         "apply flip if frame's orientation
> >>>> value is in range [1,4]",                              0,
> >>>> AV_OPT_TYPE_CONST, {.i64=ORIENTATION_AUTO_FLIP},         INT_MIN, INT_MAX,
> >>>> FLAGS, "orientation" },
> >>>> +        { "none",              "apply default transposition, same as
> >>>> dir=cclock_flip",                                   0, AV_OPT_TYPE_CONST,
> >>>> {.i64=ORIENTATION_NONE},              INT_MIN, INT_MAX, FLAGS,
> >>>> "orientation" },
> >>>> +        { "normal",            "exif orientation 'Normal', do not
> >>>> apply any flip or transposition",                      0,
> >>>> AV_OPT_TYPE_CONST, {.i64=ORIENTATION_NORMAL},            INT_MIN, INT_MAX,
> >>>> FLAGS, "orientation" },
> >>>> +        { "hflip",             "exif orientation 'Mirror horizontal',
> >>>> apply horizontal flip",                            0, AV_OPT_TYPE_CONST,
> >>>> {.i64=ORIENTATION_HFLIP},             INT_MIN, INT_MAX, FLAGS,
> >>>> "orientation" },
> >>>> +        { "rotate180",         "exif orientation 'Rotate 180', apply
> >>>> rotation 180",                                      0, AV_OPT_TYPE_CONST,
> >>>> {.i64=ORIENTATION_ROTATE180},         INT_MIN, INT_MAX, FLAGS,
> >>>> "orientation" },
> >>>> +        { "vflip",             "exif orientation 'Mirror vertical',
> >>>> apply vertical flip",                                0, AV_OPT_TYPE_CONST,
> >>>> {.i64=ORIENTATION_VFLIP},             INT_MIN, INT_MAX, FLAGS,
> >>>> "orientation" },
> >>>> +        { "hflip_rotate270cw", "exif orientation 'Mirror horizontal
> >>>> and rotate 270 CW', same effect as dir=cclock_flip", 0, AV_OPT_TYPE_CONST,
> >>>> {.i64=ORIENTATION_HFLIP_ROTATE270CW}, INT_MIN, INT_MAX, FLAGS,
> >>>> "orientation" },
> >>>> +        { "rotate90cw",        "exif orientation 'Rotate 90 CW', same
> >>>> as dir=clock",                                     0, AV_OPT_TYPE_CONST,
> >>>> {.i64=ORIENTATION_ROTATE90CW},        INT_MIN, INT_MAX, FLAGS,
> >>>> "orientation" },
> >>>> +        { "hflip_rotate90cw",  "exif orientation 'Mirror horizontal
> >>>> and rotate 90 CW', same effect as dir=clock_flip",   0, AV_OPT_TYPE_CONST,
> >>>> {.i64=ORIENTATION_HFLIP_ROTATE90CW},  INT_MIN, INT_MAX, FLAGS,
> >>>> "orientation" },
> >>>> +        { "rotate270",         "exif orientation 'Rotate 270 CW', same
> >>>> as dir=cclock",                                   0, AV_OPT_TYPE_CONST,
> >>>> {.i64=ORIENTATION_ROTATE270CW},       INT_MIN, INT_MAX, FLAGS,
> >>>> "orientation" },
> >>>>      { NULL }
> >>>>  };
> >>>>
> >>>> --
> >>>> 2.17.1
> >>>>
> >>>>
> >>> Ping :)
> >>>
> >>
> >> Ping x 2
> >>
> >
> > Ping x3, could someone please help to take a look ? Really appreciate that.
> >
> >
> Ping x4. This is to address exif auto-rotate, make fate test passed.
>
> > -Jun
> >
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

Any comments?


Thanks
Steven Liu
Liu Steven June 29, 2019, 7:06 a.m. UTC | #6
> 在 2019年6月26日,19:32,Steven Liu <lingjiujianke@gmail.com> 写道:
> 
> Jun Li <junli1026@gmail.com> 于2019年6月25日周二 上午9:26写道:
>> 
>> On Mon, Jun 17, 2019 at 5:31 PM Jun Li <junli1026@gmail.com> wrote:
>> 
>>> 
>>> 
>>> On Sat, Jun 15, 2019 at 7:09 PM Jun Li <junli1026@gmail.com> wrote:
>>> 
>>>> 
>>>> 
>>>> On Tue, Jun 11, 2019 at 7:05 PM Jun Li <junli1026@gmail.com> wrote:
>>>> 
>>>>> 
>>>>> On Sun, Jun 9, 2019 at 2:28 PM Jun Li <junli1026@gmail.com> wrote:
>>>>> 
>>>>>> Add exif orientation support and expose an option.
>>>>>> ---
>>>>>> libavfilter/hflip.h        |   2 +
>>>>>> libavfilter/transpose.h    |  14 ++++
>>>>>> libavfilter/vf_hflip.c     |  40 ++++++---
>>>>>> libavfilter/vf_transpose.c | 168 ++++++++++++++++++++++++++++++++-----
>>>>>> 4 files changed, 192 insertions(+), 32 deletions(-)
>>>>>> 
>>>>>> diff --git a/libavfilter/hflip.h b/libavfilter/hflip.h
>>>>>> index 204090dbb4..4e89bae3fc 100644
>>>>>> --- a/libavfilter/hflip.h
>>>>>> +++ b/libavfilter/hflip.h
>>>>>> @@ -35,5 +35,7 @@ typedef struct FlipContext {
>>>>>> 
>>>>>> int ff_hflip_init(FlipContext *s, int step[4], int nb_planes);
>>>>>> void ff_hflip_init_x86(FlipContext *s, int step[4], int nb_planes);
>>>>>> +int ff_hflip_config_props(FlipContext* s, AVFilterLink *inlink);
>>>>>> +int ff_hflip_filter_slice(FlipContext *s, AVFrame *in, AVFrame *out,
>>>>>> int job, int nb_jobs, int vlifp);
>>>>>> 
>>>>>> #endif /* AVFILTER_HFLIP_H */
>>>>>> diff --git a/libavfilter/transpose.h b/libavfilter/transpose.h
>>>>>> index aa262b9487..5da08bddc0 100644
>>>>>> --- a/libavfilter/transpose.h
>>>>>> +++ b/libavfilter/transpose.h
>>>>>> @@ -34,4 +34,18 @@ enum TransposeDir {
>>>>>>     TRANSPOSE_VFLIP,
>>>>>> };
>>>>>> 
>>>>>> +enum OrientationType {
>>>>>> +    ORIENTATION_AUTO_TRANSPOSE = -2,
>>>>>> +    ORIENTATION_AUTO_FLIP = -1,
>>>>>> +    ORIENTATION_NONE = 0,
>>>>>> +    ORIENTATION_NORMAL,
>>>>>> +    ORIENTATION_HFLIP,
>>>>>> +    ORIENTATION_ROTATE180,
>>>>>> +    ORIENTATION_VFLIP,
>>>>>> +    ORIENTATION_HFLIP_ROTATE270CW,
>>>>>> +    ORIENTATION_ROTATE90CW,
>>>>>> +    ORIENTATION_HFLIP_ROTATE90CW,
>>>>>> +    ORIENTATION_ROTATE270CW
>>>>>> +};
>>>>>> +
>>>>>> #endif
>>>>>> diff --git a/libavfilter/vf_hflip.c b/libavfilter/vf_hflip.c
>>>>>> index b77afc77fc..d24ca5c2e7 100644
>>>>>> --- a/libavfilter/vf_hflip.c
>>>>>> +++ b/libavfilter/vf_hflip.c
>>>>>> @@ -125,9 +125,8 @@ static void hflip_qword_c(const uint8_t *ssrc,
>>>>>> uint8_t *ddst, int w)
>>>>>>         dst[j] = src[-j];
>>>>>> }
>>>>>> 
>>>>>> -static int config_props(AVFilterLink *inlink)
>>>>>> +int ff_hflip_config_props(FlipContext* s, AVFilterLink *inlink)
>>>>>> {
>>>>>> -    FlipContext *s = inlink->dst->priv;
>>>>>>     const AVPixFmtDescriptor *pix_desc =
>>>>>> av_pix_fmt_desc_get(inlink->format);
>>>>>>     const int hsub = pix_desc->log2_chroma_w;
>>>>>>     const int vsub = pix_desc->log2_chroma_h;
>>>>>> @@ -144,6 +143,12 @@ static int config_props(AVFilterLink *inlink)
>>>>>>     return ff_hflip_init(s, s->max_step, nb_planes);
>>>>>> }
>>>>>> 
>>>>>> +static int config_props(AVFilterLink *inlink)
>>>>>> +{
>>>>>> +    FlipContext *s = inlink->dst->priv;
>>>>>> +    return ff_hflip_config_props(s, inlink);
>>>>>> +}
>>>>>> +
>>>>>> int ff_hflip_init(FlipContext *s, int step[4], int nb_planes)
>>>>>> {
>>>>>>     int i;
>>>>>> @@ -170,14 +175,10 @@ typedef struct ThreadData {
>>>>>>     AVFrame *in, *out;
>>>>>> } ThreadData;
>>>>>> 
>>>>>> -static int filter_slice(AVFilterContext *ctx, void *arg, int job, int
>>>>>> nb_jobs)
>>>>>> +int ff_hflip_filter_slice(FlipContext *s, AVFrame *in, AVFrame *out,
>>>>>> int job, int nb_jobs, int vflip)
>>>>>> {
>>>>>> -    FlipContext *s = ctx->priv;
>>>>>> -    ThreadData *td = arg;
>>>>>> -    AVFrame *in = td->in;
>>>>>> -    AVFrame *out = td->out;
>>>>>>     uint8_t *inrow, *outrow;
>>>>>> -    int i, plane, step;
>>>>>> +    int i, plane, step, outlinesize;
>>>>>> 
>>>>>>     for (plane = 0; plane < 4 && in->data[plane] &&
>>>>>> in->linesize[plane]; plane++) {
>>>>>>         const int width  = s->planewidth[plane];
>>>>>> @@ -187,19 +188,36 @@ static int filter_slice(AVFilterContext *ctx,
>>>>>> void *arg, int job, int nb_jobs)
>>>>>> 
>>>>>>         step = s->max_step[plane];
>>>>>> 
>>>>>> -        outrow = out->data[plane] + start * out->linesize[plane];
>>>>>> -        inrow  = in ->data[plane] + start * in->linesize[plane] +
>>>>>> (width - 1) * step;
>>>>>> +        if (vflip) {
>>>>>> +            outrow = out->data[plane] + (height - start - 1)*
>>>>>> out->linesize[plane];
>>>>>> +            outlinesize = -out->linesize[plane];
>>>>>> +        } else {
>>>>>> +            outrow = out->data[plane] + start * out->linesize[plane];
>>>>>> +            outlinesize = out->linesize[plane];
>>>>>> +        }
>>>>>> +
>>>>>> +        inrow = in->data[plane] + start * in->linesize[plane] +
>>>>>> (width - 1) * step;
>>>>>> +
>>>>>>         for (i = start; i < end; i++) {
>>>>>>             s->flip_line[plane](inrow, outrow, width);
>>>>>> 
>>>>>>             inrow  += in ->linesize[plane];
>>>>>> -            outrow += out->linesize[plane];
>>>>>> +            outrow += outlinesize;
>>>>>>         }
>>>>>>     }
>>>>>> 
>>>>>>     return 0;
>>>>>> }
>>>>>> 
>>>>>> +static int filter_slice(AVFilterContext *ctx, void *arg, int job, int
>>>>>> nb_jobs)
>>>>>> +{
>>>>>> +    FlipContext *s = ctx->priv;
>>>>>> +    ThreadData *td = arg;
>>>>>> +    AVFrame *in = td->in;
>>>>>> +    AVFrame *out = td->out;
>>>>>> +    return ff_hflip_filter_slice(s, in, out, job, nb_jobs, 0);
>>>>>> +}
>>>>>> +
>>>>>> static int filter_frame(AVFilterLink *inlink, AVFrame *in)
>>>>>> {
>>>>>>     AVFilterContext *ctx  = inlink->dst;
>>>>>> diff --git a/libavfilter/vf_transpose.c b/libavfilter/vf_transpose.c
>>>>>> index dd54947bd9..05dc04a89f 100644
>>>>>> --- a/libavfilter/vf_transpose.c
>>>>>> +++ b/libavfilter/vf_transpose.c
>>>>>> @@ -39,6 +39,7 @@
>>>>>> #include "internal.h"
>>>>>> #include "video.h"
>>>>>> #include "transpose.h"
>>>>>> +#include "hflip.h"
>>>>>> 
>>>>>> typedef struct TransVtable {
>>>>>>     void (*transpose_8x8)(uint8_t *src, ptrdiff_t src_linesize,
>>>>>> @@ -48,16 +49,22 @@ typedef struct TransVtable {
>>>>>>                             int w, int h);
>>>>>> } TransVtable;
>>>>>> 
>>>>>> -typedef struct TransContext {
>>>>>> -    const AVClass *class;
>>>>>> +typedef struct TransContextData {
>>>>>>     int hsub, vsub;
>>>>>>     int planes;
>>>>>>     int pixsteps[4];
>>>>>> +    TransVtable vtables[4];
>>>>>> +} TransContextData;
>>>>>> 
>>>>>> +typedef struct TransContext {
>>>>>> +    const AVClass *class;
>>>>>>     int passthrough;    ///< PassthroughType, landscape passthrough
>>>>>> mode enabled
>>>>>>     int dir;            ///< TransposeDir
>>>>>> -
>>>>>> -    TransVtable vtables[4];
>>>>>> +    int orientation;    ///< OrientationType
>>>>>> +    union {
>>>>>> +        TransContextData transpose;
>>>>>> +        FlipContext flip;
>>>>>> +    } ctx;
>>>>>> } TransContext;
>>>>>> 
>>>>>> static int query_formats(AVFilterContext *ctx)
>>>>>> @@ -207,14 +214,18 @@ static int config_props_output(AVFilterLink
>>>>>> *outlink)
>>>>>>         s->passthrough = TRANSPOSE_PT_TYPE_NONE;
>>>>>>     }
>>>>>> 
>>>>>> -    s->hsub = desc_in->log2_chroma_w;
>>>>>> -    s->vsub = desc_in->log2_chroma_h;
>>>>>> -    s->planes = av_pix_fmt_count_planes(outlink->format);
>>>>>> +    if (s->orientation == ORIENTATION_AUTO_FLIP ||
>>>>>> +       (s->orientation > ORIENTATION_NONE && s->orientation <=
>>>>>> ORIENTATION_VFLIP))
>>>>>> +        return ff_hflip_config_props(&(s->ctx.flip), inlink);
>>>>>> +
>>>>>> +    s->ctx.transpose.hsub = desc_in->log2_chroma_w;
>>>>>> +    s->ctx.transpose.vsub = desc_in->log2_chroma_h;
>>>>>> +    s->ctx.transpose.planes = av_pix_fmt_count_planes(outlink->format);
>>>>>> 
>>>>>>     av_assert0(desc_in->nb_components == desc_out->nb_components);
>>>>>> 
>>>>>> 
>>>>>> -    av_image_fill_max_pixsteps(s->pixsteps, NULL, desc_out);
>>>>>> +    av_image_fill_max_pixsteps(s->ctx.transpose.pixsteps, NULL,
>>>>>> desc_out);
>>>>>> 
>>>>>>     outlink->w = inlink->h;
>>>>>>     outlink->h = inlink->w;
>>>>>> @@ -226,10 +237,10 @@ static int config_props_output(AVFilterLink
>>>>>> *outlink)
>>>>>>         outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
>>>>>> 
>>>>>>     for (int i = 0; i < 4; i++) {
>>>>>> -        TransVtable *v = &s->vtables[i];
>>>>>> -        switch (s->pixsteps[i]) {
>>>>>> +        TransVtable *v = &s->ctx.transpose.vtables[i];
>>>>>> +        switch (s->ctx.transpose.pixsteps[i]) {
>>>>>>         case 1: v->transpose_block = transpose_block_8_c;
>>>>>> -                v->transpose_8x8   = transpose_8x8_8_c;  break;
>>>>>> +                v->transpose_8x8   = transpose_8x8_8_c; break;
>>>>>>         case 2: v->transpose_block = transpose_block_16_c;
>>>>>>                 v->transpose_8x8   = transpose_8x8_16_c; break;
>>>>>>         case 3: v->transpose_block = transpose_block_24_c;
>>>>>> @@ -262,21 +273,69 @@ static AVFrame *get_video_buffer(AVFilterLink
>>>>>> *inlink, int w, int h)
>>>>>> 
>>>>>> typedef struct ThreadData {
>>>>>>     AVFrame *in, *out;
>>>>>> +    int orientation;
>>>>>> } ThreadData;
>>>>>> 
>>>>>> -static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
>>>>>> -                        int nb_jobs)
>>>>>> +static int get_frame_orientation(AVFilterContext *ctx, const AVFrame
>>>>>> *frame)
>>>>>> +{
>>>>>> +    TransContext *s = ctx->priv;
>>>>>> +    AVDictionaryEntry *entry = NULL;
>>>>>> +    int orientation = ORIENTATION_NONE;
>>>>>> +
>>>>>> +    if (s->orientation >= ORIENTATION_NONE)
>>>>>> +        return s->orientation;
>>>>>> +
>>>>>> +    // read exif orientation data
>>>>>> +    entry = av_dict_get(frame->metadata, "Orientation", NULL, 0);
>>>>>> +    if (entry && entry->value)
>>>>>> +        orientation = atoi(entry->value);
>>>>>> +
>>>>>> +    // check frame orientation validity
>>>>>> +    if (orientation < ORIENTATION_NORMAL ||
>>>>>> +        orientation > ORIENTATION_ROTATE270CW) {
>>>>>> +        // frame orientation is invalid or empty, we have to guess one.
>>>>>> +        av_log(ctx, AV_LOG_WARNING, "Frame orientation is either empty
>>>>>> or invalid.\n");
>>>>>> +        return s->orientation == ORIENTATION_AUTO_FLIP ?
>>>>>> +               ORIENTATION_NORMAL : ORIENTATION_HFLIP_ROTATE270CW;
>>>>>> +    }
>>>>>> +
>>>>>> +    // check conflict between frame orientation and passed-in
>>>>>> orientation option
>>>>>> +    if (s->orientation == ORIENTATION_AUTO_FLIP &&
>>>>>> +        orientation > ORIENTATION_VFLIP) {
>>>>>> +        av_log(ctx, AV_LOG_WARNING,
>>>>>> +               "Found valid orientation value: %i, but auto-flip only
>>>>>> cover range [1, 4]. Set to default value 1(ORIENTATION_NONE).\n",
>>>>>> +               orientation);
>>>>>> +        // conflict happens, return the min value of orientation range
>>>>>> +        return ORIENTATION_NORMAL;
>>>>>> +    }
>>>>>> +
>>>>>> +    // check conflict between frame orientation and passed-in
>>>>>> orientation option
>>>>>> +    if (s->orientation == ORIENTATION_AUTO_TRANSPOSE &&
>>>>>> +        orientation <= ORIENTATION_VFLIP) {
>>>>>> +        av_log(ctx, AV_LOG_WARNING,
>>>>>> +               "Found valid orientation value: %i, but auto-transpose
>>>>>> only cover range [5, 8]. Set to default value
>>>>>> 5(ORIENTATION_HFLIP_ROTATE270CW).\n",
>>>>>> +               orientation);
>>>>>> +        // conflict happens, return the min value of orientation
>>>>>> range, same effect as dir=0
>>>>>> +        return ORIENTATION_HFLIP_ROTATE270CW;
>>>>>> +    }
>>>>>> +
>>>>>> +    return orientation;
>>>>>> +}
>>>>>> +
>>>>>> +static int transpose_filter_slice(AVFilterContext *ctx, void *arg, int
>>>>>> jobnr, int nb_jobs)
>>>>>> {
>>>>>>     TransContext *s = ctx->priv;
>>>>>>     ThreadData *td = arg;
>>>>>>     AVFrame *out = td->out;
>>>>>>     AVFrame *in = td->in;
>>>>>> +    TransContextData *c = &(s->ctx.transpose);
>>>>>> +    int orientation = td->orientation;
>>>>>>     int plane;
>>>>>> 
>>>>>> -    for (plane = 0; plane < s->planes; plane++) {
>>>>>> -        int hsub    = plane == 1 || plane == 2 ? s->hsub : 0;
>>>>>> -        int vsub    = plane == 1 || plane == 2 ? s->vsub : 0;
>>>>>> -        int pixstep = s->pixsteps[plane];
>>>>>> +    for (plane = 0; plane < c->planes; plane++) {
>>>>>> +        int hsub    = plane == 1 || plane == 2 ? c->hsub : 0;
>>>>>> +        int vsub    = plane == 1 || plane == 2 ? c->vsub : 0;
>>>>>> +        int pixstep = c->pixsteps[plane];
>>>>>>         int inh     = AV_CEIL_RSHIFT(in->height, vsub);
>>>>>>         int outw    = AV_CEIL_RSHIFT(out->width,  hsub);
>>>>>>         int outh    = AV_CEIL_RSHIFT(out->height, vsub);
>>>>>> @@ -285,19 +344,36 @@ static int filter_slice(AVFilterContext *ctx,
>>>>>> void *arg, int jobnr,
>>>>>>         uint8_t *dst, *src;
>>>>>>         int dstlinesize, srclinesize;
>>>>>>         int x, y;
>>>>>> -        TransVtable *v = &s->vtables[plane];
>>>>>> +        int dir = s->dir;
>>>>>> +        TransVtable *v = &c->vtables[plane];
>>>>>> 
>>>>>>         dstlinesize = out->linesize[plane];
>>>>>>         dst         = out->data[plane] + start * dstlinesize;
>>>>>>         src         = in->data[plane];
>>>>>>         srclinesize = in->linesize[plane];
>>>>>> +        switch (orientation) {
>>>>>> +            case ORIENTATION_HFLIP_ROTATE270CW:
>>>>>> +                dir = 0; // mirror horizontal and rotate 270 CW, same
>>>>>> as dir=cclock_flip
>>>>>> +                break;
>>>>>> +            case ORIENTATION_ROTATE90CW:
>>>>>> +                dir = 1; // rotate 90 CW, same as dir=clock
>>>>>> +                break;
>>>>>> +            case ORIENTATION_HFLIP_ROTATE90CW:
>>>>>> +                dir = 3; // mirror horizontal and rotate 90 CW, same
>>>>>> as dir=clock_flip
>>>>>> +                break;
>>>>>> +            case ORIENTATION_ROTATE270CW:
>>>>>> +                dir = 2; // rotate 270 CW, same as dir=cclock
>>>>>> +                break;
>>>>>> +            default:
>>>>>> +                break;
>>>>>> +        }
>>>>>> 
>>>>>> -        if (s->dir & 1) {
>>>>>> +        if (dir & 1) {
>>>>>>             src         += in->linesize[plane] * (inh - 1);
>>>>>>             srclinesize *= -1;
>>>>>>         }
>>>>>> 
>>>>>> -        if (s->dir & 2) {
>>>>>> +        if (dir & 2) {
>>>>>>             dst          = out->data[plane] + dstlinesize * (outh -
>>>>>> start - 1);
>>>>>>             dstlinesize *= -1;
>>>>>>         }
>>>>>> @@ -326,6 +402,22 @@ static int filter_slice(AVFilterContext *ctx, void
>>>>>> *arg, int jobnr,
>>>>>>     return 0;
>>>>>> }
>>>>>> 
>>>>>> +static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
>>>>>> +                        int nb_jobs)
>>>>>> +{
>>>>>> +    TransContext *s = ctx->priv;
>>>>>> +    ThreadData *td = arg;
>>>>>> +    AVFrame *out = td->out;
>>>>>> +    AVFrame *in = td->in;
>>>>>> +    int orientation = td->orientation;
>>>>>> +
>>>>>> +    if (orientation > ORIENTATION_NONE && orientation <=
>>>>>> ORIENTATION_VFLIP) {
>>>>>> +        return ff_hflip_filter_slice(&s->ctx.flip, in, out, jobnr,
>>>>>> nb_jobs,
>>>>>> +                                     orientation ==
>>>>>> ORIENTATION_ROTATE180);
>>>>>> +    }
>>>>>> +    return transpose_filter_slice(ctx, arg, jobnr, nb_jobs);
>>>>>> +}
>>>>>> +
>>>>>> static int filter_frame(AVFilterLink *inlink, AVFrame *in)
>>>>>> {
>>>>>>     AVFilterContext *ctx = inlink->dst;
>>>>>> @@ -333,10 +425,29 @@ static int filter_frame(AVFilterLink *inlink,
>>>>>> AVFrame *in)
>>>>>>     AVFilterLink *outlink = ctx->outputs[0];
>>>>>>     ThreadData td;
>>>>>>     AVFrame *out;
>>>>>> +    int orientation = ORIENTATION_NONE;
>>>>>> 
>>>>>>     if (s->passthrough)
>>>>>>         return ff_filter_frame(outlink, in);
>>>>>> 
>>>>>> +    if (s->orientation) {
>>>>>> +        orientation = get_frame_orientation(ctx, in);
>>>>>> +        if (orientation == ORIENTATION_NORMAL) // orientation
>>>>>> 'Normal', do nothing
>>>>>> +            return ff_filter_frame(outlink, in);
>>>>>> +
>>>>>> +        if (orientation == ORIENTATION_VFLIP) {
>>>>>> +            int i;
>>>>>> +            for (i = 0; i < 4; i ++) {
>>>>>> +                int height = s->ctx.flip.planeheight[i];
>>>>>> +                if (in->data[i]) {
>>>>>> +                    in->data[i] += (height - 1) * in->linesize[i];
>>>>>> +                    in->linesize[i] = -in->linesize[i];
>>>>>> +                }
>>>>>> +            }
>>>>>> +            return ff_filter_frame(outlink, in);
>>>>>> +        }
>>>>>> +    }
>>>>>> +
>>>>>>     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
>>>>>>     if (!out) {
>>>>>>         av_frame_free(&in);
>>>>>> @@ -346,12 +457,14 @@ static int filter_frame(AVFilterLink *inlink,
>>>>>> AVFrame *in)
>>>>>> 
>>>>>>     if (in->sample_aspect_ratio.num == 0) {
>>>>>>         out->sample_aspect_ratio = in->sample_aspect_ratio;
>>>>>> -    } else {
>>>>>> +    } else if (s->orientation == ORIENTATION_AUTO_TRANSPOSE ||
>>>>>> +               orientation > ORIENTATION_VFLIP) { // transpose
>>>>>>         out->sample_aspect_ratio.num = in->sample_aspect_ratio.den;
>>>>>>         out->sample_aspect_ratio.den = in->sample_aspect_ratio.num;
>>>>>>     }
>>>>>> 
>>>>>>     td.in = in, td.out = out;
>>>>>> +    td.orientation = orientation;
>>>>>>     ctx->internal->execute(ctx, filter_slice, &td, NULL,
>>>>>> FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
>>>>>>     av_frame_free(&in);
>>>>>>     return ff_filter_frame(outlink, out);
>>>>>> @@ -373,6 +486,19 @@ static const AVOption transpose_options[] = {
>>>>>>         { "portrait",  "preserve portrait geometry",   0,
>>>>>> AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT},  INT_MIN, INT_MAX,
>>>>>> FLAGS, "passthrough" },
>>>>>>         { "landscape", "preserve landscape geometry",  0,
>>>>>> AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX,
>>>>>> FLAGS, "passthrough" },
>>>>>> 
>>>>>> +    { "orientation", "apply frame exif orientation",
>>>>>> +      OFFSET(orientation), AV_OPT_TYPE_INT, {.i64 = ORIENTATION_NONE
>>>>>> },  ORIENTATION_AUTO_TRANSPOSE, ORIENTATION_ROTATE270CW, FLAGS,
>>>>>> "orientation" },
>>>>>> +        { "auto_transpose",    "apply transposition if frame's
>>>>>> orientation value is in range [5,8]",                     0,
>>>>>> AV_OPT_TYPE_CONST, {.i64=ORIENTATION_AUTO_TRANSPOSE},    INT_MIN, INT_MAX,
>>>>>> FLAGS, "orientation" },
>>>>>> +        { "auto_flip",         "apply flip if frame's orientation
>>>>>> value is in range [1,4]",                              0,
>>>>>> AV_OPT_TYPE_CONST, {.i64=ORIENTATION_AUTO_FLIP},         INT_MIN, INT_MAX,
>>>>>> FLAGS, "orientation" },
>>>>>> +        { "none",              "apply default transposition, same as
>>>>>> dir=cclock_flip",                                   0, AV_OPT_TYPE_CONST,
>>>>>> {.i64=ORIENTATION_NONE},              INT_MIN, INT_MAX, FLAGS,
>>>>>> "orientation" },
>>>>>> +        { "normal",            "exif orientation 'Normal', do not
>>>>>> apply any flip or transposition",                      0,
>>>>>> AV_OPT_TYPE_CONST, {.i64=ORIENTATION_NORMAL},            INT_MIN, INT_MAX,
>>>>>> FLAGS, "orientation" },
>>>>>> +        { "hflip",             "exif orientation 'Mirror horizontal',
>>>>>> apply horizontal flip",                            0, AV_OPT_TYPE_CONST,
>>>>>> {.i64=ORIENTATION_HFLIP},             INT_MIN, INT_MAX, FLAGS,
>>>>>> "orientation" },
>>>>>> +        { "rotate180",         "exif orientation 'Rotate 180', apply
>>>>>> rotation 180",                                      0, AV_OPT_TYPE_CONST,
>>>>>> {.i64=ORIENTATION_ROTATE180},         INT_MIN, INT_MAX, FLAGS,
>>>>>> "orientation" },
>>>>>> +        { "vflip",             "exif orientation 'Mirror vertical',
>>>>>> apply vertical flip",                                0, AV_OPT_TYPE_CONST,
>>>>>> {.i64=ORIENTATION_VFLIP},             INT_MIN, INT_MAX, FLAGS,
>>>>>> "orientation" },
>>>>>> +        { "hflip_rotate270cw", "exif orientation 'Mirror horizontal
>>>>>> and rotate 270 CW', same effect as dir=cclock_flip", 0, AV_OPT_TYPE_CONST,
>>>>>> {.i64=ORIENTATION_HFLIP_ROTATE270CW}, INT_MIN, INT_MAX, FLAGS,
>>>>>> "orientation" },
>>>>>> +        { "rotate90cw",        "exif orientation 'Rotate 90 CW', same
>>>>>> as dir=clock",                                     0, AV_OPT_TYPE_CONST,
>>>>>> {.i64=ORIENTATION_ROTATE90CW},        INT_MIN, INT_MAX, FLAGS,
>>>>>> "orientation" },
>>>>>> +        { "hflip_rotate90cw",  "exif orientation 'Mirror horizontal
>>>>>> and rotate 90 CW', same effect as dir=clock_flip",   0, AV_OPT_TYPE_CONST,
>>>>>> {.i64=ORIENTATION_HFLIP_ROTATE90CW},  INT_MIN, INT_MAX, FLAGS,
>>>>>> "orientation" },
>>>>>> +        { "rotate270",         "exif orientation 'Rotate 270 CW', same
>>>>>> as dir=cclock",                                   0, AV_OPT_TYPE_CONST,
>>>>>> {.i64=ORIENTATION_ROTATE270CW},       INT_MIN, INT_MAX, FLAGS,
>>>>>> "orientation" },
>>>>>>     { NULL }
>>>>>> };
>>>>>> 
>>>>>> --
>>>>>> 2.17.1
>>>>>> 
>>>>>> 
>>>>> Ping :)
>>>>> 
>>>> 
>>>> Ping x 2
>>>> 
>>> 
>>> Ping x3, could someone please help to take a look ? Really appreciate that.
>>> 
>>> 
>> Ping x4. This is to address exif auto-rotate, make fate test passed.
>> 
>>> -Jun
>>> 
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org
>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>> 
>> To unsubscribe, visit link above, or email
>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> 
> Any comments?

probe ok

fate passed

./tools/patcheck give me some message:


liuqideMBP:dash liuqi$ ../tools/patcheck ~/Downloads/v10-0001-lavf-vf_transpose-add-exif-orientation-support.patch
patCHeck 1e10.0
This tool is intended to help a human check/review patches. It is very far from
being free of false positives and negatives, and its output are just hints of what
may or may not be bad. When you use it and it misses something or detects
something wrong, fix it and send a patch to the ffmpeg-devel mailing list.
License: GPL, Author: Michael Niedermayer

trailing whitespace
/Users/liuqi/Downloads/v10-0001-lavf-vf_transpose-add-exif-orientation-support.patch:180:+    if (s->orientation == ORIENTATION_AUTO_FLIP ||
/Users/liuqi/Downloads/v10-0001-lavf-vf_transpose-add-exif-orientation-support.patch:227:+
/Users/liuqi/Downloads/v10-0001-lavf-vf_transpose-add-exif-orientation-support.patch:245:+        av_log(ctx, AV_LOG_WARNING,
/Users/liuqi/Downloads/v10-0001-lavf-vf_transpose-add-exif-orientation-support.patch:249:+        return ORIENTATION_NORMAL;
/Users/liuqi/Downloads/v10-0001-lavf-vf_transpose-add-exif-orientation-support.patch:253:+    if (s->orientation == ORIENTATION_AUTO_TRANSPOSE &&
/Users/liuqi/Downloads/v10-0001-lavf-vf_transpose-add-exif-orientation-support.patch:299:+            case ORIENTATION_HFLIP_ROTATE270CW:
/Users/liuqi/Downloads/v10-0001-lavf-vf_transpose-add-exif-orientation-support.patch:384:+    } else if (s->orientation == ORIENTATION_AUTO_TRANSPOSE ||
egrep: empty (sub)expression
egrep: empty (sub)expression
egrep: empty (sub)expression
egrep: empty (sub)expression
egrep: empty (sub)expression
egrep: empty (sub)expression
egrep: empty (sub)expression
egrep: empty (sub)expression
egrep: empty (sub)expression
egrep: empty (sub)expression
egrep: empty (sub)expression
xargs: illegal option -- d
usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements]] [-J replstr]
             [-L number] [-n number [-x]] [-P maxprocs] [-s size]
             [utility [argument ...]]

Missing changelog entry (ignore if minor change)
egrep: empty (sub)expression
liuqideMBP:dash liuqi$
liuqideMBP:dash liuqi$
liuqideMBP:dash liuqi$ ../tools/patcheck ~/Downloads/v10-0002-fftools-ffmpeg-add-exif-orientation-support-per-.patch
patCHeck 1e10.0
This tool is intended to help a human check/review patches. It is very far from
being free of false positives and negatives, and its output are just hints of what
may or may not be bad. When you use it and it misses something or detects
something wrong, fix it and send a patch to the ffmpeg-devel mailing list.
License: GPL, Author: Michael Niedermayer

trailing whitespace
/Users/liuqi/Downloads/v10-0002-fftools-ffmpeg-add-exif-orientation-support-per-.patch:107:+            if (ifilter->orientation == ORIENTATION_AUTO_FLIP) {
egrep: empty (sub)expression
egrep: empty (sub)expression

Missing context in av_log
/Users/liuqi/Downloads/v10-0002-fftools-ffmpeg-add-exif-orientation-support-per-.patch:85:+            av_log(NULL, AV_LOG_WARNING,
egrep: empty (sub)expression
egrep: empty (sub)expression
egrep: empty (sub)expression
egrep: empty (sub)expression
egrep: empty (sub)expression
egrep: empty (sub)expression
egrep: empty (sub)expression
egrep: empty (sub)expression
egrep: empty (sub)expression
xargs: illegal option -- d
usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements]] [-J replstr]
             [-L number] [-n number [-x]] [-P maxprocs] [-s size]
             [utility [argument ...]]

Missing changelog entry (ignore if minor change)
egrep: empty (sub)expression
liuqideMBP:dash liuqi$



> 
> 
> Thanks
> Steven Liu
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

Thanks
Steven
Jun Li June 29, 2019, 10:13 p.m. UTC | #7
On Sat, Jun 29, 2019 at 12:07 AM Steven Liu <lq@chinaffmpeg.org> wrote:

>
>
> > 在 2019年6月26日,19:32,Steven Liu <lingjiujianke@gmail.com> 写道:
> >
> > Jun Li <junli1026@gmail.com> 于2019年6月25日周二 上午9:26写道:
> >>
> >> On Mon, Jun 17, 2019 at 5:31 PM Jun Li <junli1026@gmail.com> wrote:
> >>
> >>>
> >>>
> >>> On Sat, Jun 15, 2019 at 7:09 PM Jun Li <junli1026@gmail.com> wrote:
> >>>
> >>>>
> >>>>
> >>>> On Tue, Jun 11, 2019 at 7:05 PM Jun Li <junli1026@gmail.com> wrote:
> >>>>
> >>>>>
> >>>>> On Sun, Jun 9, 2019 at 2:28 PM Jun Li <junli1026@gmail.com> wrote:
> >>>>>
> >>>>>> Add exif orientation support and expose an option.
> >>>>>> ---
> >>>>>> libavfilter/hflip.h        |   2 +
> >>>>>> libavfilter/transpose.h    |  14 ++++
> >>>>>> libavfilter/vf_hflip.c     |  40 ++++++---
> >>>>>> libavfilter/vf_transpose.c | 168
> ++++++++++++++++++++++++++++++++-----
> >>>>>> 4 files changed, 192 insertions(+), 32 deletions(-)
> >>>>>>
> >>>>>> diff --git a/libavfilter/hflip.h b/libavfilter/hflip.h
> >>>>>> index 204090dbb4..4e89bae3fc 100644
> >>>>>> --- a/libavfilter/hflip.h
> >>>>>> +++ b/libavfilter/hflip.h
> >>>>>> @@ -35,5 +35,7 @@ typedef struct FlipContext {
> >>>>>>
> >>>>>> int ff_hflip_init(FlipContext *s, int step[4], int nb_planes);
> >>>>>> void ff_hflip_init_x86(FlipContext *s, int step[4], int nb_planes);
> >>>>>> +int ff_hflip_config_props(FlipContext* s, AVFilterLink *inlink);
> >>>>>> +int ff_hflip_filter_slice(FlipContext *s, AVFrame *in, AVFrame
> *out,
> >>>>>> int job, int nb_jobs, int vlifp);
> >>>>>>
> >>>>>> #endif /* AVFILTER_HFLIP_H */
> >>>>>> diff --git a/libavfilter/transpose.h b/libavfilter/transpose.h
> >>>>>> index aa262b9487..5da08bddc0 100644
> >>>>>> --- a/libavfilter/transpose.h
> >>>>>> +++ b/libavfilter/transpose.h
> >>>>>> @@ -34,4 +34,18 @@ enum TransposeDir {
> >>>>>>     TRANSPOSE_VFLIP,
> >>>>>> };
> >>>>>>
> >>>>>> +enum OrientationType {
> >>>>>> +    ORIENTATION_AUTO_TRANSPOSE = -2,
> >>>>>> +    ORIENTATION_AUTO_FLIP = -1,
> >>>>>> +    ORIENTATION_NONE = 0,
> >>>>>> +    ORIENTATION_NORMAL,
> >>>>>> +    ORIENTATION_HFLIP,
> >>>>>> +    ORIENTATION_ROTATE180,
> >>>>>> +    ORIENTATION_VFLIP,
> >>>>>> +    ORIENTATION_HFLIP_ROTATE270CW,
> >>>>>> +    ORIENTATION_ROTATE90CW,
> >>>>>> +    ORIENTATION_HFLIP_ROTATE90CW,
> >>>>>> +    ORIENTATION_ROTATE270CW
> >>>>>> +};
> >>>>>> +
> >>>>>> #endif
> >>>>>> diff --git a/libavfilter/vf_hflip.c b/libavfilter/vf_hflip.c
> >>>>>> index b77afc77fc..d24ca5c2e7 100644
> >>>>>> --- a/libavfilter/vf_hflip.c
> >>>>>> +++ b/libavfilter/vf_hflip.c
> >>>>>> @@ -125,9 +125,8 @@ static void hflip_qword_c(const uint8_t *ssrc,
> >>>>>> uint8_t *ddst, int w)
> >>>>>>         dst[j] = src[-j];
> >>>>>> }
> >>>>>>
> >>>>>> -static int config_props(AVFilterLink *inlink)
> >>>>>> +int ff_hflip_config_props(FlipContext* s, AVFilterLink *inlink)
> >>>>>> {
> >>>>>> -    FlipContext *s = inlink->dst->priv;
> >>>>>>     const AVPixFmtDescriptor *pix_desc =
> >>>>>> av_pix_fmt_desc_get(inlink->format);
> >>>>>>     const int hsub = pix_desc->log2_chroma_w;
> >>>>>>     const int vsub = pix_desc->log2_chroma_h;
> >>>>>> @@ -144,6 +143,12 @@ static int config_props(AVFilterLink *inlink)
> >>>>>>     return ff_hflip_init(s, s->max_step, nb_planes);
> >>>>>> }
> >>>>>>
> >>>>>> +static int config_props(AVFilterLink *inlink)
> >>>>>> +{
> >>>>>> +    FlipContext *s = inlink->dst->priv;
> >>>>>> +    return ff_hflip_config_props(s, inlink);
> >>>>>> +}
> >>>>>> +
> >>>>>> int ff_hflip_init(FlipContext *s, int step[4], int nb_planes)
> >>>>>> {
> >>>>>>     int i;
> >>>>>> @@ -170,14 +175,10 @@ typedef struct ThreadData {
> >>>>>>     AVFrame *in, *out;
> >>>>>> } ThreadData;
> >>>>>>
> >>>>>> -static int filter_slice(AVFilterContext *ctx, void *arg, int job,
> int
> >>>>>> nb_jobs)
> >>>>>> +int ff_hflip_filter_slice(FlipContext *s, AVFrame *in, AVFrame
> *out,
> >>>>>> int job, int nb_jobs, int vflip)
> >>>>>> {
> >>>>>> -    FlipContext *s = ctx->priv;
> >>>>>> -    ThreadData *td = arg;
> >>>>>> -    AVFrame *in = td->in;
> >>>>>> -    AVFrame *out = td->out;
> >>>>>>     uint8_t *inrow, *outrow;
> >>>>>> -    int i, plane, step;
> >>>>>> +    int i, plane, step, outlinesize;
> >>>>>>
> >>>>>>     for (plane = 0; plane < 4 && in->data[plane] &&
> >>>>>> in->linesize[plane]; plane++) {
> >>>>>>         const int width  = s->planewidth[plane];
> >>>>>> @@ -187,19 +188,36 @@ static int filter_slice(AVFilterContext *ctx,
> >>>>>> void *arg, int job, int nb_jobs)
> >>>>>>
> >>>>>>         step = s->max_step[plane];
> >>>>>>
> >>>>>> -        outrow = out->data[plane] + start * out->linesize[plane];
> >>>>>> -        inrow  = in ->data[plane] + start * in->linesize[plane] +
> >>>>>> (width - 1) * step;
> >>>>>> +        if (vflip) {
> >>>>>> +            outrow = out->data[plane] + (height - start - 1)*
> >>>>>> out->linesize[plane];
> >>>>>> +            outlinesize = -out->linesize[plane];
> >>>>>> +        } else {
> >>>>>> +            outrow = out->data[plane] + start *
> out->linesize[plane];
> >>>>>> +            outlinesize = out->linesize[plane];
> >>>>>> +        }
> >>>>>> +
> >>>>>> +        inrow = in->data[plane] + start * in->linesize[plane] +
> >>>>>> (width - 1) * step;
> >>>>>> +
> >>>>>>         for (i = start; i < end; i++) {
> >>>>>>             s->flip_line[plane](inrow, outrow, width);
> >>>>>>
> >>>>>>             inrow  += in ->linesize[plane];
> >>>>>> -            outrow += out->linesize[plane];
> >>>>>> +            outrow += outlinesize;
> >>>>>>         }
> >>>>>>     }
> >>>>>>
> >>>>>>     return 0;
> >>>>>> }
> >>>>>>
> >>>>>> +static int filter_slice(AVFilterContext *ctx, void *arg, int job,
> int
> >>>>>> nb_jobs)
> >>>>>> +{
> >>>>>> +    FlipContext *s = ctx->priv;
> >>>>>> +    ThreadData *td = arg;
> >>>>>> +    AVFrame *in = td->in;
> >>>>>> +    AVFrame *out = td->out;
> >>>>>> +    return ff_hflip_filter_slice(s, in, out, job, nb_jobs, 0);
> >>>>>> +}
> >>>>>> +
> >>>>>> static int filter_frame(AVFilterLink *inlink, AVFrame *in)
> >>>>>> {
> >>>>>>     AVFilterContext *ctx  = inlink->dst;
> >>>>>> diff --git a/libavfilter/vf_transpose.c b/libavfilter/vf_transpose.c
> >>>>>> index dd54947bd9..05dc04a89f 100644
> >>>>>> --- a/libavfilter/vf_transpose.c
> >>>>>> +++ b/libavfilter/vf_transpose.c
> >>>>>> @@ -39,6 +39,7 @@
> >>>>>> #include "internal.h"
> >>>>>> #include "video.h"
> >>>>>> #include "transpose.h"
> >>>>>> +#include "hflip.h"
> >>>>>>
> >>>>>> typedef struct TransVtable {
> >>>>>>     void (*transpose_8x8)(uint8_t *src, ptrdiff_t src_linesize,
> >>>>>> @@ -48,16 +49,22 @@ typedef struct TransVtable {
> >>>>>>                             int w, int h);
> >>>>>> } TransVtable;
> >>>>>>
> >>>>>> -typedef struct TransContext {
> >>>>>> -    const AVClass *class;
> >>>>>> +typedef struct TransContextData {
> >>>>>>     int hsub, vsub;
> >>>>>>     int planes;
> >>>>>>     int pixsteps[4];
> >>>>>> +    TransVtable vtables[4];
> >>>>>> +} TransContextData;
> >>>>>>
> >>>>>> +typedef struct TransContext {
> >>>>>> +    const AVClass *class;
> >>>>>>     int passthrough;    ///< PassthroughType, landscape passthrough
> >>>>>> mode enabled
> >>>>>>     int dir;            ///< TransposeDir
> >>>>>> -
> >>>>>> -    TransVtable vtables[4];
> >>>>>> +    int orientation;    ///< OrientationType
> >>>>>> +    union {
> >>>>>> +        TransContextData transpose;
> >>>>>> +        FlipContext flip;
> >>>>>> +    } ctx;
> >>>>>> } TransContext;
> >>>>>>
> >>>>>> static int query_formats(AVFilterContext *ctx)
> >>>>>> @@ -207,14 +214,18 @@ static int config_props_output(AVFilterLink
> >>>>>> *outlink)
> >>>>>>         s->passthrough = TRANSPOSE_PT_TYPE_NONE;
> >>>>>>     }
> >>>>>>
> >>>>>> -    s->hsub = desc_in->log2_chroma_w;
> >>>>>> -    s->vsub = desc_in->log2_chroma_h;
> >>>>>> -    s->planes = av_pix_fmt_count_planes(outlink->format);
> >>>>>> +    if (s->orientation == ORIENTATION_AUTO_FLIP ||
> >>>>>> +       (s->orientation > ORIENTATION_NONE && s->orientation <=
> >>>>>> ORIENTATION_VFLIP))
> >>>>>> +        return ff_hflip_config_props(&(s->ctx.flip), inlink);
> >>>>>> +
> >>>>>> +    s->ctx.transpose.hsub = desc_in->log2_chroma_w;
> >>>>>> +    s->ctx.transpose.vsub = desc_in->log2_chroma_h;
> >>>>>> +    s->ctx.transpose.planes =
> av_pix_fmt_count_planes(outlink->format);
> >>>>>>
> >>>>>>     av_assert0(desc_in->nb_components == desc_out->nb_components);
> >>>>>>
> >>>>>>
> >>>>>> -    av_image_fill_max_pixsteps(s->pixsteps, NULL, desc_out);
> >>>>>> +    av_image_fill_max_pixsteps(s->ctx.transpose.pixsteps, NULL,
> >>>>>> desc_out);
> >>>>>>
> >>>>>>     outlink->w = inlink->h;
> >>>>>>     outlink->h = inlink->w;
> >>>>>> @@ -226,10 +237,10 @@ static int config_props_output(AVFilterLink
> >>>>>> *outlink)
> >>>>>>         outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
> >>>>>>
> >>>>>>     for (int i = 0; i < 4; i++) {
> >>>>>> -        TransVtable *v = &s->vtables[i];
> >>>>>> -        switch (s->pixsteps[i]) {
> >>>>>> +        TransVtable *v = &s->ctx.transpose.vtables[i];
> >>>>>> +        switch (s->ctx.transpose.pixsteps[i]) {
> >>>>>>         case 1: v->transpose_block = transpose_block_8_c;
> >>>>>> -                v->transpose_8x8   = transpose_8x8_8_c;  break;
> >>>>>> +                v->transpose_8x8   = transpose_8x8_8_c; break;
> >>>>>>         case 2: v->transpose_block = transpose_block_16_c;
> >>>>>>                 v->transpose_8x8   = transpose_8x8_16_c; break;
> >>>>>>         case 3: v->transpose_block = transpose_block_24_c;
> >>>>>> @@ -262,21 +273,69 @@ static AVFrame *get_video_buffer(AVFilterLink
> >>>>>> *inlink, int w, int h)
> >>>>>>
> >>>>>> typedef struct ThreadData {
> >>>>>>     AVFrame *in, *out;
> >>>>>> +    int orientation;
> >>>>>> } ThreadData;
> >>>>>>
> >>>>>> -static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
> >>>>>> -                        int nb_jobs)
> >>>>>> +static int get_frame_orientation(AVFilterContext *ctx, const
> AVFrame
> >>>>>> *frame)
> >>>>>> +{
> >>>>>> +    TransContext *s = ctx->priv;
> >>>>>> +    AVDictionaryEntry *entry = NULL;
> >>>>>> +    int orientation = ORIENTATION_NONE;
> >>>>>> +
> >>>>>> +    if (s->orientation >= ORIENTATION_NONE)
> >>>>>> +        return s->orientation;
> >>>>>> +
> >>>>>> +    // read exif orientation data
> >>>>>> +    entry = av_dict_get(frame->metadata, "Orientation", NULL, 0);
> >>>>>> +    if (entry && entry->value)
> >>>>>> +        orientation = atoi(entry->value);
> >>>>>> +
> >>>>>> +    // check frame orientation validity
> >>>>>> +    if (orientation < ORIENTATION_NORMAL ||
> >>>>>> +        orientation > ORIENTATION_ROTATE270CW) {
> >>>>>> +        // frame orientation is invalid or empty, we have to guess
> one.
> >>>>>> +        av_log(ctx, AV_LOG_WARNING, "Frame orientation is either
> empty
> >>>>>> or invalid.\n");
> >>>>>> +        return s->orientation == ORIENTATION_AUTO_FLIP ?
> >>>>>> +               ORIENTATION_NORMAL : ORIENTATION_HFLIP_ROTATE270CW;
> >>>>>> +    }
> >>>>>> +
> >>>>>> +    // check conflict between frame orientation and passed-in
> >>>>>> orientation option
> >>>>>> +    if (s->orientation == ORIENTATION_AUTO_FLIP &&
> >>>>>> +        orientation > ORIENTATION_VFLIP) {
> >>>>>> +        av_log(ctx, AV_LOG_WARNING,
> >>>>>> +               "Found valid orientation value: %i, but auto-flip
> only
> >>>>>> cover range [1, 4]. Set to default value 1(ORIENTATION_NONE).\n",
> >>>>>> +               orientation);
> >>>>>> +        // conflict happens, return the min value of orientation
> range
> >>>>>> +        return ORIENTATION_NORMAL;
> >>>>>> +    }
> >>>>>> +
> >>>>>> +    // check conflict between frame orientation and passed-in
> >>>>>> orientation option
> >>>>>> +    if (s->orientation == ORIENTATION_AUTO_TRANSPOSE &&
> >>>>>> +        orientation <= ORIENTATION_VFLIP) {
> >>>>>> +        av_log(ctx, AV_LOG_WARNING,
> >>>>>> +               "Found valid orientation value: %i, but
> auto-transpose
> >>>>>> only cover range [5, 8]. Set to default value
> >>>>>> 5(ORIENTATION_HFLIP_ROTATE270CW).\n",
> >>>>>> +               orientation);
> >>>>>> +        // conflict happens, return the min value of orientation
> >>>>>> range, same effect as dir=0
> >>>>>> +        return ORIENTATION_HFLIP_ROTATE270CW;
> >>>>>> +    }
> >>>>>> +
> >>>>>> +    return orientation;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static int transpose_filter_slice(AVFilterContext *ctx, void *arg,
> int
> >>>>>> jobnr, int nb_jobs)
> >>>>>> {
> >>>>>>     TransContext *s = ctx->priv;
> >>>>>>     ThreadData *td = arg;
> >>>>>>     AVFrame *out = td->out;
> >>>>>>     AVFrame *in = td->in;
> >>>>>> +    TransContextData *c = &(s->ctx.transpose);
> >>>>>> +    int orientation = td->orientation;
> >>>>>>     int plane;
> >>>>>>
> >>>>>> -    for (plane = 0; plane < s->planes; plane++) {
> >>>>>> -        int hsub    = plane == 1 || plane == 2 ? s->hsub : 0;
> >>>>>> -        int vsub    = plane == 1 || plane == 2 ? s->vsub : 0;
> >>>>>> -        int pixstep = s->pixsteps[plane];
> >>>>>> +    for (plane = 0; plane < c->planes; plane++) {
> >>>>>> +        int hsub    = plane == 1 || plane == 2 ? c->hsub : 0;
> >>>>>> +        int vsub    = plane == 1 || plane == 2 ? c->vsub : 0;
> >>>>>> +        int pixstep = c->pixsteps[plane];
> >>>>>>         int inh     = AV_CEIL_RSHIFT(in->height, vsub);
> >>>>>>         int outw    = AV_CEIL_RSHIFT(out->width,  hsub);
> >>>>>>         int outh    = AV_CEIL_RSHIFT(out->height, vsub);
> >>>>>> @@ -285,19 +344,36 @@ static int filter_slice(AVFilterContext *ctx,
> >>>>>> void *arg, int jobnr,
> >>>>>>         uint8_t *dst, *src;
> >>>>>>         int dstlinesize, srclinesize;
> >>>>>>         int x, y;
> >>>>>> -        TransVtable *v = &s->vtables[plane];
> >>>>>> +        int dir = s->dir;
> >>>>>> +        TransVtable *v = &c->vtables[plane];
> >>>>>>
> >>>>>>         dstlinesize = out->linesize[plane];
> >>>>>>         dst         = out->data[plane] + start * dstlinesize;
> >>>>>>         src         = in->data[plane];
> >>>>>>         srclinesize = in->linesize[plane];
> >>>>>> +        switch (orientation) {
> >>>>>> +            case ORIENTATION_HFLIP_ROTATE270CW:
> >>>>>> +                dir = 0; // mirror horizontal and rotate 270 CW,
> same
> >>>>>> as dir=cclock_flip
> >>>>>> +                break;
> >>>>>> +            case ORIENTATION_ROTATE90CW:
> >>>>>> +                dir = 1; // rotate 90 CW, same as dir=clock
> >>>>>> +                break;
> >>>>>> +            case ORIENTATION_HFLIP_ROTATE90CW:
> >>>>>> +                dir = 3; // mirror horizontal and rotate 90 CW,
> same
> >>>>>> as dir=clock_flip
> >>>>>> +                break;
> >>>>>> +            case ORIENTATION_ROTATE270CW:
> >>>>>> +                dir = 2; // rotate 270 CW, same as dir=cclock
> >>>>>> +                break;
> >>>>>> +            default:
> >>>>>> +                break;
> >>>>>> +        }
> >>>>>>
> >>>>>> -        if (s->dir & 1) {
> >>>>>> +        if (dir & 1) {
> >>>>>>             src         += in->linesize[plane] * (inh - 1);
> >>>>>>             srclinesize *= -1;
> >>>>>>         }
> >>>>>>
> >>>>>> -        if (s->dir & 2) {
> >>>>>> +        if (dir & 2) {
> >>>>>>             dst          = out->data[plane] + dstlinesize * (outh -
> >>>>>> start - 1);
> >>>>>>             dstlinesize *= -1;
> >>>>>>         }
> >>>>>> @@ -326,6 +402,22 @@ static int filter_slice(AVFilterContext *ctx,
> void
> >>>>>> *arg, int jobnr,
> >>>>>>     return 0;
> >>>>>> }
> >>>>>>
> >>>>>> +static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
> >>>>>> +                        int nb_jobs)
> >>>>>> +{
> >>>>>> +    TransContext *s = ctx->priv;
> >>>>>> +    ThreadData *td = arg;
> >>>>>> +    AVFrame *out = td->out;
> >>>>>> +    AVFrame *in = td->in;
> >>>>>> +    int orientation = td->orientation;
> >>>>>> +
> >>>>>> +    if (orientation > ORIENTATION_NONE && orientation <=
> >>>>>> ORIENTATION_VFLIP) {
> >>>>>> +        return ff_hflip_filter_slice(&s->ctx.flip, in, out, jobnr,
> >>>>>> nb_jobs,
> >>>>>> +                                     orientation ==
> >>>>>> ORIENTATION_ROTATE180);
> >>>>>> +    }
> >>>>>> +    return transpose_filter_slice(ctx, arg, jobnr, nb_jobs);
> >>>>>> +}
> >>>>>> +
> >>>>>> static int filter_frame(AVFilterLink *inlink, AVFrame *in)
> >>>>>> {
> >>>>>>     AVFilterContext *ctx = inlink->dst;
> >>>>>> @@ -333,10 +425,29 @@ static int filter_frame(AVFilterLink *inlink,
> >>>>>> AVFrame *in)
> >>>>>>     AVFilterLink *outlink = ctx->outputs[0];
> >>>>>>     ThreadData td;
> >>>>>>     AVFrame *out;
> >>>>>> +    int orientation = ORIENTATION_NONE;
> >>>>>>
> >>>>>>     if (s->passthrough)
> >>>>>>         return ff_filter_frame(outlink, in);
> >>>>>>
> >>>>>> +    if (s->orientation) {
> >>>>>> +        orientation = get_frame_orientation(ctx, in);
> >>>>>> +        if (orientation == ORIENTATION_NORMAL) // orientation
> >>>>>> 'Normal', do nothing
> >>>>>> +            return ff_filter_frame(outlink, in);
> >>>>>> +
> >>>>>> +        if (orientation == ORIENTATION_VFLIP) {
> >>>>>> +            int i;
> >>>>>> +            for (i = 0; i < 4; i ++) {
> >>>>>> +                int height = s->ctx.flip.planeheight[i];
> >>>>>> +                if (in->data[i]) {
> >>>>>> +                    in->data[i] += (height - 1) * in->linesize[i];
> >>>>>> +                    in->linesize[i] = -in->linesize[i];
> >>>>>> +                }
> >>>>>> +            }
> >>>>>> +            return ff_filter_frame(outlink, in);
> >>>>>> +        }
> >>>>>> +    }
> >>>>>> +
> >>>>>>     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
> >>>>>>     if (!out) {
> >>>>>>         av_frame_free(&in);
> >>>>>> @@ -346,12 +457,14 @@ static int filter_frame(AVFilterLink *inlink,
> >>>>>> AVFrame *in)
> >>>>>>
> >>>>>>     if (in->sample_aspect_ratio.num == 0) {
> >>>>>>         out->sample_aspect_ratio = in->sample_aspect_ratio;
> >>>>>> -    } else {
> >>>>>> +    } else if (s->orientation == ORIENTATION_AUTO_TRANSPOSE ||
> >>>>>> +               orientation > ORIENTATION_VFLIP) { // transpose
> >>>>>>         out->sample_aspect_ratio.num = in->sample_aspect_ratio.den;
> >>>>>>         out->sample_aspect_ratio.den = in->sample_aspect_ratio.num;
> >>>>>>     }
> >>>>>>
> >>>>>>     td.in = in, td.out = out;
> >>>>>> +    td.orientation = orientation;
> >>>>>>     ctx->internal->execute(ctx, filter_slice, &td, NULL,
> >>>>>> FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
> >>>>>>     av_frame_free(&in);
> >>>>>>     return ff_filter_frame(outlink, out);
> >>>>>> @@ -373,6 +486,19 @@ static const AVOption transpose_options[] = {
> >>>>>>         { "portrait",  "preserve portrait geometry",   0,
> >>>>>> AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT},  INT_MIN,
> INT_MAX,
> >>>>>> FLAGS, "passthrough" },
> >>>>>>         { "landscape", "preserve landscape geometry",  0,
> >>>>>> AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN,
> INT_MAX,
> >>>>>> FLAGS, "passthrough" },
> >>>>>>
> >>>>>> +    { "orientation", "apply frame exif orientation",
> >>>>>> +      OFFSET(orientation), AV_OPT_TYPE_INT, {.i64 =
> ORIENTATION_NONE
> >>>>>> },  ORIENTATION_AUTO_TRANSPOSE, ORIENTATION_ROTATE270CW, FLAGS,
> >>>>>> "orientation" },
> >>>>>> +        { "auto_transpose",    "apply transposition if frame's
> >>>>>> orientation value is in range [5,8]",                     0,
> >>>>>> AV_OPT_TYPE_CONST, {.i64=ORIENTATION_AUTO_TRANSPOSE},    INT_MIN,
> INT_MAX,
> >>>>>> FLAGS, "orientation" },
> >>>>>> +        { "auto_flip",         "apply flip if frame's orientation
> >>>>>> value is in range [1,4]",                              0,
> >>>>>> AV_OPT_TYPE_CONST, {.i64=ORIENTATION_AUTO_FLIP},         INT_MIN,
> INT_MAX,
> >>>>>> FLAGS, "orientation" },
> >>>>>> +        { "none",              "apply default transposition, same
> as
> >>>>>> dir=cclock_flip",                                   0,
> AV_OPT_TYPE_CONST,
> >>>>>> {.i64=ORIENTATION_NONE},              INT_MIN, INT_MAX, FLAGS,
> >>>>>> "orientation" },
> >>>>>> +        { "normal",            "exif orientation 'Normal', do not
> >>>>>> apply any flip or transposition",                      0,
> >>>>>> AV_OPT_TYPE_CONST, {.i64=ORIENTATION_NORMAL},            INT_MIN,
> INT_MAX,
> >>>>>> FLAGS, "orientation" },
> >>>>>> +        { "hflip",             "exif orientation 'Mirror
> horizontal',
> >>>>>> apply horizontal flip",                            0,
> AV_OPT_TYPE_CONST,
> >>>>>> {.i64=ORIENTATION_HFLIP},             INT_MIN, INT_MAX, FLAGS,
> >>>>>> "orientation" },
> >>>>>> +        { "rotate180",         "exif orientation 'Rotate 180',
> apply
> >>>>>> rotation 180",                                      0,
> AV_OPT_TYPE_CONST,
> >>>>>> {.i64=ORIENTATION_ROTATE180},         INT_MIN, INT_MAX, FLAGS,
> >>>>>> "orientation" },
> >>>>>> +        { "vflip",             "exif orientation 'Mirror vertical',
> >>>>>> apply vertical flip",                                0,
> AV_OPT_TYPE_CONST,
> >>>>>> {.i64=ORIENTATION_VFLIP},             INT_MIN, INT_MAX, FLAGS,
> >>>>>> "orientation" },
> >>>>>> +        { "hflip_rotate270cw", "exif orientation 'Mirror horizontal
> >>>>>> and rotate 270 CW', same effect as dir=cclock_flip", 0,
> AV_OPT_TYPE_CONST,
> >>>>>> {.i64=ORIENTATION_HFLIP_ROTATE270CW}, INT_MIN, INT_MAX, FLAGS,
> >>>>>> "orientation" },
> >>>>>> +        { "rotate90cw",        "exif orientation 'Rotate 90 CW',
> same
> >>>>>> as dir=clock",                                     0,
> AV_OPT_TYPE_CONST,
> >>>>>> {.i64=ORIENTATION_ROTATE90CW},        INT_MIN, INT_MAX, FLAGS,
> >>>>>> "orientation" },
> >>>>>> +        { "hflip_rotate90cw",  "exif orientation 'Mirror horizontal
> >>>>>> and rotate 90 CW', same effect as dir=clock_flip",   0,
> AV_OPT_TYPE_CONST,
> >>>>>> {.i64=ORIENTATION_HFLIP_ROTATE90CW},  INT_MIN, INT_MAX, FLAGS,
> >>>>>> "orientation" },
> >>>>>> +        { "rotate270",         "exif orientation 'Rotate 270 CW',
> same
> >>>>>> as dir=cclock",                                   0,
> AV_OPT_TYPE_CONST,
> >>>>>> {.i64=ORIENTATION_ROTATE270CW},       INT_MIN, INT_MAX, FLAGS,
> >>>>>> "orientation" },
> >>>>>>     { NULL }
> >>>>>> };
> >>>>>>
> >>>>>> --
> >>>>>> 2.17.1
> >>>>>>
> >>>>>>
> >>>>> Ping :)
> >>>>>
> >>>>
> >>>> Ping x 2
> >>>>
> >>>
> >>> Ping x3, could someone please help to take a look ? Really appreciate
> that.
> >>>
> >>>
> >> Ping x4. This is to address exif auto-rotate, make fate test passed.
> >>
> >>> -Jun
> >>>
> >> _______________________________________________
> >> ffmpeg-devel mailing list
> >> ffmpeg-devel@ffmpeg.org
> >> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >>
> >> To unsubscribe, visit link above, or email
> >> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> >
> > Any comments?
>
> probe ok
>
> fate passed
>
> ./tools/patcheck give me some message:
>
>
> liuqideMBP:dash liuqi$ ../tools/patcheck
> ~/Downloads/v10-0001-lavf-vf_transpose-add-exif-orientation-support.patch
> patCHeck 1e10.0
> This tool is intended to help a human check/review patches. It is very far
> from
> being free of false positives and negatives, and its output are just hints
> of what
> may or may not be bad. When you use it and it misses something or detects
> something wrong, fix it and send a patch to the ffmpeg-devel mailing list.
> License: GPL, Author: Michael Niedermayer
>
> trailing whitespace
> /Users/liuqi/Downloads/v10-0001-lavf-vf_transpose-add-exif-orientation-support.patch:180:+
>   if (s->orientation == ORIENTATION_AUTO_FLIP ||
>
> /Users/liuqi/Downloads/v10-0001-lavf-vf_transpose-add-exif-orientation-support.patch:227:+
> /Users/liuqi/Downloads/v10-0001-lavf-vf_transpose-add-exif-orientation-support.patch:245:+
>       av_log(ctx, AV_LOG_WARNING,
> /Users/liuqi/Downloads/v10-0001-lavf-vf_transpose-add-exif-orientation-support.patch:249:+
>       return ORIENTATION_NORMAL;
> /Users/liuqi/Downloads/v10-0001-lavf-vf_transpose-add-exif-orientation-support.patch:253:+
>   if (s->orientation == ORIENTATION_AUTO_TRANSPOSE &&
> /Users/liuqi/Downloads/v10-0001-lavf-vf_transpose-add-exif-orientation-support.patch:299:+
>           case ORIENTATION_HFLIP_ROTATE270CW:
> /Users/liuqi/Downloads/v10-0001-lavf-vf_transpose-add-exif-orientation-support.patch:384:+
>   } else if (s->orientation == ORIENTATION_AUTO_TRANSPOSE ||
> egrep: empty (sub)expression
> egrep: empty (sub)expression
> egrep: empty (sub)expression
> egrep: empty (sub)expression
> egrep: empty (sub)expression
> egrep: empty (sub)expression
> egrep: empty (sub)expression
> egrep: empty (sub)expression
> egrep: empty (sub)expression
> egrep: empty (sub)expression
> egrep: empty (sub)expression
> xargs: illegal option -- d
> usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements]] [-J
> replstr]
>              [-L number] [-n number [-x]] [-P maxprocs] [-s size]
>              [utility [argument ...]]
>
> Missing changelog entry (ignore if minor change)
> egrep: empty (sub)expression
> liuqideMBP:dash liuqi$
> liuqideMBP:dash liuqi$
> liuqideMBP:dash liuqi$ ../tools/patcheck
> ~/Downloads/v10-0002-fftools-ffmpeg-add-exif-orientation-support-per-.patch
> patCHeck 1e10.0
> This tool is intended to help a human check/review patches. It is very far
> from
> being free of false positives and negatives, and its output are just hints
> of what
> may or may not be bad. When you use it and it misses something or detects
> something wrong, fix it and send a patch to the ffmpeg-devel mailing list.
> License: GPL, Author: Michael Niedermayer
>
> trailing whitespace
> /Users/liuqi/Downloads/v10-0002-fftools-ffmpeg-add-exif-orientation-support-per-.patch:107:+
>           if (ifilter->orientation == ORIENTATION_AUTO_FLIP) {
> egrep: empty (sub)expression
> egrep: empty (sub)expression
>
> Missing context in av_log
> /Users/liuqi/Downloads/v10-0002-fftools-ffmpeg-add-exif-orientation-support-per-.patch:85:+
>           av_log(NULL, AV_LOG_WARNING,
> egrep: empty (sub)expression
> egrep: empty (sub)expression
> egrep: empty (sub)expression
> egrep: empty (sub)expression
> egrep: empty (sub)expression
> egrep: empty (sub)expression
> egrep: empty (sub)expression
> egrep: empty (sub)expression
> egrep: empty (sub)expression
> xargs: illegal option -- d
> usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements]] [-J
> replstr]
>              [-L number] [-n number [-x]] [-P maxprocs] [-s size]
>              [utility [argument ...]]
>
> Missing changelog entry (ignore if minor change)
> egrep: empty (sub)expression
> liuqideMBP:dash liuqi$
>
>
>
> >
> >
> > Thanks
> > Steven Liu
> > _______________________________________________
> > ffmpeg-devel mailing list
> > ffmpeg-devel@ffmpeg.org
> > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >
> > To unsubscribe, visit link above, or email
> > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
> Thanks
> Steven
>
>
>
>
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".



Thanks for review ! I updated a new version:
https://patchwork.ffmpeg.org/patch/13759/

-Jun
diff mbox

Patch

diff --git a/libavfilter/hflip.h b/libavfilter/hflip.h
index 204090dbb4..4e89bae3fc 100644
--- a/libavfilter/hflip.h
+++ b/libavfilter/hflip.h
@@ -35,5 +35,7 @@  typedef struct FlipContext {
 
 int ff_hflip_init(FlipContext *s, int step[4], int nb_planes);
 void ff_hflip_init_x86(FlipContext *s, int step[4], int nb_planes);
+int ff_hflip_config_props(FlipContext* s, AVFilterLink *inlink);
+int ff_hflip_filter_slice(FlipContext *s, AVFrame *in, AVFrame *out, int job, int nb_jobs, int vlifp);
 
 #endif /* AVFILTER_HFLIP_H */
diff --git a/libavfilter/transpose.h b/libavfilter/transpose.h
index aa262b9487..5da08bddc0 100644
--- a/libavfilter/transpose.h
+++ b/libavfilter/transpose.h
@@ -34,4 +34,18 @@  enum TransposeDir {
     TRANSPOSE_VFLIP,
 };
 
+enum OrientationType {
+    ORIENTATION_AUTO_TRANSPOSE = -2,
+    ORIENTATION_AUTO_FLIP = -1,
+    ORIENTATION_NONE = 0,
+    ORIENTATION_NORMAL,
+    ORIENTATION_HFLIP,
+    ORIENTATION_ROTATE180,
+    ORIENTATION_VFLIP,
+    ORIENTATION_HFLIP_ROTATE270CW,
+    ORIENTATION_ROTATE90CW,
+    ORIENTATION_HFLIP_ROTATE90CW,
+    ORIENTATION_ROTATE270CW
+};
+
 #endif
diff --git a/libavfilter/vf_hflip.c b/libavfilter/vf_hflip.c
index b77afc77fc..d24ca5c2e7 100644
--- a/libavfilter/vf_hflip.c
+++ b/libavfilter/vf_hflip.c
@@ -125,9 +125,8 @@  static void hflip_qword_c(const uint8_t *ssrc, uint8_t *ddst, int w)
         dst[j] = src[-j];
 }
 
-static int config_props(AVFilterLink *inlink)
+int ff_hflip_config_props(FlipContext* s, AVFilterLink *inlink)
 {
-    FlipContext *s = inlink->dst->priv;
     const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format);
     const int hsub = pix_desc->log2_chroma_w;
     const int vsub = pix_desc->log2_chroma_h;
@@ -144,6 +143,12 @@  static int config_props(AVFilterLink *inlink)
     return ff_hflip_init(s, s->max_step, nb_planes);
 }
 
+static int config_props(AVFilterLink *inlink)
+{
+    FlipContext *s = inlink->dst->priv;
+    return ff_hflip_config_props(s, inlink);
+}
+
 int ff_hflip_init(FlipContext *s, int step[4], int nb_planes)
 {
     int i;
@@ -170,14 +175,10 @@  typedef struct ThreadData {
     AVFrame *in, *out;
 } ThreadData;
 
-static int filter_slice(AVFilterContext *ctx, void *arg, int job, int nb_jobs)
+int ff_hflip_filter_slice(FlipContext *s, AVFrame *in, AVFrame *out, int job, int nb_jobs, int vflip)
 {
-    FlipContext *s = ctx->priv;
-    ThreadData *td = arg;
-    AVFrame *in = td->in;
-    AVFrame *out = td->out;
     uint8_t *inrow, *outrow;
-    int i, plane, step;
+    int i, plane, step, outlinesize;
 
     for (plane = 0; plane < 4 && in->data[plane] && in->linesize[plane]; plane++) {
         const int width  = s->planewidth[plane];
@@ -187,19 +188,36 @@  static int filter_slice(AVFilterContext *ctx, void *arg, int job, int nb_jobs)
 
         step = s->max_step[plane];
 
-        outrow = out->data[plane] + start * out->linesize[plane];
-        inrow  = in ->data[plane] + start * in->linesize[plane] + (width - 1) * step;
+        if (vflip) {
+            outrow = out->data[plane] + (height - start - 1)* out->linesize[plane];
+            outlinesize = -out->linesize[plane];
+        } else {
+            outrow = out->data[plane] + start * out->linesize[plane];
+            outlinesize = out->linesize[plane];
+        }
+
+        inrow = in->data[plane] + start * in->linesize[plane] +  (width - 1) * step;
+
         for (i = start; i < end; i++) {
             s->flip_line[plane](inrow, outrow, width);
 
             inrow  += in ->linesize[plane];
-            outrow += out->linesize[plane];
+            outrow += outlinesize;
         }
     }
 
     return 0;
 }
 
+static int filter_slice(AVFilterContext *ctx, void *arg, int job, int nb_jobs)
+{
+    FlipContext *s = ctx->priv;
+    ThreadData *td = arg;
+    AVFrame *in = td->in;
+    AVFrame *out = td->out;
+    return ff_hflip_filter_slice(s, in, out, job, nb_jobs, 0);
+}
+
 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
 {
     AVFilterContext *ctx  = inlink->dst;
diff --git a/libavfilter/vf_transpose.c b/libavfilter/vf_transpose.c
index dd54947bd9..05dc04a89f 100644
--- a/libavfilter/vf_transpose.c
+++ b/libavfilter/vf_transpose.c
@@ -39,6 +39,7 @@ 
 #include "internal.h"
 #include "video.h"
 #include "transpose.h"
+#include "hflip.h"
 
 typedef struct TransVtable {
     void (*transpose_8x8)(uint8_t *src, ptrdiff_t src_linesize,
@@ -48,16 +49,22 @@  typedef struct TransVtable {
                             int w, int h);
 } TransVtable;
 
-typedef struct TransContext {
-    const AVClass *class;
+typedef struct TransContextData {
     int hsub, vsub;
     int planes;
     int pixsteps[4];
+    TransVtable vtables[4];
+} TransContextData;
 
+typedef struct TransContext {
+    const AVClass *class;
     int passthrough;    ///< PassthroughType, landscape passthrough mode enabled
     int dir;            ///< TransposeDir
-
-    TransVtable vtables[4];
+    int orientation;    ///< OrientationType
+    union {
+        TransContextData transpose;
+        FlipContext flip;
+    } ctx;
 } TransContext;
 
 static int query_formats(AVFilterContext *ctx)
@@ -207,14 +214,18 @@  static int config_props_output(AVFilterLink *outlink)
         s->passthrough = TRANSPOSE_PT_TYPE_NONE;
     }
 
-    s->hsub = desc_in->log2_chroma_w;
-    s->vsub = desc_in->log2_chroma_h;
-    s->planes = av_pix_fmt_count_planes(outlink->format);
+    if (s->orientation == ORIENTATION_AUTO_FLIP || 
+       (s->orientation > ORIENTATION_NONE && s->orientation <= ORIENTATION_VFLIP))
+        return ff_hflip_config_props(&(s->ctx.flip), inlink);
+
+    s->ctx.transpose.hsub = desc_in->log2_chroma_w;
+    s->ctx.transpose.vsub = desc_in->log2_chroma_h;
+    s->ctx.transpose.planes = av_pix_fmt_count_planes(outlink->format);
 
     av_assert0(desc_in->nb_components == desc_out->nb_components);
 
 
-    av_image_fill_max_pixsteps(s->pixsteps, NULL, desc_out);
+    av_image_fill_max_pixsteps(s->ctx.transpose.pixsteps, NULL, desc_out);
 
     outlink->w = inlink->h;
     outlink->h = inlink->w;
@@ -226,10 +237,10 @@  static int config_props_output(AVFilterLink *outlink)
         outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
 
     for (int i = 0; i < 4; i++) {
-        TransVtable *v = &s->vtables[i];
-        switch (s->pixsteps[i]) {
+        TransVtable *v = &s->ctx.transpose.vtables[i];
+        switch (s->ctx.transpose.pixsteps[i]) {
         case 1: v->transpose_block = transpose_block_8_c;
-                v->transpose_8x8   = transpose_8x8_8_c;  break;
+                v->transpose_8x8   = transpose_8x8_8_c; break;
         case 2: v->transpose_block = transpose_block_16_c;
                 v->transpose_8x8   = transpose_8x8_16_c; break;
         case 3: v->transpose_block = transpose_block_24_c;
@@ -262,21 +273,69 @@  static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h)
 
 typedef struct ThreadData {
     AVFrame *in, *out;
+    int orientation;
 } ThreadData;
 
-static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
-                        int nb_jobs)
+static int get_frame_orientation(AVFilterContext *ctx, const AVFrame *frame)
+{
+    TransContext *s = ctx->priv;
+    AVDictionaryEntry *entry = NULL;
+    int orientation = ORIENTATION_NONE;
+
+    if (s->orientation >= ORIENTATION_NONE)
+        return s->orientation;
+    
+    // read exif orientation data
+    entry = av_dict_get(frame->metadata, "Orientation", NULL, 0);
+    if (entry && entry->value)
+        orientation = atoi(entry->value);
+
+    // check frame orientation validity
+    if (orientation < ORIENTATION_NORMAL ||
+        orientation > ORIENTATION_ROTATE270CW) {
+        // frame orientation is invalid or empty, we have to guess one.
+        av_log(ctx, AV_LOG_WARNING, "Frame orientation is either empty or invalid.\n");
+        return s->orientation == ORIENTATION_AUTO_FLIP ?
+               ORIENTATION_NORMAL : ORIENTATION_HFLIP_ROTATE270CW;
+    }
+
+    // check conflict between frame orientation and passed-in orientation option
+    if (s->orientation == ORIENTATION_AUTO_FLIP &&
+        orientation > ORIENTATION_VFLIP) {
+        av_log(ctx, AV_LOG_WARNING, 
+               "Found valid orientation value: %i, but auto-flip only cover range [1, 4]. Set to default value 1(ORIENTATION_NONE).\n",
+               orientation);
+        // conflict happens, return the min value of orientation range
+        return ORIENTATION_NORMAL; 
+    }
+
+    // check conflict between frame orientation and passed-in orientation option
+    if (s->orientation == ORIENTATION_AUTO_TRANSPOSE && 
+        orientation <= ORIENTATION_VFLIP) {
+        av_log(ctx, AV_LOG_WARNING,
+               "Found valid orientation value: %i, but auto-transpose only cover range [5, 8]. Set to default value 5(ORIENTATION_HFLIP_ROTATE270CW).\n",
+               orientation);
+        // conflict happens, return the min value of orientation range, same effect as dir=0
+        return ORIENTATION_HFLIP_ROTATE270CW;
+    }
+
+    return orientation;
+}
+
+static int transpose_filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
 {
     TransContext *s = ctx->priv;
     ThreadData *td = arg;
     AVFrame *out = td->out;
     AVFrame *in = td->in;
+    TransContextData *c = &(s->ctx.transpose);
+    int orientation = td->orientation;
     int plane;
 
-    for (plane = 0; plane < s->planes; plane++) {
-        int hsub    = plane == 1 || plane == 2 ? s->hsub : 0;
-        int vsub    = plane == 1 || plane == 2 ? s->vsub : 0;
-        int pixstep = s->pixsteps[plane];
+    for (plane = 0; plane < c->planes; plane++) {
+        int hsub    = plane == 1 || plane == 2 ? c->hsub : 0;
+        int vsub    = plane == 1 || plane == 2 ? c->vsub : 0;
+        int pixstep = c->pixsteps[plane];
         int inh     = AV_CEIL_RSHIFT(in->height, vsub);
         int outw    = AV_CEIL_RSHIFT(out->width,  hsub);
         int outh    = AV_CEIL_RSHIFT(out->height, vsub);
@@ -285,19 +344,36 @@  static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
         uint8_t *dst, *src;
         int dstlinesize, srclinesize;
         int x, y;
-        TransVtable *v = &s->vtables[plane];
+        int dir = s->dir;
+        TransVtable *v = &c->vtables[plane];
 
         dstlinesize = out->linesize[plane];
         dst         = out->data[plane] + start * dstlinesize;
         src         = in->data[plane];
         srclinesize = in->linesize[plane];
+        switch (orientation) {
+            case ORIENTATION_HFLIP_ROTATE270CW: 
+                dir = 0; // mirror horizontal and rotate 270 CW, same as dir=cclock_flip
+                break;
+            case ORIENTATION_ROTATE90CW:
+                dir = 1; // rotate 90 CW, same as dir=clock
+                break;
+            case ORIENTATION_HFLIP_ROTATE90CW:
+                dir = 3; // mirror horizontal and rotate 90 CW, same as dir=clock_flip
+                break;
+            case ORIENTATION_ROTATE270CW:
+                dir = 2; // rotate 270 CW, same as dir=cclock
+                break;
+            default:
+                break;
+        }
 
-        if (s->dir & 1) {
+        if (dir & 1) {
             src         += in->linesize[plane] * (inh - 1);
             srclinesize *= -1;
         }
 
-        if (s->dir & 2) {
+        if (dir & 2) {
             dst          = out->data[plane] + dstlinesize * (outh - start - 1);
             dstlinesize *= -1;
         }
@@ -326,6 +402,22 @@  static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
     return 0;
 }
 
+static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
+                        int nb_jobs)
+{
+    TransContext *s = ctx->priv;
+    ThreadData *td = arg;
+    AVFrame *out = td->out;
+    AVFrame *in = td->in;
+    int orientation = td->orientation;
+
+    if (orientation > ORIENTATION_NONE && orientation <= ORIENTATION_VFLIP) {
+        return ff_hflip_filter_slice(&s->ctx.flip, in, out, jobnr, nb_jobs,
+                                     orientation == ORIENTATION_ROTATE180);
+    }
+    return transpose_filter_slice(ctx, arg, jobnr, nb_jobs);
+}
+
 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
 {
     AVFilterContext *ctx = inlink->dst;
@@ -333,10 +425,29 @@  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
     AVFilterLink *outlink = ctx->outputs[0];
     ThreadData td;
     AVFrame *out;
+    int orientation = ORIENTATION_NONE;
 
     if (s->passthrough)
         return ff_filter_frame(outlink, in);
 
+    if (s->orientation) {
+        orientation = get_frame_orientation(ctx, in);
+        if (orientation == ORIENTATION_NORMAL) // orientation 'Normal', do nothing
+            return ff_filter_frame(outlink, in);
+
+        if (orientation == ORIENTATION_VFLIP) {
+            int i;
+            for (i = 0; i < 4; i ++) {
+                int height = s->ctx.flip.planeheight[i];
+                if (in->data[i]) {
+                    in->data[i] += (height - 1) * in->linesize[i];
+                    in->linesize[i] = -in->linesize[i];
+                }
+            }
+            return ff_filter_frame(outlink, in);
+        }
+    }
+
     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
     if (!out) {
         av_frame_free(&in);
@@ -346,12 +457,14 @@  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
 
     if (in->sample_aspect_ratio.num == 0) {
         out->sample_aspect_ratio = in->sample_aspect_ratio;
-    } else {
+    } else if (s->orientation == ORIENTATION_AUTO_TRANSPOSE || 
+               orientation > ORIENTATION_VFLIP) { // transpose
         out->sample_aspect_ratio.num = in->sample_aspect_ratio.den;
         out->sample_aspect_ratio.den = in->sample_aspect_ratio.num;
     }
 
     td.in = in, td.out = out;
+    td.orientation = orientation;
     ctx->internal->execute(ctx, filter_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
     av_frame_free(&in);
     return ff_filter_frame(outlink, out);
@@ -373,6 +486,19 @@  static const AVOption transpose_options[] = {
         { "portrait",  "preserve portrait geometry",   0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT},  INT_MIN, INT_MAX, FLAGS, "passthrough" },
         { "landscape", "preserve landscape geometry",  0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX, FLAGS, "passthrough" },
 
+    { "orientation", "apply frame exif orientation",
+      OFFSET(orientation), AV_OPT_TYPE_INT, {.i64 = ORIENTATION_NONE },  ORIENTATION_AUTO_TRANSPOSE, ORIENTATION_ROTATE270CW, FLAGS, "orientation" },
+        { "auto_transpose",    "apply transposition if frame's orientation value is in range [5,8]",                     0, AV_OPT_TYPE_CONST, {.i64=ORIENTATION_AUTO_TRANSPOSE},    INT_MIN, INT_MAX, FLAGS, "orientation" },
+        { "auto_flip",         "apply flip if frame's orientation value is in range [1,4]",                              0, AV_OPT_TYPE_CONST, {.i64=ORIENTATION_AUTO_FLIP},         INT_MIN, INT_MAX, FLAGS, "orientation" },
+        { "none",              "apply default transposition, same as dir=cclock_flip",                                   0, AV_OPT_TYPE_CONST, {.i64=ORIENTATION_NONE},              INT_MIN, INT_MAX, FLAGS, "orientation" },
+        { "normal",            "exif orientation 'Normal', do not apply any flip or transposition",                      0, AV_OPT_TYPE_CONST, {.i64=ORIENTATION_NORMAL},            INT_MIN, INT_MAX, FLAGS, "orientation" },
+        { "hflip",             "exif orientation 'Mirror horizontal', apply horizontal flip",                            0, AV_OPT_TYPE_CONST, {.i64=ORIENTATION_HFLIP},             INT_MIN, INT_MAX, FLAGS, "orientation" },
+        { "rotate180",         "exif orientation 'Rotate 180', apply rotation 180",                                      0, AV_OPT_TYPE_CONST, {.i64=ORIENTATION_ROTATE180},         INT_MIN, INT_MAX, FLAGS, "orientation" },
+        { "vflip",             "exif orientation 'Mirror vertical', apply vertical flip",                                0, AV_OPT_TYPE_CONST, {.i64=ORIENTATION_VFLIP},             INT_MIN, INT_MAX, FLAGS, "orientation" },
+        { "hflip_rotate270cw", "exif orientation 'Mirror horizontal and rotate 270 CW', same effect as dir=cclock_flip", 0, AV_OPT_TYPE_CONST, {.i64=ORIENTATION_HFLIP_ROTATE270CW}, INT_MIN, INT_MAX, FLAGS, "orientation" },
+        { "rotate90cw",        "exif orientation 'Rotate 90 CW', same as dir=clock",                                     0, AV_OPT_TYPE_CONST, {.i64=ORIENTATION_ROTATE90CW},        INT_MIN, INT_MAX, FLAGS, "orientation" },
+        { "hflip_rotate90cw",  "exif orientation 'Mirror horizontal and rotate 90 CW', same effect as dir=clock_flip",   0, AV_OPT_TYPE_CONST, {.i64=ORIENTATION_HFLIP_ROTATE90CW},  INT_MIN, INT_MAX, FLAGS, "orientation" },
+        { "rotate270",         "exif orientation 'Rotate 270 CW', same as dir=cclock",                                   0, AV_OPT_TYPE_CONST, {.i64=ORIENTATION_ROTATE270CW},       INT_MIN, INT_MAX, FLAGS, "orientation" },
     { NULL }
 };