[FFmpeg-devel] [PATCH v1 1/2] lavf/vf_transpose: add exif orientation support
Paul B Mahol
onemda at gmail.com
Sun May 26 12:09:23 EEST 2019
On 5/26/19, Jun Li <junli1026 at gmail.com> wrote:
> On Sun, May 26, 2019 at 1:16 AM Paul B Mahol <onemda at gmail.com> wrote:
>
>> On 5/25/19, Jun Li <junli1026 at gmail.com> wrote:
>> > Add exif orientation support and expose an option.
>> > ---
>> > libavfilter/vf_transpose.c | 258 +++++++++++++++++++++++++++++--------
>> > 1 file changed, 207 insertions(+), 51 deletions(-)
>> >
>> > diff --git a/libavfilter/vf_transpose.c b/libavfilter/vf_transpose.c
>> > index dd54947bd9..4aebfb9ee4 100644
>> > --- a/libavfilter/vf_transpose.c
>> > +++ b/libavfilter/vf_transpose.c
>> > @@ -46,6 +46,9 @@ typedef struct TransVtable {
>> > void (*transpose_block)(uint8_t *src, ptrdiff_t src_linesize,
>> > uint8_t *dst, ptrdiff_t dst_linesize,
>> > int w, int h);
>> > + void (*copyline_block)(uint8_t *src, ptrdiff_t src_linesize,
>> > + uint8_t *dst, ptrdiff_t dst_linesize,
>> > + int line_dir, int w, int h);
>> > } TransVtable;
>> >
>>
>> This is slow, the operations should be in one loop.
>>
>
> Thanks Paul for review.
> I see transpose is using " ctx->internal->execute(ctx, filter_slice, &td,
> NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));" .
> which looks like leveraging multi-threading.
> If we do that in one loop, I guess it would be hard to use multi-threading
> to accelerate ? But correct me if I am wrong.
Transpose filter does one thing and that is transposing.
If I'm not mistaken you are adding vertical and horizontal flipping?
If you really need to do that, do it efficiently in one loop, by that I mean
you do not do cascading operations at all.
>
> Best Regards,
> -Jun
>
>
>> > typedef struct TransContext {
>> > @@ -56,7 +59,7 @@ typedef struct TransContext {
>> >
>> > int passthrough; ///< PassthroughType, landscape passthrough
>> > mode
>> > enabled
>> > int dir; ///< TransposeDir
>> > -
>> > + int orientation; ///< Orientation
>> > TransVtable vtables[4];
>> > } TransContext;
>> >
>> > @@ -182,6 +185,105 @@ static void transpose_8x8_64_c(uint8_t *src,
>> ptrdiff_t
>> > src_linesize,
>> > transpose_block_64_c(src, src_linesize, dst, dst_linesize, 8, 8);
>> > }
>> >
>> > +
>> > +static void copyline_block_8(uint8_t *src, ptrdiff_t src_linesize,
>> > + uint8_t *dst, ptrdiff_t dst_linesize,
>> > + int line_dir, int w, int h)
>> > +{
>> > + int x, y, i;
>> > + for (y = 0; y < h; y++, dst += dst_linesize, src += src_linesize) {
>> > + for (x = 0; x < w; x ++) {
>> > + i = line_dir < 0 ? w-x-1 : x;
>> > + dst[i] = src[x];
>> > + }
>> > + }
>> > +}
>> > +
>> > +static void copyline_block_16(uint8_t *src, ptrdiff_t src_linesize,
>> > + uint8_t *dst, ptrdiff_t dst_linesize,
>> > + int line_dir, int w, int h)
>> > +{
>> > + int x, y, i;
>> > + for (y = 0; y < h; y++, dst += dst_linesize, src += src_linesize) {
>> > + for (x = 0; x < w; x ++) {
>> > + i = line_dir < 0 ? w-x-1 : x;
>> > + *((uint16_t *)(dst + 2*i)) = *((uint16_t *)(src + 2*x));
>> > + }
>> > + }
>> > +}
>> > +
>> > +static void copyline_block_24(uint8_t *src, ptrdiff_t src_linesize,
>> > + uint8_t *dst, ptrdiff_t dst_linesize,
>> > + int line_dir, int w, int h)
>> > +{
>> > + int x, y, i;
>> > + for (y = 0; y < h; y++, dst += dst_linesize, src += src_linesize) {
>> > + for (x = 0; x < w; x++) {
>> > + int32_t v = AV_RB24(src + 3*x);
>> > + i = line_dir < 0 ? w-x-1 : x;
>> > + AV_WB24(dst + 3*i, v);
>> > + }
>> > + }
>> > +}
>> > +
>> > +static void copyline_block_32(uint8_t *src, ptrdiff_t src_linesize,
>> > + uint8_t *dst, ptrdiff_t dst_linesize,
>> > + int line_dir, int w, int h)
>> > +{
>> > + int x, y, i;
>> > + for (y = 0; y < h; y++, dst += dst_linesize, src += src_linesize) {
>> > + for (x = 0; x < w; x ++) {
>> > + i = line_dir < 0 ? w-x-1 : x;
>> > + *((uint32_t *)(dst + 4*i)) = *((uint32_t *)(src + 4*x));
>> > + }
>> > + }
>> > +}
>> > +
>> > +static void copyline_block_48(uint8_t *src, ptrdiff_t src_linesize,
>> > + uint8_t *dst, ptrdiff_t dst_linesize,
>> > + int line_dir, int w, int h)
>> > +{
>> > + int x, y, i;
>> > + for (y = 0; y < h; y++, dst += dst_linesize, src += src_linesize) {
>> > + for (x = 0; x < w; x++) {
>> > + int64_t v = AV_RB48(src + 6*x);
>> > + i = line_dir < 0 ? w-x-1 : x;
>> > + AV_WB48(dst + 6*i, v);
>> > + }
>> > + }
>> > +}
>> > +
>> > +static void copyline_block_64(uint8_t *src, ptrdiff_t src_linesize,
>> > + uint8_t *dst, ptrdiff_t dst_linesize,
>> > + int line_dir, int w, int h)
>> > +{
>> > + int x, y, i;
>> > + for (y = 0; y < h; y++, dst += dst_linesize, src += src_linesize) {
>> > + for (x = 0; x < w; x ++) {
>> > + i = line_dir < 0 ? w-x-1 : x;
>> > + *((uint64_t *)(dst + 8*i)) = *((uint64_t *)(src + 8*x));
>> > + }
>> > + }
>> > +}
>> > +
>> > +static void set_outlink_width_height(AVFilterLink *inlink, AVFilterLink
>> > *outlink, int transpose)
>> > +{
>> > + if (transpose) {
>> > + outlink->w = inlink->h;
>> > + outlink->h = inlink->w;
>> > +
>> > + if (inlink->sample_aspect_ratio.num)
>> > + outlink->sample_aspect_ratio = av_div_q((AVRational) { 1, 1
>> },
>> > +
>> > inlink->sample_aspect_ratio);
>> > + else
>> > + outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
>> > + } else {
>> > + outlink->w = inlink->w;
>> > + outlink->h = inlink->h;
>> > + outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
>> > + }
>> > +}
>> > +
>> > static int config_props_output(AVFilterLink *outlink)
>> > {
>> > AVFilterContext *ctx = outlink->src;
>> > @@ -213,33 +315,44 @@ static int config_props_output(AVFilterLink
>> *outlink)
>> >
>> > av_assert0(desc_in->nb_components == desc_out->nb_components);
>> >
>> > -
>> > av_image_fill_max_pixsteps(s->pixsteps, NULL, desc_out);
>> > +
>> > + if (s->orientation && s->orientation < 5) {
>> > + outlink->h = inlink->h;
>> > + outlink->w = inlink->w;
>> > + outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
>> > + } else {
>> > + outlink->w = inlink->h;
>> > + outlink->h = inlink->w;
>> >
>> > - outlink->w = inlink->h;
>> > - outlink->h = inlink->w;
>> > -
>> > - if (inlink->sample_aspect_ratio.num)
>> > - outlink->sample_aspect_ratio = av_div_q((AVRational) { 1, 1 },
>> > -
>> > inlink->sample_aspect_ratio);
>> > - else
>> > - outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
>> > + if (inlink->sample_aspect_ratio.num)
>> > + outlink->sample_aspect_ratio = av_div_q((AVRational) { 1, 1
>> },
>> > +
>> > inlink->sample_aspect_ratio);
>> > + else
>> > + outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
>> > + }
>> >
>> > for (int i = 0; i < 4; i++) {
>> > TransVtable *v = &s->vtables[i];
>> > switch (s->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;
>> > + v->copyline_block = copyline_block_8; break;
>> > case 2: v->transpose_block = transpose_block_16_c;
>> > - v->transpose_8x8 = transpose_8x8_16_c; break;
>> > + v->transpose_8x8 = transpose_8x8_16_c;
>> > + v->copyline_block = copyline_block_16; break;
>> > case 3: v->transpose_block = transpose_block_24_c;
>> > - v->transpose_8x8 = transpose_8x8_24_c; break;
>> > + v->transpose_8x8 = transpose_8x8_24_c;
>> > + v->copyline_block = copyline_block_24; break;
>> > case 4: v->transpose_block = transpose_block_32_c;
>> > - v->transpose_8x8 = transpose_8x8_32_c; break;
>> > + v->transpose_8x8 = transpose_8x8_32_c;
>> > + v->copyline_block = copyline_block_32; break;
>> > case 6: v->transpose_block = transpose_block_48_c;
>> > - v->transpose_8x8 = transpose_8x8_48_c; break;
>> > + v->transpose_8x8 = transpose_8x8_48_c;
>> > + v->copyline_block = copyline_block_48; break;
>> > case 8: v->transpose_block = transpose_block_64_c;
>> > - v->transpose_8x8 = transpose_8x8_64_c; break;
>> > + v->transpose_8x8 = transpose_8x8_64_c;
>> > + v->copyline_block = copyline_block_64; break;
>> > }
>> > }
>> >
>> > @@ -285,42 +398,80 @@ static int filter_slice(AVFilterContext *ctx, void
>> > *arg, int jobnr,
>> > uint8_t *dst, *src;
>> > int dstlinesize, srclinesize;
>> > int x, y;
>> > + int dir = s->dir;
>> > TransVtable *v = &s->vtables[plane];
>> > +
>> > + if (s->orientation > 0 && s->orientation < 5) {
>> > + int line_dir = 1;
>> > + dstlinesize = out->linesize[plane];
>> > + dst = out->data[plane] + start * dstlinesize;
>> > + srclinesize = in->linesize[plane];
>> > + src = in->data[plane] + start * srclinesize;
>> > +
>> > + switch (s->orientation) {
>> > + case 2: // mirror horizontal
>> > + line_dir = -1;
>> > + break;
>> > + case 3: // rotate 180
>> > + dst = out->data[plane] + (outh - start -
>> 1) *
>> > dstlinesize;
>> > + line_dir = -1;
>> > + dstlinesize *= -1;
>> > + break;
>> > + case 4: // mirror vertical
>> > + dst = out->data[plane] + (outh - start -
>> 1) *
>> > dstlinesize;
>> > + dstlinesize *= -1;
>> > + break;
>> > + default:
>> > + break;
>> > + }
>> > + v->copyline_block(src, srclinesize, dst, dstlinesize,
>> line_dir,
>> > outw, end - start);
>> > + } else {
>> > + dstlinesize = out->linesize[plane];
>> > + dst = out->data[plane] + start * dstlinesize;
>> > + src = in->data[plane];
>> > + srclinesize = in->linesize[plane];
>> > + switch (s->orientation) {
>> > + case 5: // mirror horizontal and rotate 270 CW
>> > + dir = 0; break;
>> > + case 6: // rotate 90 CW
>> > + dir = 1; break;
>> > + case 7: // mirror horizontal and rotate 90 CW
>> > + dir = 3; break;
>> > + case 8: // rotate 270 CW
>> > + dir = 2; break;
>> > + default: break;
>> > + }
>> >
>> > - dstlinesize = out->linesize[plane];
>> > - dst = out->data[plane] + start * dstlinesize;
>> > - src = in->data[plane];
>> > - srclinesize = in->linesize[plane];
>> > -
>> > - if (s->dir & 1) {
>> > - src += in->linesize[plane] * (inh - 1);
>> > - srclinesize *= -1;
>> > - }
>> > + if (dir & 1) {
>> > + src += in->linesize[plane] * (inh - 1);
>> > + srclinesize *= -1;
>> > + }
>> >
>> > - if (s->dir & 2) {
>> > - dst = out->data[plane] + dstlinesize * (outh -
>> start -
>> > 1);
>> > - dstlinesize *= -1;
>> > - }
>> > + if (dir & 2) {
>> > + dst = out->data[plane] + dstlinesize * (outh -
>> > start - 1);
>> > + dstlinesize *= -1;
>> > + }
>> >
>> > - for (y = start; y < end - 7; y += 8) {
>> > - for (x = 0; x < outw - 7; x += 8) {
>> > - v->transpose_8x8(src + x * srclinesize + y * pixstep,
>> > - srclinesize,
>> > - dst + (y - start) * dstlinesize + x *
>> > pixstep,
>> > - dstlinesize);
>> > + for (y = start; y < end - 7; y += 8) {
>> > + for (x = 0; x < outw - 7; x += 8) {
>> > + v->transpose_8x8(src + x * srclinesize + y *
>> pixstep,
>> > + srclinesize,
>> > + dst + (y - start) * dstlinesize + x
>> *
>> > pixstep,
>> > + dstlinesize);
>> > + }
>> > + if (outw - x > 0 && end - y > 0)
>> > + v->transpose_block(src + x * srclinesize + y *
>> pixstep,
>> > + srclinesize,
>> > + dst + (y - start) * dstlinesize + x
>> *
>> > pixstep,
>> > + dstlinesize, outw - x, end - y);
>> > }
>> > - if (outw - x > 0 && end - y > 0)
>> > - v->transpose_block(src + x * srclinesize + y * pixstep,
>> > - srclinesize,
>> > - dst + (y - start) * dstlinesize + x
>> > *
>> > pixstep,
>> > - dstlinesize, outw - x, end - y);
>> > - }
>> >
>> > - if (end - y > 0)
>> > - v->transpose_block(src + 0 * srclinesize + y * pixstep,
>> > - srclinesize,
>> > - dst + (y - start) * dstlinesize + 0 *
>> > pixstep,
>> > - dstlinesize, outw, end - y);
>> > + if (end - y > 0)
>> > + v->transpose_block(src + 0 * srclinesize + y * pixstep,
>> > + srclinesize,
>> > + dst + (y - start) * dstlinesize + 0 *
>> > pixstep,
>> > + dstlinesize, outw, end - y);
>> > + }
>> > }
>> >
>> > return 0;
>> > @@ -334,7 +485,7 @@ static int filter_frame(AVFilterLink *inlink,
>> > AVFrame
>> > *in)
>> > ThreadData td;
>> > AVFrame *out;
>> >
>> > - if (s->passthrough)
>> > + if (s->passthrough || s->orientation == 1)
>> > return ff_filter_frame(outlink, in);
>> >
>> > out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
>> > @@ -347,8 +498,13 @@ static int filter_frame(AVFilterLink *inlink,
>> AVFrame
>> > *in)
>> > if (in->sample_aspect_ratio.num == 0) {
>> > out->sample_aspect_ratio = in->sample_aspect_ratio;
>> > } else {
>> > - out->sample_aspect_ratio.num = in->sample_aspect_ratio.den;
>> > - out->sample_aspect_ratio.den = in->sample_aspect_ratio.num;
>> > + if (s->orientation > 0 && s->orientation < 5) {
>> > + out->sample_aspect_ratio.num = in->sample_aspect_ratio.num;
>> > + out->sample_aspect_ratio.den = in->sample_aspect_ratio.den;
>> > + } else {
>> > + 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;
>> > @@ -366,13 +522,13 @@ static const AVOption transpose_options[] = {
>> > { "clock", "rotate clockwise",
>> 0,
>> > AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK }, .flags=FLAGS, .unit
>> =
>> > "dir" },
>> > { "cclock", "rotate counter-clockwise",
>> 0,
>> > AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK }, .flags=FLAGS, .unit
>> =
>> > "dir" },
>> > { "clock_flip", "rotate clockwise with vertical flip",
>> 0,
>> > AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP }, .flags=FLAGS, .unit
>> =
>> > "dir" },
>> > -
>> > +
>> > { "passthrough", "do not apply transposition if the input matches
>> the
>> > specified geometry",
>> > OFFSET(passthrough), AV_OPT_TYPE_INT,
>> {.i64=TRANSPOSE_PT_TYPE_NONE},
>> > 0, INT_MAX, FLAGS, "passthrough" },
>> > { "none", "always apply transposition", 0,
>> > AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_NONE}, INT_MIN, INT_MAX,
>> > FLAGS, "passthrough" },
>> > { "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", "set exif orientation", OFFSET(orientation),
>> > AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 8, FLAGS, "orientation" },
>> > { NULL }
>> > };
>> >
>> > --
>> > 2.17.1
>> >
>> > _______________________________________________
>> > ffmpeg-devel mailing list
>> > ffmpeg-devel at ffmpeg.org
>> > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>> >
>> > To unsubscribe, visit link above, or email
>> > ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel at ffmpeg.org
>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>
>> To unsubscribe, visit link above, or email
>> ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".
More information about the ffmpeg-devel
mailing list